com.fluidops.iwb.api.APIImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.fluidops.iwb.api.APIImpl.java

Source

/*
 * Copyright (C) 2008-2013, fluid Operations AG
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
    
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
    
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package com.fluidops.iwb.api;

import static com.fluidops.iwb.api.solution.InstallationResult.InstallationStatus.INSTALLED_SUCCESSFULLY_RESTART_REQUIRED;
import static com.fluidops.iwb.util.IWBFileUtil.getApplicationFolder;
import static com.fluidops.iwb.util.IWBFileUtil.getConfigFolder;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.model.impl.ValueFactoryImpl;
import org.openrdf.query.QueryResults;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFWriter;
import org.openrdf.rio.Rio;
import org.tanukisoftware.wrapper.WrapperManager;

import com.fluidops.api.Doc;
import com.fluidops.api.dynamic.DynamicServiceImpl;
import com.fluidops.config.ConfigDoc;
import com.fluidops.iwb.Global;
import com.fluidops.iwb.Version;
import com.fluidops.iwb.annotation.CallableFromWidget;
import com.fluidops.iwb.api.Context.ContextLabel;
import com.fluidops.iwb.api.Context.ContextType;
import com.fluidops.iwb.api.datacatalog.DataCatalogService;
import com.fluidops.iwb.api.datacatalog.impl.DataCatalogServiceImpl;
import com.fluidops.iwb.api.solution.ChainBasedSolutionService;
import com.fluidops.iwb.api.solution.CompositeSolutionHandler;
import com.fluidops.iwb.api.solution.ConfigPropHandler;
import com.fluidops.iwb.api.solution.CopyFolderStructureHandler;
import com.fluidops.iwb.api.solution.DBBootstrapHandler;
import com.fluidops.iwb.api.solution.DirReferenceBasedSolutionService;
import com.fluidops.iwb.api.solution.ExternalSolutionService;
import com.fluidops.iwb.api.solution.GroovyConfigHandler;
import com.fluidops.iwb.api.solution.GroovyHandler;
import com.fluidops.iwb.api.solution.ImagesPropHandler;
import com.fluidops.iwb.api.solution.JarHandler;
import com.fluidops.iwb.api.solution.JettyHandler;
import com.fluidops.iwb.api.solution.NamespaceHandler;
import com.fluidops.iwb.api.solution.OntologyUpdateHandler;
import com.fluidops.iwb.api.solution.ProviderClassHandler;
import com.fluidops.iwb.api.solution.ProviderHandler;
import com.fluidops.iwb.api.solution.SolutionService;
import com.fluidops.iwb.api.solution.WidgetClassHandler;
import com.fluidops.iwb.api.solution.WidgetHandler;
import com.fluidops.iwb.api.solution.WikiBootstrapHandler;
import com.fluidops.iwb.api.solution.WikiBotPropHandler;
import com.fluidops.iwb.api.solution.ZipFileBasedSolutionService;
import com.fluidops.iwb.api.solution.InstallationResult.InstallationStatus;
import com.fluidops.iwb.api.wiki.CliRunner;
import com.fluidops.iwb.api.wiki.ReaderFlag;
import com.fluidops.iwb.api.wiki.SemanticLinkExtractor;
import com.fluidops.iwb.api.wiki.WikiIndexer;
import com.fluidops.iwb.api.wiki.XMLWikiReader;
import com.fluidops.iwb.cms.Collector;
import com.fluidops.iwb.cms.util.IWBCmsUtil;
import com.fluidops.iwb.install.PropertyMergerImpl;
import com.fluidops.iwb.keywordsearch.KeywordIndexAPI;
import com.fluidops.iwb.model.Vocabulary;
import com.fluidops.iwb.page.PageContext;
import com.fluidops.iwb.provider.TableProvider.Table;
import com.fluidops.iwb.service.OwlimIndexService;
import com.fluidops.iwb.service.SparqlUpdate;
import com.fluidops.iwb.user.UserManager;
import com.fluidops.iwb.user.UserManagerImpl;
import com.fluidops.iwb.util.Config;
import com.fluidops.iwb.wiki.Wikimedia;
import com.fluidops.util.Pair;
import com.fluidops.util.Singleton;
import com.fluidops.util.Singleton.UpdatableSingleton;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;

/**
 * API server implementation
 * 
 * @author aeb
 */
public class APIImpl extends DynamicServiceImpl implements API, InternalAPI {
    private static final Logger logger = Logger.getLogger(APIImpl.class.getName());

    private static final ValueFactoryImpl valueFactory = ValueFactoryImpl.getInstance();

    public void reloadConfig() throws Exception {
        com.fluidops.config.Config.reloadConfig();
    }

    public List<List<String>> getConfigOptions() throws Exception {
        List<List<String>> res = new ArrayList<List<String>>();
        for (Method m : Config.class.getMethods()) {
            ConfigDoc cfd = m.getAnnotation(ConfigDoc.class);
            if (cfd != null) {
                List<String> item = new ArrayList<String>();
                item.add(cfd.category().toString());
                item.add(cfd.name());
                item.add(cfd.desc());
                item.add(cfd.type().toString());
                res.add(item);
            }
        }
        return res;
    }

    public Object getConfigOption(String key) throws Exception {
        Object val = null;
        for (Method m : Config.class.getMethods()) {
            ConfigDoc cfd = m.getAnnotation(ConfigDoc.class);
            if (cfd != null)
                if (cfd.name().equals(key))
                    val = m.invoke(Config.getConfig());
        }
        return val;
    }

    public void setConfigOption(String key, String value) throws Exception {
        Method found = null;
        // fbase Config
        for (Method m : Config.class.getMethods()) {
            ConfigDoc cfd = m.getAnnotation(ConfigDoc.class);
            if (cfd != null)
                if (cfd.name().equals(key))
                    found = m;
        }

        // check if prop exists
        if (found == null)
            throw new Exception("Config property does not exist: " + key);

        // check type
        if (found.getReturnType().equals(boolean.class))
            Boolean.valueOf(value);
        else if (found.getReturnType().equals(int.class))
            new Integer(value);
        else if (found.getReturnType().equals(long.class))
            new Long(value);
        else if (found.getReturnType().equals(byte.class))
            new Byte(value);
        else if (found.getReturnType().equals(short.class))
            new Short(value);
        else if (found.getReturnType().equals(float.class))
            new Float(value);
        else if (found.getReturnType().equals(double.class))
            new Double(value);
        else if (found.getReturnType().equals(char.class))
            Character.valueOf(value.charAt(0));
        else if (found.getReturnType().equals(Character.class))
            Character.valueOf(value.charAt(0));
        else
            found.getReturnType().getConstructor(String.class).newInstance(value);

        com.fluidops.config.Config.getConfig().set(key, value);
    }

    @Override
    public Table sparqlSelect(String query) throws RemoteException, Exception {
        return getDataManager().sparqlSelectAsTable(query);
    }

    @Override
    public List<Statement> sparqlConstruct(String query) throws RemoteException, Exception {
        return QueryResults.asList(getDataManager().sparqlConstruct(query, true));
    }

    @Override
    public void sparqlUpdate(String query) throws RemoteException, Exception {
        SparqlUpdate.executeUpdate(query);
    }

    @Override
    public void deleteFromSource(String source) throws RemoteException {
        ReadWriteDataManager dm = null;

        URI sourceURI = null;
        if (source != null) {
            sourceURI = getNamespaceService().parseURI(source);
            if (sourceURI == null)
                throw new RemoteException("Parsing of source URI failed.");
        }
        dm = ReadWriteDataManagerImpl.openDataManager(Global.repository);
        dm.deleteExpiredContextsOfSource(sourceURI, null, null, null);
        dm.close();
    }

    @Override
    public void deleteFromGroup(String group) throws RemoteException {
        ReadWriteDataManager dm = null;

        URI groupURI = null;
        if (group != null) {
            groupURI = getNamespaceService().parseURI(group);
            if (groupURI == null)
                throw new RemoteException("Parsing of group URI failed.");
        }
        dm = ReadWriteDataManagerImpl.openDataManager(Global.repository);
        dm.deleteExpiredContextsOfGroup(groupURI, null, null);
        dm.close();
    }

    @Override
    public String cleanupMetaGarbage() {
        ReadWriteDataManager dm = ReadWriteDataManagerImpl.openDataManager(Global.repository);
        return dm.cleanupMetaGarbage();
    }

    @Override
    public String upload(String filename, byte[] attachment) throws RemoteException, IOException {
        com.fluidops.iwb.cms.File file = IWBCmsUtil.upload(filename, null, new ByteArrayInputStream(attachment));
        return file.getName();
    }

    @Override
    public void uploadUrls(List<String> documentUrls, Collector collector) throws IOException {
        for (String urlString : documentUrls) {
            URL url = new URL(urlString);
            IWBCmsUtil.upload(url.getPath(), valueFactory.createURI(url.toString()), url.openStream(), collector);
        }
    }

    @Override
    public String load(String filename, String format, String context, String source, String group,
            Boolean userEditable) throws Exception {
        return load(filename, format, context, source, group, ContextType.CLI, userEditable);
    }

    public String load(String filename, String format, String context, String source, String group,
            ContextType contextType, Boolean userEditable) throws Exception {
        NamespaceService ns = EndpointImpl.api().getNamespaceService();

        File file = new File(filename);

        URI contextURI = null;
        URI sourceURI = null;
        URI groupURI = null;

        if (context != null) {
            contextURI = ns.parseURI(context);
            if (contextURI == null)
                return "Not a valid context URI";
        }

        if (source != null) {
            sourceURI = ns.parseURI(source);
            if (sourceURI == null)
                return "Not a valid source URI";
        }

        if (group != null) {
            groupURI = ns.parseURI(group);
            if (groupURI == null)
                return "Not a valid group URI";
        } else {
            groupURI = null;
        }

        RDFFormat rdfFormat = ReadDataManagerImpl.parseRdfFormat(format);
        if (format == null)
            rdfFormat = RDFFormat.forFileName(filename, rdfFormat);
        long start = System.currentTimeMillis();

        if (userEditable == null)
            userEditable = Config.getConfig().getContextsEditableDefault();

        ReadWriteDataManager dm = ReadWriteDataManagerImpl.openDataManager(Global.repository);
        FileInputStream in = new FileInputStream(file);
        try {
            long before = dm.size();
            long after = 0;

            dm.importRDFfromInputStream(in, file.toURI().toString(), rdfFormat, Context.getFreshPublishedContext(
                    contextType, contextURI, sourceURI, groupURI, null, userEditable, ContextLabel.RDF_IMPORT));

            after = dm.size();

            long finish = System.currentTimeMillis();
            return "Loaded " + (after - before) + " triples (including context information) in "
                    + (finish - start) / 1000 + " seconds";
        } finally {
            IOUtils.closeQuietly(in);
            ReadWriteDataManagerImpl.closeQuietly(dm);
        }
    }

    @Override
    public void loadDatasetsFromDir(String dir, String format, String context, String source, String group,
            Boolean userEditable) throws Exception {

        File file = new File(dir);
        if (!file.exists() || !file.isDirectory())
            return;

        logger.info("Loading all files from directory: " + file.getAbsolutePath());
        for (File f : file.listFiles()) {
            // for RDFFormat use heuristics from enum class, 
            // e.g. ".rdf" and ".owl" result in RDFXML
            load(f.getAbsolutePath(), format, context, source, group, userEditable);
            logger.info("Loaded file " + f.getAbsolutePath() + " into repository, context=" + context);
        }

    }

    @Override
    public String setContextsOfSourceEditable(String source, Boolean editable) throws RemoteException, Exception {
        ReadWriteDataManager cm = ReadWriteDataManagerImpl.openDataManager(Global.repository);

        URI u = EndpointImpl.api().getNamespaceService().parseURI(source);
        if (u == null)
            return "Source is not a valid URI";

        List<Context> contexts = cm.getContextsForSource(u);
        boolean e = editable != null && editable;
        for (int i = 0; i < contexts.size(); i++) {
            Context c = contexts.get(i);
            cm.setContextEditable(c, e);
        }

        String es = e ? "editable" : "non-editable";
        return "Set '" + contexts.size() + "' context(s) " + es + ".";
    }

    @Override
    public String setContextsOfGroupEditable(String group, Boolean editable) throws RemoteException, Exception {
        ReadWriteDataManager cm = ReadWriteDataManagerImpl.openDataManager(Global.repository);

        URI u = EndpointImpl.api().getNamespaceService().parseURI(group);
        if (u == null)
            return "Group is not a valid URI";

        List<Context> contexts = cm.getContextsForGroup(u);
        boolean e = editable != null && editable;
        for (int i = 0; i < contexts.size(); i++) {
            Context c = contexts.get(i);
            cm.setContextEditable(c, e);
        }

        String es = e ? "editable" : "non-editable";
        return "Set '" + contexts.size() + "' context(s) " + es + ".";
    }

    @Override
    public String export(String filename, String format, String context) throws Exception {
        Repository repository = Global.repository;

        RDFFormat rdfFormat = RDFFormat.NTRIPLES; //default is NTRIPLES
        if (format != null) {
            rdfFormat = ReadDataManagerImpl.parseRdfFormat(format);
            if (rdfFormat == null)
                return "Not a valid RDF format";
        }
        long start = System.currentTimeMillis();
        RepositoryConnection con = repository.getConnection();
        long size;

        URI contextURI = null;
        if (context != null) {
            NamespaceService ns = EndpointImpl.api().getNamespaceService();
            contextURI = ns.parseURI(context);
            if (contextURI == null)
                return "Not a valid context";
        }

        FileOutputStream out = new FileOutputStream(filename);
        RDFWriter writer = Rio.createWriter(rdfFormat, out);
        if (contextURI != null) {
            size = con.size(contextURI);
            con.export(writer, contextURI);
        }

        else {
            size = con.size();
            con.export(writer);
        }
        out.close();
        long finish = System.currentTimeMillis();
        return "Exported " + size + " triples in " + (finish - start) / 1000 + " seconds";
    }

    @Override
    public void createGraphindex() throws RemoteException, Exception {
        try {
            Class<?> cls = Class.forName("com.fluidops.iwb.ext.util.InitializerUtil");
            Method meth = cls.getMethod("redirectGraphindexDirectory");

            meth.invoke(cls);
        } catch (Exception e) {
            logger.warn("not supported", e);
        }

        //      InitializerUtil.redirectGraphindexDirectory();
    }

    @Override
    public void updateKeywordIndex() throws Exception {
        KeywordIndexAPI.updateKeywordIndex();
    }

    @Override
    public void updateWikiIndex() throws Exception {
        KeywordIndexAPI.updateWikiIndex();
    }

    public void updateOwlimIndices(String repositoryName, String repositoryServer)
            throws RemoteException, Exception {
        OwlimIndexService.Config cfg = new OwlimIndexService.Config();
        cfg.repositoryName = repositoryName;
        cfg.repositoryServer = repositoryServer;
        new OwlimIndexService().run(cfg);
    }

    @Override
    public void extractSemanticLinks() throws Exception {
        SemanticLinkExtractor.extractSemanticLinks();
    }

    @Override
    public void importMediaWiki(String filename, ReaderFlag option, final String namespace, Integer queuesize,
            Integer flags) throws Exception {
        queuesize = (queuesize == null) ? Integer.valueOf(0) : queuesize;
        flags = (flags == null) ? Integer.valueOf(0) : flags;
        XMLWikiReader wikiReader = new XMLWikiReader(filename, namespace, queuesize, flags);
        new WikiIndexer(wikiReader);

        wikiReader.parse();

        String read;
        do {
            logger.info("Hit enter to abort algorithm");
            CliRunner.input.next();
            logger.warn("Please type yes [y] if you want to abort:");
            read = CliRunner.input.next();
        } while (read.isEmpty() || read.charAt(0) != 'y');
        wikiReader.abort();
    }

    @Override
    public String version() throws RemoteException {
        return Version.getProductLongName() + " " + Version.getVersion();
    }

    @Override
    public Object[] rename(String olduri, String newuri, Boolean whatif) throws RemoteException {

        Set<Resource> affectedContexts = new HashSet<Resource>();
        ValueFactory f = ValueFactoryImpl.getInstance();
        NamespaceService ns = EndpointImpl.api().getNamespaceService();
        URI oldURI = ns.parseURI(olduri);
        URI newURI = ns.parseURI(newuri);

        try {
            RepositoryConnection con = Global.repository.getConnection();

            RepositoryResult<Statement> stmts = con.getStatements(oldURI, null, null, false);
            while (stmts.hasNext()) {
                Statement s = stmts.next();
                affectedContexts.add(s.getContext());
                if (!whatif) {
                    con.remove(s);
                    con.add(f.createStatement(newURI, s.getPredicate(), s.getObject()), s.getContext());
                }
            }

            stmts = con.getStatements(null, oldURI, null, false);
            while (stmts.hasNext()) {
                Statement s = stmts.next();
                affectedContexts.add(s.getContext());
                if (!whatif) {
                    con.remove(s);
                    con.add(f.createStatement(s.getSubject(), newURI, s.getObject()), s.getContext());
                }
            }

            stmts = con.getStatements(null, null, oldURI, false);
            while (stmts.hasNext()) {
                Statement s = stmts.next();
                affectedContexts.add(s.getContext());
                if (!whatif) {
                    con.remove(s);
                    con.add(f.createStatement(s.getSubject(), s.getPredicate(), newURI), s.getContext());
                }
            }
            con.close();
        } catch (RepositoryException e) {
            logger.error(e.getMessage(), e);
        }

        return affectedContexts.toArray();

    }

    private Singleton<ProviderService> providerService = new Singleton<ProviderService>() {
        protected ProviderService createInstance() throws Exception {
            return new ProviderServiceImpl();
        }
    };

    public ProviderService getProviderService() throws RemoteException {
        return providerService.instance();
    }

    /**
     * The communication service is parametrized along group and ontology
     * context. There is one context for each group+ontology-context pair.
     */
    protected ConcurrentMap<Pair<URI, URI>, CommunicationService> communicationServices = new ConcurrentHashMap<Pair<URI, URI>, CommunicationService>(
            16, 0.75f, 1);

    private Singleton<WidgetService> widgetService = new Singleton<WidgetService>() {
        protected WidgetService createInstance() throws Exception {
            return new WidgetServiceImpl();
        }
    };

    public WidgetService getWidgetService() {
        return widgetService.instance();
    }

    protected DataCatalogService dataCatalogService;

    @Override
    public DataCatalogService getDataCatalogService() {
        if (dataCatalogService == null)
            dataCatalogService = new DataCatalogServiceImpl();
        return dataCatalogService;
    }

    public BackupService getBackupService() {
        throw new RuntimeException("Backup Service not available in IWB Community Edition");
    }

    @Doc("Returns the current user name")
    public String getUserName() throws Exception {
        return getUserManager().getUser(null);
    }

    @Doc("Returns the current user's URI (null-safe)")
    public URI getUserURI() {
        try {
            URI userUri = getUserManager().getUserURI(null);
            return userUri == null ? Vocabulary.SYSTEM.UNKNOWNUSER : userUri;
        } catch (Exception e) {
            return Vocabulary.SYSTEM.UNKNOWNUSER;
        }
    }

    private Singleton<ExternalNamespaceService> externalNamespaceService = new Singleton<ExternalNamespaceService>() {
        protected ExternalNamespaceService createInstance() throws Exception {
            return new ExternalNamespaceServiceImpl();
        }
    };

    public ExternalNamespaceService getExternalNamespaceService() {
        return externalNamespaceService.instance();
    }

    private UpdatableSingleton<NamespaceService> namespaceService = new UpdatableSingleton<NamespaceService>() {
        protected NamespaceService createInstance() throws Exception {
            return new NamespaceServiceImpl();
        }
    };

    @Override
    public NamespaceService getNamespaceService() {
        return namespaceService.instance();
    }

    public void resetNamespaceService() {
        namespaceService.reset();
    }

    private Singleton<Layouter> layouter = new Singleton<Layouter>() {
        protected Layouter createInstance() throws Exception {
            return new LayouterImpl();
        }
    };

    @Override
    public Layouter getLayouter() {
        return layouter.instance();
    }

    private Singleton<WidgetSelector> widgetSelector = new Singleton<WidgetSelector>() {
        protected WidgetSelector createInstance() throws Exception {
            try {
                return (WidgetSelector) Class.forName(Config.getConfig().getWidgetSelector()).newInstance();
            } catch (Exception e) {
                logger.error("Error initializing widget selector: " + e.getMessage(), e);
                throw e;
            }
        }
    };

    private Singleton<RequestMapper> requestMapper = new Singleton<RequestMapper>() {
        protected RequestMapper createInstance() throws Exception {
            return new RequestMapperImpl();
        }
    };

    private Singleton<UserManager> userManager = new Singleton<UserManager>() {
        protected UserManager createInstance() throws Exception {
            return new UserManagerImpl();
        }
    };

    private Singleton<Printer> printer = new Singleton<Printer>() {
        protected Printer createInstance() throws Exception {
            return new PrinterImpl();
        }
    };

    @Override
    public RequestMapper getRequestMapper() {
        return requestMapper.instance();
    }

    @Override
    public UserManager getUserManager() {
        return userManager.instance();
    }

    @Override
    public WidgetSelector getWidgetSelector() {
        return widgetSelector.instance();
    }

    @Override
    public ReadDataManagerImpl getDataManager() {
        return ReadDataManagerImpl.getDataManager(Global.repository);
    }

    @Override
    public Printer getPrinter() {
        return printer.instance();
    }

    @Override
    public void invalidateAllCaches() {
        CacheManager cm = CacheManager.getInstance();
        cm.invalidateAllCaches();
    }

    @CallableFromWidget
    public static String refreshNamespaces(PageContext p, String param) {
        EndpointImpl.api().resetNamespaceService();
        return "Changes deployed successfully!";
    }

    @Override
    public Collection<CommunicationService> getAllCommunicationServices() {
        return communicationServices.values();
    }

    @Override
    public CommunicationService getCommunicationService(String group, String ontologyContext)
            throws RemoteException {
        URI groupUri = EndpointImpl.api().getNamespaceService().guessURI(group);
        URI ontologyContextUri = EndpointImpl.api().getNamespaceService().guessURI(ontologyContext);

        return getCommunicationService(groupUri, ontologyContextUri);
    }

    /**
     * Help method for CommunicationService setup
     * 
     * @param groupUri
     * @param ontologyContextUri
     * @return
     * @throws RemoteException
     */
    public CommunicationService getCommunicationService(URI groupUri, URI ontologyContextUri)
            throws RemoteException {
        if (groupUri == null)
            throw new RuntimeException("groupUri in CommunicationService initialization is null");
        if (ontologyContextUri == null)
            throw new RuntimeException("ontologyContext in CommunicationService initialization is null");

        Pair<URI, URI> csId = new Pair<URI, URI>(groupUri, ontologyContextUri);
        CommunicationService cs = communicationServices.get(csId);
        if (cs == null) {
            try {
                cs = new CommunicationServiceImpl(groupUri, ontologyContextUri);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
            communicationServices.put(csId, cs);
        }

        return cs;
    }

    protected Singleton<MonitoringService> monitoringService = new Singleton<MonitoringService>() {
        protected MonitoringService createInstance() throws Exception {
            return createMonitoringService();
        }
    };

    /**
     * Subclasses can overwrite this, i.e. EE can return advanced
     * monitoring service
     * @return
     */
    protected MonitoringService createMonitoringService() {
        return new MonitoringServiceImpl();
    }

    @Override
    public MonitoringService getMonitoringService() throws RemoteException {
        return monitoringService.instance();
    }

    @Override
    public String add(Statement statement) throws RemoteException {

        ReadWriteDataManager dm = null;
        try {
            Context context = Context.getFreshUserContext(ContextLabel.RDF_IMPORT);
            dm = ReadWriteDataManagerImpl.openDataManager(Global.repository);
            dm.addToContext(statement, context);
            return context.getURI().stringValue();
        } finally {
            ReadWriteDataManagerImpl.closeQuietly(dm);
        }
    }

    @Override
    public Set<String> delete(Statement statement) throws RemoteException {
        ReadWriteDataManager dm = null;
        try {
            Context changelog = Context.getFreshUserContext(ContextLabel.CHANGELOG);
            dm = ReadWriteDataManagerImpl.openDataManager(Global.repository);
            Set<URI> modifiedContexts = dm.removeInEditableContexts(statement, changelog);
            dm.cleanupMetaGarbage(modifiedContexts);
            return Sets.newHashSet(Iterables.transform(modifiedContexts, new Function<URI, String>() {
                @Override
                public String apply(URI u) {
                    return u.stringValue();
                }
            }));
        } finally {
            ReadWriteDataManagerImpl.closeQuietly(dm);
        }
    }

    @Override
    public void restartService() throws Exception {
        logger.info("Service RESTART requested by user " + EndpointImpl.api().getUserManager().getUserName(null));
        if (WrapperManager.isControlledByNativeWrapper()) {
            restartRequested = true;
            WrapperManager.restartAndReturn();
        } else
            logger.info("Service RESTART not possible, as jvm is not controlled by native wrapper");
    }

    /**
     * the restart state is remembered to avoid loading the DB after a service
     * restart has been requested.
     */
    private boolean restartRequested = false;

    /**
     * Indicates whether a restart of the service has been triggered. This is only
     * the case if the restart will be actually performed, i.e. when the service
     * is being run under the supervision of the wrapper.
     * 
     * @return <code>true</code> if a restart of the service has been triggered,
     *          <code>false</code> otherwise
     */
    public boolean isRestartReqeuested() {
        return restartRequested;
    }

    @Override
    public void stopService(Integer code) throws Exception {
        if (code == null)
            code = 0;

        logger.info("Service STOP with code " + code + " requested by user "
                + EndpointImpl.api().getUserManager().getUserName(null));
        WrapperManager.stopAndReturn(code);
    }

    private Singleton<SolutionService> solutionService = new Singleton<SolutionService>() {
        protected SolutionService createInstance() throws Exception {
            File configPropFile = new File(Config.getConfig().getSourcePath());
            DBBulkServiceImpl dbBulkService = new DBBulkServiceImpl(new Supplier<Repository>() {
                @Override
                public Repository get() {
                    return Global.repository;
                }
            });
            CompositeSolutionHandler handler = new CompositeSolutionHandler(
                    new GroovyHandler("pre-install", getApplicationFolder()))
                            .add(new ConfigPropHandler(new PropertyMergerImpl(), configPropFile.getParentFile(),
                                    configPropFile.getName()))
                            .add(new GroovyConfigHandler("config.groovy", getApplicationFolder(),
                                    configPropFile.getName()))
                            .add(new ImagesPropHandler(new PropertyMergerImpl(), getConfigFolder().getParentFile()))
                            .add(new WikiBotPropHandler(new PropertyMergerImpl(),
                                    getConfigFolder().getParentFile()))
                            .add(new JarHandler(getApplicationFolder()))
                            .add(new CopyFolderStructureHandler(getApplicationFolder(), "userlib",
                                    InstallationStatus.INSTALLED_SUCCESSFULLY_RESTART_REQUIRED))
                            .add(new CopyFolderStructureHandler(getApplicationFolder(), "scripts"))
                            .add(new CopyFolderStructureHandler(getApplicationFolder(), "assets",
                                    INSTALLED_SUCCESSFULLY_RESTART_REQUIRED))
                            .add(new CopyFolderStructureHandler(getApplicationFolder(), "webapps"))
                            .add(new CopyFolderStructureHandler(getConfigFolder().getParentFile(), "config/acl",
                                    INSTALLED_SUCCESSFULLY_RESTART_REQUIRED))
                            .add(new CopyFolderStructureHandler(getConfigFolder().getParentFile(),
                                    "config/annotations", INSTALLED_SUCCESSFULLY_RESTART_REQUIRED))
                            .add(new CopyFolderStructureHandler(getConfigFolder().getParentFile(),
                                    "config/repositories", INSTALLED_SUCCESSFULLY_RESTART_REQUIRED))
                            .add(new JettyHandler(getApplicationFolder()))
                            .add(new WidgetHandler(getWidgetSelector()))
                            .add(new WidgetClassHandler(getWidgetService()))
                            .add(new NamespaceHandler(getNamespaceService()))
                            .add(new ProviderHandler(getProviderService()))
                            .add(new ProviderClassHandler(getProviderService()))
                            .add(new DBBootstrapHandler(dbBulkService))
                            .add(new OntologyUpdateHandler(dbBulkService))
                            .add(new WikiBootstrapHandler(
                                    new WikiStorageBulkServiceImpl(Wikimedia.getWikiStorage())))
                            .add(new CopyFolderStructureHandler(getApplicationFolder(), "", "backend-*.conf",
                                    InstallationStatus.INSTALLED_SUCCESSFULLY_RESTART_REQUIRED))
                            .add(new GroovyHandler("post-install", getApplicationFolder()));

            return new ChainBasedSolutionService(new ZipFileBasedSolutionService(getApplicationFolder(), handler),
                    new DirReferenceBasedSolutionService(getApplicationFolder(), handler));
        }
    };

    @Override
    public ExternalSolutionService getSolutionService() throws RemoteException {
        return solutionService.instance();
    }

    @Override
    public OntologyManager getOntologyManager() {
        return OntologyManagerImpl.getOntologyManager();
    }
}