java - Dropwizard + Jersey : "Not inside a request scope" when creating custom annotation -
i have simple dropwizard 0.8.1 rest service pulls in jersey 2.17. upstream of rest/jetty service have authentication service adds nice authorization information http header gets passed dropwizard app.
i love able create custom annotation in resource hides messy header-parsing-to-pojo garbage. this:
@path("/v1/task") @produces(mediatype.application_json) @consumes(mediatype.application_json) public class taskresource { @usercontext // <-- custom/magic annotation private usercontextdata usercontextdata; // <-- holds authorization info @get public collection<task> fetch() { // use usercontextdata differentiate data return } i've spent last day looking around stackoverflow , found several other people had same issue , appeared (?) satisfaction, can't seem avoid getting "not inside request scope" stack trace when try this.
so stashed changes , tried implement example provided in sections 22.1 , 22.2 jersey documentation directly: https://jersey.java.net/documentation/2.17/ioc.html
following along example (but in dropwizard app), i'm trying "@sessioninject" annotation in resource, blows "not inside request scope" stack trace each time. doing wrong here?
resource:
@path("/v1/task") @produces(mediatype.application_json) @consumes(mediatype.application_json) public class taskresource { private final taskdao taskdao; @context private httpservletrequest httprequest; @sessioninject private httpsession httpsession; public taskresource(taskdao taskdao) { this.taskdao = taskdao; } @get public collection<task> fetch(@sessioninject httpsession httpsession) { if (httpsession != null) { logger.info("tom tom tom httpsession isn't null: {}", httpsession); } else { logger.error("tom tom tom httpsession null"); } return taskdao.findalltasks(); } the sessioninjectresolver:
package com.foo.admiral.integration.jersey; import com.foo.admiral.integration.core.sessioninject; import javax.inject.inject; import javax.inject.named; import javax.servlet.http.httpsession; import org.glassfish.hk2.api.injectee; import org.glassfish.hk2.api.injectionresolver; import org.glassfish.hk2.api.servicehandle; import org.slf4j.logger; import org.slf4j.loggerfactory; public class sessioninjectresolver implements injectionresolver<sessioninject> { private static final logger logger = loggerfactory.getlogger(httpsessionfactory.class); @inject @named(injectionresolver.system_resolver_name) injectionresolver<inject> systeminjectionresolver; @override public object resolve(injectee injectee, servicehandle<?> handle) { if (httpsession.class == injectee.getrequiredtype()) { return systeminjectionresolver.resolve(injectee, handle); } return null; } @override public boolean isconstructorparameterindicator() { return false; } @override public boolean ismethodparameterindicator() { return false; } } the httpsessionfactory:
package com.foo.admiral.integration.jersey; import javax.inject.inject; import javax.inject.singleton; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpsession; import org.glassfish.hk2.api.factory; import org.slf4j.logger; import org.slf4j.loggerfactory; @singleton public class httpsessionfactory implements factory<httpsession> { private static final logger logger = loggerfactory.getlogger(httpsessionfactory.class); private final httpservletrequest request; @inject public httpsessionfactory(httpservletrequest request) { logger.info("creating new httpsessionfactory request"); this.request = request; } @override public httpsession provide() { logger.info("providing new session if 1 not exist"); return request.getsession(true); } @override public void dispose(httpsession t) { } } the annotation:
package com.foo.admiral.integration.core; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; @retention(retentionpolicy.runtime) @target({elementtype.field}) public @interface sessioninject { } and, finally, binding in dropwizard application class:
@override public void run(todoconfiguration configuration, environment environment) throws exception { ... environment.jersey().register(new abstractbinder() { @override protected void configure() { bindfactory(httpsessionfactory.class).to(httpsession.class); bind(sessioninjectresolver.class) .to(new typeliteral<injectionresolver<sessioninject>>() { }) .in(singleton.class); } }); ye old stack trace:
caused by: java.lang.illegalstateexception: not inside request scope. @ jersey.repackaged.com.google.common.base.preconditions.checkstate(preconditions.java:149) @ org.glassfish.jersey.process.internal.requestscope.current(requestscope.java:233) @ org.glassfish.jersey.process.internal.requestscope.findorcreate(requestscope.java:158) @ org.jvnet.hk2.internal.methodinterceptorimpl.invoke(methodinterceptorimpl.java:74) @ org.jvnet.hk2.internal.methodinterceptorinvocationhandler.invoke(methodinterceptorinvocationhandler.java:62) @ com.sun.proxy.$proxy72.getsession(unknown source) @ com.foo.admiral.integration.jersey.httpsessionfactory.provide(httpsessionfactory.java:29) @ com.foo.admiral.integration.jersey.httpsessionfactory.provide(httpsessionfactory.java:14) some clues may useful:
1) i'm noticing logging statements in httpsessionfactory never getting fired, don't think factory correctly identified dropwizard.
2) if change annotation parameter instead of field , move use of annotation fetch( ) method signature this, doesn't throw stack trace (but httpsession still null, presumably because factory isn't firing...)
public collection<task> fetch(@sessioninject httpsession httpsession) { 3) doesn't appear matter if "register" binder environment.jersey().register() or environment.jersey().getresourceconfig().register()... appear same thing.
do see obvious problems? in advance!
this weird behavior. looks going on following
you have registered
taskresourceinstance , not.class. i'm pretty sure of (though have not mentioned).register(new taskresource()); /* instead of */ register(taskresource.class);doing former, set resource in singleton scope. latter in request scope (unless annotated otherwise - see below)
when resource model loading sees
taskresourcesingleton, ,httpservletrequestin request scope. either or factory in per request scope. i'm guessing 1 of two.
i thought might be scope issue, mentioned in error message, i'm pretty sure of @ runtime, handled thread local proxy, because of lesser scope.
you can see fixed registering taskresource class, , annotating taskresource @singleton. if do want resource class singleton. if not, leave off @singleton.
the odd thing me fact fails on startup when resource explicitly instantiated on startup, works when framework loads on first request (which happens when register class). both still in singleton scope.
one thing might want take consideration whether want resource singleton or not. have worry thread safety issues singletons, , there are other limitations. personally, prefer keep them in request scope. have performance testing see if there of impact application.
update
for parameter injection may want take @ this post
update 2
see also
- jersey 2 context injection based upon httprequest without singleton. answer should shed more light.
Comments
Post a Comment