I am developing a rest API service with Jersey and Jetty.
It is pretty simple and I have a number of endpoints like this:
#GET
#Path("/username/{username : [a-zA-Z][a-zA-Z_0-9]}")
#Produces(MediaType.TEXT_PLAIN)
public String getUsername(#Context UriInfo uriInfo, String content) {
MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters();
String nameParam = queryParams.getFirst("name");
//Dataset<Row> df = GetDataFrame.getDF();
return "test";
}
in the getUserName method, I need to use an object which I have created in the main class.
The main class is at the moment like this:
SourceHandler source = new SparkHandler(inputSource);
source.loadIntoMemory();
Server server = new Server(8080);
ServletContextHandler ctx =
new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
ctx.setContextPath("/");
server.setHandler(ctx);
ServletHolder serHol = ctx.addServlet(ServletContainer.class, "/rest/*");
serHol.setInitOrder(1);
serHol.setInitParameter("jersey.config.server.provider.packages",
"com.ed7.res");
I would like to use the source object in the GET responses.
Is there a best practice to do that in Jersey?
Otherwise, I would create another class with a static field/static method that returns that particular object.
You can use HK2 DI. What you can do to configure it is create a standalone ServiceLocator and set that locator to be the parent locator of the app, using a Jersey property.
public static void main(String... args) {
SourceHandler source = new SparkHandler(inputSource);
ServiceLocator locator = ServiceLocatorUtilities.bind(new AbstractBinder() {
#Override
protected void configure() {
bind(source).to(SourceHandler.class);
}
});
ServletHolder serHol = ctx.addServlet(ServletContainer.class, "/rest/*");
serHol.setInitParameter(ServletProperties.SERVICE_LOCATOR, locator);
}
Then you can just #Inject the SourceHandler anywhere you need it
#Path("whatever")
public class Resource {
#Inject
private SourceHandler sourceHandler;
}
Related
Following the guide here I am trying to inject my own custom property that I defined in application.properties.
The prop is defined as sendgrid.apikey=key and then my class is;
#ApplicationScoped
public class EmailConfig {
#Inject
#ConfigProperty(name = "sendgrid.apikey")
String API_KEY;
private SendGrid sendGrid;
private Request request;
public EmailConfig() {
sendGrid = new SendGrid(API_KEY);
request = new Request();
}
When I hit the first line in the constructor, I expect API_KEY to be the value in the application.properties file, but it is null. I have no idea why! I tried this with and without the #Inject annotation btw.
Any ideas?
Your expectation is wrong. There are tricks for creating an instance of a class without calling a constructor, but they are generally not exactly reliable, so what Quarkus does is what you would do by hand: to create an instance, it calls the constructor. Only after an instance exists can fields be injected.
What you can do is inject objects into the constructor as its parameters:
#ApplicationScoped
public class EmailConfig {
private SendGrid sendGrid;
private Request request;
#Inject
public EmailConfig(#ConfigProperty(name = "sendgrid.apikey") String API_KEY) {
sendGrid = new SendGrid(API_KEY);
request = new Request();
}
i am trying to implement server side events.
I have very simple resource exposed by a RESTful web service with Jersey/Grizzly. I try to broadcast the events with the SseBroadcaster. An event is created, whenever a data item comes in and is added to an internal list. A client should open a connection to the URL /events to receive the events.
#Path("sensordataelements")
public class SensorDataResource {
private SseBroadcaster broadcaster = new SseBroadcaster();
#GET
#Path("events")
#Produces(SseFeature.SERVER_SENT_EVENTS)
public EventOutput getServerSentEvents() {
final EventOutput eventOutput = new EventOutput();
broadcaster.add(eventOutput);
return eventOutput;
}
#POST
#Path("/addraw")
#Produces(MediaType.APPLICATION_JSON)
public Response addRawSensorData(String elementBody) {
... data processing stuff ...
cList.add(
new SensorDataElement.SensorDataElementBuilder().id()
.sensorReading(tmpValue)
.build()
);
OutboundEvent evt = new OutboundEvent.Builder()
.data(Float.class, Float.valueOf(tmpValue))
.build();
broadcaster.broadcast(evt);
return Response.status(201).build();
}
...
I tried to connect with
curl -v http://localhost:8080/sensordataapp/sensordataelements/events
The connection is fine, but i do not get any events. I looked at some examples, but got the impression that this should work. What did i miss?
Thanks!
By default, a new instance of the resource class is created for each request. This means that a new broadcaster is created for each request, which isn't what you want. If you want to make the resource class a Singleton, you can simply annotate the class with #Singleton
#Singleton
#Path("sensordataelements")
public class SensorDataResource {
...
}
Now, only one instance of the resource class will be created for the entire application, and it will be shared for all requests.
The other option, is if you inject the broadcaster, instead of instantiating it yourself, you can inject it as a Singleton. Whether or not the resource class is a singleton or not, it will still get injected the same broadcaster instance. To do that, you can do something like the following in your ResourceConfig subclass
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(new AbstractBinder() {
#Override
public void configure() {
bind(new SseBroadcaster()).to(SseBroadcaster.class);
}
});
}
}
Then in your resource class, just inject it
#Path("sensordataelements")
public class SensorDataResource {
#Inject
private SseBroadcaster broadcaster;
See also:
Dependency injection with Jersey 2.0
I have a large existing java program already using Guice. I'm trying to add an embedded website with swagger documentation. Somehow I need to wire it all up with Guice, but everything I've tried throws null pointer exception when I try to use things I injected in my main program. I thought maybe I could pass the injector and wire it up that way, either using the injector or creating a child injector.
I've created a sample app using just the code needed to get this working, with a URL that works but doesn't try to grab use the injections from my main program, and one that does not work that tries to use the injection.
I'm trying to do all this without needing the web.xml via:
private ContextHandler buildApiContext() {
ResourceConfig resourceConfig = new ResourceConfig();
// Replace EntityBrowser with your resource class
// io.swagger.jaxrs.listing loads up Swagger resources
resourceConfig.packages("web", ApiListingResource.class.getPackage().getName());
//apiServletContainer.reload(resourceConfig);
ServletContainer apiServletContainer = new ServletContainer(resourceConfig);
final ServletContextHandler apiContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
apiContext.setContextPath("/api");
ServletHolder apiBrowser = new ServletHolder(apiServletContainer);
apiContext.addFilter(GuiceFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
myGuiceServletContextListener.setMainInjector(blackboard.getMainInjector());
apiContext.addEventListener(myGuiceServletContextListener);
apiContext.addServlet(apiBrowser, "/*");
return apiContext;
}
and
public class MyGuiceServletContextListener extends GuiceServletContextListener {
#Inject private Blackboard blackboard;
#Override
protected Injector getInjector() {
return blackboard.getMainInjector();
}}
I also tried:
return blackboard.getMainInjector().createChildInjector();
In my main I'm starting the main program injection with:
Config config = ReadConfig.createConfig();
Injector injector = Guice.createInjector(new Bindings(config));
BigProgramInterface bbInterface = injector.getInstance(BigProgramImpl.class);
bbInterface.start(injector);
where Bindings looks like
public class Bindings implements Module {
private Config config;
public Bindings(Config config) {
this.config = config;
}
public void configure(Binder binder) {
Integer fixedThreadPoolSize = 2;
Executor fixedExecutor = Executors.newFixedThreadPool(fixedThreadPoolSize, new FixedThreadFactory());
binder.bind(Executor.class).toInstance(fixedExecutor);
binder.bind(Config.class).toInstance(config);
binder.bind(Blackboard.class).asEagerSingleton();
binder.bind(BigProgramMain.class).asEagerSingleton();
binder.bind(EmbeddedWeb.class).asEagerSingleton();
//binder.bind(MyGuiceServletContextListener.class).asEagerSingleton();
}
The blackboard is injected, and it is getting the main injector, but it can't use it.
works:
#Path("/test")
#Api (value = "/test")
public class TestSwagger {
private static final Logger log = LoggerFactory.getLogger(TestSwagger.class);
#GET
#Path("/get")
#ApiOperation(value = "a working test",
notes = "Returns my test class",
response = MyTest.class,
responseContainer="Class")
#Produces(MediaType.APPLICATION_JSON)
public Response getResult() {
MyTest myTest = new MyTest();
myTest.setMyTestString("this is a test");
return Response.ok().entity(myTest).build();
}}
not working:
#Path("/testbad")
#Api (value = "/testbad")
public class TestSwaggerBad {
private static final Logger log = LoggerFactory.getLogger(TestSwaggerBad.class);
#Inject private Blackboard blackboard;
#GET
#Path("/get")
#ApiOperation(value = "a non - working test",
notes = "Returns my test class",
response = MyTest.class,
responseContainer="Class")
#Produces(MediaType.APPLICATION_JSON)
public Response getResult() {
MyTest myTest = new MyTest();
myTest.setMyTestString(blackboard.getBigProgramCounter().toString());
return Response.ok().entity(myTest).build();
}}
Please see my code for the nitty gritty details:
https://github.com/phomlish/SwaggerSampleApiWebsite
I had a closer look at your code, here is how you get it to work:
Add the guice bridge to your pom:
<!-- https://mvnrepository.com/artifact/org.glassfish.hk2/guice-bridge -->
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>2.5.0-b15</version>
</dependency>
That adds the guice hk2 bridge to your configuration. Now, you will want to wire that up. For that, we will create a feature as outlined by this post:
Guice don't inject to Jersey's resources
#Priority(0)
public class GuiceFeature implements Feature {
private Injector i;
public GuiceFeature(Injector i) {
this.i = i;
}
#Override
public boolean configure(FeatureContext context) {
ServiceLocator locator = ServiceLocatorProvider.getServiceLocator(context);
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(i);
return true;
}
}
Note that I am passing the injector that you created into that feature. This is important since you will need that same injector in order to be able to find your service. The binding code is fairly straight forward.
Finally, you will need to register that feature. In your class EmbeddedWeb, you add:
EmbeddedWeb#buildApiContext:
resourceConfig.register(new GuiceFeature(myGuiceServletContextListener.getInjector()));
Again, we are using the same injector that you created already.
Finally that is all that you need and your services are wired up correctly.
test:
artur#pandaadb:~/dev/repo/SwaggerSampleApiWebsite$ curl "http://localhost:8080/api/testbad/get"
{"myTestString":"10"}
Hope that helps,
artur
EDIT '''IMPORTANT''':
For injection, you can not use guice annotations. Jersey doesn't seem to recognise them (likely because they did not want to add guice dependencies). Luckily, guice can work with both javax and guice annotations. So in your TestSwaggerBad class you will also need to change the import to standard javax annotations.
I'm working on a component that creates HTTP requests dynamically, and I'd like to be able to mock those requests for unit testing.
Currently the implementation looks something like this:
class ModelClass {
public void populate() {
HTTPRequest request = new HTTPRequest();
//configure request...
request.send();
}
}
Is there a way to use Guice to instantiate request so I can replace it with an instance of a mock class for testing? The nearest I can figure out would be to add an injector as instance variable of ModelClass:
class ModelClass {
private final Injector injector;
ModelClass(Injector injector){
this.injector = injector;
}
public void populate() {
HTTPRequest request = injector.getInstance(HTTPRequest.class);
//configure request...
request.send();
}
}
But that's basically like using a factory, which misses the point of Guice entirely.
You can inject a provider which provides 'HTTPRequest' instances in your code.
class ModelClass {
#Inject
Provider<HTTPRequest> httpRequestProvider;
public void populate() {
HTTPRequest request = httpRequestProvider.get();
}
}
Then, in your test code, you can mock the 'httpRequestProvider' to return mock 'HTTPRequest' instances.
Provider<HTTPRequest> mockHttpRequestProvider = mock(Provider.class);
when(mockHttpReqestProvider.get()).thenReturn(yourMockHTTPRequestObject);
// Set this mock provider to the ModelClass instance. (You may have to use reflection)
Info on injecting providers: https://github.com/google/guice/wiki/InjectingProviders
I'm writing some utlility methods that helps constructing complex object structures for webservice requests.
When using Spring: is it better to provide these classes as static utilities or inject/autowire them?
Example:
//the service used to build different parts for the webservice request
class RequestService {
public addCurrency(Req req) {
//create currency xml object
req.addCurrency(cur);
}
public addPassengers(Req req, List<String> names) {
for (String name : names) {
req.getPassengers().add(passenger);
//create passengers
}
}
}
class WebserviceClient {
void runWithStatic() {
Req req = new Req();
RequestService.addCurrency(req, ..);
RequestService.addPassengers(req, ..);
send(req);
}
//or
#Autowired
private RequestService reqService;
void runWithInjected() {
Req req = new Req();
reqService.addCurrency(req, ..);
reqService.addPassengers(req, ..);
send(req);
}
}
Is there one approach one would prefer from the overal spring design point of view?