This page applies to Jetty 7 and Jetty 8. For Jetty 9 documentation please look to the Documentation Hub.


Contents

 [hide]

Introduction

Jetty has a slogan, "Don't deploy your application in Jetty, deploy Jetty in your application." What this means is that as an alternative to bundling your application as a standard WAR to be deployed in Jetty, Jetty is designed to be a software component that can be instantiated and used in a Java program just like any POJO. Put another way, running Jetty in embedded mode means putting an HTTP module into your application, rather than putting your application into an HTTP server.

This tutorial takes you step-by-step from the simplest Jetty server instantiation to running multiple web applications with standards-based deployment descriptors. The source for most of these examples is part of the standard Jetty project.

Before doing this tutorial, it is worth while to do the Hello World tutorial. This tutorial is also available as a Embedding Jetty Webinar recording.

Jetty Version
The code in this tutorial is from Jetty 7, but should also work on Jetty 8. For the latest stable code, follow the links to the code xref from the latest release, which might vary slightly from the code examples given on this page.

Details

To embed a Jetty server, the following steps are typical:

  1. Create the server
  2. Add/Configure Connectors
  3. Add/Configure Handlers
  4. Add/Configure Servlets/Webapps to Handlers
  5. Start the server
  6. Wait (join the server to prevent main exiting)

Creating a Server

The following code from SimplestServer.java instantiates and runs the simplest possible Jetty server:

public class SimplestServer
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);
        server.start();
        server.join();
    }
}

This runs an HTTP server on port 8080. It is not a very useful server as it has no handlers and thus returns a 404 error for every request.


Writing Handlers

To produce a response to a request, Jetty requires a Handler to be set on the server. A handler may:

  • Examine/modify the HTTP request.
  • Generate the complete HTTP response.
  • Call another Handler (see HandlerWrapper).
  • Select one or many Handlers to call (see HandlerCollection).

Hello World Handler

The following code based on HelloHandler.java shows a simple hello world handler:

public class HelloHandler extends AbstractHandler
{
    public void handle(String target,Request baseRequest,HttpServletRequest request,HttpServletResponse response) 
        throws IOException, ServletException
    {
        response.setContentType("text/html;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        baseRequest.setHandled(true);
        response.getWriter().println("<h1>Hello World</h1>");
    }
}

The parameters passed to the handle method are:

  • target–the target of the request, which is either a URI or a name from a named dispatcher.
  • baseRequest–the Jetty mutable request object, which is always unwrapped.
  • request–the immutable request object, which might have been wrapped.
  • response–the response, which might have been wrapped.

The handler sets the response status, content-type and marks the request as handled before it generates the body of the response using a writer.

The following code from OneHandler.java shows how a Jetty server can use this handler:

public static void main(String[] args) throws Exception
{
    Server server = new Server(8080);
    server.setHandler(new HelloHandler());
 
    server.start();
    server.join();
}

You now know everything you need to know to write an HTTP server based on Jetty. However, complex request handling is typically built from multiple Handlers. We will look in later sections at how you can combine handlers like aspects. You can see some of the handlers available in Jetty in the org.eclipse.jetty.server.handlerpackage.

Configuring Connectors

To configure the HTTP connectors that the server uses, you can set one or more connectors on the server. You can configure each connector with details such as interface, port, buffer sizes, timeouts, etc.

The following code is based on ManyConnectors.java and shows how to set and configure connectors for the Hello World example:

public class ManyConnectors
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server();
 
        SelectChannelConnector connector0 = new SelectChannelConnector();
        connector0.setPort(8080);
        connector0.setMaxIdleTime(30000);
        connector0.setRequestHeaderSize(8192);
 
        SelectChannelConnector connector1 = new SelectChannelConnector();
        connector1.setHost("127.0.0.1");
        connector1.setPort(8888);
        connector1.setThreadPool(new QueuedThreadPool(20));
        connector1.setName("admin");
 
        SslSelectChannelConnector ssl_connector = new SslSelectChannelConnector();
        String jetty_home = 
          System.getProperty("jetty.home","../jetty-distribution/target/distribution");
        System.setProperty("jetty.home",jetty_home);
        ssl_connector.setPort(8443);
        SslContextFactory cf = ssl_connector.getSslContextFactory();
        cf.setKeyStore(jetty_home + "/etc/keystore");
        cf.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
        cf.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
 
        server.setConnectors(new Connector[]{ connector0, connector1, ssl_connector });
 
        server.setHandler(new HelloHandler());
 
        server.start();
        server.join();
    }
}

Understanding Handler Collections, Wrappers and Scopes

Complex request handling is typically built from multiple Handlers that can be combined in various ways:

  • Handler Collection holds a collection of other handlers and calls each handler in order. This is useful for combining statistics and logging handlers with the handler that generates the response.
  • Handler List is a Handler Collection that calls each handler in turn until either an exception is thrown, the response is committed or the request.isHandled() returns true. It can be used to combine handlers that conditionally handle a request.
  • Handler Wrapper is a handler base class that can be used to daisy chain handlers together in the style of aspect-oriented programming. For example, a standard web application is implemented by a chain of a context, session, security and servlet handlers.
  • Context Handler Collection uses the longest prefix of the request URI (the contextPath) to select a specific ContextHandler to handle the request.

See also How to Write a Jetty Handler.

Configuring a File Server

The following code from FileServer.java uses a HandlerList to combine the ResourceHandler with the DefaultHandler:

public class FileServer
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server();
        SelectChannelConnector connector = new SelectChannelConnector();
        connector.setPort(8080);
        server.addConnector(connector);
 
        ResourceHandler resource_handler = new ResourceHandler();
        resource_handler.setDirectoriesListed(true);
        resource_handler.setWelcomeFiles(new String[]{ "index.html" });
 
        resource_handler.setResourceBase(".");
 
        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() });
        server.setHandler(handlers);
 
        server.start();
        server.join();
    }
}

The resource handler is passed the request first and looks for a matching file in the local directory to serve. If it does not find a file, the request passes to the default handler which generates a 404 (or favicon.ico).

Configuring a File Server with XML

Now is a good time to remind you that the Jetty XML configuration format can render simple Java code into XML configuration. So the File Server example above can be written with a little reordering in Jetty XML as follows:

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
 
<Configure id="FileServer" class="org.eclipse.jetty.server.Server">
 
    <Call name="addConnector">
      <Arg>
          <New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
            <Set name="port">8080</Set>
          </New>
      </Arg>
    </Call>
 
    <Set name="handler">
      <New class="org.eclipse.jetty.server.handler.HandlerList">
        <Set name="handlers">
	  <Array type="org.eclipse.jetty.server.Handler">
	    <Item>
	      <New class="org.eclipse.jetty.server.handler.ResourceHandler">
	        <Set name="directoriesListed">true</Set>
		<Set name="welcomeFiles">
		  <Array type="String"><Item>index.html</Item></Array>
		</Set>
	        <Set name="resourceBase">.</Set>
	      </New>
	    </Item>
	    <Item>
	      <New class="org.eclipse.jetty.server.handler.DefaultHandler">
	      </New>
	    </Item>
	  </Array>
        </Set>
      </New>
    </Set>
</Configure>

You can run this XML file from the FileServerXml.java class:

public class FileServerXml
{
    public static void main(String[] args) throws Exception
    {
        Resource fileserver_xml = Resource.newSystemResource("fileserver.xml");
        XmlConfiguration configuration = new XmlConfiguration(fileserver_xml.getInputStream());
        Server server = (Server)configuration.configure();
        server.start();
        server.join();
    }
}

Using Spring to Configure a File Server

You can also use the Spring framework to assemble Jetty servers. The file server example above can be written in Spring configuration as:

<beans>
  <bean id="Server" class="org.eclipse.jetty.server.Server" init-method="start" destroy-method="stop">
 
    <property name="connectors">
      <list>
        <bean id="Connector" class="org.eclipse.jetty.server.nio.SelectChannelConnector">
          <property name="port" value="8080"/>
        </bean>
      </list>
    </property>
 
    <property name="handler">
      <bean id="handlers" class="org.eclipse.jetty.server.handler.HandlerList">
        <property name="handlers">
          <list>
            <bean class="org.eclipse.jetty.server.handler.ResourceHandler">
              <property name="directoriesListed" value="true"/>
              <property name="welcomeFiles">
                <list>
                  <value>index.html</value>
                </list>
              </property>
              <property name="resourceBase" value="."/>
            </bean>       
            <bean class="org.eclipse.jetty.server.handler.DefaultHandler"/>
          </list>
        </property>
      </bean>
    </property>
  </bean>
</beans>

See also How to Configure Jetty with Spring.

Setting Contexts

ContextHandler is a HandlerWrapper that responds only to requests that have a URI prefix that matches the configured context path.

Requests that match the context path have their path methods updated accordingly, and the following optional context features applied as appropriate:

* A Thread Context classloader.
* A set of attributes
* A set of init parameters
* A resource base (aka document root)
* A set of virtual host names

Requests that don't match are not handled.

The following code is based on OneContext.java and sets context path and classloader for the hello handler:

public class OneContext
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);
 
        ContextHandler context = new ContextHandler();
        context.setContextPath("/hello");
        context.setResourceBase(".");
        context.setClassLoader(Thread.currentThread().getContextClassLoader());
        server.setHandler(context);
 
        context.setHandler(new HelloHandler());
 
        server.start();
        server.join();
    }
}

Creating Servlets

Servlets are the standard way to provide application logic that handles HTTP requests. Servlets are like constrained Handlers with standard ways to map specific URIs to specific servlets. The following code is based on HelloServlet.java:

public class HelloServlet extends HttpServlet
{
    private String greeting="Hello World";
    public HelloServlet(){}
    public HelloServlet(String greeting)
    {
        this.greeting=greeting;
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        response.setContentType("text/html");
        response.setStatus(HttpServletResponse.SC_OK);
        response.getWriter().println("<h1>"+greeting+"</h1>");
        response.getWriter().println("session=" + request.getSession(true).getId());
    }
}

Setting a ServletContext

ServletContextHandler is a specialization of ContextHandler with support for standard servlets. The following code from OneServletContext shows three instances of the helloworld servlet registered with a ServletContextHandler:

public class OneServletContext
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);
 
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);
 
        context.addServlet(new ServletHolder(new HelloServlet()),"/*");
        context.addServlet(new ServletHolder(new HelloServlet("Buongiorno Mondo")),"/it/*");
        context.addServlet(new ServletHolder(new HelloServlet("Bonjour le Monde")),"/fr/*");
 
        server.start();
        server.join();
    }
}

Setting a Web Application Context

Web Applications context is a variation of ServletContextHandler that uses the standard layout and web.xml to configure the servlets, filters and other features:

public class OneWebApp
{
    public static void main(String[] args) throws Exception
    {
        String jetty_home = System.getProperty("jetty.home","..");
 
        Server server = new Server(8080);
 
        WebAppContext webapp = new WebAppContext();
        webapp.setContextPath("/");
        webapp.setWar(jetty_home+"/webapps/test.war");
        server.setHandler(webapp);
 
        server.start();
        server.join();
    }
}

If during development, you have not assembled your application into a WAR file, you can run it from its source components with something like:

public class OneWebAppUnassembled
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);
 
        WebAppContext context = new WebAppContext();
        context.setDescriptor(context+"/WEB-INF/web.xml");
context.setResourceBase("../test-jetty-webapp/src/main/webapp"); context.setContextPath("/"); context.setParentLoaderPriority(true);   server.setHandler(context);   server.start(); server.join(); } }

Configuring a Context Handler Collection

Context Handler Collection uses the longest prefix of the request URI (the contextPath) to select a specific context. The following example combines the previous two examples in a single Jetty server:

public class ManyContexts
{
    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);
 
        ServletContextHandler context0 = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context0.setContextPath("/ctx0");
        context0.addServlet(new ServletHolder(new HelloServlet()),"/*");
        context0.addServlet(new ServletHolder(new HelloServlet("Buongiorno Mondo")),"/it/*");
        context0.addServlet(new ServletHolder(new HelloServlet("Bonjour le Monde")),"/fr/*");
 
        WebAppContext webapp = new WebAppContext();
        webapp.setContextPath("/ctx1");
        webapp.setWar(jetty_home+"/webapps/test.war");
 
        ContextHandlerCollection contexts = new ContextHandlerCollection();
        contexts.setHandlers(new Handler[] { context0, webapp });
 
        server.setHandler(contexts);
 
        server.start();
        server.join();
    }
}

Embedding JSP

Embedding JSP support can be a bit confusing if you look at the Jars under the lib/jsp directory in the jetty distribution. This is because we have to ship from Eclipse with the JSP bundles that are marked up as OSGi bundles, which you cannot directly download from Maven Central. There are dependencies available in Maven Central that work because they were the actual source for the OSGi bundles themselves. The OSGi bundles are simply these Maven Central artifacts decomposed into a few extra bundles.

An example of what you can use follows:

[INFO] org.eclipse.jetty:jetty-jsp-2.1:jar:7.2.2-SNAPSHOT
[INFO] +- org.eclipse.jetty:jetty-util:jar:7.2.2-SNAPSHOT:provided
[INFO] +- org.mortbay.jetty:jsp-2.1-glassfish:jar:2.1.v20100127:provided
[INFO] |  +- org.eclipse.jdt.core.compiler:ecj:jar:3.5.1:provided
[INFO] |  +- org.mortbay.jetty:jsp-api-2.1-glassfish:jar:2.1.v20100127:provided
[INFO] |  \- ant:ant:jar:1.6.5:provided
[INFO] \- javax.servlet:servlet-api:jar:2.5:provided

You should be able to depend on one of the embedding aggregates that we provide to get these dependencies without too much trouble.

http://repo2.maven.org/maven2/org/eclipse/jetty/aggregate


출처 - http://wiki.eclipse.org/Jetty/Tutorial/Embedding_Jetty





고민의 시작은 사용법 괴상한 Tomcat 을 버리자는 것 하나. web.xml 없애고 spring.xml 도 없애서 자바 코드로 모든 설정을 하자는 것 하나. 웹 컨테이너 위에서 war 를 돌리는 것이 아니라 Servlet 컨테이너를 임베딩해서 평범한 자바 어플리케이션 실행시키듯이 main 메서드로 실행하자는 것 하나였습니다.

줄거리는 이러합니다. Jetty 서버를 임베딩해서 JettyServer POJO 를 만듭니다. 기존에 web.xml 에서 하던 Servlet Config 를 Java 코드로 대신합니다. servlet-spring.xml 설정들도 Spring Java Annotation 으로 대신합니다. 기존에 익숙하던 Servlet 모델 개념은 그대로 사용합니다.

기존에 Spring MVC 를 사용하시던 분들은 ContextLoaderListener, DispatcherServlet 에 익숙하실 겁니다. 아래 Jetty 설정 메서드들은 web.xml 에서 하던 것과 완전히 같은 기능을 합니다. 스터틱 파일 서비스를 위해서 Jetty 에 딸려오는 DefaultServlet 을 추가해줬습니다. JspServlet 도 추가했습니다.

package com.drypot.sleek.draft;

import org.apache.jasper.servlet.JspServlet;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.servlet.DispatcherServlet;

public class JettySetter {

    public JettySetter() {
    }

    public void configure(Server server) {
        setConnector(server);
        setHandler(server);
    }

    public void setConnector(Server server) {
        SelectChannelConnector connector = new SelectChannelConnector();
        connector.setPort(8080);
        server.addConnector(connector);
    }

    public void setHandler(Server server) {
        ServletContextHandler handler = new ServletContextHandler(ServletContextHandler.SESSIONS) {
            @Override
            protected boolean isProtectedTarget(String target) {
                while (target.startsWith("//"))
                    target = URIUtil.compactPath(target);
                return StringUtil.startsWithIgnoreCase(target, "/web-inf") || StringUtil.startsWithIgnoreCase(target, "/meta-inf");
            }
        };
        handler.setContextPath("/");
        handler.setMaxFormContentSize(512 * 1024 * 1024);
        handler.setResourceBase("/Users/drypot/Documents/java/sleek/drypot-sleek-web/src/main/webapp");
        addContextLoaderListener(handler);
        addDispatcherServlet(handler);
        addJspServlet(handler);
        addDefaultServlet(handler);
        server.setHandler(handler);
    }

    public void addContextLoaderListener(ServletContextHandler handler) {
        handler.addEventListener(new ContextLoaderListener());
        handler.setInitParameter("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
        handler.setInitParameter("contextConfigLocation", "com.drypot.sleek.draft");
    }

    public void addDispatcherServlet(ServletContextHandler handler) {
        ServletHolder holder = new ServletHolder(new DispatcherServlet());
        holder.setInitParameter("contextConfigLocation", "");
        holder.setInitOrder(3);
        handler.addServlet(holder, "/");
    }

    public void addJspServlet(ServletContextHandler handler) {
        ServletHolder holder = new ServletHolder(new JspServlet());
        holder.setInitParameter("modificationTestInterval", "5");
        holder.setInitParameter("logVerbosityLevel", "DEBUG");
        holder.setInitParameter("sendErrorToClient", "true");
        holder.setInitParameter("compilerSourceVM", "1.5");
        holder.setInitParameter("compilerTargetVM", "1.5");
        holder.setInitParameter("fork", "false");
        holder.setInitParameter("trimSpaces", "true");
        holder.setInitOrder(2);
        handler.addServlet(holder, "*.jsp");
    }

    public void addDefaultServlet(ServletContextHandler handler) {
        ServletHolder holder = new ServletHolder(new DefaultServlet());
        //holder.setInitParameter("resourceBase", "");
        holder.setInitParameter("dirAllowed", "true");
        holder.setInitParameter("welcomeServlets", "false");
        holder.setInitParameter("gzip", "false");
        handler.addServlet(holder, "*.css");
        handler.addServlet(holder, "*.js");
        handler.addServlet(holder, "*.ico");
    }

    public static void main(String[] args) {
        try {
            Server server = new Server();
            new JettySetter().configure(server);
            server.start();
            server.join();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

servlet-spring.xml 을 대신하는 스프링 설정 클래스입니다. Jetty 설정부에서 contextConfigLocation 으로 Spring Component 스캐닝 범위를 설정하므로 여기에서는 @ComponentScan 어노테이션을 뺏습니다. 평소처럼 Datasource 등을 여기에 줄줄이 매달면 됩니다.

package com.drypot.sleek.draft;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableTransactionManagement
@EnableWebMvc
public class SpringConfig extends WebMvcConfigurerAdapter {

    @Autowired
    ApplicationContext context;

    @Bean
    public String someCoolObj() {
        return "The Cool One";
    }

}

테스트에 사용했던 Spring MVC 컨트롤러입니다.

package com.drypot.sleek.draft;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class DemoController {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    @ResponseBody
    public String hello(@RequestParam String name) {
        return "Hello " + name;
    }
}

다른 글에서 자세히 언급했었는데 Java Code-base Configuration 은 Spring 3.1.0.M2 부터 가능합니다.

Spring : Code-based Configuration For Spring MVC
http://drypot.com/post/215

Spring 관련 디펜던시는

<repositories>
    <repository>
        <id>org.springframework.maven.milestone</id>
        <name>Spring Maven Milestone Repository</name>
        <url>http://maven.springframework.org/milestone</url>
    </repository>
</repositories>

<properties>
    <org.springframework.version>3.1.0.M2</org.springframework.version>
</properties>

...

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${org.springframework.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>${org.springframework.version}</version>
</dependency>

Jetty 관련 디펜던시는

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-simple</artifactId>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-servlet</artifactId>
    <version>8.0.0.v20110901</version>
</dependency>
<dependency>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>jsp-2.1-glassfish</artifactId>
    <version>2.1.v20100127</version>
</dependency>

JSP 처리하는 jasper 가 참 문제였는데, 예전 처럼 tomcat 에 포함된 jasper 를 그냥 가져다 쓰지 못합니다. Jetty 주위에 있는 jasper 수정판이 여러 가지인데 현재로서는 위에 포함한 아티펙트가 제일 쓰기 편합니다.

Jetty 를 쓰지 않고 개발자들에게 익숙한 Tomcat 을 써도 Embedded 스타일 코드를 쉽게 만들 수는 있습니다. 그런데 Tomcat 구조가 참으로 이상하여 당장 해놔야 겠다는 생각은 들지 않고 있는데, 인터넷 검색하시거나 org.apache.catalina.startup.Tomcat 클래스 사용하시면 별로 어렵지 않게(약간의 짜증을 수반? =,=) Tomcat 도 임베딩하실 수 있습니다.

2011-09-05 16:38


출처 - http://drypot.tumblr.com/post/11171558936/jetty-spring-jsp-noxml






이 글은 Jetty 6 버전 기준이긴 하지만, 플러그인 버전이나 의존 라이브러리에서 차이가 날 뿐 Jetty 7 버전에서도 크게 다르지 않다.

Jetty 서버를 임베딩하기 위한 pom.xml 설정

Maven 프로젝트에서 Jetty 서버를 임베딩하려면 먼저 Jetty 관련 클래스들을 사용할 수 있어야 하므로, 아래와 같이 의존 라이브러리에 Jetty 관련 artifact를 추가해 준다.

<dependencies>
    <dependency>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jsp-api-2.1</artifactId>
        <version>6.1.14</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>servlet-api-2.5</artifactId>
        <version>6.1.14</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty</artifactId>
        <version>6.1.14</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jsp-2.1</artifactId>
        <version>6.1.14</version>
        <scope>test</scope>
    </dependency>
    ...

위 설정에서 눈여겨 볼 점은 서블릿/JSP API를 위한 artifact는 provided 범위를 갖는 반면에 Jetty 자체를 임베딩해서 실행하기 위한 artifact는 test 범위를 갖는다는 것이다. test 범위를 갖도록 한 이유는 Jetty 임베딩을 ATDD를 위한 용도로만 사용하기 때문이다.

Jetty 서버 임베딩으로 실행하기

Maven 프로젝트 구조에서 Jetty 서버를 임베딩으로 실행하고 종료하는데 사용된 코드는 다음과 같다.

public class JettyServer {

    private static Server server;
    
    public static void start() throws Exception {
        server = new Server(9090);

        WebAppContext context = new WebAppContext();
        context.setResourceBase("src/main/webapp");
        context.setContextPath("/goodjob");
        context.setParentLoaderPriority(true);

        server.setHandler(context);
        
        server.start();
    }

    public static void stop() throws Exception {
        server.stop();
    }
}

ATDD를 수행하는 코드에서는 다음과 같이 픽스처 구성시 JettyServer.start() 메서드를 이용해서 Jetty 서버를 임베딩해서 실행하고, 테스트 완료 후 JettyServer.stop() 메서드를 이용해서 Jetty 서버를 중지시켜주면 된다.

public class EndToEndTestBase {

    @BeforeClass
    public static void init() throws Exception {
        JettyServer.start();
    }

    @AfterClass
    public static void close() throws Exception {
        JettyServer.stop();
    }

}

일단, Jetty 서버를 임베딩으로 실행하면 http://localhost:9090/goodjob/authentication/login 과 같은 URL을 이용해서 실제 기능 테스트를 수행할 수 있다. 예를 들어, 웹 기능 테스트를 위한 코드는 아래와 같은 구성을 갖게 된다.

public class AllTeamsSummaryReportEndToEndTest extends EndToEndTestBase {

    @BeforeClass
    public static void initData() throws Exception {
        // 테스트 목적을 위한 데이터 초기화
        ...
    }

    @Test
    public void queryAllTeamsWeekSummaryReport() throws Throwable {
        // HtmlUnit이나 HttpClient와 같은 라이브러리를 이용해서
        // http://localhost:9090/goodjob/testurl 등 테스트 URL에 연결하고
        // 그 결과를 검증하는 코드 삽입
    }

}


출처 - http://javacan.tistory.com/entry/Jetty-Embedding-In-Maven-Project








jetty embedding


jetty를 embed해서 사용하는 방법에 대해서 쉽게 따라해 볼 수 있는 페이지이다. 

링크 : http://wiki.eclipse.org/Jetty/Tutorial/Jetty_HelloWorld


따라해보기위해서는 물론 java가 설치되어있어야한다. 

jetty 라이브러리를 다운로드하는 방법이다. 여기서는 jetty의 모든 class가 들어있는 jar를 다운로드한다. 

mkdir Demo
cd Demo
JETTY_VERSION=7.0.2.v20100331
wget -U none http://repo1.maven.org/maven2/org/eclipse/jetty/aggregate/jetty-all/$JETTY_VERSION/jetty-all-$JETTY_VERSION.jar
wget -U none http://repo1.maven.org/maven2/javax/servlet/servlet-api/2.5/servlet-api-2.5.jar



라이브러리를 다운로드 한 directory에 아래와 같은 java코드를 작성한다. 

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
 
import java.io.IOException;
 
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
 
public class HelloWorld extends AbstractHandler
{
    public void handle(String target,
                       Request baseRequest,
                       HttpServletRequest request,
                       HttpServletResponse response) 
        throws IOException, ServletException
    {
        response.setContentType("text/html;charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        baseRequest.setHandled(true);
        response.getWriter().println("<h1>Hello World</h1>");
    }
 
    public static void main(String[] args) throws Exception
    {
        Server server = new Server(8080);
        server.setHandler(new HelloWorld());
 
        server.start();
        server.join();
    }
}


아래와 같이 클래스패스에 다운로드 받은 jar를 알려주고 컴파일 한다.

javac -cp servlet-api-2.5.jar:jetty-all-$JETTY_VERSION.jar HelloWorld.java


컴파일된 HelloWorld를 실행하면 서버가 뜬다. 

java -cp .:servlet-api-2.5.jar:jetty-all-$JETTY_VERSION.jar HelloWorld


난 20108포트로 띄웠다. 




브라우저로 접근한 모습



출처 - http://the-earth.tistory.com/149






September 21, 2010

How to embed Jetty Server

« Track window and widget events with AT-SPI | Main | AutoFS - Auto-Mount of Hard Drives »

The Jetty J2EE webcontainer represents a highly modularized server application. Because of that you are able to start the web server directly in Java code. For example that allows developers to deploy Servlets, JSP pages and WAR files within JUnit tests or use the webcontainer directly for other issues. The following example shows basically how to start and configure Jetty. 



package org.developers.blog.jetty.embedded;

import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.webapp.WebAppContext;

public class JettyExample {

    public static void main(String[] args) throws Exception {
        Server server = new Server();

        Connector connector = new SelectChannelConnector();
        connector.setPort(8080);
        connector.setHost("127.0.0.1");
        server.addConnector(connector);

        WebAppContext wac = new WebAppContext();
        wac.setContextPath("/");
        //expanded war or path of war file
        wac.setWar("./src/main/resources/web");
        server.addHandler(wac);
        server.setStopAtShutdown(true);

        //another way is to use an external jetty configuration file
        //XmlConfiguration configuration =
        //new XmlConfiguration(new File("/path/jetty-config.xml").toURL());
        //configuration.configure(server);

        server.start();
    }


출처 - http://developers-blog.org/blog/default/2010/09/21/How-to-embedd-Jetty-Server






Jetty는 Jetty Embeding에서 포스팅한 것 처럼 프로그램에 임베딩이 가능한 Web Server/라이브러리이다.

요즘 Spring에 맛을 들인 후 여기저기 뒤적뒤적 하고 있는데... Spring과 Jetty를 통합하는 예제를 우연찮게 발견했다.

Christopher J. Stehno라는 분의 포스팅을 보면 매우 쉽고 간결하게 통합할 수 있는 예제가 있다. Spring의 위력을 절감하게 되는 부분이다.

- Spring 설정

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="server.Server" class="org.mortbay.jetty.Server" destroy-method="stop">
        <property name="threadPool">
            <bean class="org.mortbay.thread.QueuedThreadPool">
                <property name="maxThreads" value="100" />
            </bean>
        </property>
        <property name="connectors">
            <list>
                <bean class="org.mortbay.jetty.nio.SelectChannelConnector">
                    <property name="port" value="8080" />
                    <property name="maxIdleTime" value="30000" />
                </bean>
            </list>
        </property>
        <property name="handler">
            <bean class="org.mortbay.jetty.handler.HandlerCollection">
                <property name="handlers">
                    <list>
                        <ref local="server.ContextHandlerCollection" />
                        <bean class="org.mortbay.jetty.handler.DefaultHandler" />
                        <bean class="org.mortbay.jetty.handler.RequestLogHandler">
                            <property name="requestLog">
                                <bean class="org.mortbay.jetty.NCSARequestLog">
                                    <constructor-arg value="cfg/logs/jetty-yyyy_mm_dd.log" />
                                    <property name="extended" value="false"/>
                                </bean>
                            </property>
                        </bean>
                    </list>
                </property>
            </bean>
        </property>

        <property name="userRealms">
            <list>
                <bean class="org.mortbay.jetty.security.HashUserRealm">
                    <property name="name" value="Test Realm" />
                    <property name="config" value="cfg/etc/realm.properties" />
                </bean>
            </list>
        </property>

        <property name="stopAtShutdown" value="true" />
        <property name="sendServerVersion" value="true"/>
    </bean>

    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="server.Server" />
        <property name="targetMethod" value="addLifeCycle" />
        <property name="arguments">
            <list><ref local="server.ContextDeployer" /></list>
       </property>
    </bean>

    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="server.Server" />
        <property name="targetMethod" value="addLifeCycle" />
        <property name="arguments">
            <list><ref local="server.WebAppDeployer" /></list>
       </property>
    </bean>

    <bean id="server.ContextHandlerCollection" class="org.mortbay.jetty.handler.ContextHandlerCollection" />

    <bean id="server.ContextDeployer" class="org.mortbay.jetty.deployer.ContextDeployer">
        <property name="contexts" ref="server.ContextHandlerCollection" />
        <property name="configurationDir">
            <bean class="org.mortbay.resource.FileResource">
                <constructor-arg value="file://./cfg/contexts" />
            </bean>
        </property>
        <property name="scanInterval" value="1" />
    </bean>

    <bean id="server.WebAppDeployer" class="org.mortbay.jetty.deployer.WebAppDeployer">
        <property name="contexts" ref="server.ContextHandlerCollection" />
        <property name="webAppDir" value="cfg/webapps" />
        <property name="parentLoaderPriority" value="false" />
        <property name="extract" value="true" />
        <property name="allowDuplicates" value="false" />
        <property name="defaultsDescriptor" value="cfg/etc/webdefault.xml" />
    </bean>
</beans>

- Main Class
public class Main {
    public static void main(String[] args) throws Exception {
        ApplicationContext context = new FileSystemXmlApplicationContext("cfg/server-context.xml");
        Server server = (Server)context.getBean("server.Server");
        server.start();
        server.join();
    }
}

여기서 설정 몇가지만 변경해 주면... 웹서버가 만들어진다.
기존에 만들었던 Embed된 소스에 즉시 적용해 봐야겠다.


출처 - http://pigmon.tistory.com/157






Posted by linuxism
,