eu.stratuslab.marketplace.server.MarketPlaceApplication.java Source code

Java tutorial

Introduction

Here is the source code for eu.stratuslab.marketplace.server.MarketPlaceApplication.java

Source

/**
 * Created as part of the StratusLab project (http://stratuslab.eu),
 * co-funded by the European Commission under the Grant Agreement
 * INSFO-RI-261552.
 *
 * Copyright (c) 2011
 *
 * 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 eu.stratuslab.marketplace.server;

import static eu.stratuslab.marketplace.server.cfg.Parameter.DATA_DIR;
import static eu.stratuslab.marketplace.server.cfg.Parameter.ENDORSER_REMINDER;
import static eu.stratuslab.marketplace.server.cfg.Parameter.PENDING_DIR;
import static eu.stratuslab.marketplace.server.cfg.Parameter.STORE_TYPE;
import static eu.stratuslab.marketplace.server.cfg.Parameter.FILESTORE_TYPE;
import static eu.stratuslab.marketplace.server.cfg.Parameter.REPLICATION_ENABLED;

import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import eu.stratuslab.marketplace.server.resources.BaseResource;
import org.json.simple.JSONValue;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.LocalReference;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.data.Status;
import org.restlet.ext.freemarker.ContextTemplateLoader;
import org.restlet.ext.freemarker.TemplateRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.Directory;
import org.restlet.routing.Filter;
import org.restlet.routing.Router;
import org.restlet.routing.Template;
import org.restlet.routing.TemplateRoute;
import org.restlet.service.StatusService;

import eu.stratuslab.marketplace.server.cfg.Configuration;
import eu.stratuslab.marketplace.server.cfg.Parameter;
import eu.stratuslab.marketplace.server.query.QueryBuilder;
import eu.stratuslab.marketplace.server.query.SparqlBuilder;
import eu.stratuslab.marketplace.server.resources.EndorserResource;
import eu.stratuslab.marketplace.server.resources.EndorsersResource;
import eu.stratuslab.marketplace.server.resources.ErrorResource;
import eu.stratuslab.marketplace.server.resources.HomeResource;
import eu.stratuslab.marketplace.server.resources.MDataResource;
import eu.stratuslab.marketplace.server.resources.MDatumResource;
import eu.stratuslab.marketplace.server.resources.QueryResource;
import eu.stratuslab.marketplace.server.resources.TagsResource;
import eu.stratuslab.marketplace.server.resources.UploadResource;
import eu.stratuslab.marketplace.server.resources.SyncResource;
import eu.stratuslab.marketplace.server.routers.ActionRouter;
import eu.stratuslab.marketplace.server.store.file.CouchbaseStore;
import eu.stratuslab.marketplace.server.store.file.FileStore;
import eu.stratuslab.marketplace.server.store.file.FlatFileStore;
import eu.stratuslab.marketplace.server.store.file.GitStore;
import eu.stratuslab.marketplace.server.store.rdf.RdfStore;
import eu.stratuslab.marketplace.server.store.rdf.RdfStoreFactory;
import eu.stratuslab.marketplace.server.store.rdf.RdfStoreFactoryImpl;
import eu.stratuslab.marketplace.server.store.rdf.Processor;
import eu.stratuslab.marketplace.server.store.rdf.RdfStoreUpdater;
import eu.stratuslab.marketplace.server.utils.EndorserWhitelist;
import eu.stratuslab.marketplace.server.utils.MetadataFileUtils;
import eu.stratuslab.marketplace.server.utils.Reminder;

public class MarketPlaceApplication extends Application {

    private static final Logger LOGGER = Logger.getLogger("org.restlet");

    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    private final ScheduledExecutorService indexUpdater = Executors.newScheduledThreadPool(1);

    private ScheduledFuture<?> reminderHandle;
    private ScheduledFuture<?> indexUpdateHandle;

    private static final int REMINDER_INTERVAL = 30;
    private static final int EXPIRY_INTERVAL = 1;

    private Reminder reminder;
    private Reminder expiry;

    private RdfStore store = null;
    private FileStore fileStore = null;
    private QueryBuilder queryBuilder = null;

    private String dataDir = null;

    private freemarker.template.Configuration freeMarkerConfiguration = null;

    private EndorserWhitelist whitelist;

    private RdfStoreUpdater rdfUpdater;

    private boolean invalidConfig = false;

    public MarketPlaceApplication() {
        try {
            String storeType = Configuration.getParameterValue(STORE_TYPE);
            String fileStoreType = Configuration.getParameterValue(FILESTORE_TYPE);
            String dataDir = Configuration.getParameterValue(DATA_DIR);
            init(dataDir, storeType, fileStoreType);
        } catch (ExceptionInInitializerError e) {
            LOGGER.severe("incorrect configuration: " + e.getCause().getMessage());
            invalidConfig = true;
        }
    }

    public MarketPlaceApplication(String dataDir, String storeType, String fileStoreType) {
        init(dataDir, storeType, fileStoreType);
    }

    private void init(String dataDir, String storeType, String fileStoreType) {
        setName("StratusLab Marketplace");
        setDescription("Marketplace for StratusLab images");
        setOwner("StratusLab");
        setAuthor("Stuart Kenny");

        getMetadataService().addExtension("multipart", MediaType.MULTIPART_FORM_DATA, false);
        getMetadataService().addExtension("www_form", MediaType.APPLICATION_WWW_FORM, false);
        getMetadataService().addExtension("application_rdf", MediaType.APPLICATION_RDF_XML, true);
        getMetadataService().addExtension("application_xml", MediaType.APPLICATION_XML, false);

        getMetadataService().setDefaultMediaType(MediaType.APPLICATION_XML);

        setStatusService(new MarketPlaceStatusService());

        getTunnelService().setUserAgentTunnel(true);

        this.dataDir = dataDir;

        boolean success = MetadataFileUtils.createIfNotExists(dataDir);
        if (!success) {
            LOGGER.severe("Unable to create directory: " + dataDir);
        }

        success = MetadataFileUtils.createIfNotExists(Configuration.getParameterValue(PENDING_DIR));
        if (!success) {
            LOGGER.severe("Unable to create directory: " + Configuration.getParameterValue(PENDING_DIR));
        }

        whitelist = new EndorserWhitelist();

        RdfStoreFactory factory = new RdfStoreFactoryImpl();
        store = factory.createRdfStore(RdfStoreFactory.SESAME_PROVIDER, storeType);
        store.initialize();

        FileStore internalStore = null;

        if (fileStoreType.equals("file")) {
            internalStore = new FlatFileStore(dataDir);
        } else if (fileStoreType.equals("couchbase")) {
            internalStore = new CouchbaseStore(dataDir);
        }

        if (Configuration.getParameterValueAsBoolean(REPLICATION_ENABLED)) {
            fileStore = new GitStore(dataDir, internalStore);

            final Runnable update = new Runnable() {
                public void run() {
                    rdfIndexUpdate();
                }
            };

            rdfUpdater = new RdfStoreUpdater(fileStore, new Processor(this));
            indexUpdateHandle = indexUpdater.scheduleWithFixedDelay(update, 1, 5, TimeUnit.MINUTES);
        } else {
            fileStore = internalStore;
        }

        queryBuilder = new SparqlBuilder();

        final Runnable remind = new Runnable() {
            public void run() {
                remind();
            }
        };

        final Runnable expires = new Runnable() {
            public void run() {
                expiry();
            }
        };

        reminder = new Reminder(this);
        expiry = new Reminder(this);

        if (Configuration.getParameterValueAsBoolean(ENDORSER_REMINDER)) {
            reminderHandle = scheduler.scheduleWithFixedDelay(remind, REMINDER_INTERVAL, REMINDER_INTERVAL,
                    TimeUnit.DAYS);
            reminderHandle = scheduler.scheduleWithFixedDelay(expires, EXPIRY_INTERVAL, EXPIRY_INTERVAL,
                    TimeUnit.DAYS);
        }

    }

    /**
     * Creates a root Restlet that will receive all incoming calls.
     */
    @Override
    public Restlet createInboundRoot() {
        Context context = getContext();

        // Create the FreeMarker configuration.
        freeMarkerConfiguration = MarketPlaceApplication.createFreeMarkerConfig(context);

        // Create a router Restlet that defines routes.
        Router router = new Router(context);

        if (invalidConfig) {
            router.attach("/", ErrorResource.class);
        } else {
            TemplateRoute route = router.attach("/metadata/{email}?tag={tag}", MDatumResource.class);
            route.setMatchingQuery(true);

            router.attach("/tag/{email}/{tag}", MDatumResource.class);
            router.attach("/tag/{email}/{tag}/", MDatumResource.class);

            // Defines a route for the resource "list of metadata entries"
            router.attach("/metadata", MDataResource.class);
            router.attach("/metadata/", MDataResource.class);
            router.attach("/metadata/{arg1}", MDataResource.class);
            router.attach("/metadata/{arg1}/", MDataResource.class);
            router.attach("/metadata/{arg1}/{arg2}", MDataResource.class);
            router.attach("/metadata/{arg1}/{arg2}/", MDataResource.class);
            router.attach("/metadata?query={query}", MDataResource.class);

            // Defines a route for the resource "metadatum"
            router.attach("/metadata/{identifier}/{email}/{date}", MDatumResource.class);
            router.attach("/metadata/{identifier}/{email}/{date}/", MDatumResource.class);

            // Defines a route for the resource "endorsers"
            router.attach("/endorsers", EndorsersResource.class);
            router.attach("/endorsers/", EndorsersResource.class);

            // Defines a route for the resource "endorser"
            router.attach("/endorsers/{email}", EndorserResource.class);
            router.attach("/endorsers/{email}/", EndorserResource.class);

            router.attach("/endorsers/{email}/tags", TagsResource.class);
            router.attach("/endorsers/{email}/tags/", TagsResource.class);

            // Defines a route for queries
            router.attach("/query", QueryResource.class);
            router.attach("/query/", QueryResource.class);

            // Defines a route for the upload form
            router.attach("/upload", UploadResource.class);
            router.attach("/upload/", UploadResource.class);

            // Defines a route for the sync resource
            router.attach("/sync", SyncResource.class);
            router.attach("/sync/", SyncResource.class);

            // Defines a router for actions
            route = router.attach("/action/", new ActionRouter());
            route.getTemplate().setMatchingMode(Template.MODE_STARTS_WITH);

            attachDirectory(router, getContext(), "/css/", Configuration.getParameterValue(Parameter.STYLE_PATH));

            attachDirectory(router, getContext(), "/js/", Configuration.getParameterValue(Parameter.JS_PATH));

            attachDirectory(router, getContext(), "/img/", Configuration.getParameterValue(Parameter.IMG_PATH));

            // Unknown root pages get the home page.
            router.attachDefault(HomeResource.class);
        }

        Filter filter = new DoubleSlashFilter();
        filter.setNext(router);

        return filter;
    }

    private static void attachDirectory(Router router, Context context, String attachmentPoint, String path) {

        Reference styleRef = LocalReference.createClapReference(LocalReference.CLAP_THREAD, path);

        Directory dir = new Directory(context, styleRef);
        dir.setNegotiatingContent(false);
        dir.setIndexName("index.html");
        router.attach(attachmentPoint, dir);

    }

    private void remind() {
        reminder.remind();
    }

    private void expiry() {
        expiry.expiry();
    }

    private void rdfIndexUpdate() {
        rdfUpdater.update();
    }

    @Override
    public void stop() {
        if (store != null) {
            store.shutdown();
        }

        if (fileStore != null) {
            fileStore.shutdown();
        }

        if (reminderHandle != null) {
            reminderHandle.cancel(true);
        }

        if (indexUpdateHandle != null) {
            indexUpdateHandle.cancel(true);
        }
    }

    public RdfStore getMetadataRdfStore() {
        return store;
    }

    public FileStore getMetadataFileStore() {
        return fileStore;
    }

    public QueryBuilder getQueryBuilder() {
        return queryBuilder;
    }

    public String getDataDir() {
        return dataDir;
    }

    public EndorserWhitelist getWhitelist() {
        return whitelist;
    }

    public freemarker.template.Configuration getFreeMarkerConfiguration() {
        return freeMarkerConfiguration;
    }

    private static freemarker.template.Configuration createFreeMarkerConfig(Context context) {

        freemarker.template.Configuration cfg = new freemarker.template.Configuration();
        cfg.setLocalizedLookup(false);

        LocalReference fmBaseRef = LocalReference.createClapReference("/freemarker/");

        cfg.setTemplateLoader(new ContextTemplateLoader(context, fmBaseRef));

        return cfg;
    }

    static class DoubleSlashFilter extends Filter {
        @Override
        protected int beforeHandle(Request request, Response response) {
            Reference ref = request.getResourceRef();
            String originalPath = ref.getPath();
            if (originalPath.contains("//")) {
                String newPath = originalPath.replaceAll("//", "/");
                ref.setPath(newPath);
            }
            return Filter.CONTINUE;
        }
    }

    class MarketPlaceStatusService extends StatusService {

        public Representation getRepresentation(Status status, Request request, Response response) {

            if (request.getClientInfo().getAcceptedMediaTypes().get(0).getMetadata().equals(MediaType.TEXT_XML)
                    || request.getClientInfo().getAcceptedMediaTypes().get(0).getMetadata()
                            .equals(MediaType.APPLICATION_XML)
                    || request.getClientInfo().getAcceptedMediaTypes().get(0).getMetadata()
                            .equals(MediaType.APPLICATION_RDF_XML)) {

                Representation r = generateXmlErrorRepresentation(response.getStatus().getDescription(),
                        Integer.toString(response.getStatus().getCode()));
                return r;
            } else if (request.getClientInfo().getAcceptedMediaTypes().get(0).getMetadata()
                    .equals(MediaType.APPLICATION_JSON)) {
                Representation r = generateJsonErrorRepresentation(response.getStatus().getDescription(),
                        Integer.toString(response.getStatus().getCode()));
                return r;
            } else {
                // Create the data model
                Map<String, String> dataModel = new TreeMap<String, String>();
                dataModel.put("baseurl", BaseResource.getBaseUrl(request));
                dataModel.put("statusName", response.getStatus().getReasonPhrase());
                dataModel.put("statusDescription", response.getStatus().getDescription());
                dataModel.put("title", response.getStatus().getReasonPhrase());

                freemarker.template.Configuration freeMarkerConfig = freeMarkerConfiguration;

                return new TemplateRepresentation("status.ftl", freeMarkerConfig, dataModel, MediaType.TEXT_HTML);
            }
        }

        protected Representation generateXmlErrorRepresentation(String errorMessage, String errorCode) {
            StringRepresentation result = new StringRepresentation(
                    "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" + "<error>" + errorMessage
                            + "</error>",
                    MediaType.APPLICATION_XML);

            return result;
        }

        protected Representation generateJsonErrorRepresentation(String errorMessage, String errorCode) {
            StringRepresentation result = new StringRepresentation(
                    "{\"" + errorCode + "\" : " + JSONValue.toJSONString(errorMessage) + "}",
                    MediaType.APPLICATION_JSON);

            return result;
        }

    }

}