com.marklogic.client.tutorial.util.Bootstrapper.java Source code

Java tutorial

Introduction

Here is the source code for com.marklogic.client.tutorial.util.Bootstrapper.java

Source

/*
 * Copyright 2012 MarkLogic Corporation
 *
 * 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 com.marklogic.client.tutorial.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.auth.params.AuthPNames;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.AuthPolicy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;

import com.marklogic.client.DatabaseClientFactory.Authentication;

/**
 * Bootstrapper provides an example of how to create a REST server
 * using an HTTP client.
 * SSL is not supported, and error handling is minimal.
 */
public class Bootstrapper {
    /**
     * Command-line invocation.
     * @param args   command-line arguments specifying the configuration and REST server
     */
    public static void main(String[] args)
            throws ClientProtocolException, IOException, XMLStreamException, FactoryConfigurationError {
        Properties properties = new Properties();
        for (int i = 0; i < args.length; i++) {
            String name = args[i];
            if (name.startsWith("-") && name.length() > 1 && ++i < args.length) {
                name = name.substring(1);
                if ("properties".equals(name)) {
                    InputStream propsStream = Bootstrapper.class.getClassLoader().getResourceAsStream(name);
                    if (propsStream == null)
                        throw new IOException("Could not read bootstrapper properties");
                    Properties props = new Properties();
                    props.load(propsStream);
                    props.putAll(properties);
                    properties = props;
                } else {
                    properties.put(name, args[i]);
                }
            } else {
                System.err.println("invalid argument: " + name);
                System.err.println(getUsage());
                System.exit(1);
            }
        }

        String invalid = joinList(listInvalidKeys(properties));
        if (invalid != null && invalid.length() > 0) {
            System.err.println("invalid arguments: " + invalid);
            System.err.println(getUsage());
            System.exit(1);
        }

        new Bootstrapper().makeServer(properties);

        System.out.println("Created " + properties.getProperty("restserver") + " server on "
                + properties.getProperty("restport") + " port for " + properties.getProperty("restdb")
                + " database");
    }

    /**
     * Invocation based on properties.
     * @param properties   the specification of the configuration and REST server
     */
    public void makeServer(Properties properties)
            throws ClientProtocolException, IOException, XMLStreamException, FactoryConfigurationError {
        makeServer(new ConfigServer(properties), new RESTServer(properties));
    }

    /**
     * Programmatic invocation.
     * @param configServer   the configuration server for creating the REST server
     * @param restServer   the specification of the REST server
     */
    public void makeServer(ConfigServer configServer, RESTServer restServer)
            throws ClientProtocolException, IOException, XMLStreamException, FactoryConfigurationError {

        DefaultHttpClient client = new DefaultHttpClient();

        String host = configServer.getHost();
        int configPort = configServer.getPort();

        Authentication authType = configServer.getAuthType();
        if (authType != null) {
            List<String> prefList = new ArrayList<String>();
            if (authType == Authentication.BASIC)
                prefList.add(AuthPolicy.BASIC);
            else if (authType == Authentication.DIGEST)
                prefList.add(AuthPolicy.DIGEST);
            else
                throw new IllegalArgumentException("Unknown authentication type: " + authType.name());
            client.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF, prefList);

            String configUser = configServer.getUser();
            String configPassword = configServer.getPassword();
            client.getCredentialsProvider().setCredentials(new AuthScope(host, configPort),
                    new UsernamePasswordCredentials(configUser, configPassword));
        }

        BasicHttpContext context = new BasicHttpContext();

        StringEntity content = new StringEntity(restServer.toXMLString());
        content.setContentType("application/xml");

        HttpPost poster = new HttpPost("http://" + host + ":" + configPort + "/v1/rest-apis");
        poster.setEntity(content);

        HttpResponse response = client.execute(poster, context);
        //poster.releaseConnection();

        StatusLine status = response.getStatusLine();

        int statusCode = status.getStatusCode();
        String statusPhrase = status.getReasonPhrase();

        client.getConnectionManager().shutdown();

        if (statusCode >= 300) {
            throw new RuntimeException("Failed to create REST server: " + statusCode + " " + statusPhrase + "\n"
                    + "Please check the server log for detail");
        }
    }

    static public String getUsage() {
        Map<String, String> propNames = getPropNames();

        StringBuilder buffer = new StringBuilder();
        buffer.append("usage:\n");
        for (Map.Entry<String, String> entry : propNames.entrySet()) {
            buffer.append(entry.getKey());
            buffer.append("\t= ");
            buffer.append(entry.getValue());
            buffer.append("\n");
        }

        return buffer.toString();
    }

    static public void checkProperties(Properties properties) {
        String invalid = joinList(listInvalidKeys(properties));
        if (invalid != null && invalid.length() > 0) {
            throw new IllegalArgumentException("invalid bootstrapping names: " + invalid);
        }
    }

    static public List<String> listInvalidKeys(Properties properties) {
        Map<String, String> propNames = getPropNames();

        List<String> invalid = null;
        for (String key : properties.stringPropertyNames()) {
            if (propNames.containsKey(key))
                continue;

            if (invalid == null)
                invalid = new ArrayList<String>();

            invalid.add(key);
        }

        return invalid;
    }

    static public String joinList(List<String> list) {
        return joinList(list, ", ");
    }

    static public String joinList(List<String> list, String sep) {
        if (list == null || list.size() == 0)
            return null;

        StringBuilder buffer = null;
        for (String key : list) {
            if (buffer == null)
                buffer = new StringBuilder();
            else
                buffer.append(sep);
            buffer.append(key);
        }

        return buffer.toString();
    }

    static private Map<String, String> getPropNames() {
        Map<String, String> propNames = new HashMap<String, String>();
        propNames.put("confighost", "the host for configuring a new REST server");
        propNames.put("configport", "the port (typically 8002) for the configuration server");
        propNames.put("configuser", "the user (typically admin) for the configuration server");
        propNames.put("configpassword", "the password for the configuration user");
        propNames.put("configauth", "the type of authentication (digest or basic) for the configuration server");
        propNames.put("restdb", "the name of the database exposed by the new REST server");
        propNames.put("restmodulesdb", "the name of the modules database (if any) for new REST server");
        propNames.put("restgroup", "the name of the group (on a cluster with many groups) for new REST server");
        propNames.put("restserver", "the name of the new REST server");
        propNames.put("restport", "the port for the new REST server");

        return propNames;
    }

    /**
     * ConfigServer specifies a configuration server supporting
     * requests to create a REST server.
     */
    static public class ConfigServer {
        private String host = "localhost";
        private int port = 8002;
        private String user = null;
        private String password = null;
        private Authentication authType = Authentication.DIGEST;

        /**
         * Construct the configuration server specification based on properties.
         * @param properties   specifies the configuration server
         */
        public ConfigServer(Properties properties) {
            for (String key : properties.stringPropertyNames()) {
                String value = properties.getProperty(key);
                if ("confighost".equals(key))
                    host = value;
                else if ("configport".equals(key))
                    port = Integer.parseInt(value);
                else if ("configuser".equals(key))
                    user = value;
                // TODO: secure password configuration
                else if ("configpassword".equals(key))
                    password = value;
                else if ("configauth".equals(key))
                    authType = Authentication.valueOf(value.toUpperCase());
            }
            validate();
        }

        /**
         * Construct the configuration server specification programmatically.
         * @param host   the host (often localhost) for the configuration and REST server
         * @param port   the port (usually 8002) for the configuration server
         * @param user   the user (often admin) for the configuration server
         * @param password   the password for the configuration server
         * @param authType   the authentication type (usually DIGEST) for the configuration server
         */
        public ConfigServer(String host, int port, String user, String password, Authentication authType) {
            if (host != null)
                this.host = host;
            if (port != -1)
                this.port = port;
            if (user != null)
                this.user = user;
            if (password != null)
                this.password = password;
            if (authType != null)
                this.authType = authType;
            validate();
        }

        private void validate() {
            if ((authType != null || user != null || password != null)
                    && (authType == null || user == null || password == null))
                throw new IllegalArgumentException("requires all or no user, password, and authentication type");
        }

        public String getHost() {
            return host;
        }

        public int getPort() {
            return port;
        }

        public String getUser() {
            return user;
        }

        public String getPassword() {
            return password;
        }

        public Authentication getAuthType() {
            return authType;
        }
    }

    /**
     * RESTServer specifies an application providing access to a database
     * using built-in RESTful services.
     */
    static public class RESTServer {
        private String database;
        private String modulesDatabase;
        private String group;
        private String server;
        private int port = -1;

        /**
         * Construct the REST server specification based on properties.
         * @param properties   specifies the REST server
         */
        public RESTServer(Properties properties) {
            for (String key : properties.stringPropertyNames()) {
                String value = properties.getProperty(key);
                if ("restdb".equals(key))
                    database = value;
                else if ("restmodulesdb".equals(key))
                    modulesDatabase = value;
                else if ("restgroup".equals(key))
                    group = value;
                else if ("restserver".equals(key))
                    server = value;
                else if ("restport".equals(key))
                    port = Integer.parseInt(value);
            }
            validate();
        }

        /**
         * Construct the REST server specification programmatically.
         * @param database   the database exposed by the REST server
         * @param modulesDatabase   the modules database for the REST server
         * @param group   the group containing the REST server
         * @param server   the name of the REST server
         * @param port   the port for the REST server
         */
        public RESTServer(String database, String modulesDatabase, String group, String server, int port) {
            this.database = database;
            if (modulesDatabase != null)
                this.modulesDatabase = modulesDatabase;
            if (group != null)
                this.group = group;
            this.server = server;
            this.port = port;
            validate();
        }

        private void validate() {
            if (database == null)
                throw new IllegalArgumentException("database required");
            if (server == null)
                throw new IllegalArgumentException("server required");
            if (port == -1)
                throw new IllegalArgumentException("port required");
        }

        public String getDatabase() {
            return database;
        }

        public String getModulesDatabase() {
            return modulesDatabase;
        }

        public String getGroup() {
            return group;
        }

        public String getServer() {
            return server;
        }

        public int getPort() {
            return port;
        }

        public String toXMLString() throws XMLStreamException, FactoryConfigurationError {
            StringWriter buffer = new StringWriter();

            XMLStreamWriter writer = XMLOutputFactory.newFactory().createXMLStreamWriter(buffer);

            String server = getServer();
            int port = getPort();
            String group = getGroup();
            String database = getDatabase();
            String modulesDatabase = getModulesDatabase();

            writer.writeStartDocument();
            writer.writeStartElement("rest-api");
            writer.writeDefaultNamespace("http://marklogic.com/rest-api");

            writer.writeStartElement("name");
            writer.writeCharacters(server);
            writer.writeEndElement();

            if (group != null && group.length() > 0) {
                writer.writeStartElement("group");
                writer.writeCharacters(group);
                writer.writeEndElement();
            }

            writer.writeStartElement("database");
            writer.writeCharacters(database);
            writer.writeEndElement();

            if (modulesDatabase != null && modulesDatabase.length() > 0) {
                writer.writeStartElement("modules-database");
                writer.writeCharacters(modulesDatabase);
                writer.writeEndElement();
            }

            writer.writeStartElement("port");
            writer.writeCharacters(String.valueOf(port));
            writer.writeEndElement();

            writer.writeEndElement();
            writer.writeEndDocument();

            return buffer.toString();
        }
    }
}