uk.ac.open.kmi.iserve.sal.manager.impl.RegistryManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.open.kmi.iserve.sal.manager.impl.RegistryManagerImpl.java

Source

/*
 * Copyright (c) 2014. Knowledge Media Institute - The Open University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package uk.ac.open.kmi.iserve.sal.manager.impl;

import com.google.common.collect.ImmutableSet;
import com.google.common.eventbus.EventBus;
import com.google.inject.Singleton;
import org.apache.commons.configuration.ConfigurationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.open.kmi.iserve.core.ConfigurationProperty;
import uk.ac.open.kmi.iserve.core.iServeProperty;
import uk.ac.open.kmi.iserve.sal.exception.SalException;
import uk.ac.open.kmi.iserve.sal.exception.ServiceException;
import uk.ac.open.kmi.iserve.sal.manager.*;
import uk.ac.open.kmi.iserve.sal.util.UriUtil;
import uk.ac.open.kmi.msm4j.Operation;
import uk.ac.open.kmi.msm4j.Service;
import uk.ac.open.kmi.msm4j.io.MediaType;
import uk.ac.open.kmi.msm4j.io.ServiceReader;
import uk.ac.open.kmi.msm4j.io.Syntax;
import uk.ac.open.kmi.msm4j.io.TransformationException;
import uk.ac.open.kmi.msm4j.io.impl.ServiceReaderImpl;
import uk.ac.open.kmi.msm4j.io.impl.ServiceTransformationEngine;
import uk.ac.open.kmi.msm4j.io.util.FilenameFilterBySyntax;

import javax.inject.Inject;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * iServe Manager implementation providing a facade to the entire Storage and Access Layer
 * Delegates the actual implementation to each of the specific managers configured.
 * This class implements the Singleton pattern and takes care of all dependency injection transparently for the user.
 *
 * @author Carlos Pedrinaci (Knowledge Media Institute - The Open University)
 */
@Singleton
public class RegistryManagerImpl extends IntegratedComponent implements RegistryManager {

    private static final Logger log = LoggerFactory.getLogger(RegistryManagerImpl.class);

    private final DocumentManager docManager;
    private final ServiceManager serviceManager;
    private final KnowledgeBaseManager kbManager;
    private final ServiceTransformationEngine serviceTransformationEngine;

    @Inject
    private RegistryManagerImpl(EventBus eventBus,
            @iServeProperty(ConfigurationProperty.ISERVE_URL) String iServeUri, DocumentManager docManager,
            ServiceManager serviceManager, KnowledgeBaseManager kbManager,
            ServiceTransformationEngine serviceTransformationEngine) throws ConfigurationException, SalException {

        super(eventBus, iServeUri);
        this.docManager = docManager;
        this.serviceManager = serviceManager;
        this.kbManager = kbManager;
        this.serviceTransformationEngine = serviceTransformationEngine;

    }

    /**
     * Register the given object as an Observer for this iServe instance.
     * All events created within iServe would be notified. Currently based on Guava's EventBus.
     * In order to process them, the corresponding observer should only implement a method that takes as sole parameter
     * the event wanted to process. This method should additionally be annotated with {@code @Subscribe} for the system
     * to work.
     *
     * @param obj The observer
     */
    @Override
    public void registerAsObserver(Object obj) {
        this.getEventBus().register(obj);
    }

    /**
     * Unregister observer. Events won't be notified to this observer any longer.
     *
     * @param obj The observer
     */
    @Override
    public void unregisterObserver(Object obj) {
        this.getEventBus().unregister(obj);
    }

    /**
     * Obtains the Service Manager for this instance of iServe
     *
     * @return the Service Manager
     */
    @Override
    public ServiceManager getServiceManager() {
        return this.serviceManager;
    }

    /**
     * Obtains the Knowledge Base Manager for this instance of iServe
     *
     * @return the Knowledge Base Manager
     */
    @Override
    public KnowledgeBaseManager getKnowledgeBaseManager() {
        return this.kbManager;
    }

    /**
     * Obtains the Document Manager for this instance of iServe
     *
     * @return the Document Manager
     */
    @Override
    public DocumentManager getDocumentManager() {
        return this.docManager;
    }

    /**
     * Obtains the Service Transformer for this instance
     *
     * @return the transformer
     */
    @Override
    public ServiceTransformationEngine getServiceTransformationEngine() {
        return this.serviceTransformationEngine;
    }

    /**
     * This method will be called when the server is being shutdown.
     * Ensure a clean shutdown.
     */
    @Override
    public void shutdown() {
        this.serviceManager.shutdown();
        this.docManager.shutdown();
        this.kbManager.shutdown();
    }

    /**
     * Obtains the list of supported media types the engine can import
     *
     * @return the Set of media types
     */
    @Override
    public Set<String> getSupportedInputMediaTypes() {
        ImmutableSet.Builder<String> result = ImmutableSet.builder();
        result.addAll(MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.keySet());
        result.addAll(this.serviceTransformationEngine.getSupportedMediaTypes());
        return result.build();
    }

    /**
     * Checks if the given media type can be imported
     *
     * @param mediaType the media type we wish to check if it can be imported
     * @return true if it is supported or false otherwise.
     */
    @Override
    public boolean canImport(String mediaType) {
        return (MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.containsKey(mediaType)
                || this.serviceTransformationEngine.canTransform(mediaType));
    }

    /**
     * Gets the corresponding filename filter to a given media type by checking both native formats
     * and those supported through transformation
     *
     * @param mediaType the media type for which to obtain the file extension
     * @return the filename filter or null if it is not supported. Callers are advised to check
     * first that the media type is supported @see canTransform .
     */
    @Override
    public FilenameFilter getFilenameFilter(String mediaType) {
        if (mediaType == null) {
            return null;
        }

        if (MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.containsKey(mediaType)) {
            return new FilenameFilterBySyntax(MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.get(mediaType));
        } else if (this.getServiceTransformationEngine().canTransform(mediaType)) {
            return this.getServiceTransformationEngine().getFilenameFilter(mediaType);
        }

        return null;
    }

    /**
     * Imports a new service within iServe. The original document is stored
     * in the server and the transformed version registered within iServe.
     *
     * @param servicesContentStream
     * @param mediaType
     * @return the List of URIs of the services imported
     * @throws SalException
     */
    @Override
    public List<URI> importServices(InputStream servicesContentStream, String mediaType) throws SalException {

        boolean isNativeFormat = MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.containsKey(mediaType);
        // Throw error if Format Unsupported
        if (!isNativeFormat && !this.serviceTransformationEngine.canTransform(mediaType)) {
            log.error("The media type {} is not natively supported and has no suitable transformer.", mediaType);
            throw new ServiceException("Unable to import service. Format unsupported.");
        }

        // Obtain the file extension to use
        String fileExtension = findFileExtensionToUse(mediaType, isNativeFormat);

        List<Service> services = null;
        List<URI> importedServices = new ArrayList<URI>();
        URI sourceDocUri = null;
        InputStream localStream = null;
        try {
            // 1st Store the document
            sourceDocUri = this.docManager.createDocument(servicesContentStream, fileExtension, mediaType);
            if (sourceDocUri == null) {
                throw new ServiceException("Unable to save service document. Operation aborted.");
            }

            // 2nd Parse and Transform the document
            // The original stream may be a one-of stream so save it first and read locally
            localStream = this.docManager.getDocument(sourceDocUri);
            services = getServicesFromStream(mediaType, isNativeFormat, localStream);

            // 3rd - Store the resulting MSM services. There may be more than one
            URI serviceUri = null;
            if (services != null && !services.isEmpty()) {
                log.info("Importing {} services", services.size());
                for (Service service : services) {
                    // The service is being imported -> update the source
                    service.setSource(sourceDocUri);
                    serviceUri = this.serviceManager.addService(service);
                    if (serviceUri != null) {
                        importedServices.add(serviceUri);
                    }
                }
            }

            // 4th Log it was all done correctly
            // TODO: log to the system and notify observers
            log.info("Source document imported: {}", sourceDocUri.toASCIIString());

        } finally {
            // Rollback if something went wrong
            if ((services == null || (services != null && services.size() != importedServices.size()))
                    && sourceDocUri != null) {
                this.docManager.deleteDocument(sourceDocUri);
                for (URI svcUri : importedServices) {
                    this.serviceManager.deleteService(svcUri);
                }
                log.warn("There were problems importing the service. Changes undone.");
            }

            if (localStream != null)
                try {
                    localStream.close();
                } catch (IOException e) {
                    log.error("Error closing the service content stream", e);
                }
        }

        return importedServices;
    }

    @Override
    public List<URI> importServices(URI servicesContentLocation, String mediaType) throws SalException {
        boolean isNativeFormat = MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.containsKey(mediaType);
        // Throw error if Format Unsupported
        if (!isNativeFormat && !this.serviceTransformationEngine.canTransform(mediaType)) {
            log.error("The media type {} is not natively supported and has no suitable transformer.", mediaType);
            throw new ServiceException("Unable to import service. Format unsupported.");
        }

        // Obtain the file extension to use
        String fileExtension = findFileExtensionToUse(mediaType, isNativeFormat);

        List<Service> services = null;
        List<URI> importedServices = new ArrayList<URI>();
        URI sourceDocUri = null;
        InputStream localStream = null;
        try {
            // 1st Store the document
            sourceDocUri = this.docManager.createDocument(servicesContentLocation, fileExtension, mediaType);
            if (sourceDocUri == null) {
                throw new ServiceException("Unable to save service document. Operation aborted.");
            }

            // 2nd Parse and Transform the document
            // The original stream may be a one-of stream so save it first and read locally
            services = getServicesFromRemoteLocation(mediaType, isNativeFormat, servicesContentLocation);

            // 3rd - Store the resulting MSM services. There may be more than one
            URI serviceUri = null;
            if (services != null && !services.isEmpty()) {
                log.info("Importing {} services", services.size());
                for (Service service : services) {
                    // The service is being imported -> update the source
                    URI originalSource = service.getSource();
                    service.setSource(sourceDocUri);
                    for (Operation op : service.getOperations()) {
                        if (op.getSource().equals(originalSource)) {
                            op.setSource(sourceDocUri);
                        }
                        if (op.getSource().toASCIIString().contains(originalSource.toASCIIString())) {
                            String subPath = op.getSource().toASCIIString().replace(originalSource.toASCIIString(),
                                    "");
                            try {
                                op.setSource(new URI(new StringBuilder(sourceDocUri.toASCIIString()).append(subPath)
                                        .toString()));
                            } catch (URISyntaxException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                    serviceUri = this.serviceManager.addService(service);
                    if (serviceUri != null) {
                        importedServices.add(serviceUri);
                    }
                }
            }

            // 4th Log it was all done correctly
            // TODO: log to the system and notify observers
            log.info("Source document imported: {}", sourceDocUri.toASCIIString());

        } finally {
            // Rollback if something went wrong
            if ((services == null || (services != null && services.size() != importedServices.size()))
                    && sourceDocUri != null) {
                this.docManager.deleteDocument(sourceDocUri);
                for (URI svcUri : importedServices) {
                    this.serviceManager.deleteService(svcUri);
                }
                log.warn("There were problems importing the service. Changes undone.");
            }

            if (localStream != null)
                try {
                    localStream.close();
                } catch (IOException e) {
                    log.error("Error closing the service content stream", e);
                }
        }

        return importedServices;
    }

    private List<Service> getServicesFromRemoteLocation(String mediaType, boolean nativeFormat,
            URI servicesContentLocation) throws ServiceException {
        if (servicesContentLocation == null)
            throw new ServiceException("Unable to parse the saved document. Operation aborted.");

        List<Service> services = null;
        if (nativeFormat) {
            // Parse it directly
            ServiceReader reader = new ServiceReaderImpl();
            Syntax syntax = MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.get(mediaType);
            try {
                services = reader.parse(servicesContentLocation.toURL().openStream(), null, syntax);
            } catch (IOException e) {
                e.printStackTrace();
                throw new ServiceException("Unable to retrieve the document");
            }
        } else {
            // Its an external format: use the appropriate importer
            // We should have a suitable importer
            try {
                services = this.serviceTransformationEngine.transform(servicesContentLocation.toURL().openStream(),
                        servicesContentLocation.toASCIIString(), mediaType);
            } catch (IOException e) {
                e.printStackTrace();
                throw new ServiceException("Unable to retrieve the document");
            } catch (TransformationException e) {
                throw new ServiceException("Errors transforming the service", e);
            }
        }
        log.debug("Services parsed:", services);
        return services;
    }

    private List<Service> getServicesFromStream(String mediaType, boolean nativeFormat, InputStream localStream)
            throws ServiceException {
        if (localStream == null)
            throw new ServiceException("Unable to parse the saved document. Operation aborted.");

        List<Service> services;
        if (nativeFormat) {
            // Parse it directly
            ServiceReader reader = new ServiceReaderImpl();
            Syntax syntax = MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.get(mediaType);
            services = reader.parse(localStream, null, syntax);
        } else {
            // Its an external format: use the appropriate importer
            // We should have a suitable importer
            try {
                services = this.serviceTransformationEngine.transform(localStream, mediaType);
            } catch (TransformationException e) {
                throw new ServiceException("Errors transforming the service", e);
            }
        }
        log.debug("Services parsed:", services);
        return services;
    }

    private String findFileExtensionToUse(String mediaType, boolean nativeFormat) {
        String fileExtension = null;
        if (nativeFormat) {
            fileExtension = MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.get(mediaType).getExtension();
            log.debug("The media type is natively supported. File extension {}", fileExtension);
        } else {
            // should not be null since it is supported
            fileExtension = this.serviceTransformationEngine.getFileExtension(mediaType);
            log.debug("The media type is supported through transformations. File extension {}", fileExtension);
        }
        return fileExtension;
    }

    /**
     * Clears the registry entirely: all documents and services are deleted
     * This operation cannot be undone. Use with care.
     *
     * @return true if the registry was cleared.
     * @throws SalException
     */
    @Override
    public boolean clearRegistry() throws SalException {
        boolean result = this.serviceManager.clearServices();
        // Only try if we could delete the services
        if (result)
            result = result & this.docManager.clearDocuments();

        // Only try if we could delete the services and the docs
        if (result)
            result = result & this.kbManager.clearKnowledgeBase();

        return result;
    }

    /**
     * Registers all the services found on a given source document. This operation takes care of performing the
     * appropriate format transformation. The source document is not stored on the server. For keeping a copy of the
     * source document within the server use @see importServices instead.
     *
     * @param sourceDocumentUri
     * @param mediaType
     * @return the List of URIs of the services registered
     * @throws SalException
     */
    @Override
    public List<URI> registerServices(URI sourceDocumentUri, String mediaType) throws SalException {

        boolean isNativeFormat = MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.containsKey(mediaType);
        // Throw error if Format Unsupported
        if (!isNativeFormat && !this.serviceTransformationEngine.canTransform(mediaType)) {
            log.error("The media type {} is not natively supported and has no suitable transformer.", mediaType);
            throw new ServiceException("Unable to import service. Format unsupported.");
        }

        List<URI> registeredServices = new ArrayList<URI>();

        log.debug("Registering services from document: {}", sourceDocumentUri.toASCIIString());
        // 1st Obtain the document and parse/transform it
        List<Service> services = getServicesFromRemoteLocation(mediaType, isNativeFormat, sourceDocumentUri);

        if (services != null && !services.isEmpty()) {
            log.debug("Services found: {}. Registering ...", services.size());
            URI serviceUri;
            for (Service service : services) {
                // 2nd Add the service
                serviceUri = this.serviceManager.addService(service);
                if (serviceUri != null) {
                    registeredServices.add(serviceUri);
                }
            }
        }

        if (registeredServices.size() != services.size()) {
            log.warn("Found {} services in document {} but only imported {} services", services.size(),
                    sourceDocumentUri, registeredServices.size());
        }

        return registeredServices;
    }

    @Override
    public List<URI> registerServices(InputStream is, String mediaType) throws SalException {
        boolean isNativeFormat = MediaType.NATIVE_MEDIATYPE_SYNTAX_MAP.containsKey(mediaType);
        // Throw error if Format Unsupported
        if (!isNativeFormat && !this.serviceTransformationEngine.canTransform(mediaType)) {
            log.error("The media type {} is not natively supported and has no suitable transformer.", mediaType);
            throw new ServiceException("Unable to import service. Format unsupported.");
        }

        List<URI> registeredServices = new ArrayList<URI>();

        log.debug("Registering services from document");
        // 1st Obtain the document and parse/transform it
        List<Service> services = getServicesFromStream(mediaType, isNativeFormat, is);

        if (services != null && !services.isEmpty()) {
            log.debug("Services found: {}. Registering ...", services.size());
            URI serviceUri;
            for (Service service : services) {
                // 2nd Add the service
                serviceUri = this.serviceManager.addService(service);
                if (serviceUri != null) {
                    registeredServices.add(serviceUri);
                }
            }
        }

        if (registeredServices.size() != services.size()) {
            log.warn("Found {} services in the document but only imported {} services", services.size(),
                    registeredServices.size());
        }

        return registeredServices;
    }

    /**
     * Unregisters a service from the registry. Effectively this will delete the service description and remove any
     * related documents on the server.
     *
     * @param serviceUri the URI of the service to unregister
     * @return true if it was properly unregistered, false otherwise.
     * @throws SalException
     */
    @Override
    public boolean unregisterService(URI serviceUri) throws SalException {

        // Check the URI is correct and belongs to the server
        if (serviceUri == null || !UriUtil.isResourceLocalToServer(serviceUri, this.getIserveUri())) {
            return false;
        }

        if (this.serviceManager.deleteService(serviceUri)) {
            // delete documents
            Set<URI> docs = this.serviceManager.listDocumentsForService(serviceUri);
            for (URI doc : docs) {
                this.docManager.deleteDocument(doc);
            }
            // Some documents may not have been deleted properly. TODO: handle properly this case
            return true;
        }

        return false;
    }

    /**
     * Exports a given service in the media type requested
     *
     * @param serviceUri the URI of the service to export
     * @param mediaType  the media type to use for the export
     * @return the String representation of the service in the given format
     * @throws ServiceException
     */
    @Override
    public String exportService(URI serviceUri, String mediaType) throws ServiceException {
        //      return this.serviceManager.getServiceSerialisation(serviceUri, syntax);
        // TODO
        return null;
    }

}