2014-12-20 2 views
0

У меня есть приложение, использующее встроенный Jetty 9.2.6 с аннотированными асинхронными сервлетами (и я использую Facelets для создавать шаблоны интерфейса). Существует случайное исключение, которое возникает, когда я обращаюсь к любому сервлету с asyncSupported = true.Embedded Jetty 9 с асинхронными сервлетами генерирует NullPointerException в org.eclipse.jetty.server.Request.extractFormParameters (Request.java:326)

Вот один трассировки стека случайного исключения:

09:31:42.801 [qtp1262773598-20] DEBUG c.d.a.v.c.CitiesPerStateServlet - APPTEST-BUG 
java.lang.NullPointerException: null 
    at org.eclipse.jetty.server.Request.extractFormParameters(Request.java:326) ~[jetty-server-9.2.6.v20141205.jar:9.2.6.v20141205] 
    at org.eclipse.jetty.server.Request.extractContentParameters(Request.java:302) ~[jetty-server-9.2.6.v20141205.jar:9.2.6.v20141205] 
    at org.eclipse.jetty.server.Request.extractParameters(Request.java:256) ~[jetty-server-9.2.6.v20141205.jar:9.2.6.v20141205] 
    at org.eclipse.jetty.server.Request.getParameter(Request.java:827) ~[jetty-server-9.2.6.v20141205.jar:9.2.6.v20141205] 
    at com.doitlabs.app99vendas.view.controller.CitiesPerStateServlet$1.run(CitiesPerStateServlet.java:55) ~[classes/:na] 
    at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1173) [jetty-server-9.2.6.v20141205.jar:9.2.6.v20141205] 
    at org.eclipse.jetty.server.AsyncContextState$2.run(AsyncContextState.java:168) [jetty-server-9.2.6.v20141205.jar:9.2.6.v20141205] 
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:626) [jetty-util-9.2.6.v20141205.jar:9.2.6.v20141205] 
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:546) [jetty-util-9.2.6.v20141205.jar:9.2.6.v20141205] 
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_20] 

Вот мой сервлет, на который ссылается в трассировки стека:

@WebServlet(urlPatterns = { "/controllers/cities-per-state" }, asyncSupported = true) 
public class CitiesPerStateServlet extends HttpServlet { 

    final Logger LOGGER = LoggerFactory.getLogger(CitiesPerStateServlet.class); 

    @Override 
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
     this.processRequest(req, resp); 
    } 

    @Override 
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
     this.processRequest(req, resp); 
    } 

    private void processRequest(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 

     AsyncContext ac = req.startAsync(req, resp); 

     ac.start(new Runnable() { 

      @Override 
      public void run() { 

       HttpServletRequest req_ = (HttpServletRequest)ac.getRequest(); 

       try { 

        String   stateId = req_.getParameter("state_id"); 
        Collection<City> cities = null; 

        if (stateId != null) { 
         cities = CityDAO.retrieveByState(new Long(stateId)); 
        } 

        req_.setAttribute("cities", cities); 
        ac.dispatch("/pages/system-ops/campaign/cities-per-state.xhtml"); 

       } catch (Exception e) { 

        if (LogUtil.shouldLog(e)) { 
         LOGGER.debug(BUG, e); 
        }      
       }      
      } 
     });  
    } 
} 

Вот как я начинаю внедренного Jetty:

public class Main { 

    public static void main(String[] args) throws Exception { 

     String webappDirLocation = "./src/main/webapp/"; 

     String webPort = System.getenv("PORT"); 
     if (webPort == null || webPort.isEmpty()) { 
      webPort = "8080"; 
     } 

     Server server = new Server(Integer.valueOf(webPort)); 

     ClassList classlist = org.eclipse.jetty.webapp.Configuration.ClassList.setServerDefault(server); 
     classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration", "org.eclipse.jetty.annotations.AnnotationConfiguration"); 

     WebAppContext context = new WebAppContext(); 
     context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*/classes/.*"); 
     context.setDescriptor(webappDirLocation + "/WEB-INF/web.xml"); 
     context.setBaseResource(new ResourceCollection(new String[] { webappDirLocation, "./target" })); 
     context.setResourceAlias("/WEB-INF/classes/", "/classes/"); 
     context.setContextPath("/"); 
     context.setParentLoaderPriority(true); 

     server.setHandler(context); 

     server.start(); 
     server.join(); 
    } 

} 

файл web.xml:

<?xml version="1.0" encoding="UTF-8"?> 

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" 
      version="3.1"> 

    <!-- Parameters ######################################################## --> 
    <context-param> 
     <param-name>javax.faces.DEFAULT_SUFFIX</param-name> 
     <param-value>.xhtml</param-value> 
    </context-param> 
    <context-param> 
     <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name> 
     <param-value>true</param-value> 
    </context-param> 

    <!-- Servlet mappings ################################################## --> 
    <servlet-mapping> 
     <servlet-name>Faces Servlet</servlet-name> 
     <url-pattern>*.xhtml</url-pattern> 
    </servlet-mapping> 
    <servlet> 
     <servlet-name>Faces Servlet</servlet-name> 
     <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> 
     <load-on-startup>1</load-on-startup> 
    </servlet> 

    <!-- Listerners ######################################################## --> 
    <listener> 
     <listener-class> 
      com.sun.faces.config.ConfigureListener 
     </listener-class> 
    </listener> 

    <!-- Session config #################################################### --> 
    <session-config> 
     <session-timeout>15</session-timeout> 
    </session-config> 


    <!-- Welcome ########################################################### --> 
    <welcome-file-list> 
     <welcome-file>index.xhtml</welcome-file> 
    </welcome-file-list> 

</web-app> 

Это проект Maven, так вот мой pom.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>com.doitlabs.app99vendas</groupId> 
    <version>1.0-SNAPSHOT</version> 
    <name>app99vendas.net</name> 
    <artifactId>99vendas.net</artifactId> 
    <packaging>jar</packaging> 

    <properties> 
     <java.version>1.8</java.version> 
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
     <maven.compiler.source>1.8</maven.compiler.source> 
     <maven.compiler.target>1.8</maven.compiler.target> 
    </properties> 

    <dependencies> 

     <dependency> 
      <groupId>org.eclipse.jetty</groupId> 
      <artifactId>jetty-server</artifactId> 
      <version>9.2.6.v20141205</version> 
     </dependency>   
     <dependency> 
      <groupId>org.eclipse.jetty</groupId> 
      <artifactId>jetty-jsp</artifactId> 
      <version>9.2.6.v20141205</version> 
     </dependency> 
     <dependency> 
      <groupId>org.eclipse.jetty</groupId> 
      <artifactId>jetty-maven-plugin</artifactId> 
      <version>9.2.6.v20141205</version> 
     </dependency> 

     <dependency> 
      <groupId>com.sun.faces</groupId> 
      <artifactId>jsf-api</artifactId> 
      <version>2.2.8</version> 
     </dependency> 
     <dependency> 
      <groupId>com.sun.faces</groupId> 
      <artifactId>jsf-impl</artifactId> 
      <version>2.2.8</version> 
     </dependency> 

     <dependency> 
      <groupId>org.eclipse.persistence</groupId> 
      <artifactId>org.eclipse.persistence.jpa</artifactId> 
      <version>2.5.2</version> 
     </dependency>   
     <dependency> 
      <groupId>org.postgresql</groupId> 
      <artifactId>postgresql</artifactId> 
      <version>9.3-1102-jdbc41</version> 
     </dependency> 

     <dependency> 
      <groupId>com.mandrillapp.wrapper.lutung</groupId> 
      <artifactId>lutung</artifactId> 
      <version>0.0.5</version> 
     </dependency> 
     <dependency> 
      <groupId>com.google.code.gson</groupId> 
      <artifactId>gson</artifactId> 
      <version>2.3</version> 
     </dependency>      
     <dependency> 
      <groupId>org.apache.httpcomponents</groupId> 
      <artifactId>httpclient</artifactId> 
      <version>4.3.5</version> 
     </dependency>     
     <dependency> 
      <groupId>commons-io</groupId> 
      <artifactId>commons-io</artifactId> 
      <version>2.4</version> 
     </dependency> 
     <dependency> 
      <groupId>commons-logging</groupId> 
      <artifactId>commons-logging</artifactId> 
      <version>1.2</version> 
     </dependency> 

     <dependency> 
      <groupId>com.thoughtworks.xstream</groupId> 
      <artifactId>xstream</artifactId> 
      <version>1.4.7</version> 
     </dependency> 

     <dependency> 
      <groupId>ch.qos.logback</groupId> 
      <artifactId>logback-classic</artifactId> 
      <version>1.0.13</version> 
     </dependency> 

    </dependencies> 

    <build> 
     <plugins> 

      <plugin> 
       <groupId>org.apache.maven.plugins</groupId> 
       <artifactId>maven-dependency-plugin</artifactId> 
       <version>2.8</version> 
       <executions> 
        <execution> 
         <id>copy-dependencies</id> 
         <phase>package</phase> 
         <goals> 
          <goal>copy-dependencies</goal> 
         </goals> 
        </execution> 
       </executions> 
      </plugin> 

     </plugins> 
    </build> 
</project> 

Кто-нибудь есть ключ, почему это происходит?

Если вам нужна дополнительная информация, то дайте мне знать.

Заранее благодарен!

ответ

3

Доступ к HttpServletRequest и HttpServletResponse объектам в AsyncContext является не такими же, как доступ к внешнему из AsyncContext (они не являются поточно, и даже могут быть переработаны до AsyncContext жизненного цикла завершен). Фактически, большинство контейнеров перерабатывают то, что они могут для причин сбора мусора, как только начинается AsyncContext.

Пара это вместе с HttpServletRequest.getParameter(String name) поведения (это необходимо прочитать содержимое тела запроса, а также для получения каких-либо возможных параметров), и у Вас есть ситуация, когда вам необходимо изменить свой код.

Было бы посоветовал читать то, что вы можете с HttpServletRequestперед тем вы звоните startAsync() и передать эту информацию в ваш AsyncListener.onStartAsync() или Runnable реализации.

+0

спасибо, @Joakim Erdfelt. Была деталь, о которой я не знал, потому что многие примеры, которые я видел во многих блогах. Один последний вопрос, внутри асинхронного контекста (т. Е. Метод run), после некоторой обработки, если мне нужно поместить некоторые данные на HttpSession, будет безопасно вызывать ((HttpServletRequest) ac.getRequest()). GetSession(). setAttribute ("SOME_DATA", объект)? – reinaldoluckman