I think I have a fair idea what my problem is here, but have absolutely NO idea how I might fix it...
Here is how I am starting my application in dropwizard :
@Override
public void run(ServerConfiguration configuration, Environment environment)
{
// Setting up the database.
final DBIFactory factory = new DBIFactory();
final DBI jdbi = factory.build(environment, configuration.getDataSourceFactory(), "mysql");
//Hibernate
final UserDAO dao = new UserDAO(hibernate.getSessionFactory());
environment.jersey().register(new UserResource(dao));
final TemplateHealthCheck healthCheck = new TemplateHealthCheck(configuration.getTemplate());
environment.healthChecks().register("template", healthCheck);
// security
//****** Dropwizard security - custom classes ***********/
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new BasicAuth(dao))
.setAuthorizer(new BasicAuthorizer())
.setRealm("BASIC-AUTH-REALM")
.buildAuthFilter()));
environment.jersey().register(RolesAllowedDynamicFeature.class);
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
Now, as you can see here, I am passing my User dao into my authenticator... No tutorial I have seen online does this, and this is because every tutorial online uses hardcoded values instead of showing how to query the DB.
That said, here is how I am trying to authenticate...
public class BasicAuth implements Authenticator<BasicCredentials, User> {
UserDAO _userDAO;
final Encryption enc = new Encryption();
public BasicAuth(UserDAO dao)
{
this._userDAO = dao;
}
@Override
public Optional<User> authenticate(BasicCredentials credentials)
throws AuthenticationException {
// Get the user record.
User requestedUser = _userDAO.findOneByUsername(credentials.getUsername());
if (requestedUser != null)
{
// check pw.
if(enc.compare(credentials.getPassword(), requestedUser.getPassword())) {
return Optional.of(requestedUser);
}
else {
return Optional.empty();
}
}
return Optional.empty();
}
}
please excuse the terrible indentation above, I pasted my code here from intelliJ and it just isn't behaving well - anyways, when I try to run this application, the authenticator tells me :
No session currently bound to execution context
Here is the kicker, I know this is only for the security aspect of this that I am getting this error, because if I remove the security lines from the Application class, and run it up, I can still hit my create user endpoints (which also use this DAO) and that works well.
So my question here really, is - Am I meant to use that dao in the authenticator? If not, how the hell am I meant to query the database?
If I am, then where am I going wrong?
Thanks in advance.
What is Dropwizard? Dropwizard is an open source Java framework for developing ops-friendly, high-performance RESTful backends. It was developed by Yammer to power their JVM based backend. Dropwizard provides best-of-breed Java libraries into one embedded application package.
The bare bone spring boot app starts in 1.64 seconds whereas the bare bone Dropwizard app took 1.526 seconds to startup. Spring Boot consumes much more memory. This was true. Spring Boot loaded 7591 classes whereas Dropwizard loaded 6255 classes.
Dropwizard is an open-source Java framework used for the fast development of high-performance RESTful web services. It gathers some popular libraries to create the light-weight package. The main libraries that it uses are Jetty, Jersey, Jackson, JUnit, and Guava. Furthermore, it uses its own library called Metrics.
Instead of deploying your applications to an application server or web server, Dropwizard defines a main method that invokes the Jetty server as a standalone process. As of now, Dropwizard recommends only running the application with Jetty; other web services like Tomcat are not officially supported.
First, your issue:
From the DW documentation:
Currently creating transactions with the @UnitOfWork annotation works out-of-box only for resources managed by Jersey. If you want to use it outside Jersey resources, e.g. in authenticators, you should instantiate your class with UnitOfWorkAwareProxyFactory.
With your code, you create an Authenticator
but you never hook that back to the Hibernate Session. How would the Authenticator
know when to open a new Session for the DAO
to operate on? This is done by the UnitOfWork
mechanism. This however only currently works for the Jersey resources and needs to be enabled for any other class that wants to participate in this.
So, fortunately the Docs gives us an exact Authenticator
example on here:
http://www.dropwizard.io/1.0.6/docs/manual/hibernate.html
I am not going to copy their code as I have a standalone example for you to play with:
public class HibernateTest extends io.dropwizard.Application<DBConfiguration> {
private final HibernateBundle<DBConfiguration> hibernate = new HibernateBundle<DBConfiguration>(Object.class) {
@Override
public DataSourceFactory getDataSourceFactory(DBConfiguration configuration) {
return configuration.getDataSourceFactory();
}
};
@Override
public void initialize(Bootstrap<DBConfiguration> bootstrap) {
super.initialize(bootstrap);
bootstrap.addBundle(hibernate);
}
@Override
public void run(DBConfiguration configuration, Environment environment) throws Exception {
MyDao dao = new MyDao(hibernate.getSessionFactory());
environment.jersey().register(new MyHelloResource(dao));
// THIS IS ABSOLUTELY CRITICAL
MyAuthenticator proxyAuth = new UnitOfWorkAwareProxyFactory(hibernate).create(MyAuthenticator.class, MyDao.class, dao);
AuthDynamicFeature authDynamicFeature = new AuthDynamicFeature(
new BasicCredentialAuthFilter.Builder<Principal>()
.setAuthenticator(proxyAuth)
.setRealm("SUPER SECRET STUFF")
.buildAuthFilter());
environment.jersey().register(authDynamicFeature);
}
public static void main(String[] args) throws Exception {
new HibernateTest().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/db.yaml");
}
@Path("test")
@Produces(MediaType.APPLICATION_JSON)
public static class MyHelloResource {
private MyDao dao;
public MyHelloResource(MyDao dao) {
this.dao = dao;
}
@GET
@Path("/test")
@UnitOfWork
@PermitAll
public Response downloadFile() throws Exception {
dao.get();
return Response.ok().build();
}
}
public static class MyAuthenticator implements Authenticator<BasicCredentials, Principal> {
private MyDao dao;
MyAuthenticator(MyDao dao) {
this.dao = dao;
}
@Override
@UnitOfWork
public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
dao.get();
return Optional.empty();
}
}
public static class MyDao extends AbstractDAO<Object> {
public MyDao(SessionFactory sessionFactory) {
super(sessionFactory);
}
public Object get() {
// if not bridged to Jersey this will throw an exception due to session
currentSession().createSQLQuery("SELECT 1;").uniqueResult();
return new Object();
}
}
}
The above code runs a minimal DW application with an h2 db setup in memory. You will have to apply your configuration changes so that it starts (and change the server configuration file)
What this does is:
DataSourceFactory
DAO
Authenticator
as a proxy The important bits are:
MyAuthenticator proxyAuth = new UnitOfWorkAwareProxyFactory(hibernate).create(MyAuthenticator.class, MyDao.class, dao);
This creates a proxy for you that is aware of the UnitOfWork
annotation. It enables the Authenticator
to hook into the (I believe) event system that will open and close sessions on request.
You then use that proxy in your AuthDynamicFeature
Finally, in your Authenticator
you must tell it to open a new Session when the authentication is executed, such as:
@Override
@UnitOfWork
public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
dao.get();
return Optional.empty();
}
Now all of this will work without an exception:
curl user:pass@localhost:9085/api/test/test
Credentials are required to access this resource.
As for your last question:
I'm actually more fluent with spring do you think it would be a good idea to switch instead of dealing with this constantly?
I think this is mostly opinion based, but: Spring DI
!= Jersey DI
. You essentially do the same thing with Spring, you bridge Jersey DI
with Spring DI
such that jersey
can access these beans in Spring
. However, all the session
logic is still handled the same way. Spring
simply takes the explicit abstraction away afaik - it creates the proxies for you already when creating the bean. So I don't think you will have many advantages with this and from personal experience, bridging Spring and Jersey is not all that easy. The dependency (spring-jersey-bridge
) jersey advertises does not work with embedded Jetties (such as DW setups) but rather by hooking into a Servlet at startup (which you don't have). This can still work, however it requires a bit of hacking to get that setup. In my experience, guice (e.g. https://github.com/xvik/dropwizard-guicey) integrates much easier and nicer into DW and will give you the same advantages. Guice
obviously does not do all the things that Spring does (neither does Spring
do all the things that Guice
does) so you may need to do your own research.
I hope this clears things up and gets you started :)
Regards,
Artur
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With