I have a problem with Spring MVC. I am using Stanford NLP and I put initialization of it to singleton class.
#Component
#Scope("singleton")
public class JavaNLP implements NlpInterface
{
private DependencyNLP nlp_object = null;
#PostConstruct
public void init()
{
if (nlp_object == null) {
nlp_object = new DependencyNLP();
nlp_object.init("tokenize, ssplit, pos, lemma, ner, parse, dcoref");
}
}
#Override
public void init(String name, DataContainer container)
{
this.container = container;
nlp_object.annotate(container.getText());
}
#Override
public void execute()
{
...
}
}
Every request is calling init(String name, DataContainer container) and execute. Problem is in 70% requests nlp_object is not initialized.
Inside of a controller:
#Autowired
#Qualifier("javaNLP")
private NlpInterface nlpInterface;
#RequestMapping(value = "/parse", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, headers = {"Content-type=application/json"})
public #ResponseBody
String parseWithGazetteerinJSON(#RequestBody DataContainer container)
{
String name = "Parsing text";
nlpInterface.init(name, container);
nlpInterface.execute();
JSONArray triples = nlpInterface.getTriplesAsJSON();
return triples.toString();
}
EDIT 1
Unfortunetly it is still not working. I think I found where is the problem, just dont know how to fix it.
I put configuration + calling init function into config
<mvc:annotation-driven />
<context:component-scan base-package="com.metadata.tripletws.model" />
<context:component-scan base-package="com.metadata.tripletws.controller" />
<bean id="nlp" class="com.metadata.tripletws.service.Nlp" init-method="init"></bean>
I have created new object called Nlp and it is just sort of envelope for DependencyNLP (external library)
#Component
public class Nlp
{
private DependencyNLP nlp;
public void init()
{
nlp = new DependencyNLP();
nlp.init();
}
public DependencyNLP getInstance()
{
return nlp;
}
public void execute()
{
...
}
...
}
Then I added this code to the controller:
DependencyNLP nlpInstance = nlp.getInstance();
System.out.println(nlpInstance);
nlpInstance.annotate(container.getSection().getText());
nlpInstance.execute(...);
and defined private Autowired variable
#Autowired
private Nlp nlp;
Printed nlpInstance is always same. That makes sense in the end. But here is the problem I beliave. Requests are influencing each other.
Does somebody know how to make it run?
Thanks
Instead of initializing the nlp_object in a #PostConstruct method try injecting it as a resource to your implementation of NlpInterface
Not sure if this will solve your problem, but if it were me I'd give it a go.
Also, you init() method(if you must go that way) should be synhcronized with double if ( singletonObj == null) gates outside and inside the sync block.
Related
I am writing a Spring Boot Application. I want to audit methods with my annotation #AuditMetod: For example I have method foo() with the annotation:
#AuditMetod(name = "SomeValue")
foo() {...}
I want to handle and audit such methods like this (the simplest example):
auditMethod(Method method) {
if (method.hasAnnotation(AuditMethod.class)) {
System.out.println (method.getName() + " was called at " + new Date())
}
}
upd
Thanks to #Karthikeyan #Swapnil Khante and #misha2048 I understood, that I need to use AOP. But I have 2 problems:
The only method in Aspect class in not being called and I don't see the inscription "----------ASPECT METHOD IS CALLED-----------" in log
How can I check in aspect method what method it is intercepting. To get an instance of Method class.
Now I have the following code:
Controller:
#PostMapping
#LoggingRest(executor = "USER", method = "CREATE", model = "SUBSCRIPTION")
public ResponseEntity<?> create(#Valid #RequestBody SubscriptionRequestDto dto) {
...
}
Aspect:
`#Aspect
#Slf4j
#Component
public class AuditAspect {
#Pointcut(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----------ASPECT METHOD IS CALLED------------");
}`
And annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LoggingRest {
String executor() default "SYSTEM";
String method() default "";
String model() default "";
}
Auditing is a cross-cutting concern and can be handled using AOP.
Another solution would be to use a low-level solution by writing a custom annotation and using a Spring interceptorto write your business logic.
To use the Spring interceptor you will need to implement the HandlerInterceptor interface
Example of the annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Audit {
boolean active() default true;
}
Interceptor example
#Component
public class AuditInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Audit annotation = handlerMethod.getMethodAnnotation(Audit.class);
if (annotation != null && annotation.active()) {
// your business logic
}
}
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
check this interceptor example
I think one of the solutions here, as #Karthikeyan mentioned, is to use Spring AOP.
If you are not aware a brief introduction - spring-aop module implements the aspect oriented programming paradigm. We extract some common functionality, that we generally want to apply to some subset of functions/methods, to an entity called Aspect (see class annotated with #Aspect). This class will contain out cross-cutting functionality - such as auditing, for instance we want to audit the methods execution time, lets say. We just put the code to be executed, the condition, which tell the spring what exact beans methods should be affect by this aspect, see below.
For example, if I can audit the method execution duration with the following very simple example (in my case I said that any public method, returning void inside the Class com.example.stackoverflow.BusinessLogicClass must be inspected by this Aspect):
#SpringBootApplication
#EnableAspectJAutoProxy
public class StackoverflowApplication implements ApplicationRunner {
#Autowired
private BusinessLogicClass businessLogicClass;
public static void main(String[] args) {
SpringApplication.run(StackoverflowApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
businessLogicClass.test();
}
}
#Aspect
#Component
class MyAspectLogicClass {
#Around("execution(public void com.example.stackoverflow.BusinessLogicClass.*(..))")
public Object hangAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long before = System.currentTimeMillis();
Object returnedValue = proceedingJoinPoint.proceed();
long after = System.currentTimeMillis();
System.out.printf("Retruned in '%s' ms %n", (after - before));
return returnedValue;
}
}
#Component
class BusinessLogicClass {
public void test() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
In my case, I will get the time before method execution, then by the means of
proceedingJoinPoint.proceed() call I delegate the execution to the real method, and then, once I get the response back, I will get the current system time and calculate the execution time, fairly simple.
I hope I have at least directed you somewhere, if you are looking for documentation, this are the resources I suggest you should look for:
https://docs.spring.io/spring-framework/docs/2.5.x/reference/aop.html offical spring doc (stale a bit, but there are some valuable things to learn)
https://docs.spring.io/spring-framework/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html is more fresh doc
Hope it helped :)
The problem was in right annotation. In Aspect class I tried #Around and everything works as I need.
#Aspect
#Slf4j
#Component
public class AuditAspect {
#Around(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
var method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
log.info("----------ASPECT METHOD IS CALLED------------");
}
}
For getting a Method instance I use fallowing code
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
Below is the method I'm trying to write unit test using junit 5
#Value("${proxy.host}")
private String endpoint;
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
Below is the unit test I'm trying to run
#InjectMocks
private AWSAuthHandler testAWSAuthHandler;
#Test
public void testSetAwsRequestGETParameter() throws Exception {
URI mockedURI = Mockito.mock(URI.class);
assertNotNull(testAWSAuthHandler.setAwsRequestGETParameter("/status/7deaed5e-3080-45ec-89ba-403977d60c0c"));
}
Below is the stack trace:
java.lang.NullPointerException
at java.base/java.net.URI$Parser.parse(URI.java:3106)
at java.base/java.net.URI.<init>(URI.java:600)
at java.base/java.net.URI.create(URI.java:881)
Can someone please help me with the missing part? Thank you
For setting properties of class that you can't mock you can use Spring Reflection Utils, like that:
ReflectionUtils.setField(field, target, value);
where the field is the name of the field which you want to set ("endpoint" for your case),
target is the mocked class (testAWSAuthHandler for your case)
value is the wanted value
As Sweta Sharma said, you need to initialise AWSAuthHandler with some value for endpoint field. That's why it is better to use constructor injection rather than field one.
Assuming your AWSAuthHandler class look like this (as you didn't provide the code for the whole class):
public class AWSAuthHandler {
#Value("${proxy.host}")
private String endpoint;
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
You can refactor it in the following way:
public class AWSAuthHandler {
private String endpoint;
public AWSAuthHandler(#Value("${proxy.host}") String endpoint) {
this.endpoint = endpoint;
}
public Request<Void> setAwsRequestGETParameter(String setStatusPath) {
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.GET);
requestAws.setEndpoint(URI.create(endpoint));
requestAws.setResourcePath(setStatusPath);
return requestAws;
}
Then you can create tests for this class:
private AWSAuthHandler testAWSAuthHandler;
#BeforeEach
void setUpTests() {
this.testAWSAuthHandler = new AWSAuthHandler("some-endpoint-here");
}
#Test
public void testSetAwsRequestGETParameter() throws Exception {
assertNotNull(testAWSAuthHandler.setAwsRequestGETParameter("/status/7deaed5e-3080-45ec-89ba-403977d60c0c"));
}
You can read more about Spring #Value annotation here, for example: https://www.baeldung.com/spring-value-annotation
I am attempting to use Spring Boot Cache with a Caffeine cacheManager.
I have injected a service class into a controller like this:
#RestController
#RequestMapping("property")
public class PropertyController {
private final PropertyService propertyService;
#Autowired
public PropertyController(PropertyService propertyService) {
this.propertyService = propertyService;
}
#PostMapping("get")
public Property getPropertyByName(#RequestParam("name") String name) {
return propertyService.get(name);
}
}
and the PropertyService looks like this:
#CacheConfig(cacheNames = "property")
#Service
public class PropertyServiceImpl implements PropertyService {
private final PropertyRepository propertyRepository;
#Autowired
public PropertyServiceImpl(PropertyRepository propertyRepository) {
this.propertyRepository = propertyRepository;
}
#Override
public Property get(#NonNull String name, #Nullable String entity, #Nullable Long entityId) {
System.out.println("inside: " + name);
return propertyRepository.findByNameAndEntityAndEntityId(name, entity, entityId);
}
#Cacheable
#Override
public Property get(#NonNull String name) {
return get(name, null, null);
}
}
Now, when I call the RestController get endpoint and supply a value for the name, every request ends up doing inside the method that should be getting cached.
However, if I call the controller get endpoint but pass a hardcoded String into the service class method, like this:
#PostMapping("get")
public Property getPropertyByName(#RequestParam("name") String name) {
return propertyService.get("hardcoded");
}
Then the method is only invoked the first time, but not on subsequent calls.
What's going on here? Why is it not caching the method call when I supply a value dynamically?
Here is some configuration:
#Configuration
public class CacheConfiguration {
#Bean
public CacheManager cacheManager() {
val caffeineCacheManager = new CaffeineCacheManager("property", "another");
caffeineCacheManager.setCaffeine(caffeineCacheBuilder());
return caffeineCacheManager;
}
public Caffeine<Object, Object> caffeineCacheBuilder() {
return Caffeine.newBuilder()
.initialCapacity(200)
.maximumSize(500)
.weakKeys()
.recordStats();
}
}
2 solutions (they work for me):
remove .weakKeys()
propertyService.get(name.intern()) - wouldn't really do that, possibly a big cost
Sorry, but I don't have enough knowledge to explain this. Probably something to do with internal key representation by Caffeine.
I was trying to find it but I found many different scenarios but not this one.
What I want to do is to add "/api/" prefix to all routes in controllers under com.myproject.api .
I want "/api/*" for all controllers under package com.myapp.api and no prefix for all controllers under com.myapp.web
Is it possible with Spring / Spring Boot ?
With Spring Boot, this worked for me :
#Configuration
#EnableWebMvc
public class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("/api",
HandlerTypePredicate.forBasePackage("com.your.package"));
}
}
If you are using springboot, you can add the following:
server.servlet.context-path=/api
to application.properties file.
I achieved the result I think you are looking for in the following way, so long as you are using MVC.
First make a configuration class that implements WebMvcRegistrations
#Configuration
public class WebMvcConfig implements WebMvcRegistrations {
#Value("${Prop.Value.String}") //"api"
private String apiPrefix;
#Value("${Prop.Package.Names}") //["com.myapp.api","Others if you like"]
private String[] prefixedPackages;
#Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new PrefixedApiRequestHandler(apiPrefix,prefixedPackages);
}
}
Then create a class that extends RequestMappingHandlerMapping
and overrides getMappingForMethod
#Log4j2
public class PrefixedApiRequestHandler extends RequestMappingHandlerMapping {
private final String prefix;
private final String[] prefixedPackages;
public PrefixedApiRequestHandler(final String prefix, final String... packages) {
super();
this.prefix = prefix;
this.prefixedPackages = packages.clone();
}
#Override
protected RequestMappingInfo getMappingForMethod(final Method method, final Class<?> handlerType) {
RequestMappingInfo info = super.getMappingForMethod(method, handlerType);
if (info == null) {
return null;
}
for (final String packageRef : prefixedPackages) {
if (handlerType.getPackageName().contains(packageRef)) {
info = createPrefixedApi().combine(info);
log.trace("Updated Prefixed Mapping " + info);
return info;
}
}
log.trace("Skipped Non-Prefixed Mapping " + info);
return info;
}
private RequestMappingInfo createPrefixedApi() {
String[] patterns = new String[prefix.length()];
for (int i = 0; i < patterns.length; i++) {
// Build the URL prefix
patterns[i] = prefix;
}
return new RequestMappingInfo(
new PatternsRequestCondition(patterns,
getUrlPathHelper(),
getPathMatcher(),
useSuffixPatternMatch(),
useTrailingSlashMatch(),
getFileExtensions()),
new RequestMethodsRequestCondition(),
new ParamsRequestCondition(),
new HeadersRequestCondition(),
new ConsumesRequestCondition(),
new ProducesRequestCondition(),
null);
}
}
You should then see /api/(ControllerMapping) for all mappings, in the specified packages only. Note: I have #RequestMapping("/") at the top of my controller.
Already answered here and here.
Add an application.properties file under src/main/resources, with the following option:
server.contextPath=/api
Check the official reference for common properties.
You should add #RequestMapping("/api") to top of every desired #Controller or #RestController class.
When both the class and method have that annotation, Spring Boot appends them while building the url. In below example the method will be bound to /api/user route.
#RequestMapping("/api")
#RestController
public class MyController {
#RequestMapping("/user")
public List<User> getUsers(){
return ...
}
}
I have the following configuration to intercept a method and apply advice after returning from method, But, the following configuration does not work. Could you suggest on what I am missing?
#Service("txnEventSubscriber")
EventSubscriberImpl
...
#Resource(name="txnEventSubscriber")
private EventSubscriberImpl subscriber;
#Bean
public Advice myAdvice() {
return new AfterReturningAdvice() {
#Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
{
System.out.println("inside advice");
}
};
}
#Bean
public ProxyFactoryBean myProxyFactoryBean() {
return new ProxyFactoryBean() {
private static final long serialVersionUID = 6296720408391985671L;
#PostConstruct
public void afterPropertiesSet() throws ClassNotFoundException {
setTarget(subscriber);
setInterceptorNames(new String[] {"myAdvice"});
}
};
}
I have the EventSubscriber which when invoked and when method is returned, I need to intercept the method call and do something... in this case, print "inside advice".
I am not seeing any exceptions, just method advice is not getting called.
First of all, I see you have the class name as EventSubscriberImpl and you are injecting the same type of class. Meaning, you are not programming to interfaces. In this case, you would want to setProxyTargetClass(true); for your ProxyFactoryBean bean and put CGLIB in your project's classpath.
Secondly, you would need something like this
#Resource(name="myProxyFactoryBean")
private EventSubscriberImpl subscriber;
whenever you want to use the proxied version of your EventSubscriberImpl. Meaning, you need to explicitly get that proxied bean by its proxied bean name.
Thirdly, I'd use something like this, to avoid getting the bean by its proxied name:
#Resource(name="txnEventSubscriber")
private EventSubscriberImpl subscriber;
#Bean
public Advice myAdvice() {
return new AfterReturningAdvice() {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target)
{
System.out.println("inside advice");
}
};
}
#Bean
public Advisor myAdvisor() {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(public * com.foo.bar.EventSubscriberImpl.*(..))");
return new DefaultPointcutAdvisor(pointcut, myAdvice());
}