3

I would like to bind DataSource object to (eclipse) jetty's JNDI context programatically. I need for testing purpose. Here's a piece of code I have now:

server = new Server(SERVER_PORT);
webAppContext = new WebAppContext();
webAppContext.setResourceBase(".");
webAppContext.setContextPath("/" + SERVER_CONTEXT);
webAppContext.addEventListener(prepareServletContextListener());
webAppContext.addFilter(GuiceFilter.class, "/*", null);
webAppContext.addServlet(DefaultServlet.class, "/");
Resource r = new Resource(webAppContext,"jdbc/testDS",createDataSource());

server.setHandler(webAppContext);
server.start();

Of course the line with Resource isn't working.I have no idea how to bind it programatically to obtain sth similar to:

<New id="DSTest" class="org.eclipse.jetty.plus.jndi.Resource">
 <Arg></Arg>
 <Arg>jdbc/DSTest</Arg>
 <Arg>
    <New class="com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource">
       <Set name="Url">jdbc:mysql://localhost:3306/databasename</Set>
       <Set name="User">user</Set>
       <Set name="Password">pass</Set>
    </New>
 </Arg>
</New>

Any help would be greatly appreciated.

Opal
  • 81,889
  • 28
  • 189
  • 210

3 Answers3

4

you need to add the defined resource object as attribute to the server object. The following should work:

Resource r = new Resource("jdbc/testDS",createDataSource());
server.setAttribute("myDs", r);
langm
  • 171
  • 7
2

I encountered the same issue and here is what I did to fix it:

    server = new Server(PORT);
    WebAppContext context = new WebAppContext();
    context.setContextPath(CONTEXT);
    context.setConfigurationClasses(new String[] { "org.eclipse.jetty.plus.webapp.PlusConfiguration",
            "org.eclipse.jetty.webapp.FragmentConfiguration" });
    // A filter needed by Guice, but this is independent
    context.addFilter(GuiceFilter.class, "/*", 0);
    PGSimpleDataSource simpleDataSource = new PGSimpleDataSource();
    simpleDataSource.setDatabaseName("db-name");
    simpleDataSource.setUser("user");
    simpleDataSource.setPassword("pwd");
    String jndiName = "jdbc/myDS";
    Resource resource = new Resource("java:comp/env/" + jndiName, simpleDataSource);
    server.setHandler(context);
    // an event listener (because I use Guice, but this is independent)
    GuiceServletConfig guiceServletConfig = new GuiceServletConfig();
    context.addEventListener(guiceServletConfig);
    server.start();

This requires to have the following additional libraries:

  • jetty-jndi
  • jetty-plus

I tested this code on Embedded Jetty 7 (7.6.10.v20130312)

Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
1

The topic is old but I have encountered the same issue. Unfortunately existing answers provide no solution.

When you create a Resource specifying an instance of the DataSource, Jetty will not provide this instance when JNDI resource is requested. Instead constructor of Resource makes an attempt to transform the instance to some kind of "recipe" (javax.naming.Reference to be precise) that tells how to build the new instance of configuration with exact same internals.

It works well in case of basic classes but fails to recreate complex data structures (it failed with List<String> in my case). So you end up not getting:

  • the exact same instance (as you expect of a singleton)
  • the exact equivalent of the instance (as you expect of properly working JNDI)

I have implemented a wrapper that allows providing the instance specified when creating the Resource.

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;

public class ExistingInstanceReference extends Reference implements ObjectFactory {

    private Object instance;

    private static final long serialVersionUID = -2718709718400909747L;

    public ExistingInstanceReference() {
        super(null);
    }

    public ExistingInstanceReference(Object instance) {
        super(instance.getClass().getName(), ExistingInstanceReference.class.getName(), null);
        this.instance = instance;
    }

    @Override
    public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> environment) throws Exception {
        if (obj == null) {
            return null;
        }
        ExistingInstanceReference reference = (ExistingInstanceReference) obj;
        return reference.instance;
    }

}

Note that this implementation fails to comply with Serializable interface as instance variable may contain non-serializable object.

Usage:

Resource r = new Resource(webAppContext, "jdbc/testDS", new ExistingInstanceReference(createDataSource()));

Please, consider this solution as a workaround because the real issue is still to be fixed somewhere in Jetty's sources. Unfortunately I have no time to trace this misbehavior to the root.

In addition, I had to set webAppContext.setParentLoaderPriority(true) because the webapp's classloader didn't see ExistingInstanceReference class in my setup.

Oleg Kurbatov
  • 1,376
  • 1
  • 19
  • 32