/******************************************************************************* * Copyright (c) 2008, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.help.internal.server; import java.util.Dictionary; import java.util.Hashtable; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.Platform; import org.eclipse.equinox.http.jetty.JettyConfigurator; import org.eclipse.equinox.http.jetty.JettyConstants; import org.eclipse.help.internal.base.BaseHelpSystem; import org.eclipse.help.internal.base.HelpBasePlugin; import org.eclipse.help.server.HelpServer; import org.osgi.framework.Bundle; import org.osgi.framework.BundleException; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; public class JettyHelpServer extends HelpServer { private abstract class WorkerThread extends Thread { private Throwable exception; public WorkerThread(String name) { super(name); } public synchronized void setException(Throwable status) { this.exception = status; } public synchronized Throwable getException() { return exception; } } private final class StartServerThread extends WorkerThread { private final String webappName; public StartServerThread(String webappName) { super("Start Help Server"); //$NON-NLS-1$ this.webappName = webappName; } @Override public void run() { try { final Dictionary<String, Object> d = new Hashtable<>(); final int SESSION_TIMEOUT_INTERVAL_IN_SECONDS = 30*60; // 30 minutes configurePort(); d.put("http.port", new Integer(getPortParameter())); //$NON-NLS-1$ // set the base URL d.put("context.path", getContextPath()); //$NON-NLS-1$ d.put("other.info", getOtherInfo()); //$NON-NLS-1$ d.put(JettyConstants.CONTEXT_SESSIONINACTIVEINTERVAL, new Integer(SESSION_TIMEOUT_INTERVAL_IN_SECONDS)); // suppress Jetty INFO/DEBUG messages to stderr Logger.getLogger("org.mortbay").setLevel(Level.WARNING); //$NON-NLS-1$ if (bindServerToHostname()) { d.put("http.host", getHost()); //$NON-NLS-1$ } JettyConfigurator.startServer(webappName, d); } catch (Throwable t) { setException(t); } } } private final class StopServerThread extends WorkerThread { private final String webappName; public StopServerThread(String webappName) { super("Stop Help Server"); //$NON-NLS-1$ this.webappName = webappName; } @Override public void run() { try { JettyConfigurator.stopServer(webappName); port = -1; } catch (Throwable t) { setException(t); } } } private String host; protected int port = -1; protected static final int AUTO_SELECT_JETTY_PORT = 0; @Override public void start(final String webappName) throws Exception { WorkerThread startRunnable = new StartServerThread(webappName); execute(startRunnable); checkBundle(); } /* * Ensures that the bundle with the specified name and the highest available * version is started and reads the port number */ protected void checkBundle() throws InvalidSyntaxException, BundleException { Bundle bundle = Platform.getBundle("org.eclipse.equinox.http.registry"); //$NON-NLS-1$ if (bundle == null) { throw new BundleException("org.eclipse.equinox.http.registry"); //$NON-NLS-1$ } if (bundle.getState() == Bundle.RESOLVED) { bundle.start(Bundle.START_TRANSIENT); } if (port == -1) { // Jetty selected a port number for us ServiceReference<?>[] reference = bundle.getBundleContext().getServiceReferences("org.osgi.service.http.HttpService", "(other.info=" + getOtherInfo() + ')'); //$NON-NLS-1$ //$NON-NLS-2$ Object assignedPort = reference[0].getProperty("http.port"); //$NON-NLS-1$ port = Integer.parseInt((String)assignedPort); } } @Override public void stop(final String webappName) throws CoreException { try { WorkerThread stopRunnable = new StopServerThread(webappName); execute(stopRunnable); } catch (Exception e) { HelpBasePlugin.logError("An error occured while stopping the help server", e); //$NON-NLS-1$ } } private void execute(WorkerThread runnable) throws Exception { boolean interrupted = false; Thread thread = runnable; thread.setDaemon(true); thread.start(); while(true) { try { thread.join(); break; } catch (InterruptedException e) { interrupted = true; } } if (interrupted) Thread.currentThread().interrupt(); Throwable t = runnable.getException(); if (t != null) { if (t instanceof Exception) { throw (Exception)t; } throw (Error) t; } } @Override public int getPort() { return port; } private void configurePort() { if (port == -1) { String portCommandLineOverride = HelpBasePlugin.getBundleContext().getProperty("server_port"); //$NON-NLS-1$ if (portCommandLineOverride != null && portCommandLineOverride.trim().length() > 0) { try { port = Integer.parseInt(portCommandLineOverride); } catch (NumberFormatException e) { String msg = "Help server port specified in VM arguments is invalid (" + portCommandLineOverride + ")"; //$NON-NLS-1$ //$NON-NLS-2$ HelpBasePlugin.logError(msg, e); } } } } /* * Get the port number which will be passed to Jetty */ protected int getPortParameter() { if (port == -1) { return AUTO_SELECT_JETTY_PORT; } return port; } @Override public String getHost() { if (host == null) { String hostCommandLineOverride = HelpBasePlugin.getBundleContext().getProperty("server_host"); //$NON-NLS-1$ if (hostCommandLineOverride != null && hostCommandLineOverride.trim().length() > 0) { host = hostCommandLineOverride; } else { host = "127.0.0.1"; //$NON-NLS-1$ } } return host; } protected String getOtherInfo() { return "org.eclipse.help"; //$NON-NLS-1$ } protected String getContextPath() { return "/help"; //$NON-NLS-1$ } public boolean bindServerToHostname() { if (BaseHelpSystem.getMode() == BaseHelpSystem.MODE_WORKBENCH) { return true; } String host = HelpBasePlugin.getBundleContext().getProperty("server_host"); //$NON-NLS-1$ return host != null && host.trim().length() > 0; } }