org.pentaho.pac.server.JettyServer.java Source code

Java tutorial

Introduction

Here is the source code for org.pentaho.pac.server.JettyServer.java

Source

/*
 * This program is free software; you can redistribute it and/or modify it under the 
 * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software 
 * Foundation.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this 
 * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html 
 * or from the Free Software Foundation, Inc., 
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * This program 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.
 *
 * Copyright 2008 - 2009 Pentaho Corporation.  All rights reserved.
*/
package org.pentaho.pac.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.BindException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.StringTokenizer;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Handler;
import org.mortbay.jetty.HttpConnection;
import org.mortbay.jetty.Request;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.handler.AbstractHandler;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.handler.ResourceHandler;
import org.mortbay.jetty.plus.jaas.JAASUserRealm;
import org.mortbay.jetty.security.Constraint;
import org.mortbay.jetty.security.ConstraintMapping;
import org.mortbay.jetty.security.SecurityHandler;
import org.mortbay.jetty.security.SslSocketConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHolder;
import org.pentaho.pac.server.common.ConsoleProperties;
import org.pentaho.pac.server.i18n.Messages;

public class JettyServer implements Halter, IJettyServer {
    protected Server server;
    String delimeter = null;
    String consoleHome = null;
    String callbackHandler = null;
    boolean securityEnabled = false;
    private int portNumber;
    String roles = null;
    String authLoginConfigPath = null;
    String realmName = null;
    String loginModuleName = null;
    String securitEnabledValue = null;
    private static final Log logger = LogFactory.getLog(JettyServer.class);
    private static int stopPort = 0;
    private boolean running = false;
    public static JettyServer jettyServer;

    public static final int DEFAULT_PORT_NUMBER = 8099;
    public static final int DEFAULT_SSL_PORT_NUMBER = 8043;
    public static final int DEFAULT_STOP_PORT_NUMBER = 8011;
    public static final String DEFAULT_DELIMETER = ","; //$NON-NLS-1$
    public static final String DEFAULT_HOSTNAME = "localhost"; //$NON-NLS-1$
    public static final String CURRENT_DIR = "."; //$NON-NLS-1$
    public static final String JETTY_HOME = "jetty.home"; //$NON-NLS-1$
    public static final String AUTH_LOGIN_CONFIG_ENV_VAR = "java.security.auth.login.config"; //$NON-NLS-1$
    public static final String DEFAULT_CALLBACK_HANDLER = "org.mortbay.jetty.plus.jaas.callback.DefaultCallbackHandler"; //$NON-NLS-1$

    public JettyServer() {
        // Get the CONSOLE_HOME Environment variable. This is required as it is needed to cofigure Jetty
        consoleHome = System.getProperty("CONSOLE_HOME", CURRENT_DIR);//$NON-NLS-1$
        // Set the jetty.home to PEHTAHO_HOME
        System.setProperty(JETTY_HOME, consoleHome);
        readConfiguration();
        server = new Server();
        setupServer();
        startServer();
        stopHandler(this, stopPort);
    }

    public static JettyServer getInstance() {
        return jettyServer;
    }

    public boolean isRunning() {
        return running;
    }

    public void stop() {

        Halter halter = new Halter(this);
        // Create the thread supplying it with the runnable object
        Thread thread = new Thread(halter);

        // Start the thread
        thread.start();
    }

    public void haltNow() {
        try {
            server.stop();
            running = false;
        } catch (Exception e) {
            logger.error(Messages.getErrorString("JettyServer.ERROR_0001_UNABLE_START_SERVER"), e); //$NON-NLS-1$
        }
    }

    protected void startServer() {
        Connector connector = null;
        // Check whether ssl needs to be enabled or not
        String value = ConsoleProperties.getInstance().getProperty(ConsoleProperties.SSLENABLED);
        boolean sslEnable = (value != null && value.length() > 0) ? Boolean.parseBoolean(value) : false;
        SslParameters sslParameters = new SslParameters(ConsoleProperties.getInstance());
        if (sslEnable) {
            connector = setupSslConnector(sslParameters);
            connector.setPort(sslParameters.getSslPort());
        } else {
            connector = new SocketConnector();
            connector.setPort(portNumber);
        }
        String hostIP;
        String hostName;
        try {
            hostIP = InetAddress.getLocalHost().getHostAddress();
            hostName = InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            hostIP = DEFAULT_HOSTNAME;
            hostName = DEFAULT_HOSTNAME;
        }

        if (connector instanceof SocketConnector) {
            ((SocketConnector) connector).setResolveNames(true);
        }

        server.setConnectors(new Connector[] { connector });
        server.setStopAtShutdown(true);
        logger.info(Messages.getString("JettyServer.CONSOLE_STARTING")); //$NON-NLS-1$

        try {
            server.start();
            logger.info(Messages.getString("JettyServer.CONSOLE_STARTED", ((sslEnable) ? "https" : "http")//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    + "://" + hostName + ":" + connector.getPort(), //$NON-NLS-1$//$NON-NLS-2$
                    ((sslEnable) ? "https" //$NON-NLS-1$
                            : "http") + "://" + hostIP + ":" + connector.getPort())); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        } catch (BindException e) {
            haltNow();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            logger.error(Messages.getErrorString("JettyServer.ERROR_0001_UNABLE_START_SERVER"), e); //$NON-NLS-1$
        }
    }

    public String getResourceBaseName() {
        return "www/org.pentaho.pac.PentahoAdminConsole"; //$NON-NLS-1$
    }

    public String[] getWelcomeFiles() {
        return new String[] { "PentahoAdminConsole.html" };//$NON-NLS-1$
    }

    protected Context createServletContext() {
        ContextHandlerCollection contextHandlers = new ContextHandlerCollection();
        Context servletContext = new Context(contextHandlers, "/", Context.SESSIONS); //$NON-NLS-1$
        servletContext.setResourceBase(getResourceBaseName());
        servletContext.setWelcomeFiles(getWelcomeFiles());

        return servletContext;
    }

    public void configureResourceHandlers(Context servletContext, SecurityHandler securityHandler) {
        ResourceHandler resources = new ResourceHandler();
        resources.setResourceBase(getResourceBaseName());
        resources.setWelcomeFiles(getWelcomeFiles());
        if (securityHandler != null) {
            server.setHandlers(new Handler[] { securityHandler, resources, servletContext });
        } else {
            server.setHandlers(new Handler[] { resources, servletContext });
        }
    }

    public SecurityHandler configureSecurityHandler() {
        SecurityHandler securityHandler = null;
        // This is required for the jetty security to locate the security configuration file
        System.setProperty(AUTH_LOGIN_CONFIG_ENV_VAR, authLoginConfigPath);

        // configure security if necessary
        if (securityEnabled) {
            Constraint constraint = new Constraint();
            constraint.setName(Constraint.__BASIC_AUTH);

            // Creating the roles list
            StringTokenizer token = new StringTokenizer(roles, delimeter);
            String[] rolesList = new String[token.countTokens()];
            int i = 0;
            while (token.hasMoreTokens()) {
                rolesList[i++] = token.nextToken();
            }

            constraint.setRoles(rolesList);
            constraint.setAuthenticate(true);
            ConstraintMapping constraintMapping = new ConstraintMapping();
            constraintMapping.setConstraint(constraint);
            constraintMapping.setPathSpec("/*"); //$NON-NLS-1$
            JAASUserRealm realm = new JAASUserRealm(realmName);
            realm.setLoginModuleName(loginModuleName);
            realm.setCallbackHandlerClass(callbackHandler);
            securityHandler = new SecurityHandler();
            securityHandler.setUserRealm(realm);
            securityHandler.setConstraintMappings(new ConstraintMapping[] { constraintMapping });
        }
        return securityHandler;
    }

    public void configureServlets(Context servletContext) {
        // add servlets
        ServletHolder defaultServlet = new ServletHolder(new DefaultConsoleServlet("/", this)); //$NON-NLS-1$
        servletContext.addServlet(defaultServlet, "/*"); //$NON-NLS-1$
        servletContext.addServlet(defaultServlet, "/halt"); //$NON-NLS-1$

        ServletHolder welcomeServlet = new ServletHolder(new BrowserLocaleServlet());
        servletContext.addServlet(welcomeServlet, "/browserlocalesvc"); //$NON-NLS-1$

        ServletHolder pacsvc = new ServletHolder(new org.pentaho.pac.server.PacServiceImpl());
        servletContext.addServlet(pacsvc, "/pacsvc"); //$NON-NLS-1$

        ServletHolder schedulersvc = new ServletHolder(new org.pentaho.pac.server.SchedulerServiceImpl());
        servletContext.addServlet(schedulersvc, "/schedulersvc"); //$NON-NLS-1$

        ServletHolder subscriptionsvc = new ServletHolder(new org.pentaho.pac.server.SubscriptionServiceImpl());
        servletContext.addServlet(subscriptionsvc, "/subscriptionsvc"); //$NON-NLS-1$

        ServletHolder solutionrepositorysvc = new ServletHolder(
                new org.pentaho.pac.server.SolutionRepositoryServiceImpl());
        servletContext.addServlet(solutionrepositorysvc, "/solutionrepositorysvc"); //$NON-NLS-1$

        ServletHolder jdbcdriverdiscoveryservice = new ServletHolder(
                new org.pentaho.pac.server.common.JdbcDriverDiscoveryServiceImpl());
        servletContext.addServlet(jdbcdriverdiscoveryservice, "/jdbcdriverdiscoverysvc"); //$NON-NLS-1$

        ServletHolder hibernateconfigurationservice = new ServletHolder(
                new org.pentaho.pac.server.common.HibernateConfigurationServiceImpl());
        servletContext.addServlet(hibernateconfigurationservice, "/hibernateconfigurationsvc"); //$NON-NLS-1$

    }

    public void configureEventListeners(Context servletContext) {
        // no-op for now
    }

    protected final void setupServer() {
        server = new Server();
        SecurityHandler securityHandler = configureSecurityHandler();
        Context servletContext = createServletContext();
        configureServlets(servletContext);
        configureEventListeners(servletContext);
        configureResourceHandlers(servletContext, securityHandler);
    }

    public int getPortNumber() {

        return portNumber;
    }

    public void setPortNumber(int portNumber) {

        this.portNumber = portNumber;
    }

    public static void main(String[] args) {
        jettyServer = new JettyServer();
    }

    // TODO sbarkdull, can this be deleted?
    public static class HomeHandler extends AbstractHandler {
        public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
                throws IOException, ServletException {
            Request base_request = (request instanceof Request) ? (Request) request
                    : HttpConnection.getCurrentConnection().getRequest();
            base_request.setHandled(true);

            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType("text/html"); //$NON-NLS-1$
            response.getWriter().println("<h1>Hello OneContext</h1>"); //$NON-NLS-1$

        }
    }

    private class Halter implements Runnable {
        // This method is called when the thread runs

        private JettyServer jettyServer;

        public Halter(JettyServer jettyServer) {
            this.jettyServer = jettyServer;
        }

        public void run() {
            logger.info(Messages.getString("JettyServer.WAITING_TO_HALT")); //$NON-NLS-1$
            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                // ignore this
            }
            logger.info(Messages.getString("JettyServer.HALTING")); //$NON-NLS-1$
            jettyServer.haltNow();
        }
    }

    public void readConfiguration() {
        String port = ConsoleProperties.getInstance().getProperty(ConsoleProperties.CONSOLE_PORT_NUMBER);
        String stopPortNumber = ConsoleProperties.getInstance().getProperty(ConsoleProperties.STOP_PORT);

        if (port != null && port.length() > 0) {
            this.portNumber = Integer.parseInt(port);
        } else {
            this.portNumber = DEFAULT_PORT_NUMBER;
        }

        if (stopPortNumber != null && stopPortNumber.length() > 0) {
            stopPort = Integer.parseInt(stopPortNumber);
        } else {
            stopPort = DEFAULT_STOP_PORT_NUMBER;
        }

        roles = ConsoleProperties.getInstance().getProperty(ConsoleProperties.CONSOLE_SECURITY_ROLES_ALLOWED);
        String delimeterValue = ConsoleProperties.getInstance()
                .getProperty(ConsoleProperties.CONSOLE_SECURITY_ROLE_DELIMITER);

        if (delimeterValue != null && delimeterValue.length() > 0) {
            this.delimeter = delimeterValue;
        } else {
            this.delimeter = DEFAULT_DELIMETER;
        }

        authLoginConfigPath = ConsoleProperties.getInstance()
                .getProperty(ConsoleProperties.CONSOLE_SECURITY_AUTH_CONFIG_PATH);
        realmName = ConsoleProperties.getInstance().getProperty(ConsoleProperties.CONSOLE_SECURITY_REALM_NAME);
        loginModuleName = ConsoleProperties.getInstance()
                .getProperty(ConsoleProperties.CONSOLE_SECURITY_LOGIN_MODULE_NAME);
        String securitEnabledValue = ConsoleProperties.getInstance()
                .getProperty(ConsoleProperties.CONSOLE_SECURITY_ENABLED);
        if (securitEnabledValue != null && securitEnabledValue.length() > 0) {
            securityEnabled = Boolean.parseBoolean(securitEnabledValue);
        }
        String callbackHandlerValue = ConsoleProperties.getInstance()
                .getProperty(ConsoleProperties.CONSOLE_SECURITY_CALLBACK_HANDLER);
        if (callbackHandlerValue != null && callbackHandlerValue.length() > 0) {
            callbackHandler = callbackHandlerValue;
        } else {
            callbackHandler = DEFAULT_CALLBACK_HANDLER;
        }

    }

    public void stopHandler(JettyServer jServer, int stopPort) {
        ServerSocket server = null;
        try {
            server = new ServerSocket(stopPort);
        } catch (IOException ioe) {
            logger.error("IO Error:" + ioe.getLocalizedMessage()); //$NON-NLS-1$
        }
        try {
            Socket s = server.accept();
            Thread t = new Thread(new RequestHandler(jServer, s));
            t.start();
        } catch (Exception e) {
            logger.error("IO Error:" + e.getLocalizedMessage()); //$NON-NLS-1$
        }
    }

    protected void configureCiphers(SslSocketConnector sslConn) {
        // This is a noop here - you can override to black-list ciphers if desired by calling
        // sslConn.setExcludeCipherSuites(cipherSuites)
    }

    protected Connector setupSslConnector(SslParameters ssl) {
        Connector connector;
        String keyStore = ssl.getKeyStore();
        if (keyStore == null) {
            keyStore = System.getProperty("javax.net.ssl.keyStore", ""); //$NON-NLS-1$ //$NON-NLS-2$
            if (keyStore == null) {
                throw new IllegalArgumentException(
                        Messages.getErrorString("JettyServer.ERROR_0001_KEY_STORE_MUST_BE_SET")); //$NON-NLS-1$
            }
        }

        String keyStorePassword = ssl.getKeyStorePassword();
        if (keyStorePassword == null) {
            keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword"); //$NON-NLS-1$
            if (keyStorePassword == null) {
                throw new IllegalArgumentException(
                        Messages.getErrorString("JettyServer.ERROR_0002_KEY_STORE_PASSWORD_MUST_BE_SET")); //$NON-NLS-1$
            }
        }
        SslSocketConnector sslConnector = new SslSocketConnector();
        sslConnector.setConfidentialPort(ssl.getSslPort());
        sslConnector.setPassword(ssl.getKeyStorePassword());
        sslConnector.setKeyPassword(ssl.getKeyPassword() != null ? ssl.getKeyPassword() : keyStorePassword);
        sslConnector.setKeystore(keyStore);
        sslConnector.setKeystoreType(ssl.getKeyStoreType());
        sslConnector.setNeedClientAuth(ssl.isNeedClientAuth());
        sslConnector.setWantClientAuth(ssl.isWantClientAuth());

        // important to set this values for selfsigned keys
        // otherwise the standard truststore of the jre is used
        sslConnector.setTruststore(ssl.getTrustStore());
        if (ssl.getTrustStorePassword() != null) {
            // check is necessary because if a null password is set
            // jetty would ask for a password on the comandline
            sslConnector.setTrustPassword(ssl.getTrustStorePassword());
        }
        sslConnector.setTruststoreType(ssl.getTrustStoreType());
        sslConnector.setResolveNames(true);
        configureCiphers(sslConnector);
        connector = sslConnector;
        return connector;
    }

    private class RequestHandler implements Runnable {
        // This method is called when the thread runs

        private JettyServer jettyServer;

        private Socket socket;

        public RequestHandler(JettyServer jettyServer, Socket socket) {
            this.jettyServer = jettyServer;
            this.socket = socket;
        }

        public void run() {
            try {
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                String inputLine;
                while ((inputLine = in.readLine()) != null) {
                    if (inputLine != null && inputLine.length() > 0) {
                        inputLine = inputLine.trim();
                        if (inputLine.equalsIgnoreCase(ConsoleProperties.STOP_ARG)) {
                            logger.info(Messages.getString("JettyServer.WAITING_TO_HALT")); //$NON-NLS-1$

                            try {
                                Thread.sleep(3000);
                            } catch (Exception e) {
                                // ignore this
                            }
                            logger.info(Messages.getString("JettyServer.HALTING")); //$NON-NLS-1$
                            jettyServer.haltNow();
                        }
                    }
                }
            } catch (IOException ioe) {
                logger.error(Messages.getErrorString("JettyServer.ERROR_0002_IO_ERROR", ioe.getLocalizedMessage())); //$NON-NLS-1$
            }
        }
    }
}