org.callimachusproject.test.TemporaryServerFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.callimachusproject.test.TemporaryServerFactory.java

Source

/*
 * Copyright (c) 2013 3 Round Stones Inc., Some Rights Reserved
 *
 * 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 org.callimachusproject.test;

import info.aduna.io.FileUtil;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringReader;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Scanner;

import org.apache.http.HttpHost;
import org.apache.http.client.utils.URIUtils;
import org.callimachusproject.client.HttpClientFactory;
import org.callimachusproject.repository.CalliRepository;
import org.callimachusproject.repository.DatasourceManager;
import org.callimachusproject.server.WebServer;
import org.callimachusproject.setup.CallimachusSetup;
import org.callimachusproject.util.SystemProperties;
import org.openrdf.OpenRDFException;
import org.openrdf.model.Graph;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;
import org.openrdf.model.impl.LinkedHashModel;
import org.openrdf.model.util.GraphUtil;
import org.openrdf.model.util.GraphUtilException;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.repository.Repository;
import org.openrdf.repository.config.RepositoryConfig;
import org.openrdf.repository.config.RepositoryConfigException;
import org.openrdf.repository.config.RepositoryConfigSchema;
import org.openrdf.repository.manager.LocalRepositoryManager;
import org.openrdf.repository.manager.RepositoryProvider;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import org.openrdf.rio.RDFParser;
import org.openrdf.rio.Rio;
import org.openrdf.rio.helpers.StatementCollector;

public class TemporaryServerFactory {
    private static final String ADMIN_GROUP = "/auth/groups/admin";
    private static int MIN_PORT = 49152;
    private static int MAX_PORT = 65535;
    private static final String CHANGES_PATH = "../changes/";
    private static final String SCHEMA_GRAPH = "types/RdfSchemaGraph";
    private static final File WEBAPP_CAR = findCallimachusWebappCar();
    private static final int PORT = findPort(WEBAPP_CAR.getAbsolutePath().hashCode());
    private static final TemporaryServerFactory instance = new TemporaryServerFactory("http://localhost:" + PORT,
            PORT, "test@example.com", "test".toCharArray());

    public static TemporaryServerFactory getInstance() {
        return instance;
    }

    private static File findCallimachusWebappCar() {
        File dist = new File("dist");
        if (dist.list() != null) {
            for (String file : dist.list()) {
                if (file.startsWith("callimachus-webapp") && file.endsWith(".car"))
                    return new File(dist, file);
            }
        }
        File car = SystemProperties.getWebappCarFile();
        if (car != null && car.exists())
            return car;
        throw new AssertionError("Could not find callimachus-webapp.car in " + dist.getAbsolutePath());
    }

    private static int findPort(int seed) {
        int range = (MAX_PORT - MIN_PORT) / 2;
        return (seed % range) + range + MIN_PORT;
    }

    private final String origin;
    private final int port;
    private final String email;
    private final char[] password;
    private final Map<Integer, TemporaryServer> running = new LinkedHashMap<Integer, TemporaryServer>();

    public TemporaryServerFactory(String origin, int port, String email, char[] password) {
        assert email.indexOf('@') > 0;
        this.origin = origin;
        this.port = port;
        this.email = email;
        this.password = password;
    }

    public TemporaryServer createServer() {
        return new TemporaryServer() {
            private SoftReference<TemporaryServer> ref;

            private synchronized TemporaryServer getDelegate() {
                TemporaryServer delegate = null;
                if (ref != null) {
                    delegate = ref.get();
                }
                if (delegate == null) {
                    delegate = createTemporaryServer();
                    ref = new SoftReference<TemporaryServer>(delegate);
                }
                return delegate;
            }

            public void start() throws InterruptedException, Exception {
                getDelegate().start();
            }

            public void pause() throws Exception {
                getDelegate().pause();
            }

            public void resume() throws Exception {
                getDelegate().resume();
            }

            public void stop() throws Exception {
                getDelegate().stop();
            }

            public void destroy() throws Exception {
                getDelegate().destroy();
            }

            public String getOrigin() {
                return getDelegate().getOrigin();
            }

            public String getUsername() {
                return getDelegate().getUsername();
            }

            public char[] getPassword() {
                return getDelegate().getPassword();
            }

            @Override
            public CalliRepository getRepository() {
                return getDelegate().getRepository();
            }
        };
    }

    private synchronized TemporaryServer createTemporaryServer() {
        try {
            final File dir = createCallimachus(origin);
            return new TemporaryServer() {
                private LocalRepositoryManager manager;
                private WebServer server;
                private CalliRepository repository;
                private boolean stopped;

                public synchronized void start() throws InterruptedException, Exception {
                    manager = RepositoryProvider.getRepositoryManager(dir);
                    File dataDir = manager.getRepositoryDir("callimachus");
                    Repository repo = manager.getRepository("callimachus");
                    repository = new CalliRepository(repo, dataDir);
                    repository.setDatasourceManager(new DatasourceManager(manager, "callimachus") {
                        protected CalliRepository createCalliRepository(URI uri, Repository delegate, File dataDir)
                                throws OpenRDFException, IOException {
                            CalliRepository secondary;
                            secondary = super.createCalliRepository(uri, delegate, dataDir);
                            String uriSpace = repository.getChangeFolder();
                            String webapp = repository.getCallimachusWebapp(uriSpace);
                            secondary.setChangeFolder(uriSpace, webapp);
                            return secondary;
                        }
                    });
                    String url = repository.getCallimachusUrl(origin, CHANGES_PATH);
                    String schema = repository.getCallimachusUrl(origin, SCHEMA_GRAPH);
                    repository.addSchemaGraphType(schema);
                    repository.setChangeFolder(url);
                    repository.setCompileRepository(true);
                    server = new WebServer(new File(dataDir, "cache/server"));
                    server.addOrigin(origin, repository);
                    server.listen(new int[] { port }, new int[0]);
                    server.start();
                    HttpHost host = URIUtils.extractHost(java.net.URI.create(origin + "/"));
                    HttpClientFactory.getInstance().setProxy(host, server);
                    Thread.sleep(100);
                }

                public synchronized void pause() throws Exception {
                }

                public synchronized void resume() throws Exception {
                    synchronized (running) {
                        TemporaryServer other = running.get(port);
                        if (stopped || !this.equals(other)) {
                            if (other != null) {
                                other.stop();
                            }
                            start();
                            running.put(port, this);
                        }
                    }
                }

                public synchronized void stop() throws Exception {
                    if (!stopped) {
                        server.stop();
                        server.destroy();
                        server = null;
                        repository = null;
                        manager.shutDown();
                        stopped = true;
                        Thread.sleep(100);
                        System.gc();
                        System.runFinalization();
                    }
                }

                public synchronized void destroy() throws Exception {
                    stop();
                    FileUtil.deltree(dir);
                }

                @Override
                protected void finalize() throws Exception {
                    destroy();
                }

                public String getOrigin() {
                    return origin;
                }

                public String getUsername() {
                    return email;
                }

                public char[] getPassword() {
                    return password;
                }

                public CalliRepository getRepository() {
                    return this.repository;
                }

                public String toString() {
                    return server.toString();
                }
            };
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }

    private File createCallimachus(String origin) throws Exception {
        int code = origin.hashCode() + WEBAPP_CAR.getAbsolutePath().hashCode();
        String name = TemporaryServer.class.getSimpleName() + Integer.toHexString(code);
        File dir = createDirectory(name);
        if (!dir.isDirectory() || WEBAPP_CAR.lastModified() > dir.lastModified()) {
            if (dir.isDirectory()) {
                FileUtil.deleteDir(dir);
            }
            dir.delete();
            String configStr = readRepositoryConfigFile();
            System.setProperty("org.callimachusproject.config.webapp", WEBAPP_CAR.getAbsolutePath());
            RepositoryConfig config = getRepositoryConfig(configStr);
            LocalRepositoryManager manager = RepositoryProvider.getRepositoryManager(dir);
            Repository repo = getRepository(manager, config);
            if (repo == null)
                throw new RepositoryConfigException("Missing repository configuration");
            File dataDir = manager.getRepositoryDir(config.getID());
            CalliRepository repository = new CalliRepository(repo, dataDir);
            repository.setDatasourceManager(new DatasourceManager(manager, config.getID()));
            CallimachusSetup setup = new CallimachusSetup(repository);
            setup.prepareWebappOrigin(origin);
            setup.createWebappOrigin(origin);
            setup.updateWebapp(origin);
            setup.finalizeWebappOrigin(origin);
            String username = email.substring(0, email.indexOf('@'));
            setup.inviteUser(email, origin);
            setup.addInvitedUserToGroup(email, ADMIN_GROUP, origin);
            setup.registerDigestUser(email, username, password, origin);
            repository.shutDown();
            manager.shutDown();
        }
        File temp = FileUtil.createTempDir(name);
        copyDir(dir, temp);
        return temp;
    }

    private String readRepositoryConfigFile() throws FileNotFoundException {
        Scanner scanner = new Scanner(new File("etc", "callimachus-repository.ttl"));
        try {
            return scanner.useDelimiter("\\A").next();
        } finally {
            scanner.close();
        }
    }

    private Repository getRepository(LocalRepositoryManager manager, RepositoryConfig config)
            throws OpenRDFException, MalformedURLException, IOException {
        if (config == null || manager == null)
            return null;
        String id = config.getID();
        if (manager.hasRepositoryConfig(id))
            return manager.getRepository(id);
        config.validate();
        manager.addRepositoryConfig(config);
        if (manager.getInitializedRepositoryIDs().contains(id)) {
            manager.getRepository(id).shutDown();
        }
        return manager.getRepository(id);
    }

    private RepositoryConfig getRepositoryConfig(String configString) throws IOException, RDFParseException,
            RDFHandlerException, GraphUtilException, RepositoryConfigException {
        Graph graph = parseTurtleGraph(configString);
        Resource node = GraphUtil.getUniqueSubject(graph, RDF.TYPE, RepositoryConfigSchema.REPOSITORY);
        return RepositoryConfig.create(graph, node);
    }

    private Graph parseTurtleGraph(String configString) throws IOException, RDFParseException, RDFHandlerException {
        Graph graph = new LinkedHashModel();
        RDFParser rdfParser = Rio.createParser(RDFFormat.TURTLE);
        rdfParser.setRDFHandler(new StatementCollector(graph));
        String base = new File(".").getAbsoluteFile().toURI().toASCIIString();
        rdfParser.parse(new StringReader(configString), base);
        return graph;
    }

    private void copyDir(File src, File dest) throws IOException {
        if (src.isDirectory()) {
            dest.mkdir();
            for (String file : src.list()) {
                copyDir(new File(src, file), new File(dest, file));
            }
        } else {
            FileUtil.copyFile(src, dest);
        }
    }

    private File createDirectory(String name) throws IOException {
        String tmpDirStr = System.getProperty("java.io.tmpdir");
        if (tmpDirStr != null) {
            File tmpDir = new File(tmpDirStr);
            if (!tmpDir.exists()) {
                tmpDir.mkdirs();
            }
        }
        File tmp = File.createTempFile(name, "");
        tmp.delete();
        return new File(tmp.getParentFile(), name);
    }
}