I have simple restful WS
#Path("basic")
public class ServiceRS
{
private IServiceJAX service;
#GET
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String find(#FormParam("searchRequest") final String searchRequest)
{
//...
final List<Info> response = service.find(search);
//...
}
}
Where IServiceJAX is #Local interface of jax-webservice.
Can I inject IServiceJAX to ServiceRS using annotation?
I don't want use JNDI lookup...
Sure, you can. Although I suppose there are other ways, I have successfully run a simple test project with a #Stateless #WebService, #Local implementation of an interface, injected through #EJB annotation into a #Stateless RESTFul web service annotated with #Path.
This is not properly a CDI injection as you have demanded, but it works nicely and probably fits your needs anyway.
IServiceJAX class:
public interface IServiceJAX {
public String hello(String txt);
}
IServiceJAXImpl class:
#WebService(serviceName = "NewWebService")
#Local
#Stateless
public class IServiceJAXImpl implements IServiceJAX {
#WebMethod(operationName = "hello")
#Override
public String hello(#WebParam(name = "name") String txt) {
return "Hello " + txt + " !";
}
}
ServiceRS class:
#Path("basic")
#Stateless
public class ServiceRS {
#EJB private IServiceJAX wsi;
#GET
#Path("{id}")
#Produces(MediaType.APPLICATION_JSON)
public String result(#PathParam("id") String id) {
return wsi.hello(id);
}
}
UPDATE
If you prefer CDI injection, you can keep the above code and simply remove #Local and #Stateless annotations from IServiceJAXImpl. You can inject an instance of this class using:
#Inject private IServiceJAX wsi;
instead of
#EJB private IServiceJAX wsi;
Related
I have a interface witch use a variable tableName
public interface TableNameService {
void printName();
}
#Serive
public class TableNameServiceImpl implements TableNameService{
#Ovveride
public void printName() {
System.out.println(tableName);
}
}
This interface I inject to other services like OracleService etc and initialize tableName in services through #Value.
For example:
#Service
public class OracleService {
private TableNameService service;
#Value("${tableName}")
private String tableName;
public void print() {
service.printName()
}
}
How to pass field from OracleService to TableNameService during injection TableNameServiceImpl so as not to pass it in the method parameters and not to put it down all the time with your hands?
A common reason why dependency injection became so popular is because it avoids boilerplate code where you need to pass from one method into the other the same instances and values.
In your case here you could also take advantage of DI by adding #Value("${tableName}") private String tableName; in your TableNameServiceImpl class and autowiring this service class inside OracleService.
#Service
public class TableNameServiceImpl implements TableNameService{
#Value("${tableName}")
private String tableName;
#Ovveride
public void printName() {
System.out.println(tableName);
}
}
And then you can have
#Service
public class OracleService {
#Autowired
private TableNameService service;
public void print() {
service.printName()
}
}
I want autowire a class which implements in a Component. Here is a part of the interface:
#Service
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class GenericResourceHandlerService<T extends ResourceRequest, A extends ResultType, B extends ResourceService<T, A>> {
private final B service;
public Response get(String x) {
various checks(x, service.getType());
B res = service.get(x);
if (res!= null) {
return Response.status(Response.Status.OK).entity(res).build();
} else {
return Response.status(Response.Status.NOT_FOUND).build();
}
}
}
Then, a class which implements ResourceService would look like this:
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class TypeOneService implements EntityService<CityRequest, City> {
private final Repository repository;
#Override
public ResultType getType() {
return ResultType.TYPE_ONE;
}
#Timed
public TYPE_ONE get(String resource) {
return repository.get(resource);
}
}
And the interface itself, looks like this:
public interface EntityService<T extends EntityRequest, A extends ReturnableEntity> {
ResourceType getResourceType();
A get(String resource);
}
Now, I have a set of controllers which tries to autowire GenericResourceHandlerService and call it's get method. Which looks like this:
public class TypeOneController {
private final TypeOneService typeOneService;
private final GenericResourceHandlerService<TypeOneRequest, TypeOne, TypeOneService> genericResourceHandlerService;
public Response getListItemByResource(
String resource
) {
return genericResourceHandlerService.get(resource);
}
}
Or this:
public class TypTwoController {
private final TypeTwoService typeTwoService;
private final GenericResourceHandlerService<TypeTwoRequest, TypeTwo, TypeTwoService> genericResourceHandlerService;
public Response getListItemByResource(
String resource
) {
return genericResourceHandlerService.get(resource);
}
}
This compiles but when the app starts then, then I get the following error message:
Parameter 0 of constructor in path.GenericResourceHandlerService required a single bean, but 2 were found:
- typeOneSerivce: defined in file [C:\Path\TypeOneService.class]
- typeTwoService: defined in file [C:\Path\TypeTwoService.class]
I think this is because, Spring Boot can't work out which one to service to autowire with. Is it possible what I am trying to do?
Spring tries to inject a bean to resolve the GenericResourceHandlerService.service but service has type B and B extends ResourceService. And spring found 2 beans implementing this interface so doesn't know which implementation to autowire..
You can put #Qualifier on field service but I imagine you will lost the genericity of this type GenericResourceHandlerService
Maybe the best way is to let the controller pass through the implementation in the GenericResourceHandlerService and let this last as a simple pojo..not a spring bean (so remove #Service on GenericResourceHandlerService
Like this
public class TypeOneController {
#Autowired
private final TypeOneService typeOneService;
private final GenericResourceHandlerService<TypeOneRequest, TypeOne, TypeOneService> genericResourceHandlerService = new GenericResourceHandlerService(typeOneService);
public Response getListItemByResource(
String resource
) {
return genericResourceHandlerService.get(resource);
}
}
Is it possible to use Context annotation and RolesAllowed annotation in a JAX-RS resource with Apache CXF 2.4.6 and Spring Security 3.2.8?
My CXF configuration:
<jaxrs:server address="/example">
<jaxrs:serviceBeans>
<ref bean="myResourceImpl"/>
</jaxrs:serviceBeans>
</jaxrs:server>
My Java source code:
#Path("/myresource")
public interface MyResource {
#GET
#Produces(MediaType.TEXT_XML)
String get();
}
#Named
public class MyResourceImpl implements MyResource {
#Context
private SecurityContext securityContext;
#Override
#RolesAllowed("ROLE_user")
public String get() {
return securityContext.getUserPrincipal().getName();
}
}
After starting the server, I get following exception:
Caused by: java.lang.IllegalArgumentException: Can not set javax.ws.rs.core.SecurityContext field MyResourceImpl.securityContext to com.sun.proxy.$Proxy473
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:55)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
at java.lang.reflect.Field.set(Field.java:741)
at org.apache.cxf.jaxrs.utils.InjectionUtils$1.run(InjectionUtils.java:164)
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.cxf.jaxrs.utils.InjectionUtils.injectFieldValue(InjectionUtils.java:160)
at org.apache.cxf.jaxrs.utils.InjectionUtils.injectContextProxiesAndApplication(InjectionUtils.java:912)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.injectContexts(JAXRSServerFactoryBean.java:354)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.updateClassResourceProviders(JAXRSServerFactoryBean.java:380)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:145)
... 59 more
If I remove one of both annotations, it works fine.
The problem seems to be that Spring creates a proxy and Apache CXF cannot inject that proxy with the SecurityContext.
I have to use Spring Security and cannot use container-based security.
I found four work-arounds:
Extended Interface
#Path("/myresource")
public interface MyResource {
#Context
public void setSecurityContext(Security securityContext);
#GET
#Produces(MediaType.TEXT_XML)
String get();
}
#Named
public class MyResourceImpl implements MyResource {
private SecurityContext securityContext;
#Override
public void setSecurityContext(Security securityContext) {
this.securityContext = securityContext
}
#Override
#RolesAllowed("ROLE_user")
public String get() {
return securityContext.getUserPrincipal().getName();
}
}
But this solution is not perfect, because my client should not see implementation details.
Dedicated interface
If I add a second interface with a public setter for SecurityContext, Apache CXF could inject the JDK proxy with SecurityContext.
public interface ContextAware {
#Context
public void setSecurityContext(Security securityContext);
}
#Path("/myresource")
public interface MyResource {
#GET
#Produces(MediaType.TEXT_XML)
String get();
}
#Named
public class MyResourceImpl implements MyResource, ContextAware {
private SecurityContext securityContext;
#Override
public void setSecurityContext(Security securityContext) {
this.securityContext = securityContext
}
#Override
#RolesAllowed("ROLE_user")
public String get() {
return securityContext.getUserPrincipal().getName();
}
}
CGLIB proxy without interface
If I remove the interface Spring uses a CGLIB proxy.
#Named
#Path("/myresource")
public class MyResourceImpl {
#Context
private SecurityContext securityContext;
#RolesAllowed("ROLE_superadmin")
#GET
#Produces(MediaType.TEXT_XML)
public String get() {
return securityContext.getUserPrincipal().getName();
}
}
But this solution is not good, because my client should not see implementation details. And my client should not need implementation dependencies.
CGLIB proxy with interface
#Path("/myresource")
public interface MyResource {
#GET
#Produces(MediaType.TEXT_XML)
String get();
}
#Named
public class MyResourceImpl implements MyResource {
#Context
private SecurityContext securityContext;
#Override
#RolesAllowed("ROLE_user")
public String get() {
return securityContext.getUserPrincipal().getName();
}
}
I took a slight variation on the solution from #dur. Instead of having the #Context as a field, I passed it as a parameter to my method that needed it (I was using SearchContext):
#Path("/myresource")
public interface MyResource {
#GET
#Produces(MediaType.TEXT_XML)
String get(#Context SecurityContext securityContext);
}
#Named
public class MyResourceImpl implements MyResource {
#Override
#RolesAllowed("ROLE_user")
public String get(SecurityContext securityContext) {
return securityContext.getUserPrincipal().getName();
}
}
Thanks to migration to jersey 2 I need to migrate from guice to HK2. I have an Assisted injection approach for some of my dependencies which I couldn't get my head around to implement in HK2. It looks like it's supposed to be solved via Custom Injection Resolvers but I don't really see how. The examples are not clear enough for me..
Here is how it looks on Guice:
public interface MyFactory {
public MyClass createMyClass(#Assisted String dynamicParameter);
public HisClass createHisClass(#Assisted String dynamicParameter);
...
}
binder.install(new FactoryModuleBuilder().build(MyFactory.class));
public class MyClass {
...
#Inject
public MyClass(#Assisted String dynamicParameter, SomeService someOtherServiceInjectedAutomatically){
...
}
}
How can I implement this on HK2?
After posting the question I thought of doing this:
public class MyFactoryImpl implements MyFactory{
private final SomeService someService;
#Inject
public MyFactoryImpl(SomeService someService){
this.someService = someService;
}
public MyClass createMyClass(String dynamicParameter){
return new MyClass(dynamicParameter, someService);
}
...
}
There is a Guice-Bridge :-D
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>${hk2.version}</version>
</dependency>
Here is an example using Guice 3.0 and HK2 2.3.0 (which comes bundled with Jersey 2.13). This is just a standalone, but it should work in Jersey environment just the same.
Guice classes
public class GuiceGreeter {
public String getGreeting(String name) {
return "Hello, " + name;
}
}
import com.google.inject.assistedinject.Assisted;
import javax.inject.Inject;
public class Message {
private final String message;
#Inject
public Message(GuiceGreeter greeter, #Assisted String name) {
message = greeter.getGreeting(name);
}
public String getMessage() {
return message;
}
}
public interface GuiceMessageFactory {
public Message getMessage(String name);
}
import com.google.inject.AbstractModule;
import com.google.inject.assistedinject.FactoryModuleBuilder;
public class GuiceMessageModule extends AbstractModule {
#Override
protected void configure() {
install(new FactoryModuleBuilder().build(GuiceMessageFactory.class));
bind(GuiceGreeter.class);
}
}
HK2 service, which injects the Guice factory
import javax.inject.Inject;
public class HK2Service {
private final GuiceMessageFactory messageFactory;
#Inject
public HK2Service(GuiceMessageFactory messageFactory) {
this.messageFactory = messageFactory;
}
public void printMessage(String name) {
System.out.println(messageFactory.getMessage(name).getMessage());
}
}
Main
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.ServiceLocatorFactory;
import org.glassfish.hk2.utilities.ServiceLocatorUtilities;
import org.jvnet.hk2.guice.bridge.api.GuiceBridge;
import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge;
public class Main {
public static void main(String[] args) {
// Create service locator. In Jersey context, you should be able to
// inject the `ServiceLocator` into the `Application/ResourceConfig`
// subclass constructor, or as a field
ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
ServiceLocator locator = factory.create("SimpleServiceLocator");
// bridge the two frameworks to allow Guice injected services
GuiceBridge.getGuiceBridge().initializeGuiceBridge(locator);
Injector injector = Guice.createInjector(new GuiceMessageModule());
GuiceIntoHK2Bridge guiceBridge = locator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
// Add my HK2 Service
ServiceLocatorUtilities.addClasses(locator, HK2Service.class);
// Look up HK2 service. If this lookup works, `#Inject` in Jersey should.
HK2Service service = locator.getService(HK2Service.class);
service.printMessage("peeskillet");
}
}
This prints out "Hello, peeskillet". See comment below main method to obtain ServiceLocator in Jersey app. And in case you are unfamailiar with the ServiceLocator, all the bindings you add with an AbstractBinder will get put in the service locator context also, so you don't have to explicitly add the class as I am going above with HK2Service.
I'm using a container-item pattern and I have:
Container to store users:
#Path("/user")
#Stateless
public class UsersResource {
#Context
private UriInfo context;
#EJB
private UserBeanLocal userBean;
public UsersResource() {
}
#GET
#Produces("application/json")
public String getJson(#HeaderParam("authorization") String authorization) {
return userBean.sampleJSON();
}
#Path("{id}")
public UserResource getUserResource(#PathParam("id") String id) {
return UserResource.getInstance(id);
}
}
Item that is representing single user:
#Stateless
public class UserResource {
#EJB
private UserBeanLocal userBean;
private String id;
public UserResource() {
}
private UserResource(String id) {
this.id = id;
}
public static UserResource getInstance(String id) {
return new UserResource(id);
}
#GET
#Produces("application/json")
public String getJson() {
//TODO return proper representation object
return userBean.sampleJSON();
}
}
And bean. It has only one method:
#Local
public interface UserBeanLocal {
String sampleJSON();
}
#Stateless
public class UserBean implements UserBeanLocal {
#Override
public String sampleJSON() {
return "{\"Name\": \"Jan\", \"Lastname\": \"Węglarz\", \"PESEL\": \"47092412341\"}";
}
}
EJB in container works fine but in item returns null. Why?
I've tried to return in getJson() something else, for example id, and there was no problem. Everything worked fine. But when I'm returning something using EJB there is null exception.
App deploys on jboss 7 without any problem.
That is because you are creating instance of UserResource by yourself:
return UserResource.getInstance(id);
Because it is not created by container via JNDI lookup or injection, it is not container managed. Dependency injection can only take place in container managed components.
I added in UsersResource:
#EJB
private UserResource userResource;
and then changed:
#Path("{id}")
public UserResource getUserResource(#PathParam("id") String id) {
return userResource;
}
And now it works. Thanks :)