When trying to invoke the REST endpoint of the following interface for a Spring #RestController:
public interface ComputationCallback
{
#PostMapping("task")
boolean startTask(
#RequestParam(value = "taskId") UUID taskId
);
}
I get this strange error message:
Cannot resolve parameter names for constructor public java.util.UUID(long,long)
The controller implementing the method looks like this:
#RestController
public class ComputationCallbackController implements ComputationCallback
{
#Override public boolean startTask(UUID taskId)
{
...
}
}
Actually, I discovered that this error does not relate to the conversion of the UUID value. What was missing is the annotation on the method in the implementing class. The parameters in the interface do not seem to be discoverable. So changing the implementation to
#RestController
public class ComputationCallbackController implements ComputationCallback
{
#Override public boolean startTask(#RequestParam(value = "taskId") UUID taskId)
{
...
}
}
fixed the error.
Related
I have a Spring REST API I am developing that keeps returning a NullReferenceException for some reason. I can't figure out why. Here is my code:
The interface:
public interface InitiativesService {
InitiativeDto createInitiative(InitiativeDto initiativeDto);
}
The class that implements it:
public class InitiativeServiceImpl implements InitiativesService {
private final InitiativeRepository initiativeRepository;
#Autowired
public InitiativeServiceImpl(InitiativeRepository initiativeRepository)
{
this.initiativeRepository = initiativeRepository;
}
#Override
public InitiativeDto createInitiative(InitiativeDto initiativeDto) {
initiativeRepository.Save(initiativeDto);
}
The Repository class for communicating to the dB:
#Repository
public interface InitiativeRepository extends JpaRepository<Initiative, Long> {}
And lastly the controller:
public class InitiativesController {
private InitiativesService initiativesService;
public InitiativesController(InitiativesService initiativesService) {
this.initiativesService = initiativesService;
}
#PostMapping(produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<InitiativeDto> createInitiative(#Valid #RequestBody InitiativeDto initiative) {
log.info("Saving");
InitiativeDto createdInitiative = initiativesService.createInitiative(initiative);
return ResponseEntity.status(HttpStatus.CREATED).body(createdInitiative);
}
When I try running this and testing via Postman, it returns a NullPointerException. I debug it and I can see the initiativesService is null. I am confused as I have the same implementation for creating Users which works fine without this issue. Can anyone spot any problems I have here?
Add #Autowired annotation on your constructor to autowire your initiativeService
#Autowired
public InitiativesController(InitiativesService initiativesService) {
this.initiativesService = initiativesService;
}
Implemented a strategy pattern very simple with the help of Spring Boot:
I have an interface:
public interface IOneStrategy {
void executeTheThing();
}
I have an implementation of the strategy One like this:
#Service("FIRST")
public class OneStrategyFirst implements IOneStrategy {
#Override
public void executeTheThing() {
System.out.println("OneStrategyFirst.executeTheThing");
}
}
I have a class which consumes the injected implementations:
#Service
public class ExecuteStrategyOne {
private Map<String, IOneStrategy> strategies;
public void executeStrategyOne(String name) {
if (!strategies.containsKey(name)) {
throw new IllegalArgumentException("The key " + name + " does not exist.");
}
strategies.get(name).executeTheThing();
}
}
and those implementations will be injected by Spring boot automatically by using the name FIRST, 'SECOND' etc. (assuming that this is simply a String etc. works very well.).
But now I want to implement another strategy via second interface:
public interface ITwoStrategy {
void executeTheThing();
}
and the executing service for the strategy:
#Service
public class ExecuteStrategyTwo {
private Map<String, ITwoStrategy> strategies;
...
}
and now the problematic part, because my application uses the same name which should be made part of the key of the above map I want to use the following:
#Service("FIRST")
public class TwoStrategyFirst implements ITwoStrategy {
#Override
public void executeTheThing() {
System.out.println("TwoStrategyFirst.executeTheThing");
}
}
This will of course result into an exception based on the duplicate bean name. The name FIRST is really needed to make the difference between the implementation.
I already found things about #Qualifier which I could use instead of #Service(FIRST)
#Service
#Qualifier(FIRST)
public class TwoStrategyFirst implements ITwoStrategy {
#Override
public void executeTheThing() {
System.out.println("TwoStrategyFirst.executeTheThing");
}
}
which unfortunately does not inject the classes into the map by using the name of the qualifier just by the name of the class which is not what I intended to do.
Does exist a solution to get the key of the map in the strategy execution the same as with the #Service("FIRST")?
I could use a solution via using the Qualifier annotation like this:
#Service
#Qualifier(FIRST)
public class TwoStrategyFirst implements ITwoStrategy {
...
}
And based on that there is a more or less easy solution via a bit of code:
#Service
public class ExecuteStrategyTwo {
private Map<String, ITwoStrategy> strategies;
public ExecuteStrategyOne(List<ITwoStrategy> strategies) {
this.strategies = strategies.stream()
.collect(
Collectors.toMap(k -> k.getClass().getDeclaredAnnotation(Qualifier.class).value(), Function.identity()));
}
This will inject all implementation into the list of the constructor and will be translated into the map by using the qualifier annotation.
In the testing framework I'm using, there is a Precondition annotation that takes a class and calls call() method in it at runtime.
Core Modules of the Testing Framework:
Precondition Annotation
public #interface Precondition {
Class<? extends Scriptlet<? extends Context>> scriptlet();
String value() default Constants.EMPTY_STRING;
}
Scriptlet
public interface Scriptlet<V> extends Callable<V> {
}
Context
public interface Context {
}
If I pass the following precondition class to Precondition annotation there will be no errors.
CommonContextInit precondition class
class CommonContextInit implements Scriptlet<DataModel>{
CommonContextInit(Object script,String value){
}
#override
public DataModel call() throws Exception {
return null;
}
}
Script
#Precondition(scriptlet=CommonContextInit.class)
But I want to make CommonContextInit a generic where user can pass the type of the scriptlet so I changed the CommonContextInit class as follows
CommonContextInit precondition class
class CommonContextInit<T extends Context> implements Scriptlet<T>{
CommonContextInit(Object script,String value){
}
#override
public T call() throws Exception {
return null;
}
}
But the issue here is I have no idea how to pass the class to Precondition. The following I have tried but looks like it's totally invalid syntax.
// ERROR: The annotation #Precondition is disallowed for this location.
#Precondition(scriptlet=CommonContextInit<DataModel>.class)
How should I pass the Class of CommonContextInit<DataModel> to Precondition?
To extend my comment you shall have something like
class DataModelInstance extends CommonContextInit<DataModel> implements Scriptlet<DataModel>{
DataModelInstance(Object script,String value){
super(script, value);
}
//other overrides
}
and then call #Precondition(scriptlet=DataModelInstance.class)
I have a server built with java and spring.
What i am trying to do is that my controller with the same endpoint will get two different objects.
This is an example for what I mean:
I know I can do that:
public class Option1{
private String name;
...
//getter and setter
}
public class Option2{
private Long id;
...
//getter and setter
}
#Controller
public class Controller{
#RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
#ResponseBody
public String searchProv(#ResponseBody Option1 data1){
return "option1"
}
#RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
#ResponseBody
public String searchProv(#ResponseBody Option2 data2){
return "option2"
}
}
but I wonder if it is possible to passing different json object to the same endpoint and do that:
#Controller
public class Controller{
#RequestMapping(value = "service/getData", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Any> getData(#ResponseBody Option1And2 data){
if(data instanceof Option1){
return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}
if(data instanceof Option2){
return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
such that 'Option1And2' is generic object can be option1 or option2.
I tried to replace 'Option1And2' to 'Any' but it didn't went well because I get a list of keys and values
You should use JsonNode object.
for your example you should do this:
#Controller
public class Controller{
#RequestMapping(value = "service/getData", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Any> getData(#RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
the JsonNode and the ObjectMapper you should import from here:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
this link should help you to understand better on JsonNode and give you more details.
and this link should help you with the convertValue from JsonNode to java object(POJO).
This is a good time to use inheritance and Java Generics. It is worth noting, if your controller has any dependencies such as a #Service or #Repository, then those too must be generic.
You might have a generic controller:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
#GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
#PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// #DeleteMapping, #PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
#Controller
#RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
#Autowired CategoryServ serv;
#Override
public GenericService<Category> getService() {
return serv;
}
}
#Controller
#RequestMapping("/product")
class ProductContr extends GenericController<Product> {
#Autowired ProductServ serv;
#Override
public GenericService<Product> getService() {
return serv;
}
}
You then have to have abstract versions of the dependencies. The services:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
#Service
class CategoryServ extends GenericService<Category> {
#Autowired CategoryRepo repo;
#Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
#Service
class ProductServ extends GenericService<Product> {
#Autowired ProductRepo repo;
#Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
Then, the services have their dependencies as well - the repositories:
#NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
#Repository
interface CategoryRepo extends GenericRepository<Category> {
}
#Repository
interface ProductRepo extends GenericRepository<Product> {
}
This was my first approach. It works very nicely. However, this does create a strong coupling between the business logic of each service and the generic service. The same holds true for the generic controller and its child classes. You can of course always override a particular CRUD operation. But, you must do this with care as you may created unexpected behavior. It is also worth noting that inheriting from classes that have methods that are annotated with #RequestMapping automatically exposes all of the annotated methods. This may be undesirable. For example, we may not want a delete option for categories, but we want it for products. To combat this, instead of annotating the method in the parent class, we can simply define it in the parent class, and override the desired CRUD operations with the added #RequestMapping annotation and then call the super class method.
Another approach is using annotations.
Seems like you want program itself to determine what type the option is.But before you do that,are you sure what is the difference between these two Object?
First is,what is the Option1And2 actually is?If the Option1And2 contains all the field of Option1 and Option2 but it's not the subclass of those,then probably the Option1And2 could be like:
#Data
public class Option1And2{
private String name;
private Long id;
}
If you have other limits like "one of them and only one of them has
to be null",then you could determine it by this rule.
If you don't have any other limitation,then maybe you could add a new
field as a flag.
In fact those code style are not recommend.If those two functions have different responsibilities,then maybe it's better to not mix them together.You will understand what I mean when you have to refactor these code.
If these two functions do have lots of things in common,maybe it's better for you to refactor the service logic instead of just combining two service roughly by creating a new param Option1And2.
By the way,what are you exactly want to do?Why do you want to merge those two object into one?
I'm using Spring Boot to set up a REST API. I'll be making a bunch of #RestControllers and want to set a pointcut on those methods that return a subtype of a specific abstract class I call Model. These controllers look something like this:
#RestController
public class UserController {
#RequestMapping(...)
public Person getAllPeople() {
...
}
}
Where my Person class would look something like this:
public class Person extends Model {
...
}
So would it be possible to write advice that looks something like this:
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(<T extends mypackages.Model> T mypackages.api.*.*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
Of course that won't work because the advice is not valid syntactically. In the reference documentation, I have only found information about generic parameters, not return types (Spring AOP reference). What I have now is this, but I think something like the example above would be a lot more efficient:
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(* mypackages.api.*.*(..))",
returning = "model")
public void doSomething(Object model) {
if (model instanceof Model)
doSomethingWithModel((Model) model);
}
}
My next question would be, is the same possible for those methods that return a Collection of suptypes of Model? Because the reference states that parameter types cannot be generic Collections.
Have you tried using + after your interface?
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(mypackages.Model+ mypackages.api.*.*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
You could try do not specify the return type. Based on the documentation it will be resolved by the type of the parameter used at the returning clause:
A returning clause also restricts matching to only those method
executions that return a value of the specified type ( Object in this
case, which will match any return value).
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(* mypackages.api.*.*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
Have a look to the below link. It answers also your second question, about generic collections.
Aspectj Matching Return Type
Just for curiosity I have created a project for testing this and it started working for me straight forward. I can only think the path your pointcut is pointing to is wrong. Try with:
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(* mypackages.api..*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
You can have a look to my project at: spring-aspectj-interfaces
There you will see different values for the pointcut (only one not commented, of course), all of them valid.