I'm creating a REST service using RestEasy and Spring 4.
The service is basically an endpoint for a complex batch process. So clients call the service passing a bunch of parameters and then the processing is triggered.
As there are many parameters that are initially passed to the service, and those parameters are used pretty much everywhere in the system, I've chosen to create a 'helper' bean that will hold the parameters, then every other bean can autowire the 'parameter bean' and use it.
example:
the url called by the client would be: http://localhost/rest/service/execute?processType=A&initialDate=20141220&finalDate=20141231......
The REST Service endpoint would be something like:
#Path("/service")
public class RESTService {
#Autowired
private RequestParams params;
#Autowired
private ProcessOrchestrator orchestrator;
#POST
#Path("/execute")
#Produces(MediaType.TEXT_PLAIN)
public Response executa(
#NotNull #QueryParam("processType") String processType,
#NotNull #QueryParam("initialDate") String initialDate,
#NotNull #QueryParam("finalDate") String finalDate,
...
) {
params.setProcessType(processType);
params.setInitialDate(initialDate);
params.setFinalDate(finalDate);
orchestrator.triggerBatchProcess();
}
}
The RequestParams bean will only hold the parameters values:
#Component
public class RESTService {
private String processType;
private String initialDate;
private String finalDate;
...
// getters and setters
}
And the other beans would #Autowire the params bean and use its parameters:
#Component
public class DataProcessor {
#Autowire
private RequestParams params;
//...
}
#Component
public class DataConverter {
#Autowire
private RequestParams params;
//...
}
#Component
public class FileWritter {
#Autowire
private RequestParams params;
//...
}
The design looks correct, right? Now, my concern is: how do I make sure that a new RequestParams instance is created every time the service is called? Do I need to declare a scope ("request" for instance) for all my beans?
Related
I am trying to autowire a component into a custom JsonDeserializer but cannot get it right even with the following suggestions I found:
Autowiring in JsonDeserializer: SpringBeanAutowiringSupport vs HandlerInstantiator
Right way to write JSON deserializer in Spring or extend it
How to customise the Jackson JSON mapper implicitly used by Spring Boot?
Spring Boot Autowiring of JsonDeserializer in Integration test
My final goal is to accept URLs to resources in different microservices and store only the ID of the resource locally. But I don't want to just extract the ID from the URL but also verify that the rest of the URL is correct.
I have tried many things and lost track a bit of what I tried but I believe I tried everything mentioned in the links above. I created tons of beans for SpringHandlerInstantiator, Jackson2ObjectMapperBuilder, MappingJackson2HttpMessageConverter, RestTemplate and others and also tried with setting the SpringHandlerInstantiator in RepositoryRestConfigurer#configureJacksonObjectMapper.
I am using Spring Boot 2.1.6.RELEASE which makes me think something might have changed since some of the linked threads are quite old.
Here's my last attempt:
#Configuration
public class JacksonConfig {
#Bean
public HandlerInstantiator handlerInstantiator(ApplicationContext applicationContext) {
return new SpringHandlerInstantiator(applicationContext.getAutowireCapableBeanFactory());
}
}
#Configuration
public class RestConfiguration implements RepositoryRestConfigurer {
#Autowired
private Validator validator;
#Autowired
private HandlerInstantiator handlerInstantiator;
#Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", validator);
validatingListener.addValidator("beforeSave", validator);
}
#Override
public void configureJacksonObjectMapper(ObjectMapper objectMapper) {
objectMapper.setHandlerInstantiator(handlerInstantiator);
}
}
#Component
public class RestResourceURLSerializer extends JsonDeserializer<Long> {
#Autowired
private MyConfig config;
#Override
public Long deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
ServiceConfig serviceConfig = config.getServices().get("identity");
URI serviceUri = serviceConfig.getExternalUrl();
String servicePath = serviceUri.getPath();
URL givenUrl = p.readValueAs(URL.class);
String givenPath = givenUrl.getPath();
if (servicePath.equals(givenPath)) {
return Long.parseLong(givenPath.substring(givenPath.lastIndexOf('/') + 1));
}
return null;
}
}
I keep getting a NullPointerException POSTing something to the API endpoint that is deserialized with the JsonDeserializer above.
I was able to solve a similar problem by marking my deserializer constructor accept a parameter (and therefore removing the empty constructor) and marking constructor as #Autowired.
public class MyDeserializer extends JsonDeserializer<MyEntity> {
private final MyBean bean;
// no default constructor
#Autowired
public MyDeserializer(MyBean bean){
this.bean = bean
}
...
}
#JsonDeserialize(using = MyDeserializer.class)
public class MyEntity{...}
My entity is marked with annotation #JsonDeserialize so I don't have to explicitly register it with ObjectMapper.
I am trying to create http client on the fly from jaxrs annotations provided on the interface for each api.
public #interface RestClient {
String baseUrlKey();
}
#Component
#RestClient(baseUrlKey="NOTIFICATION")
#Path("/notification/")
public interface NotificationClient {
#Path("/")
#POST
#Produces("application/json")
#Consumes("application/json")
public abstract NotificationResponse create(NotificationEntry entry);
}
Now, I am trying to write another class which is trying to find all interfaces annotated with #RestClient. I cant seem to find any solution for this. All application context methods seem to return classes which can be instantiated. Is there any way I can load this interface and create bean instance of apache http client on the fly and register with application context.
Here is the bean initializer class -
public class RestClientBeanProcessor {
private static final Logger LOGGER = LoggerFactory.getLogger(RestClientBeanProcessor.class);
#Autowired
RestTemplate restTemplate;
#Autowired
ApplicationContext applicationContext;
#PostConstruct
private void registerClientBeans() {
Map<String, Object> beans = applicationContext.getBeansWithAnnotation(RestClient.class);
LOGGER.info("Registering client beans for - " + beans);
}
}
My beans object is empty. Spring is unable to tell me list of interfaces asked by my query.
I have a Class that accepts the following constructor
public Student(int id, String name, Map<String, List<String>> mapInject) {
super();
this.id = id;
this.name = name;
this.mapInject = mapInject;
}
And from spring Java Config, I am injecting the constructor args like below..
#Configuration
public class JavaConfig {
#Bean
public Employee getEmployeeBean() {
Map<String,List<String>> mapInject = new HashMap<String,List<String>>();
//Add map element
return new Employee(3123,"John",mapInject);
}
}
Am i doing constructor injection here? Is this the right way to do so?
I wouldn't use Spring to handle this bean creation, unless you want ALL employees to have the same id and name which I doubt.
The power behind Spring is its Dependency Injection (DI) where you define beans for providers such as database managers, services, etc. and inject those into your components. Defining a #Bean like you have there serves no purpose as now you can only inject employees with an id of 3123 and name John.
It's important to understand that just because you are using Spring it doesn't mean EVERYTHING needs to be handled as a bean - you will always need standard POJOs for housing and passing around state (such as your Employee class) which doesn't need to have anything to do with Spring.
Down the line you might have an EmployeeService for example which houses business logic to fetch employees from a database or something, this could then be configured as a bean so it can be injected across the application.
EDIT
#Configuration
public class JavaConfig {
#Bean
#Autowired //assuming a sessionfactory been is configured elsewhere
public EmployeeService employeeService(final SessionFactory sessionfactory) {
return new EmployeeService(sessionFactory);
}
}
You could then inject this anywhere (maybe in a controller for example):
#RestController
public class EmployeeController {
private final EmployeeService employeeService;
#Autowired
public EmployeeController(final EmployeeService employeeService) {
this.employeeService = employeeService;
}
}
Where the EmployeeController doesn't need to know or care that the userService has a DB connection and doesn't need to worry about configuring it as Spring will handle all of that.
I have a session-scoped bean (Ama04Service) in Spring that is set within the web context. I have a task that runs as a Callable, and I need access to this bean from jersey web services. How should I accomplish this? If I simply attempt autowiring the bean the first call of Ama04ServiceImpl (public Response verifNum) the autowiring run successfully, then if i call an another method (clickReponse) in the jersey web service(Ama_04Service) I get NullPointerException
this looks like sprine re-instantiate the autowired bean
please any idea ?
thanks in advance.
The session-scoped bean I am injecting looks like this:
Service("ama04Service")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Transactional(readOnly = false)
public class Ama04ServiceImpl implements Ama04Service, LineListener {
////some code here//////
}
the jersey web service that calls session-scoped bean looks like this
#Path("/ama04")
public class Ama_04Service {
#Autowired
Ama04Service ama04Service;
#Autowired
Ama01Service ama01Service;
#Autowired
Ama03Service ama03Service;
#Autowired
ResourceBundleService resourceBundleService;
#GET
#Path("/quest/{date}/{numDonn}/{language}")
#Produces(MediaType.APPLICATION_JSON)
public Response verifNum(#PathParam("numDonn") final String numDonn,
#PathParam("date") final String date,
#PathParam("language") final String language) {
///////// some code here///////
#GET
#Path("/clickRepons/{numDonn}/{occRep}/{occ}/{date}/{language}")
#Produces("application/json; charset=UTF-8")
public String clickReponse(#PathParam("numDonn") final String numDonn,
#PathParam("occRep") int occRep, #PathParam("occ") int occ,
#PathParam("date") final String date,
#PathParam("language") final String language) {
Ama4 currentAma = new Ama4();
}
I can't figure out how to get spring to autowire dependencies in a sub resource. I need to specify some request state to the sub resource. I'll also need access to cxf managed #Contexts.
If I have a class
#Path("/resource/")
public class Resource {
#Autowired
private dependency
#Path("{id}/sub/")
public getSub(#PathParam("id") String id){
// I know this is not right. I could autowire subresource, but it needs
// to be request scoped and I get errors about proxying request scoped bean
// into singleton bean.
// I have also tried instantiating the subresource using ResourceContext
// but then Subresource's dependencies don't get injected
return new Subresource(id);
}
}
And another class
public class Subresource{
#Context
private UriInfo uriInfo;
#Autowired
private Dependency2 dependency2;
private String id;
public Subresource(String id){
}
#GET
public Response get(){
return Response.ok(id).build();
}
}
How do I get the spring managed dependencies to be auto injected in the sub resources? How does the sub resource get access to CXF managed dependencies?
I create factory bean to instantiate sub-resource:
public class SubResourceFactory {
#Autowired
private AutowireCapableBeanFactory autowireBeanFactory;
public SubResource createBean(String parameter1, Parameter2 parameter2) {
... creating SubResource instance, setting parameters to it
autowireBeanFactory.autowireBean(subResource);
return (SubResource) autowireBeanFactory.initializeBean(subResource, "subResource");
}
}
AutowireCapableBeanFactory allows to init bean created outside of Spring Context and inject any Spring beans to it.
SubResourceFactory is instantiated as Spring singleton bean in my project and injected in Root Resource, so i can use it like this:
#Resource
private SubResourceFactory subResourceFactory;
#Override
public SubResource getInfoFromSubResource(String parameter1) {
Parameter2 parameter2 = parameter2StoreService.getParameter2(parameter1);
return subResourceFactory.createBean(parameter1, parameter2);
}
But this subResource is managed only by Spring, so You can inject only Spring beans, You can not inject CXF-RS #Context beans.
There must be a way to do same thing to allow Spring and CXF-RS injections with ResourceContext, but I can see now, that it injects only CXF-RS beans, no Spring at all.