/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.solr.client.solrj.embedded; import java.io.IOException; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Random; import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.solr.servlet.SolrDispatchFilter; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.bio.SocketConnector; import org.eclipse.jetty.server.handler.GzipHandler; import org.eclipse.jetty.server.session.HashSessionIdManager; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.ThreadPool; /** * Run solr using jetty * * @since solr 1.3 */ public class JettySolrRunner { Server server; FilterHolder dispatchFilter; String context; private String solrConfigFilename; private String schemaFilename; private boolean waitOnSolr = false; private int lastPort = -1; private String shards; private String dataDir; private volatile boolean startedBefore = false; private String solrHome; private boolean stopAtShutdown; public JettySolrRunner(String solrHome, String context, int port) { this.init(solrHome, context, port, true); } public JettySolrRunner(String solrHome, String context, int port, String solrConfigFilename, String schemaFileName) { this.init(solrHome, context, port, true); this.solrConfigFilename = solrConfigFilename; this.schemaFilename = schemaFileName; } public JettySolrRunner(String solrHome, String context, int port, String solrConfigFilename, String schemaFileName, boolean stopAtShutdown) { this.init(solrHome, context, port, stopAtShutdown); this.solrConfigFilename = solrConfigFilename; this.schemaFilename = schemaFileName; } private void init(String solrHome, String context, int port, boolean stopAtShutdown) { this.context = context; server = new Server(port); this.solrHome = solrHome; this.stopAtShutdown = stopAtShutdown; server.setStopAtShutdown(stopAtShutdown); if (!stopAtShutdown) { server.setGracefulShutdown(0); } System.setProperty("solr.solr.home", solrHome); if (System.getProperty("jetty.testMode") != null) { // SelectChannelConnector connector = new SelectChannelConnector(); // Normal SocketConnector is what solr's example server uses by default SocketConnector connector = new SocketConnector(); connector.setPort(port); connector.setReuseAddress(true); if (!stopAtShutdown) { QueuedThreadPool threadPool = (QueuedThreadPool) connector .getThreadPool(); if (threadPool != null) { threadPool.setMaxStopTimeMs(100); } } server.setConnectors(new Connector[] {connector}); server.setSessionIdManager(new HashSessionIdManager(new Random())); } else { if (!stopAtShutdown) { for (Connector connector : server.getConnectors()) { if (connector instanceof SocketConnector) { QueuedThreadPool threadPool = (QueuedThreadPool) ((SocketConnector) connector) .getThreadPool(); if (threadPool != null) { threadPool.setMaxStopTimeMs(100); } } } } } // Initialize the servlets final ServletContextHandler root = new ServletContextHandler(server,context,ServletContextHandler.SESSIONS); root.setHandler(new GzipHandler()); server.addLifeCycleListener(new LifeCycle.Listener() { public void lifeCycleStopping(LifeCycle arg0) { System.clearProperty("hostPort"); } public void lifeCycleStopped(LifeCycle arg0) {} public void lifeCycleStarting(LifeCycle arg0) { synchronized (JettySolrRunner.this) { waitOnSolr = true; JettySolrRunner.this.notify(); } } public void lifeCycleStarted(LifeCycle arg0) { lastPort = getFirstConnectorPort(); System.setProperty("hostPort", Integer.toString(lastPort)); if (solrConfigFilename != null) System.setProperty("solrconfig", solrConfigFilename); if (schemaFilename != null) System.setProperty("schema", schemaFilename); // SolrDispatchFilter filter = new SolrDispatchFilter(); // FilterHolder fh = new FilterHolder(filter); dispatchFilter = root.addFilter(SolrDispatchFilter.class, "*", EnumSet.of(DispatcherType.REQUEST) ); if (solrConfigFilename != null) System.clearProperty("solrconfig"); if (schemaFilename != null) System.clearProperty("schema"); System.clearProperty("solr.solr.home"); } public void lifeCycleFailure(LifeCycle arg0, Throwable arg1) { System.clearProperty("hostPort"); } }); // for some reason, there must be a servlet for this to get applied root.addServlet(Servlet404.class, "/*"); } public FilterHolder getDispatchFilter() { return dispatchFilter; } public boolean isRunning() { return server.isRunning(); } public boolean isStopped() { return server.isStopped(); } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ public void start() throws Exception { start(true); } public void start(boolean waitForSolr) throws Exception { // if started before, make a new server if (startedBefore) { waitOnSolr = false; init(solrHome, context, lastPort, stopAtShutdown); } else { startedBefore = true; } if( dataDir != null) { System.setProperty("solr.data.dir", dataDir); } if(shards != null) { System.setProperty("shard", shards); } if (!server.isRunning()) { server.start(); } synchronized (JettySolrRunner.this) { int cnt = 0; while (!waitOnSolr) { this.wait(100); if (cnt++ == 5) { throw new RuntimeException("Jetty/Solr unresponsive"); } } } System.clearProperty("shard"); System.clearProperty("solr.data.dir"); } public void stop() throws Exception { // we try and do a bunch of extra stop stuff because // jetty doesn't like to stop if it started // and ended up in a failure state (like when it cannot get the port) if (server.getState().equals(Server.FAILED)) { Connector[] connectors = server.getConnectors(); for (Connector connector : connectors) { connector.stop(); } } Filter filter = dispatchFilter.getFilter(); ThreadPool threadPool = server.getThreadPool(); server.getServer().stop(); server.stop(); if (threadPool instanceof QueuedThreadPool) { ((QueuedThreadPool) threadPool).setMaxStopTimeMs(30000); ((QueuedThreadPool) threadPool).stop(); ((QueuedThreadPool) threadPool).join(); } //server.destroy(); if (server.getState().equals(Server.FAILED)) { filter.destroy(); } server.join(); } /** * Returns the Local Port of the jetty Server. * * @exception RuntimeException if there is no Connector */ private int getFirstConnectorPort() { Connector[] conns = server.getConnectors(); if (0 == conns.length) { throw new RuntimeException("Jetty Server has no Connectors"); } return conns[0].getLocalPort(); } /** * Returns the Local Port of the jetty Server. * * @exception RuntimeException if there is no Connector */ public int getLocalPort() { if (lastPort == -1) { throw new IllegalStateException("You cannot get the port until this instance has started"); } return lastPort; } // -------------------------------------------------------------- // -------------------------------------------------------------- /** * This is a stupid hack to give jetty something to attach to */ public static class Servlet404 extends HttpServlet { @Override public void service(HttpServletRequest req, HttpServletResponse res) throws IOException { res.sendError(404, "Can not find: " + req.getRequestURI()); } } /** * A main class that starts jetty+solr This is useful for debugging */ public static void main(String[] args) { try { JettySolrRunner jetty = new JettySolrRunner(".", "/solr", 8983); jetty.start(); } catch (Exception ex) { ex.printStackTrace(); } } public void setShards(String shardList) { this.shards = shardList; } public void setDataDir(String dataDir) { this.dataDir = dataDir; } } class NoLog implements Logger { private static boolean debug = System.getProperty("DEBUG", null) != null; private final String name; public NoLog() { this(null); } public NoLog(String name) { this.name = name == null ? "" : name; } public boolean isDebugEnabled() { return debug; } public void setDebugEnabled(boolean enabled) { debug = enabled; } public void debug(String msg, Throwable th) { } public Logger getLogger(String name) { if ((name == null && this.name == null) || (name != null && name.equals(this.name))) return this; return new NoLog(name); } @Override public String toString() { return "NOLOG[" + name + "]"; } @Override public void debug(Throwable arg0) { } @Override public void debug(String arg0, Object... arg1) { } @Override public String getName() { return toString(); } @Override public void ignore(Throwable arg0) { } @Override public void info(Throwable arg0) { } @Override public void info(String arg0, Object... arg1) { } @Override public void info(String arg0, Throwable arg1) { } @Override public void warn(Throwable arg0) { } @Override public void warn(String arg0, Object... arg1) { } @Override public void warn(String arg0, Throwable arg1) { } }