【问题标题】:JNDI Lookup Failing For Embedded Jetty Server嵌入式 Jetty 服务器的 JNDI 查找失败
【发布时间】:2021-10-26 19:54:00
【问题描述】:

我的一个项目中有一个集成测试,我想在嵌入式码头服务器上运行。我跟着这里的例子:https://www.eclipse.org/jetty/documentation/jetty-9/index.html#jndi-embedded 但是当我去实际运行我的测试时它失败并出现错误:

javax.naming.NameNotFoundException: env is not bound; remaining name 'env/jdbc/NavDS'
    at org.eclipse.jetty.jndi.NamingContext.getContext(NamingContext.java:241)
    at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:491)
    at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:491)
    at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:505)
    at org.eclipse.jetty.jndi.java.javaRootURLContext.lookup(javaRootURLContext.java:101)
    at javax.naming.InitialContext.lookup(InitialContext.java:417)
    at com.tura.eyerep.test.TestWebServices.setUpBeforeClass(TestWebServices.java:63)

我确信我在某个地方犯了一个简单的错误,但我似乎无法发现它。任何人都可以提出我在这里做错了什么的建议吗?

在我的测试中,我正在设置服务器:

@BeforeClass
public static void setUpBeforeClass() throws Exception {
            
    server = new Server(8080);
    ClassList classList = ClassList.setServerDefault(server);
    classList.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration", "org.eclipse.jetty.plus.webapp.PlusConfiguration");
    
    WebAppContext context = new WebAppContext();
    context.setExtractWAR(false);
    context.setDescriptor("src/main/webapp/WEB-INF/web.xml");
    context.setResourceBase("src/main/webapp");
    context.setConfigurationDiscovered(false);  
    
    BasicDataSource ds = null;      
    ds = new BasicDataSource();
    ds.setUrl("jdbc:h2:mem:myDB;create=true;MODE=MSSQLServer;DATABASE_TO_UPPER=FALSE;");
    org.eclipse.jetty.plus.jndi.Resource mydatasource = new org.eclipse.jetty.plus.jndi.Resource(context, "jdbc/NavDS",ds);
    
    server.setHandler(context);
    server.start();
}

@Test
public void testLookup()
{
    InitialContext ctx = new InitialContext();
    DataSource myds = (DataSource) ctx.lookup("java:comp/env/jdbc/NavDS");
    assertNotNull( myds);
}

在我的 web.xml 中,我有一个资源引用条目:

  <resource-ref>
    <description>Nav Datasource</description>
    <res-ref-name>jdbc/NavDS</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

【问题讨论】:

    标签: java jetty jndi embedded-jetty


    【解决方案1】:

    让我们先清理你的测试用例。

    // meaningless when you have a WAR that is a directory.
    context.setExtractWAR(false); // remove this line
    
    // This prevents Servlet 3 behaviors when using Servlet 2.x descriptors
    context.setConfigurationDiscovered(false); // Remove this
    

    你得到的错误......

    javax.naming.NameNotFoundException: env is not bound; 
      remaining name 'env/jdbc/NavDS'
    

    这可能意味着服务器仍在某处运行,可能忘记停止/清理以前的服务器实例。 (查看下面的示例 junit5 测试用例,了解它如何处理非固定端口、如何引用非固定端口以及它如何停止服务器的生命周期)。

    接下来,注意你的作用域。

    new org.eclipse.jetty.plus.jndi.Resource(context, "jdbc/NavDS",ds);
    

    这会将资源条目绑定到作用域context 上的jdbc/NavDS

    这意味着如果您在 WebAppContext 范围之外查找资源, 就像你对 testLookup() 方法所做的那样,它将存在于 initialContext.lookup("jdbc/NavDS"),而在其他任何地方,java:comp/env 前缀/树甚至不存在于 testLookup() 方法范围内。

    在您的 web 应用程序内部,例如在过滤器或 Servlet 中,该上下文特定资源被绑定并在 jdbc:comp/env/jdbc/NavDS 可用。

    你有 3 个典型的作用域。

    Order Scope EnvEntry or Resource first parameter
    1 WebApp new EnvEntry(webappContext, ...) or new Resource(webappContext, ...)
    2 Server new EnvEntry(server, ...) or new Resource(server, ...)
    3 JVM new EnvEntry(null, ...) or new Resource(null, ...)

    如果该值在 WebApp 范围内不存在,则检查服务器范围,然后检查 JVM 范围。

    您的服务器可以有一个名称 val/foo 的值,而特定的 web 应用程序可以有一个 不同 的相同名称 val/foo 的值,这取决于范围的定义方式。

    接下来是 Servlet 规范中的绑定,您已经指定了 &lt;resource-ref&gt;,这与服务器端的声明相结合,绑定到 context 意味着您可以从特定 web 应用程序中的 servlet 访问 java:comp/env/jdbc/NavDS .

    以不同的方式看待这一点,在代码中......

    package jetty.jndi;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintStream;
    import java.net.HttpURLConnection;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Paths;
    import java.util.List;
    import javax.naming.InitialContext;
    import javax.naming.NameNotFoundException;
    import javax.naming.NamingException;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.eclipse.jetty.plus.webapp.EnvConfiguration;
    import org.eclipse.jetty.plus.webapp.PlusConfiguration;
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.util.IO;
    import org.eclipse.jetty.util.component.LifeCycle;
    import org.eclipse.jetty.util.resource.PathResource;
    import org.eclipse.jetty.webapp.Configuration;
    import org.eclipse.jetty.webapp.FragmentConfiguration;
    import org.eclipse.jetty.webapp.WebAppContext;
    import org.junit.jupiter.api.AfterAll;
    import org.junit.jupiter.api.BeforeAll;
    import org.junit.jupiter.api.Test;
    
    public class WebAppWithJNDITest
    {
        private static Server server;
        private static WebAppContext context;
    
        public static class JndiDumpServlet extends HttpServlet
        {
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
            {
                resp.setCharacterEncoding("utf-8");
                resp.setContentType("text/plain");
                PrintStream out = new PrintStream(resp.getOutputStream(), false, StandardCharsets.UTF_8);
                try
                {
                    dumpJndi(out);
                }
                catch (NamingException e)
                {
                    throw new ServletException(e);
                }
            }
        }
    
        @BeforeAll
        public static void startServer() throws Exception
        {
            server = new Server(0); // let os/jvm pick a port
            Configuration.ClassList classList = Configuration.ClassList.setServerDefault(server);
            classList.addAfter(FragmentConfiguration.class.getName(),
                EnvConfiguration.class.getName(),
                PlusConfiguration.class.getName());
    
            context = new WebAppContext();
            context.setContextPath("/");
            // This directory only has WEB-INF/web.xml
            context.setBaseResource(new PathResource(Paths.get("src/main/webroots/jndi-root")));
            context.addServlet(JndiDumpServlet.class, "/jndi-dump");
    
    
            new org.eclipse.jetty.plus.jndi.Resource(null, "val/foo", Integer.valueOf(707));
            new org.eclipse.jetty.plus.jndi.Resource(server, "val/foo", Integer.valueOf(808));
            new org.eclipse.jetty.plus.jndi.Resource(context, "val/foo", Integer.valueOf(909));
    
            new org.eclipse.jetty.plus.jndi.EnvEntry(null, "entry/foo", Integer.valueOf(440), false);
            new org.eclipse.jetty.plus.jndi.EnvEntry(server, "entry/foo", Integer.valueOf(550), false);
            new org.eclipse.jetty.plus.jndi.EnvEntry(context, "entry/foo", Integer.valueOf(660), false);
    
            server.setHandler(context);
            server.start();
        }
    
        @AfterAll
        public static void stopServer()
        {
            LifeCycle.stop(server);
        }
    
        public static void dumpJndi(PrintStream out) throws NamingException
        {
            InitialContext ctx = new InitialContext();
    
            List<String> paths = List.of("val/foo", "entry/foo");
            List<String> prefixes = List.of("java:comp/env/", "");
    
            for (String prefix : prefixes)
            {
                for (String path : paths)
                {
                    try
                    {
                        Integer val = (Integer)ctx.lookup(prefix + path);
                        out.printf("lookup(\"%s%s\") = %s%n", prefix, path, val);
                    }
                    catch (NameNotFoundException e)
                    {
                        out.printf("lookup(\"%s%s\") = NameNotFound: %s%n", prefix, path, e.getMessage());
                    }
                }
            }
        }
    
        @Test
        public void testLookup() throws NamingException, IOException
        {
            System.out.println("-- Dump from WebApp Scope");
            HttpURLConnection http = (HttpURLConnection)server.getURI().resolve("/jndi-dump").toURL().openConnection();
            try (InputStream in = http.getInputStream())
            {
                String body = IO.toString(in, StandardCharsets.UTF_8);
                System.out.println(body);
            }
    
            System.out.println("-- Dump from Test scope");
            dumpJndi(System.out);
        }
    }
    

    src/main/webroot/jndi-root/WEB-INF/web.xml的内容

    <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">
    
      <resource-ref>
        <description>My Foo Resource</description>
        <res-ref-name>val/foo</res-ref-name>
        <res-type>java.lang.Integer</res-type>
        <res-auth>Container</res-auth>
      </resource-ref>
    </web-app>
    

    输出看起来像...

    2021-10-26 17:05:16.834:INFO:oejs.Server:main: jetty-9.4.44.v20210927; built: 2021-06-30T11:07:22.254Z; git: 526006ecfa3af7f1a27ef3a288e2bef7ea9dd7e8; jvm 11.0.12+7
    2021-10-26 17:05:17.012:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@19932c16{/,file:///home/joakim/code/jetty/junk/src/main/webroots/jndi-root/,AVAILABLE}
    2021-10-26 17:05:17.033:INFO:oejs.AbstractConnector:main: Started ServerConnector@212b5695{HTTP/1.1, (http/1.1)}{0.0.0.0:45387}
    2021-10-26 17:05:17.034:INFO:oejs.Server:main: Started @816ms
    -- Dump from WebApp Scope
    lookup("java:comp/env/val/foo") = 909
    lookup("java:comp/env/entry/foo") = 660
    lookup("val/foo") = 707
    lookup("entry/foo") = 440
    
    -- Dump from Test scope
    lookup("java:comp/env/val/foo") = NameNotFound: env is not bound
    lookup("java:comp/env/entry/foo") = NameNotFound: env is not bound
    lookup("val/foo") = 707
    lookup("entry/foo") = 440
    2021-10-26 17:05:17.209:INFO:oejs.AbstractConnector:main: Stopped ServerConnector@212b5695{HTTP/1.1, (http/1.1)}{0.0.0.0:0}
    2021-10-26 17:05:17.210:INFO:oejs.session:main: node0 Stopped scavenging
    2021-10-26 17:05:17.214:INFO:oejsh.ContextHandler:main: Stopped o.e.j.w.WebAppContext@19932c16{/,file:///home/joakim/code/jetty/junk/src/main/webroots/jndi-root/,STOPPED}
    

    希望上面的范围差异很明显。

    Eclipse Jetty Embedded Cookbook 项目现在提供了上述测试的变体。

    https://github.com/jetty-project/embedded-jetty-cookbook/

    提供 3 种不同的 Jetty 口味

    【讨论】:

      猜你喜欢
      • 2016-06-23
      • 1970-01-01
      • 2019-03-19
      • 2013-06-29
      • 2018-09-18
      • 1970-01-01
      • 1970-01-01
      • 2020-05-18
      • 2012-04-21
      相关资源
      最近更新 更多