Статьи

Собираюсь REST: встраивание Tomcat с Spring и JAX-RS (Apache CXF)

Этот пост является логическим продолжением предыдущего . Единственное отличие — это контейнер, который мы собираемся использовать: вместо Jetty это будет наш старый приятель Apache Tomcat . Удивительно, но было очень легко встроить последнюю версию Apache Tomcat 7, поэтому позвольте мне показать это сейчас. Я не буду повторять последний пост в полном объеме, поскольку нет никаких изменений, за исключением файла POM и класса Starter . Помимо этих двух, мы повторно используем все, что мы сделали раньше. Для файла POM нам нужно удалить зависимости Jetty и заменить их на Apache Tomcat . Первое изменение будет в разделе свойств , мы заменим org.eclipse.jetty.version на org.apache.tomcat .

Итак, эта строка:

1
<org.eclipse.jetty.version>8.1.8.v20121106</org.eclipse.jetty.version>

будет выглядеть так:

1
<org.apache.tomcat>7.0.34</org.apache.tomcat>

Вторым изменением будут сами зависимости, мы заменим эти строки:

01
02
03
04
05
06
07
08
09
10
11
<dependency>
    <groupid>org.eclipse.jetty</groupid>
    <artifactid>jetty-server</artifactid>
    <version>${org.eclipse.jetty.version}</version>
</dependency>
      
<dependency>
    <groupid>org.eclipse.jetty</groupid>
    <artifactid>jetty-webapp</artifactid>
    <version>${org.eclipse.jetty.version</version>
</dependency>

с этими:

01
02
03
04
05
06
07
08
09
10
11
<dependency>
    <groupid>org.apache.tomcat.embed</groupid>
    <artifactid>tomcat-embed-core</artifactid>
    <version>${org.apache.tomcat}</version>
</dependency>
   
<dependency>
    <groupid>org.apache.tomcat.embed</groupid>
    <artifactid>tomcat-embed-logging-juli</artifactid>
    <version>${org.apache.tomcat}</version>
</dependency>

Отлично, эта часть сделана. Последняя часть посвящена изменениям в реализации нашего основного класса, где мы заменим Jetty на Apache Tomcat .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.example;
 
import java.io.File;
import java.io.IOException;
 
import org.apache.catalina.Context;
import org.apache.catalina.loader.WebappLoader;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.transport.servlet.CXFServlet;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
 
import com.example.config.AppConfig;
 
public class Starter {
    private final static Log log = LogFactory.getLog( Starter.class );
  
    public static void main(final String[] args) throws Exception {
        final File base = createBaseDirectory();
        log.info( "Using base folder: " + base.getAbsolutePath() );
   
        final Tomcat tomcat = new Tomcat();
        tomcat.setPort( 8080 );
        tomcat.setBaseDir( base.getAbsolutePath() );
   
        Context context = tomcat.addContext( "/", base.getAbsolutePath() );
        Tomcat.addServlet( context, "CXFServlet", new CXFServlet() );
   
        context.addServletMapping( "/rest/*", "CXFServlet" );
        context.addApplicationListener( ContextLoaderListener.class.getName() );
        context.setLoader( new WebappLoader( Thread.currentThread().getContextClassLoader() ) );
   
        context.addParameter( "contextClass", AnnotationConfigWebApplicationContext.class.getName() );
        context.addParameter( "contextConfigLocation", AppConfig.class.getName() );
    
        tomcat.start();
        tomcat.getServer().await();
    }
 
    private static File createBaseDirectory() throws IOException {
        final File base = File.createTempFile( "tmp-", "" );
   
        if( !base.delete() ) {
            throw new IOException( "Cannot (re)create base folder: " + base.getAbsolutePath()  );
        }
   
        if( !base.mkdir() ) {
            throw new IOException( "Cannot create base folder: " + base.getAbsolutePath()  );        
        }
   
        return base;
    }
}

Код выглядит довольно простым, но многословным из-за того, что кажется невозможным запустить Apache Tomcat во встроенном режиме без указания какого-либо рабочего каталога. Небольшая функция createBaseDirectory () создает временную папку, которую мы передаем в Apache Tomcat в качестве baseDir . Реализация показывает, что мы запускаем экземпляр сервера Apache Tomcat на порту 8080 , настраиваем сервлет Apache CXF для обработки всех запросов по пути / rest / * , добавляем прослушиватель контекста Spring и, наконец, запускаем сервер.

После того, как мы построили проект в виде толстой или одной банки , у нас есть полноценный сервер, на котором размещено наше приложение JAR-RS :

1
2
mvn clean package
java -jar target/spring-one-jar-0.0.1-SNAPSHOT.one-jar.jar

И мы должны увидеть результат так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Jan 28, 2013 5:54:56 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ['http-bio-8080']
Jan 28, 2013 5:54:56 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
Jan 28, 2013 5:54:56 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.34
Jan 28, 2013 5:54:56 PM org.apache.catalina.startup.DigesterFactory register
WARNING: Could not get url for /javax/servlet/jsp/resources/jsp_2_0.xsd
Jan 28, 2013 5:54:56 PM org.apache.catalina.startup.DigesterFactory register
WARNING: Could not get url for /javax/servlet/jsp/resources/jsp_2_1.xsd
Jan 28, 2013 5:54:56 PM org.apache.catalina.startup.DigesterFactory register
WARNING: Could not get url for /javax/servlet/jsp/resources/jsp_2_2.xsd
Jan 28, 2013 5:54:56 PM org.apache.catalina.startup.DigesterFactory register
WARNING: Could not get url for /javax/servlet/jsp/resources/web-jsptaglibrary_1_1.dtd
Jan 28, 2013 5:54:56 PM org.apache.catalina.startup.DigesterFactory register
WARNING: Could not get url for /javax/servlet/jsp/resources/web-jsptaglibrary_1_2.dtd
Jan 28, 2013 5:54:56 PM org.apache.catalina.startup.DigesterFactory register
WARNING: Could not get url for /javax/servlet/jsp/resources/web-jsptaglibrary_2_0.xsd
Jan 28, 2013 5:54:56 PM org.apache.catalina.startup.DigesterFactory register
WARNING: Could not get url for /javax/servlet/jsp/resources/web-jsptaglibrary_2_1.xsd
Jan 28, 2013 5:54:57 PM org.apache.catalina.loader.WebappLoader setClassPath
INFO: Unknown loader com.simontuffs.onejar.JarClassLoader@187a84e4 class com.simontuffs.onejar.JarClassLoader
Jan 28, 2013 5:54:57 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring root WebApplicationContext
Jan 28, 2013 5:54:57 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization started
Jan 28, 2013 5:54:58 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing Root WebApplicationContext: startup date [Mon Jan 28 17:54:58 EST 2013]; root of context hierarchy
Jan 28, 2013 5:54:58 PM org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider registerDefaultFilters
INFO: JSR-330 'javax.inject.Named' annotation found and supported for component scanning
Jan 28, 2013 5:54:58 PM org.springframework.web.context.support.AnnotationConfigWebApplicationContext loadBeanDefinitions
INFO: Successfully resolved class for [com.example.config.AppConfig]
Jan 28, 2013 5:54:58 PM org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
 
INFO: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
Jan 28, 2013 5:54:58 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@62770d2e: defining beans [org.springframework.context.annotation.internal
ConfigurationAnnotationProcessor,org.springframework.context.annotation.
internalAutowiredAnnotationProcessor,org.springframework.context.annotation.
internalRequiredAnnotationProces
sor,org.springframework.context.annotation.internalCommonAnnotationProcessor,
appConfig,org.springframework.context.annotation.ConfigurationClassPostProcessor.
importAwareProcessor,
cxf,jaxRsServer,jaxRsApiApplication,peopleRestService,peopleService,jsonProvider]; root of factory hierarchy
Jan 28, 2013 5:54:59 PM org.apache.cxf.endpoint.ServerImpl initDestination
INFO: Setting the server's publish address to be /api
Jan 28, 2013 5:54:59 PM org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 1747 ms
Jan 28, 2013 5:54:59 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ['http-bio-8080']

Давайте выполним несколько HTTP-запросов, чтобы убедиться, что все работает так, как мы ожидали:

01
02
03
04
05
06
07
08
09
10
11
> curl http://localhost:8080/rest/api/people?page=2
[
  {'email':'person+6@at.com','firstName':null,'lastName':null},
  {'email':'person+7@at.com','firstName':null,'lastName':null},
  {'email':'person+8@at.com','firstName':null,'lastName':null},
  {'email':'person+9@at.com','firstName':null,'lastName':null},
  {'email':'person+10@at.com','firstName':null,'lastName':null}
]
 
> curl http://localhost:8080/rest/api/people -X PUT -d 'email=a@b.com'
{'email':'a@b.com','firstName':null,'lastName':null}

И мы все еще на 100% свободны от XML ! Одно важное замечание : мы создаем временную папку каждый раз, но никогда не удаляем ее (вызов deleteOnShutdown для базы не работает должным образом для непустых папок). Пожалуйста, имейте это в виду (например, добавьте свой собственный способ отключения), так как я решил оставить код чистым.

Ссылка: http://aredko.blogspot.gr/2013/01/going-rest-embedding-tomcat-with-spring.html от нашего партнера по JCG Андрея Редько в блоге Андрея Редько .