带有异步Servlet的嵌入式Jetty 9会在org.eclipse.jetty.server.Request.extractFormParameters(Request.java:326)中抛出NullPointerException。

时间:2014-12-20 12:46:39

标签: jetty embedded-jetty servlet-3.1

我有一个使用嵌入式Jetty 9.2.6的应用程序,带有带注释的异步Servlet(我使用Facelets创建接口模板)。当我使用asyncSupported = true访问任何Servlet时会发生随机异常。

以下是随机异常的一个堆栈跟踪:

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]

这是我在堆栈跟踪中引用的Servlet:

@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>

有没有人知道为什么会这样?

如果您需要更多信息,请与我们联系。

提前致谢!

1 个答案:

答案 0 :(得分:3)

HttpServletRequestHttpServletResponseAsyncContext个对象的访问 AsyncContext之外的访问权限相同(他们不是线程安全,甚至可以在AsyncContext生命周期完成之前回收。事实上,一旦AsyncContext启动,大多数容器都会回收垃圾收集的原因。

将此与HttpServletRequest.getParameter(String name)行为结合在一起(此需要以读取请求正文内容以获取任何可能的参数),并且您需要更改代码。

建议您在致电HttpServletRequest 之前阅读startAsync() 中的内容,并将该信息传递到AsyncListener.onStartAsync()或{{1}实施。