#RequestMapping with placeholder not working - spring-mvc

So i have spend hours to try to get the anwser of this post working:
Overriding RequestMapping on SpringMVC controller
But it really is not working. What I have so far:
springmvc-servlet.xml
<context:property-placeholder location="classpath:numbernick.properties"/>
<context:component-scan base-package="com.numbernick" />
<context:annotation-config />
And I've got a Controller:
#Value("${requestmapping.test}")
private String test;
#RequestMapping("${requestmapping.test}.html")
public ModelAndView test() {
ModelAndView mav = new ModelAndView();
mav.setViewName(test.html);
log.debug("Test: "+test);
return mav;
}
numbernick.properties:
requestmapping.test=myUrl
This should work fine. When I call the page, I get a logmessage saying "Test: myUrl"
. BUT! this comes when I call "/${requestmapping.test},html". And it should work with calling "/myUrl.html". I have absolutely no Idea why it is this way. Obviously the PropertyPlaceholder works and doesn't work at the same time. (BTW: It is a nested RequestMapping. But it also doesn't work at topLvl-RequestMapping as well)
How can this be and what can I do to fix this? I'm currently working with spring verion 3.2.8

I had this problem also and solved it once I realized that a PropertyPlaceholderConfigurer bean wasn't loaded into the context of the module where many of the placeholders existed.
Simple solution was to refactor our externalized configuration. In the end, I moved the #PropertySources definition and PropertyPlaceholderConfigurer bean to a common module and all is well:
#Configuration
#PropertySources(value = {#PropertySource("classpath:app-config.properties")})
public class ExternalizedConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
The request mappings like this work as expected now:
#RequestMapping(value="/${foo.bar.rest_proxy_uri}/**", method = RequestMethod.GET)
In fact, on server startup, you will see the placeholders have been resolved:
2015-05-06 16:21:52 INFO RequestMappingHandlerMapping:220 - Mapped "{[/restProxy/**],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.lang.String> foo.bar.web.controllers.RestfulFooBarProxyController.proxyGet(javax.servlet.http.HttpServletRequest)

Related

Kafka Streams Serdes having nested generic doesn't work

I have following code which uses functional style to define two functions for kafka topics
#Bean
public Function<KStream<String, CloudEvent<ClassA>>, KStream<String, CloudEvent<ClassB>>> method1() {
....... //lambda
}
#Bean
public Function<KStream<String, CloudEvent<ClassB>>, KStream<String, CloudEvent<ClassC>>> method2() {
...... //lambda
}
For these two functions I define serdes so
#Bean
public Serde<CloudEventMessage<ClassA>> classASerde(ObjectMapper mapper, Validator validator) {
return StreamsSerdes.classASerde(mapper,validator);
}
#Bean
public Serde<CloudEventMessage<ClassB>> classBSerde(ObjectMapper mapper, Validator validator) {
return StreamsSerdes.classBSerde(mapper,validator);
}
This construction doesn't work as at runtime spring tries to deserialize CloudEvent<ClassB> with Serde of CloutEvent<ClassA>. Is there someway to give hint to use the correct serde for method1 and method2 ?
Secondly I could bypass the above issues by mentioning Serdes in application.properties
spring.application.cloud.stream.kafka.streams.bindings.method1-in-0.consumer.valueSerde=package.serde.StreamsSerdes$ClassASerde
spring.application.cloud.stream.kafka.streams.bindings.method2-in-0.consumer.valueSerde=package.serde.StreamsSerdes$ClassBSerde
However now I get other issues as these Serde classes don't have default constructor. I do need ObjectMapper, Validator from Spring to inject beans (#Service) to perfrom converstions/validations during deserialization.
Has anyone come across similar issues or perhaps have ideas how to resolve them ?
Thanks
I think it is a gap that the nested generics are not working right now in the binder. Do you mind creating an issue in the repository and linking this thread?
As to the second issue that you are running into when providing properties in application.properties, you can try using a workaround. The Serde interface has a configure method that takes a map.
default void configure(Map<String, ?> configs, boolean isKey) {
// intentionally left blank
}
Override this method in your Serde implementation and set those bean objects under some keys.
ObjectMapper mapper;
Validator validator;
#Override
public void configure(Map<String, ?> configs, boolean isKey) {
this.mapper = (ObjectMapper) configs.get("mapper.key");
this.validator = (Validator) configs.get("validator.key");
}
You need to remove accessing them from the constructor and use those fields directly for deserialization and serialization.
Then you provide this bean in your application to populate the map:
#Bean
public StreamsBuilderFactoryBeanCustomizer streamsBuilderFactoryBeanCustomizer(ObjectMapper mapper, Validator validator) {
return factoryBean -> {
factoryBean.getStreamsConfiguration().put("mappeer.key", mapper);
factoryBean.getStreamsConfiguration().put("validator.key", validator);
};
}
I haven't tried this code in an application, but it is something that you can try and see if it works with your code.

Can't use a session ejb in my managed bean cause i get a Null Pointer Exception

First of all I want to say I'm pretty new in programming with ejb and jsf, and I'm trying to complete a project started by a friend of mine.
I'm getting a NullPointerException caused by the invoke of the method utenteSessionBean.CheckUtentebyId(username) of the session bean object called utenteSessionBean, declared inside the managed bean called Neo4jMBean.
I learned that it's not necessary creating and initializing a session bean (as you must do with a normal java object) in managed bean, but it's enough declaring it.
Here is the code of the session bean, which retrieves data from a DB
#Stateless
#LocalBean
public class UtenteSessionBean {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("EnterpriseApplication2-ejbPU");
public boolean CheckUtentebyId(String username){
EntityManager em = emf.createEntityManager();
Query query = em.createNamedQuery("Utente.findByUsername");
query.setParameter("username", username);
List<Utente> Res=query.getResultList();
//completare funzione ctrl+spazio
System.out.println("pre");
System.out.println("pre"+Res.isEmpty());
em.close();
System.out.println("post");
System.out.println("post"+Res.isEmpty());
if(Res.size()>=1)
{
return true;
}
else
{
return false;
}
}
}
Here's the code of the managed bean:
#ManagedBean
#ViewScoped
public class Neo4jMBean {
#EJB
private UtenteSessionBean utenteSessionBean;
static String SERVER_ROOT_URI = "http://localhost:7474/db/data/";
public Neo4jMBean() {
}
public boolean getUser(String username) {
return utenteSessionBean.CheckUtentebyId(username);
}
}
I've searched on StackOverFlow many times a solution for fixing this problem, but I haven't found something that works for me yet.
I fixed it accessing the EJB Components using JNDI.
In few words, if i use an EJB in a managed bean method, i need to add the next lines of code:
InitialContext ic = new InitialContext();
SessionBeanName = (SessionBeanClass) ic.lookup("java:global/NameOfTheApplication/NameOfTheEJBpackage/NameOfTheSessionBean");
It must be surronded by a try-catch statement
Create empty beans.xml file in your WEB-INF folder to enable CDI

Aspect not getting called in Spring MVC

I have our aspect, annotation & MVC controller written as follows:
Aspect
#Aspect
public class AuditAspect {
#Around(value = "#annotation(com.test.Audit)")
public Object audit(ProceedingJoinPoint pjp) {
System.out.println("Inside the Audit aspect ...");
Object result = null;
try {
result = pjp.proceed();
} catch (Throwable t) {
}
return result;
}
}
The annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Audit
{
AuditType auditType();
}
The controller:
#RestController
#RequestMapping("/patients")
public class PatientController {
#Audit(auditType = AuditType.PATIENT_LIST)
#RequestMapping(value="", method=RequestMethod.GET)
public APIResponse getPatients(HttpServletRequest request, HttpServletResponse response, #RequestParam(required = false, value="audit") String sAudit) {
System.out.println("Inside getPatients ...");
return null;
}
}
However, the aspect's audit method is not getting called whenever I make rest requests.
Looked around for some help. Found few posts where there were mentions of AspectJ not working with Spring MVC controllers. However, I tried this same example with a simple spring MVC application, and the aspect was getting called properly, even if controller methods were annotated. Not sure what is going wrong here. Any pointers/suggestions here would be very helpful.
The sample application I tried didn't have use of spring transaction manager, or integration with hibernate etc... Would that make any difference?
Also, given below is the context file entries:
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.test">
<context:include-filter type="aspectj" expression="com.test.AuditAspect" />
</context:component-scan>
<context:annotation-config />
In order to make Spring AOP work, both your aspect and the target object must be a Spring #Component.

Controller Inheritance and Ambiguous Mappings with URL Versioning in Spring MVC

I am trying to setup versioned services with Spring MVC, using inheritance to extend older controllers to avoid rewriting unchanged controller methods. I've based my solution on a previous question about versioning services, however I've run into a problem with ambiguous mappings.
#Controller
#RequestMapping({"/rest/v1/bookmark"})
public class BookmarkJsonController {
#ResponseBody
#RequestMapping(value = "/write", produces = "application/json", method = RequestMethod.POST)
public Map<String, String> writeBookmark(#RequestParam String parameter) {
// Perform some operations and return String
}
}
#Controller
#RequestMapping({"/rest/v2/bookmark"})
public class BookmarkJsonControllerV2 extends BookmarkJsonController {
#ResponseBody
#RequestMapping(value = "/write", produces = "application/json", method = RequestMethod.POST)
public BookmarkJsonModel writeBookmark(#RequestBody #Valid BookmarkJsonModel bookmark) {
// Perform some operations and return BookmarkJsonModel
}
}
With this setup I get IllegalStateException: Ambiguous mapping found. My thought regarding this is that because I have two methods with different return/argument types I have two methods in BookmarkJsonControllerV2 with the same mapping. As a workaround I attempted to override writeBookmark in BookmarkJsonControllerV2 without any request mapping:
#Override
public Map<String, String> writeBookmark(#RequestParam String parameter) {
return null; // Shouldn't actually be used
}
However, when I compiled and ran this code I still got the exception for an ambiguous mapping. However, when I hit the URL /rest/v2/bookmark/write I got back an empty/null response. Upon changing return null to:
return new HashMap<String, String>() {{
put("This is called from /rest/v2/bookmark/write", "?!");
}};
I would receive JSON with that map, indicating that despite not having any request mapping annotation, it is apparently "inheriting" the annotation from the super class. At this point, my only "solution" to future-proofing the extension of the controllers is to make every controller return Object and only have the HttpServletRequest and HttpServletResponse objects as arguments. This seems like a total hack and I would rather never do this.
So is there a better approach to achieve URL versioning using Spring MVC that allows me to only override updated methods in subsequent versions or is my only real option to completely rewrite each controller?
For whatever reason, using the #RequestMapping annotation was causing the ambiguous mapping exceptions. As a workaround I decided to try using springmvc-router for my REST services which would allow me to leverage inheritance on my controller classes so I would not have to reimplement endpoints that did not change between versions as desired. My solution also allowed me to continue using annotation mappings for my non-REST controllers.
Note: I am using Spring 3.1, which has different classes for the handler mappings than previous versions.
The springmvc-router project brings the router system from the Play framework over to Spring MVC. Inside of my application-context.xml, the relevant setup looks like:
<mvc:annotation-driven/>
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" />
<bean class="org.resthub.web.springmvc.router.RouterHandlerMapping">
<property name="routeFiles">
<list>
<value>routes/routes.conf</value>
</list>
</property>
<property name="order" value="0" />
</bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="order" value="1" />
</bean>
This allows me to continue using my annotated controllers alongside the router. Spring uses a chain-of-responsibility system, so we can assign multiple mapping handlers. From here, I have a router configuration like so:
# Original Services
POST /rest/bookmark/write bookmarkJsonController.write
POST /rest/bookmark/delete bookmarkJsonController.delete
# Version 2 Services
POST /rest/v2/bookmark/write bookmarkJsonControllerV2.write
POST /rest/v2/bookmark/delete bookmarkJsonControllerV2.delete
Alongside controllers looking like:
#Controller
public class BookmarkJsonController {
#ResponseBody
public Map<String, Boolean> write(#RequestParam String param) { /* Actions go here */ }
#ResponseBody
public Map<String, Boolean> delete(#RequestParam String param) { /* Actions go here */ }
}
#Controller
public class BookmarkJsonControllerV2 extends BoomarkJsonController {
#ResponseBody
public Model write(#RequestBody Model model) { /* Actions go here */ }
}
With a configuration like this, the URL /rest/v2/bookmark/write will hit the method BookmarkJsonControllerV2.write(Model model) and the URL /rest/v2/bookmark/delete will hit the inherited method BookmarkJsonController.delete(String param).
The only disadvantage from this comes from having to redefine entire routes for new versions, as opposed to changing the #RequestMapping(value = "/rest/bookmark") to #RequestMapping(value = "/rest/v2/bookmark") on the class.

How to make this Spring Injection work

I have this inside my spring xml file:
<object type="Test.Web.Utilities.TestClass, Test.Web" singleton="false" id="TestClass">
<property name="TestService" ref="TestService"/>
</object>
In the TestClass I have this:
public ITestService TestService { get; set; }
and this:
public void TestMethod()
{
var x = TestService.ExampleMethod();
}
next, inside my controller I have this:
TestClass t = new TestClass();
t.TestMethod();
However, I don't seem to get this to work. other spring related stuff on the controller works well.
Any ideas how can I get this to work?
EDIT: Forgot to add, the error I get is a NullReferenceException
This cannot work since you're creating TestClass instance via new and Spring is of course not aware to inject dependencies into objects created via the frameworks new. You'll not find a .NET IOC framework that can do this (for good reasons!).
So what you need to do is to inject instances of TestClass into your controller. You could register these instances with a request or prototype scope.
You cannot use the normal "new" keyword, because this is handled via the .NET VM. You actually need to call Spring's Activator Context to give you an instance.
First you acquire the Spring.NET Activator context by calling:
IApplicationContext ctx = ContextRegistry.GetContext();
Then you can get an instance of your class specified in your configuration xml:
ITestService svc = (ITestService)ctx.GetObject("TestClass");
This enables you to configure via your XML file what concrete implementation class that implements the ITestService interface.

Resources