Showing posts with label Jetty. Show all posts
Showing posts with label Jetty. Show all posts

Sunday, 27 May 2012

Embedded Jetty Runner now has a new sample

I added a sample that shows a very simple JSP/HTML5 boilerplate site that works on desktops and phones. Need to expand it.

Just follow the link from the top menu to get to github.

Anyone who wants to contribute, just e-ping me.

Wednesday, 11 January 2012

Stopping and/or Restarting an embedded Jetty instance via web call


I had a need to allow a web call to stop and restart an embedded Jetty instance.
So basically, I need to have something like this:

curl -v http://localhost:9103/some/path  # Calls a handler and does something RESTful 
curl -v http://localhost:9103/stop       # Stops the Jetty instance
curl -v http://localhost:9103/restart    # Restarts the Jetty instance

First up create a main class:

package com.company;

import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.NCSARequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EmbeddedJetty {

  private static Logger log = LoggerFactory.getLogger(EmbeddedJetty.class);

  public static void main(String[] args) throws Exception {
    while (true) {
      // Realistically all parameters should be read from some external file...
      log.info("Starting Jetty on port 9103");
      Server server = new Server(9103);

      HandlerCollection handlers = new HandlerCollection();
      ContextHandlerCollection contexts = new ContextHandlerCollection();

      // I added this to show how to add access logs to an embedded server.
      RequestLogHandler requestLogHandler = new RequestLogHandler();
      NCSARequestLog requestLog = new NCSARequestLog("/tmp/jetty-yyyy_mm_dd.request.log");
      requestLog.setAppend(true);
      requestLog.setExtended(true);
      requestLog.setLogTimeZone("UTC");
      requestLogHandler.setRequestLog(requestLog);

      // We want the server to gracefully allow current requests to stop
      server.setGracefulShutdown(1000);
      server.setStopAtShutdown(true);

      // Now add the handlers
      YourHandler yourHandler = new YourHandler(server);
      handlers.setHandlers(new Handler[]{contexts, yourHandler, requestLogHandler});
      server.setHandler(handlers);
      
      // Start the server
      server.start();
      server.join();
      
      // It's stopped.
      log.info("Jetty stopped");
      if (!yourHandler.restartPlease) {
        break;
      }
      log.warn("Restarting Jetty");
    }
  }
}

Now the handler:

package com.company;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class YourHandler extends AbstractHandler {

  private static Logger log = LoggerFactory.getLogger(YourHandler.class);
  private Server server = null;
  public Boolean restartPlease = false;

  public YourHandler(Server server) {
    this.server = server;
  }

  private boolean stopServer(HttpServletResponse response) throws IOException {
    log.warn("Stopping Jetty");
    response.setStatus(202);
    response.setContentType("text/plain");
    ServletOutputStream os = response.getOutputStream();
    os.println("Shutting down.");
    os.close();
    response.flushBuffer();
    try {
      // Stop the server.
      new Thread() {

        @Override
        public void run() {
          try {
            log.info("Shutting down Jetty...");
            server.stop();
            log.info("Jetty has stopped.");
          } catch (Exception ex) {
            log.error("Error when stopping Jetty: " + ex.getMessage(), ex);
          }
        }
      }.start();
    } catch (Exception ex) {
      log.error("Unable to stop Jetty: " + ex);
      return false;
    }
    return true;
  }

  @Override
  public void handle(String string, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {

    String pathInfo = request.getPathInfo();
    // THIS SHOULD OBVIOUSLY BE SECURED!!!
    if ("/stop".equals(pathInfo)) {
      stopServer(response);
      return;
    }
    if ("/restart".equals(pathInfo)) {
      restartPlease = true;
      stopServer(response);
      return;
    }

  // Go off and do the rest of your RESTful calls...
  // And close off how you please
    response.sendRedirect("http://nowhere.com");
  }
}

And you can now run your main class from your IDE of choice.

Just for reference I added a log4j.properties:

# Site
log4j.logger.com.company=DEBUG, SITE_CONSOLE
log4j.additivity.com.company=false

# Set root logger level
log4j.rootLogger=DEBUG, CONSOLE
log4j.logger.org.eclipse=INFO, CONSOLE
log4j.additivity.org.eclipse=false

# ---------------------------------------------------------------------------------
# Appenders - Notice I have added a discriminator to the conversion pattern
# --------------------------------------------------------------------------------- 
# For console logging - not in production obviously as you'd use a rolling file appender
log4j.appender.SITE_CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.SITE_CONSOLE.layout=org.apache.log4j.EnhancedPatternLayout
log4j.appender.SITE_CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS}{UTC} SITE  %-5p [%t] %c{1.} %x - %m%n

# The baseline CONSOLE logger
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.EnhancedPatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS}{UTC} JETTY %-5p [%t] %c{1.} %x - %m%n

I created a plain Java SE app and added a 'lib' folder with these jars in it:

jetty-continuation-8.0.4.v20111024.jar
jetty-http-8.0.4.v20111024.jar
jetty-io-8.0.4.v20111024.jar
jetty-server-8.0.4.v20111024.jar
jetty-servlet-8.0.4.v20111024.jar
jetty-servlets-8.0.4.v20111024.jar
jetty-util-8.0.4.v20111024.jar
jetty-webapp-8.0.4.v20111024.jar
log4j-1.2.16.jar
servlet-api-3.0.jar
slf4j-api-1.6.2.jar
slf4j-log4j12-1.6.2.jar

From the Jetty8 release.
I then ensured that the MANIFEST included these jars.
Just for reference the META-INF/MANIFEST.MF looks like this:

cat META-INF/MANIFEST.MF 
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.6.0_29-b11-402-10M3527 (Apple Inc.)
Class-Path: lib/jetty-all-8.0.4.v20111024-javadoc.jar lib/jetty-contin
 uation-8.0.4.v20111024.jar lib/jetty-http-8.0.4.v20111024.jar lib/jet
 ty-io-8.0.4.v20111024.jar lib/jetty-server-8.0.4.v20111024.jar lib/je
 tty-servlet-8.0.4.v20111024.jar lib/jetty-servlets-8.0.4.v20111024.ja
 r lib/jetty-util-8.0.4.v20111024.jar lib/jetty-webapp-8.0.4.v20111024
 .jar lib/log4j-1.2.16.jar lib/servlet-api-3.0.jar lib/slf4j-api-1.6.2
 .jar lib/slf4j-log4j12-1.6.2.jar
X-COMMENT: Main-Class will be added automatically by build
Main-Class: com.company.EmbeddedJetty

I know I included the javadoc un-intentionally...
This scheme requires a lib folder alongside the jar with those files in it.
(Need to figure out how to bundle the Jetty8 jars so that the app is self-contained...)
So the jar can be started like this:

java -jar /some/folder/EmbeddedJetty/dist/EmbeddedJetty.jar

K

Thursday, 1 December 2011

Hosting multiple Jetty instances each with multiple ports!


I had a requirement to fulfill.
On the web tier I needed multiple instances of Jetty.
Each of these must have each webapp running on a different port.
And on the app tier the same.

So for example:
web tier:
  /opt/jetty91xx
    web-app-1 running on port 9101
    web-app-2 running on port 9102
    web-app-3 running on port 9103
  /opt/jetty92xx
    web-app-4 running on port 9201
    web-app-5 running on port 9202
    web-app-6 running on port 9203
app tier:
  /opt/jetty94xx
    web-app-7 running on port 9401
    web-app-8 running on port 9402
    web-app-9 running on port 9403
  /opt/jetty95xx
    web-app-10 running on port 9501
    web-app-11 running on port 9502
    web-app-12 running on port 9503
It's not as fiddly as that in reality, but the reason was to have sets of apps running in the same VM that are 'associated'.

So... How to do it?

Easy.
I'll use the jetty91xx example for the explanation below.

First go get the latest release of jetty and unpack it into /opt/jetty-latest or whatever.
Then create /opt/jetty91xx and copy the entire contents of /opt/jetty-latest into it effectively creating a clone.
So now we switch the /opt/jetty91xx.
(If you're on Mac OS-X you need to do a "mkdir work" in the folder to avoid OS-X /tmp/nasties)
Edit the etc/jetty.xml.
Part the way down is an <call name="addConnector">...</call>.
Comment this out.
Above it add this:
<set name="connectors">
  <array type="org.eclipse.jetty.server.Connector">
    <item>
      <new class="org.eclipse.jetty.server.nio.SelectChannelConnector" id="conn9101">
        <set name="port">9101</set>
        <set name="maxIdleTime">30000</set>
        <set name="Acceptors">1</set>
        <set name="name">conn9101</set>
      </new>
    </item>
    <item>
      <new class="org.eclipse.jetty.server.nio.SelectChannelConnector" id="conn9102">
        <set name="port">9102</set>
        <set name="maxIdleTime">30000</set>
        <set name="Acceptors">1</set>
        <set name="name">conn9102</set>
      </new>
    </item>
    <item>
      <new class="org.eclipse.jetty.server.nio.SelectChannelConnector" id="conn9103">
        <set name="port">9103</set>
        <set name="maxIdleTime">30000</set>
        <set name="Acceptors">1</set>
        <set name="name">conn9103</set>
      </new>
    </item>
  </array>
</set>
Ok. Your connectors are in and named.
Now go off and prepare your war files.
For simple example purposes I will assume you have port.9100.war, port.9101.war and port.9102.war.
Create contexts/port.9101.xml and put this in it:
<configure class="org.eclipse.jetty.webapp.WebAppContext">
  <set name="contextPath">/port.9101</set>
  <set name="war"><systemproperty default="." name="jetty.home">/webapps/port.9101.war</systemproperty></set>
  <set name="extractWAR">true</set>
  <set name="copyWebDir">false</set>
  <set name="defaultsDescriptor"><systemproperty default="." name="jetty.home">/etc/webdefault.xml</systemproperty></set>
  <set name="connectorNames"><array type="String"><item>conn9101</item></array></set>
</configure>
Create contexts/port.9102.xml and contexts/port.9103.xml in the same manner changing the contextPath, war name and connectorNames.

Now start the instance:
/usr/bin/java -Djetty.home=/opt/jetty91xx -Djava.io.tmpdir=/tmp \
  -Xms1024M -Xmx1024M -Xmn640M -Xss128k \
  -XX:ParallelGCThreads=4 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC \
  -XX:+CMSIncrementalMode -XX:SurvivorRatio=8 \
  -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31 \
  -Dcom.sun.management.jmxremote.port=9876 \
  -Dcom.sun.management.jmxremote.ssl=false \
  -Dcom.sun.management.jmxremote.authenticate=false \
  -DOPTIONS=server,security,servlet,xml,webapp,deploy,jmx,requestlog \
  -jar /opt/jetty91xx/start.jar \
  --pre=etc/jetty-logging.xml \
  --daemon
You will need to change -Djetty.home and the '-jar' option for each instance.
Also I've given a full path here with a bunch of options.
Normally you would use /etc/init.d/jetty91xx.sh start of course.
Tip: While trying this out, leave of the --daemon and you can stop and start jetty by using ^c.

Ok. You should be able to drop the 3 war files into the webapps folder now.
And then...
http://[whatever]:9101/port.9101
  http://[whatever]:9102/port.9102
  http://[whatever]:9103/port.9103
And they'll all be running in the same VM.

You can then create a new /opt/jetty92xx and start agin.

Tuesday, 18 October 2011

Jetty8 Snow Leopard org.apache.jasper.JasperException: PWC6033: Error in Javac compilation for JSP

Argh!
I have a dinky app with an index.jsp.
Create a war and deploy to Tomcat on Snow Leopord.
No problem.
Create a war and deploy to Tomcat on Ubuntu.
No problem.
Deploy to GF3 on Snow Leopard.
No problem.
Deploy to GF3 on Ubuntu.
No problem.
Deploy to Jetty7 or Jetty8 on Ubuntu.
No problem.
Deploy to Jetty7 or Jetty 8 on Snow Leopard and:

Oct 17, 2011 5:37:49 PM org.apache.jasper.compiler.Compiler generateClass
SEVERE: Error compiling file: /private/var/folders/dd/dd3VsetWHLuS+PT+sxCe+k+++TI/-Tmp-/jetty-0.0.0.0-8083-com.myapp.war-_com.myapp-any-/jsp/org/apache/jsp/index_jsp.java
424497 [qtp1681653767-16] WARN org.eclipse.jetty.servlet.ServletHandler - /com.myapp/
org.apache.jasper.JasperException: PWC6033: Error in Javac compilation for JSP

PWC6199: Generated servlet error:
string:///index_jsp.java:6: package com.myapp does not exist

PWC6199: Generated servlet error:
string:///index_jsp.java:7: package com.myapp does not exist

PWC6197: An error occurred at line: 10 in the jsp file: /index.jsp
PWC6199: Generated servlet error:
string:///index_jsp.java:52: cannot find symbol
symbol : class SomeProperties
location: class org.apache.jsp.index_jsp

PWC6197: An error occurred at line: 10 in the jsp file: /index.jsp
PWC6199: Generated servlet error:
string:///index_jsp.java:52: cannot find symbol
symbol : variable AResource
location: class org.apache.jsp.index_jsp


at org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:126)
at org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:296)
at org.apache.jasper.compiler.Compiler.generateClass(Compiler.java:372)
...etc...

The thing that should ring alarm bells is that /private/var/folders... stuff.
The trick is to:

cd [your jetty install]
mkdir work

That's it.
It's that retar... er... *unique* /tmp folder that seems to be causing the problem.
You could set the java.io.tmpdir to something meaningful as well.

See: http://wiki.eclipse.org/Jetty/Reference/Temporary_Directories for more information.

Configuring a context root different from war file name to deploy to Jetty


<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="contextPath">/myapp</Set>
<Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/com.mycompany.web.myapp.war</Set>
<Set name="extractWAR">true</Set>
<Set name="copyWebDir">false</Set>
<Set name="defaultsDescriptor"><SystemProperty name="jetty.home" default="."/>/etc/webdefault.xml</Set>
</Configure>


ant clean dist
cp dist/com.mycompany.web.myapp.war /[your_path_to_jetty_install]/webapps