context:component-scan, why do I have to scan multiple times? - spring

I'm trying to clear up some Spring concepts here. I have two contextConfigureLocation xml files as defined in web.xml here
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-datasource.xml
/WEB-INF/security.xml
</param-value>
</context-param>
So, in the first configuraton file "spring-datasource.xml", I did a component-scan like so
<context:annotation-config />
<context:component-scan base-package="com.mycompany.root" />
my source code structure is like so
com
--->mycompany
--------------->root
--------------------->ui
--------------------->server
--------------------->etc
The issue is, my controllers decorated with #controllers under "com.mycompany.root.ui" never got picked up.
in the spring-servlet.xml, I had to do another componet-scan like so
<context:annotation-config />
<context:component-scan base-package="com.mycompany.root.ui" />
<mvc:resources mapping="/images/**" location="/images/" />
<mvc:annotation-driven />
<mvc:default-servlet-handler/>
for my controllers to get picked up.
Why is that? I thought whatever higher up in the parent configuration files should automatically be avaialbe to the children configuraton file? Or is that not the case as evident here.
[EDit] - After some reading, I think I'm more curious with controllers instantiated in the root application context, what happened to them when the spring-dispatch servlet got to them? The child applicaton context just ignored them? It will be nice if anyonne can show me some source code of dispatch servlet that did the ignore.
Thanks

Related

Transactional annotation does not save on exit

I have configured spring jpa with annotation driven. I would expect the following code to persist the changes to the database upon method exist.
#Transactional
public Foo changeValue(int id){
final Foo foo = fooRepository.findOne(id);
if(foo != null){
foo.setValue("new value");
//fooRepository.save(foo);
}
}
FooRepository is a JPARepository and the foo object is getting fetched so it is managed. Based on what I read about #Transactional I would expect that even without the fooRepository.save(foo) call the changes in foo's value column in the database would be persisted upon method exist. However, this only happens if I uncomment the call to fooRepository.save(foo)
No exceptions are thrown and the configuration for the jpa datasource is as below.
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
...
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<tx:annotation-driven transaction-manager="transactionManager" />
<context:component-scan
base-package="com.example.package.data" />
<jpa:repositories
base-package="com.example.package.data.repository"
entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager" />
Any ideas?
Update
I do have a ContextLoaderListener and a DispatcherServlet but I do not have component scan for both.
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
</servlet>
....
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Update
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
And there I do
<context:component-scan base-package="com.example.package">
<context:exclude-filter type="regex"
expression="com.example.package.data.*" />
<context:exclude-filter type="regex"
expression="com.example.package.web.controller.*" />
<context:exclude-filter type="regex"
expression="com.example.package.web.service.*" />
</context:component-scan>
And in servlet-context
<context:component-scan base-package="com.example.package.web" />
Your general approach is correct. The changes should get saved and committed on exit of the method. While this approach works great when it works it is a bitch to debug.
Your configuration looks ok to me, so I would double check, that you actually have Spring Beans at your disposal and not some instance that you created simply by calling the constructor.
To verify this, just put a breakpoint at the code point where the annotated method gets called. The bean with the annotated method should NOT by of the class you wrote, but some ProxySomething class.
This article also provides some pointers what might be wrong with your setup.
If you have both a ContextLoaderListener and DispatcherServlet they both load a configuration file.
Judging from your configuration the services are loaded by the DispatcherServlet and your <tx:annotation-driven /> is loaded by the ContextLoaderListener. They both create an ApplicationContext and as AOP is only applied to the beans in the same context your services will not be transactional (Transaction management is done by using AOP).
As a rule of thumb your ContextLoaderListener should contain all shared or global resources (like services, daos, datasources etc.) whereas your DispatcherServlet should only contain web related beans like controllers, view resolvers etc.

#RequestMapping not working with no exception

I have a class
#Controller
#RequestMapping("/auth")
public class LoginController {
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String getLoginPage(#RequestParam(value="error", required=false) boolean error,
ModelMap model) {
}
}
web.xml
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:META-INF/spring-servlet.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
appcontext.xml
<mvc:annotation-driven/>
<context:component-scan base-package="mysrc.src"/>
spring servlet.xml
<context:component-scan base-package="mysrc.src"/>
<mvc:annotation-driven/>
I WAS getting an error like "no mapping found in dispatcher blah blah"
But NOW i can see no exception on console.
I only see 404
http://localhost:9090/app/auth/login
I put breakpoint in getLoginPage method in debug mode but no flow.
Beside suggestions i want to ask how can i be sure abour Spring has scanned an realised my #RequestMapping class? Searching on starting up console maybe?
Usually when you get a 404 with no error message from Spring, it usually means your app is deployed under a different context than you are expecting. For example, your code sample indicates you are deploying your webapp to /, are you sure thats where its being deployed? Are you using tomcat? If so, your webapp should be named ROOT.war. Can you provide more details like where/how you are deploying your webapp and maybe the web.xml as well?
As far as your mappings go, Spring will log which controllers it scanned along with their request path at the INFO logging level. The dispatcher servlet will give always you an error message if it cannot match your request to a controller, so no error message indicates to me your requests are not going through the dispatcher servlet.
Your request mapping is looking right.
You can create an constructor to your controller and put a log statement or a break point there to see if the controller is created by spring.
Note that your component-scan would also initialize the controller twice (once for appcontext.xml and once for servlet.xml). You should add filters to make sure controllers are only created from the servlet.xml:
appcontext.xml (everything except controllers):
<context:component-scan base-package="x.y.z">
<context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation" />
</context:component-scan>
servlet.xml (only controllers):
<context:component-scan base-package="x.y.z" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>
And just to be sure: The base-package attribute of component-scan has to be a valid java package and not a path to your src directory (mysrc.src looks a bit like your src folder ;-) )

Coexisting JSF and JSP

I have a java web project in which I use JSP, now I have to use jsf because I need a functionality wich cannot be provided by jsp only. The problem that I have is that I can't cohexist the two technologies into the same project, and I cannot re-code all what I already did using the jsp technology. Can someone help me in things I should do do merge the two technologies into the same project; I'm using spring, hibernate, tiles with maven, which explain the diffoculty that I have in trying to recode all what I did ;
here is parts of my configuration files:
from my web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-security.xml
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
from my application-context.xml
<context:component-scan base-package="org.me.myproject" />
<mvc:annotation-driven />
<import resource="hibernate-context.xml" />
<import resource="tiles-context.xml" />
from my spring-servlet.xml
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
if I try to change the resolver by the JSF one, it doesn't work even with jsf pages
org.springframework.web.jsf.el.SpringBeanFacesELResolver
did someone have any idea what I have to do, thank you
I get It, the solution that I made to coexist the two technologies into the same project is by redirections, in fact, I add a file which redirect from a jsp to jsf file or from a jsf to a jsf file, after the redirection, the target page (a jsf one for example) is managed by his own controller ( the managed bean in the case of jsf ).

Defining spring security http element in two different files?

Is it possible to define the security:intercept-url elements and security:custom-filter elements for a single security:http in two different Spring configuration files?
This is so we can cleanly reuse the security:custom-filter definitions which will be common across many applications with intercept rules that will not.
I can't simply duplicate the <security:http> element because I get BeanDefinitionParsingException: Configuration problem: Duplicate <http> element detected. I am well well aware of how to split a normal bean file with import
As requested in comment:
Spring Security versions prior to 3.1.x do not allow multiple http element definitions.
3.1 does however.
Here is the Jira issue for the feature.
This article on 3.1 changes might also be helpful.
You can define another context file in your web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-contexts/context1.xml
/WEB-INF/spring-contexts/context2.xml
</param-value>
</context-param>
Or you can define a directory where your contexts would be and name them any way you like without having to specify each context file separately:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-contexts/*
</param-value>
</context-param>
Regarding Ayusman's answer, you actually can import your security contexts into your bean contexts:
<beans>
<import resource="classpath*:/security-context-*.xml"/>
<bean><!-- blah blah --></bean>
</beans>
use the import in application context file..
custom-filter.appcontext.xml
.
.
<import resource="interceptor-url-file.xml"/>
Note that both files need to have the proper spring xml schema details and MUST be valid XML files.
I have been working on this error for 5 hours. Really stupid problem.
This error is a parse error that when you comment some lines in applicationContext-security.xml, files are not generated correctly.
Let me explain on an example code.
<port-mappings>
<port-mapping http="7001" https="7002" />
</port-mappings>
<!-- <port-mappings>
<port-mapping http="7015" https="7515" />
</port-mappings>
-->
this lines are generated as,
<port-mappings>
<port-mapping http="7001" https="7002" />
</port-mappings>
<port-mappings>
<port-mapping http="7015" https="7515" />
</port-mappings>
-->
so that, compiler tells you "duplicate element detected". Because generated file includes duplicate elements.
I hope to help you .

#Service are constructed twice

I have a problem with my Spring application where my #Service classes are being created twice when the application starts. I know this is a problem with my configuration, as I've experienced it before, but what exactly am I doing wrong?
Is there anything fundamentally wrong with the way I've laid out my config, below? (I have omitted everything I deem to be irrelevant)
web.xml:
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/myapp-config.xml
/WEB-INF/myapp-security.xml
/WEB-INF/myapp-mvc.xml
</param-value>
</context-param>
<listener>
<listener-class>com.myapp.servlet.MyAppContextListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
myapp-servlet.xml
<context:component-scan base-package="com.myapp" annotation-config="true" />
<mvc:annotation-driven />
myapp-config.xml
<context:component-scan base-package="com.myapp" annotation-config="true" />
<context:annotation-config />
In addition to #GaryF's answer, there is a following beautiful solution for this problem (used in projects generated by Spring Roo):
myapp-config.xml
<!-- Load everything except #Controllers -->
<context:component-scan base-package="com.myapp">
<context:exclude-filter expression="org.springframework.stereotype.Controller"
type="annotation"/>
</context:component-scan>
myapp-servlet.xml
<!-- Load #Controllers only -->
<context:component-scan base-package="com.myapp" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller"
type="annotation"/>
</context:component-scan>
EDIT:
Removing <context:component-scan> from myapp-config.xml means, that all your autodiscovered annotated beans are registered in DispatcherServlet's context (that is, the context loaded from myapp-servlet.xml).
However the recommended approach is to use servlet's context for presentation-specific things (such as controllers), and use the root context (myapp-config.xml) for the core services of your application. The solution above make this approach easy.
Regarding the practical considerations, when your core services are placed in servlet's application context, they can't be accessed from outside the scope of that servlet, for example, from another servlets (you may need to use another servlets to implement another access technologies) or filters (such as Spring Security filters). That's the reason for having core services in the root application context.
As an addition to the answer #axtavt gave, I’d like to give the matching Spring JavaConfig here.
In RootConfig.java:
#ComponentScan(basePackages = { "com.myapp" },
excludeFilters = #Filter({Controller.class, Configuration.class}))
In WebMvcConfig.java:
#ComponentScan(basePackages = { "com.myapp" },
useDefaultFilters = false, includeFilters = #Filter(Controller.class))
You're doing two separate component-scans over the same base-package. Remove one of them.

Resources