Error while splitting application context file in spring - spring

I am trying to split the ApplicationContext file in Spring.
For ex. the file is testproject-servlet.xml having all the entries. Now I want to split this single file into multiple files according to logical groups like :
group1-services.xml, group2-services.xml
I have created following entries in web.xml :
<servlet>
<servlet-name>testproject</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/group1-services.xml, /WEB-INF/group2-services.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
I am using SimpleUrlHandlerMapping as:
<bean id="simpleUrlMapping"class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="order" value="0"/>
<property name="mappings">
<props>
<prop key="/register.htm">RegisterController</prop> <prop key="/payroll_services.htm">PayrollServicesController</prop>
</props>
</property>
</bean>
I also have the controller defined as :
<bean id="PayrollServicesController" class="com.triforce.b2bseek.businessservices.controller.PayrollServicesController">
<property name="facadeLookup" ref="FacadeLookup"/>
..
..
</property>
</bean>
The problem is that I have splitted the ApplicationContext file "testproject-servlet.xml" into two different files and I have kept the above entries in "group1-services.xml". Is it fine? I want to group things logically based on their use in seperate .xml files.
But I am getting the following error when I try to access a page inside the application :
org.springframework.web.servlet.DispatcherServlet noHandlerFound
WARNING: No mapping for [/TestProject/payroll_services.htm] in DispatcherServlet with name 'testproject'
Please tell me how to resolve it.
Thanks in Advance !

Replace this
/WEB-INF/group1-services.xml, /WEB-INF/group2-services.xml
with
/WEB-INF/group1-services.xml
/WEB-INF/group2-services.xm
Hope this will work...:)

I don't think its a problem with your contextConfigLocation as such
I think its more that the dispatcher needs to know where to send payroll_servives.htm to, and can't find an appropriate handler knowing what to do with this pattern.
See reference documentation
Did you really want *.htm files to be matched to the dispatcher servlet?
If you are using annotation-based controllers (#Controller), then you need to have a line similar to:
<context:component-scan base-package="org.springframework.samples.petclinic.web"/>
This installs an appropriate annotation-based handler, that searchs for classes/methods annotated like:
#Controller
public class PayrollController {
#RequestMapping("payroll_services.htm")
public ModelAndView payrollServices() {
....
}
}
Otherwise, if you are using more traditional handlers/controllers, you need to define this using XML. The reference documentation link earlier in this posting mentions two such handlers: SimpleUrlHandlerMapping or BeanNameUrlHandlerMapping
Perhaps if you updated your question with snippets of handler/controller XML, this may help?

Did you add the ContextLoaderListener to your web.xml? I don't see it:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

You need to include something like this in each of your files:
<context:component-scan base-package="com.example.dao" />
If it is missing from a file, it seems the annotated classes are not known in the context of that file.
You may want to revisit the documentation for details.

Either the handler mapping of your DispatcherServlet is incorrect or you're using the wrong url. If you fix the layout around your SimpleUrlHandlerMapping configuration I can tell you what to change.

Related

Spring DispatcherServlet matches for wrong url-pattern

I have following configuration in the Spring based project -
web.xml -
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/img/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
Handler mapping from spring-servlet.xml -
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="alwaysUseFullPath" value="true" />
</bean>
and Spring Controller as -
#RestController
public class TestController {
#PostMapping("/test")
public String test() {
return "hello";
}
}
Now when I send request to
GET http://localhost:8080/test
I get response as hello which is fine but when I send request on following URL's -
GET http://localhost:8080/api/test
GET http://localhost:8080/img/test
then also I get hello response i.e. controller code gets executed for above mentioned wrong URLs.
Is there anything wrong in the configuration or it's expected behaviour?
The mapping of an URL to a handler (a method) in Spring MVC is a two-step process:
first the servlet container must decide whether to forward the request to the DispatcherServlet according to the <servlet-mapping>,
then the DispatcherServlet consults its HandlerMappings to see which one matches the request. A HandlerMapping can do anything, but usually calls UrlPathHelper#getLookupPathForRequest to transform the request into a path.
The default behavior of the UrlPathHelper depends on the <servlet-mapping> used:
if you used an exact mapping as in the case of /test, a request for /test will give a path of /test,
if you used a prefix mapping /api/*, a request for /api/foo/bar will result in a path of only /foo/bar.
As mentioned in the comment by M. Deinum you need to set the alwaysUseFullPath property to true on the RequestMappingHandlerMapping instance used by the DispatcherServlet.
In your XML application context configuration you define a bean of class RequestMappingHandlerMapping, while you should overwrite the bean used by the DispatcherServlet. Therefore you need to use the same id internally used by Spring:
<mvc:annotation-driven/>
<bean id="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
<property name="alwaysUseFullPath" value="true"/>
</bean>
Remark: This solution should work unless you invert the order of the definitions in your XML. In such a case the default definition will overwrite yours. Set the logger org.springframework.beans.factory.support do DEBUG to check which definition overwrites the other. In one case you get:
Overriding user-defined bean definition for bean 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' with a framework-generated bean definition: ...
in the other:
Overriding bean definition for bean 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' with a different definition: ...

Restlet: can't locate static files through Directory

Serving static files through Restlet is proving harder than it feels like it ought to be. I suspect some deep issue to do with context initialization, but I could be wrong. Basically, I have yet to be able to get any actual content served through a Directory although all the routing and all classic restlets are working just fine.
The core problem looks like it shows in log messages:
WARNING: No client dispatcher is available on the context. Can't get the target URI: war:///
I've tried a bunch of different base URLs, and none of them seem to make a difference. And when I debug, sure enough getClientDispatcher() is returning null.
When I modified the Spring initialization (wrongly) to use the original context rather than a child context, I didn't get this warning, and directory listings showed, but it also displayed about seven SEVERE messages about that being a security risk.
My Spring XML looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<bean id="trackerComponent" class="org.restlet.ext.spring.SpringComponent">
<property name="defaultTarget" ref="trackerApplication" />
</bean>
<bean id="trackerComponentChildContext" class="org.restlet.Context">
<lookup-method name="createChildContext" bean="trackerComponent.context" />
</bean>
<bean id="trackerApplication" class="ca.uhnresearch.pughlab.tracker.application.TrackerApplication">
<property name="root" ref="router" />
</bean>
<!-- Define the router -->
<bean name="router" class="org.restlet.ext.spring.SpringRouter">
<constructor-arg ref="trackerComponentChildContext" />
<property name="attachments">
<map>
<entry key="/" value-ref="staticsDirectory" />
</map>
</property>
</bean>
<bean id="staticsDirectory" class="ca.uhnresearch.pughlab.tracker.resource.SpringDirectory">
<constructor-arg ref="router" />
<constructor-arg value="war:///" />
<property name="listingAllowed" value="true" />
</bean>
...
My class SpringDirectory is just a wrapper to provide a Context from a parent Router, like the other Spring helpers, and it seems to work just fine: the same context is passed along -- and none of the child contexts have a getClientDispatcher() that works.
In case it's needed, here's some of the relevant sections of web.xml. I'm not entirely sure why I needed to add the FILE to allow client access (I want to serve through the web container after all) but I did try with a file: URL too, and that didn't make any difference that I could see, except that I had to add extra settings in unusual places.
<servlet>
<servlet-name>tracker</servlet-name>
<servlet-class>org.restlet.ext.spring.SpringServerServlet</servlet-class>
<init-param>
<param-name>org.restlet.clients</param-name>
<param-value>HTTP HTTPS FILE</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>tracker</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
I'd be really grateful for advice on this -- I've actually spent more time trying to get an index.html displayed than I have on all the Restlets combined, so any thoughts or pointers will be very welcome.
UPDATE
Thierry's response has been helpful, but I still have some issues and still can't serve static files. The only variation from this is that I don't have a test.CustomSpringServerServlet -- and it's not entirely clear I need one, as the additional init-param seems to be accepted by the default.
When running with a Jetty JAR file (mvn jetty:run-war) I now get a stack backtrace:
WARNING: Exception or error caught in status service
java.lang.NoSuchMethodError: org.restlet.Context.getClientDispatcher()Lorg/restlet/Restlet;
at ca.uhnresearch.pughlab.tracker.restlets.DirectoryFactoryBean$1.getContext(DirectoryFactoryBean.java:16)
at org.restlet.Restlet.handle(Restlet.java:220)
at org.restlet.resource.Finder.handle(Finder.java:449)
at org.restlet.resource.Directory.handle(Directory.java:245)
at org.restlet.routing.Filter.doHandle(Filter.java:156)
...
This stack backtrace could easily be a consequence of my updates -- I'm now using Restlets 2.3.1 and Spring 3.1.4, as it's very similar to the previous warning.
Strangely enough, I don't get this from mvn jetty:run, which paradoxically is now serving directory listings at the top level, but still refuses to serve any files. So that confirms the WAR protocol is able to access something. The war:// protocol isn't highly documented so I'm kind of guessing it's just going to serve from the war contents, and it certainly isn't yet able to do that.
I'd still like the file:// protocol to work -- to be honest, any serving of any static files would be fantastic, I am getting beyond caring. However, trying that still results in errors:
WARNING: The protocol used by this request is not declared in the list of client connectors. (FILE). In case you are using an instance of the Component class, check its "clients" property.
Which I don't really understand, as it is in the clients list in the web.xml and there's nowhere else that makes sense I can put it.
This is proving hard enough that I feel maybe I should throw together a Github repo for people to play with.
Edit: I completely updated my answer to describe how to fix your problem
I investigated more deeper you problem and I can reproduce it. In fact, it's a problem when configuring the directory itself in Spring. In fact, it's located at the context which is responsible to bring the client dispatcher.
Here are more details. In fact, the client protocol WAR is automatically registered when the Restlet component is created within the default or Spring servlet. See the method ServerServlet#init(Component). So the corresponding client dispatcher is set within the context of the component.
So you need to pass this context in the constructor of the Directory when instantiating it. I reworked the Spring configuration to show how to fix your problem:
public class DirectoryFactoryBean implements FactoryBean<Directory> {
private Component component;
#Override
public Directory getObject() throws Exception {
Directory directory = new Directory(component.getContext(), "war:///") {
#Override
public Context getContext() {
// Display if the client dispatcher is correctly set!
System.out.println(">> getContext().getClientDispatcher() = "+super.getContext().getClientDispatcher());
return super.getContext();
}
};
directory.setListingAllowed(true);
return directory;
}
#Override
public Class<?> getObjectType() {
return Directory.class;
}
#Override
public boolean isSingleton() {
return true;
}
public Component getComponent() {
return component;
}
public void setComponent(Component component) {
this.component = component;
}
}
Here is the content of the class ``:
public class CustomSpringServerServlet extends SpringServerServlet {
#Override
protected void init(Application application) {
super.init(application);
}
#Override
protected void init(Component component) {
super.init(component);
ClientList list = component.getClients();
for (Client client : list) {
System.out.println(">> client = "+client);
}
}
}
Here is the configuration in the web.xml file:
<web-app>
<context-param>
<param-name>org.restlet.application</param-name>
<param-value>org.restlet.tutorial.MyApplication</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>tracker</servlet-name>
<servlet-class>test.CustomSpringServerServlet</servlet-class>
<init-param>
<param-name>org.restlet.component</param-name>
<param-value>myComponent</param-value>
</init-param>
<init-param>
<param-name>org.restlet.clients</param-name>
<param-value>HTTP HTTPS FILE</param-value>
</init-param>
</servlet>
</web-app>
Here is the corresponding Spring configuration:
<bean id="myComponent" class="org.restlet.ext.spring.SpringComponent">
<property name="defaultTarget" ref="router" />
</bean>
<bean name="router" class="org.restlet.ext.spring.SpringRouter">
<property name="attachments">
<map>
<entry key="/dir" value-ref="staticsDirectory" />
</map>
</property>
</bean>
<bean id="staticsDirectory" class="test.DirectoryFactoryBean">
<property name="component" ref="myComponent" />
</bean>
Hope it helps,
Thierry

Transaction not working correctly - Spring/MyBatis

I have a Spring MVC Controller method which is tagged as "Transactional" which makes several service calls which are also tagged "Transactional" but they are treated as independent transactions and are committed separately instead of all under one transaction as I desire.
Based on the debug output, it appears Spring is only creating transactions when it reaches the service calls. I will see the output "Creating new transaction with name..." only for them but not one for the controller method who calls them.
Is there something obvious I am missing?
Sample code (abbreviated) below, controller:
#Controller
public class BlahBlah etc...
#Autowired
SomeService someService;
#Transactional
#RequestMapping(value="windows/submit", method=RequestMethod.POST)
#ResponseBody
public String submit (#RequestBody SomeObject blah) throws Exception {
someService.doInsert(blah);
if (true) throw Exception("blah");
... other stuff
}
service code:
public interface SomeService {
#Transactional
public void doInsert (SomeObject blah);
}
I tried removing the Transactional tag from the service call thinking maybe I messed it up and I am telling it to create one for each call but then in the debug output no transaction is created.
So the result is once I get my forced exception and check the table, the insert has committed instead of rolled back like I want.
So what did I do wrong?
Why is Spring ignoring the Transactional tag on the controller?
Posting relevant part of my context as per request from commenter:
Web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring.xml
classpath:spring-security.xml
classpath:spring-datasource.xml
</param-value>
</context-param>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
sping-mvc.xml:
<context:annotation-config/>
<context:component-scan base-package="com.blah"/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver" p:order="1" />
... non-relevent stuff
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="webContentInterceptor" class="org.springframework.web.servlet.mvc.WebContentInterceptor">
<property name="cacheSeconds" value="0"/>
<property name="useExpiresHeader" value="true"/>
<property name="useCacheControlHeader" value="true"/>
<property name="useCacheControlNoStore" value="true"/>
</bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="httpInterceptor" class="com.blah.BlahInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
<mvc:view-controller path="/" view-name="blah"/>
<context:component-scan base-package="com.blah"/>
Hmm, thats interested and unintended - I have component scan duplicated. Could that be causing problems with this?
I think Spring ignores the #Transactional annotation here because it creates a proxy for the transaction, but the dispatcher isn't calling the controller through the proxy.
There's an interesting note in the Spring MVC documentation, 17.3.2, about annotating controllers, it doesn't describe your exact problem but shows that there are problems with this approach:
A common pitfall when working with annotated controller classes
happens when applying functionality that requires creating a proxy for
the controller object (e.g. #Transactional methods). Usually you will
introduce an interface for the controller in order to use JDK dynamic
proxies. To make this work you must move the #RequestMapping
annotations, as well as any other type and method-level annotations
(e.g. #ModelAttribute, #InitBinder) to the interface as well as the
mapping mechanism can only "see" the interface exposed by the proxy.
Alternatively, you could activate proxy-target-class="true" in the
configuration for the functionality applied to the controller (in our
transaction scenario in ). Doing so indicates
that CGLIB-based subclass proxies should be used instead of
interface-based JDK proxies. For more information on various proxying
mechanisms see Section 9.6, “Proxying mechanisms”.
I think you'd be better off creating another service to wrap the existing ones and annotating that.

environment specific log4j configuration by spring

I am loading log4j.xml using the traditional way
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:conf/log4j.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
This works fine but now I need to load a different log4j.xml file based on which environment I am in which is defined by a environment variable/jndi entry .. so I was hoping that with new spring 3.1 property management I could just change this to
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:conf/log4j-${ENV-NAME}.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
and spring will load the correct log4j file in each environment but this doesnt works probably because web.xml is loaded before spring. I came across this method
<bean id="log4jInitialization" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.springframework.util.Log4jConfigurer" />
<property name="targetMethod" value="initLogging" />
<property name="arguments">
<list>
<value>log4j-${ENV-NAME}.xml</value>
</list>
</property>
</bean>
so essentially moving configuration of log4j into spring context file instead of web.xml. But this doesnt works either for some reason logging gets enabled in a way that everything is logged. So how can I use a different log4j.xml file based on an environment variable or loading it programmatically in the servletcontextlistener.
It's too late to help adeelmahmood, but hope others will benefit from my answer.
The thing is adeelmahmood was right, this configuration:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>classpath:conf/log4j-${ENV-NAME}.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
is working, but:
Log4jConfigListener should be registered before ContextLoaderListener in web.xml !!!
You must also remember that Log4jConfigListener assumes an expanded WAR file.
Then, if ENV-NAME environment variable is set, it works as expected.
By the way, ${ENV-NAME} can be used in spring config files as well! That's not everything... You can also use our env.property to set spring profile:
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>${ENV-NAME}</param-value>
</context-param>
This way you can set log4jConfigLocation and spring profile, both with one and the same env. variable.
BTW and just in case: using Maven profiles to have separate builds for dev, test and production isn't good practice. Read eg. http://java.dzone.com/articles/maven-profile-best-practices and remember: "Use profiles to manage build-time variables, not run-time variables and not (with RARE exceptions) alternative versions of your artifact".
This might help too for more recent readers: in Log4j 2.7 (but it's probably older) the parameter name has been altered:
see interface Log4jWebSupport :
/**
* The {#link javax.servlet.ServletContext} parameter name for the location of the configuration.
*/
String LOG4J_CONFIG_LOCATION = "log4jConfiguration";
By default, Spring Boot picks up the native configuration from its default location for the system (such as classpath:logback.xml for Logback), but you can set the location of the config file by using the logging.config property.
logging.config=classpath:log4j-<ENV-NAME>.xml
https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/reference/html/howto-logging.html

Beans injected into Apache Wink with Spring aren't registered

Following on from How do I inject a Spring bean into Apache Wink?
I'm now using wink-spring-support and I thought I had things set up correctly.
web.xml includes:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:META-INF/wink/wink-core-context.xml
classpath:applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>restServlet</servlet-name>
<servlet-class>org.apache.wink.server.internal.servlet.RestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>restServlet</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
META-INF/wink/wink-core-context.xml contains:
<bean class="org.apache.wink.spring.Registrar">
<property name="instances">
<set>
<ref bean="myservice" />
</set>
</property>
</bean>
<bean id="myservice" class="mystuff.ServiceImpl"/>
There's a #Autowired annotation in mystuff.ServiceImpl that injects other Spring stuff, and mystuff.ServiceImpl implements a JAX-RS annotated interface and itself includes a JAX-RS #Path("/services") annotation.
I can see Spring loading up this stuff just fine, including the myservice bean. However when I request my resources, I get a 404 not found. As Wink starts, I can see a couple of log entries that might indicate the problem:
applicationConfigLocation property was not defined
Using application classes null named in init-param applicationConfigLocation
Have I missed something somewhere? Any advice?
The problem was my misunderstanding the docs.
There is a Spring configuration META-INF/server/wink-core-context.xml provided with wink-spring-support. This registers the BeanPostProcessors that actually do the setup and must be referenced from contextConfigLocation.
I thought that I put my configuration in there, which explains why the application didn't get registered with Wink on startup.

Resources