Java tutorial
/* * 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$ } } } }