Log4j2 web look up cannot access servletcontext attribute - servlets

We are using Tomcat 7.0.54.
The web.xml:
<context-param>
<param-name>log4jContextName</param-name>
<param-value>SabaLog4jContext</param-value>
</context-param>
There is sample servlet which starts on load
<servlet>
<servlet-name>startUp</servlet-name>
<servlet-class>foo.StartupServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
The StartupServlet simple as:
public class StartupServlet extends HttpServlet {
#Override
public void init() throws ServletException {
getServletContext().setAttribute("test", "ATest");
super.init();
}
}
The log4j2 can not access the test attribute with ${web:attr.test} and I got the warning as:
INFO: org.apache.logging.log4j.web.WebLookup unable to resolve key 'test'
It seems that Log4j2 works fine but the problem is that it starts before my Startup. I tried to use a servletContextListener class but no luck.
I also tried to disable Log4jAutoInitialization in web.xml and manually start set them as below.
<listener>
<listener-class>org.apache.logging.log4j.web.Log4jServletContextListener</listener-class>
</listener>
<filter>
<filter-name>log4jServletFilter</filter-name>
<filter-class>org.apache.logging.log4j.web.Log4jServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>log4jServletFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
But no luck:(
The log4j2.xml is as below:
<property name="baseFolder">${web:rootDir}/../logs/${web:test}</property>
So how can setup my web.xml so that my code execute before Log4j context.
The web.xml also contains spring Listeners as:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

First, make sure Tomcat is configured to provide the functionality of a servlet 3.0 container in your web.xml file:
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
You'll want the 3.0 functionality so you can specify the order in which the servlets are loaded. Then you'll want to have your own ServletContainerInitializer to initialize the ServletContext attributes. Here's an snippet of one of mine:
/* Initializer that is configured, via web.xml, to initialize before Log4j2's initializer. This gives us the
* opportunity to set some servlet context attributes that Log4j2 will use when it eventually initializes.
*/
public class BitColdHardCashContainerInitializer implements ServletContainerInitializer {
#Override
public void onStartup(final Set<Class<?>> classes, final ServletContext servletContext) throws ServletException {
if (servletContext.getMajorVersion() > 2) {
servletContext.log("BitColdHardCashContainerInitializer starting up in Servlet 3.0+ environment.");
}
// Set the webapp.name attribute so that Log4j2 may use it to create a path for log files.
servletContext.setAttribute("webapp.name", servletContext.getContextPath().replaceAll("/", "").trim());
Next, you want your ServerContainerInitializer to run before Log4j2's. In your web.xml, give your servlet a name:
<absolute-ordering>
<name>BitColdHardCash</name>
<others/>
</absolute-ordering>
This needs to be be specified before the <servlet> element.
Create a web-fragment.xml file:
<web-fragment xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-fragment_3_0.xsd"
version="3.0" metadata-complete="true">
<name>BitColdHardCash</name>
<distributable />
<ordering>
<before>
<others />
</before>
</ordering>
</web-fragment>
This tells Tomcat to initialize your ServletContainerInitializer first, before anything else, including Log4j2's. This goes in the META-INF directory.
That should do it. One more thing to check would be your catalina.properties file. You are using a version of Tomcat that fixes a bug regarding the calling of ServletContextInitializers. I'm not sure if the bug was in Tomcat source code or in the default supplied catalina.properties file. In the event you are using a catalina.properties file that pre-dates the fix, just crack it open an ensure that log4j*.jar is not included in the list of files specified for the tomcat.util.scan.DefaultJarScanner.jarsToSkip property.

Related

#SessionScoped CDI bean is a different Instance when injected

My config is a bean that I inject in my code wherever I need it. However, when injected, I get a new instance of the bean instead of the one from the session.
My bean:
#Named
#SessionScoped
public class TestModel implements Serializable {
private static final long serialVersionUID = 4873651498076344849L;
private String version;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public void changeVersion() {
this.version = "Version 2";
System.out.println("New version : " + version + ", Object : " + this);
}
}
When injected in different classes, all occurences are different instances.
When annotating the bean with #ApplicationScoped, it is the same instance.
I do need the bean to be #SessionScoped since every user should have his own config.
The WebApp is running on TomEE 1.7.4
UPDATE: I created a new project to test it, and the SessionScope works. I now need to find out what is wrong with my current project in order to fix it.
Facets:
CDI 1.0
Dynamic Web Module 3.0
Java 1.8
JSF 2.2 (MyFaces impl from TomEE)
JPA 2.1
Web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>Project</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<context-param>
<description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>resources.application</param-value>
</context-param>
<context-param>
<param-name>primefaces.THEME</param-name>
<param-value>omega</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
</web-app>
faces-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
version="2.2">
</faces-config>
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1" bean-discovery-mode="annotated">
</beans>
Any ideas ?
Looks like your test doesn't work:
testModel object = model.TestModel#689a6064
New version : Version 2, Object : model.TestModel#61606aa6
So you update an instance which is not the same as the one linked to the session (another request not reusing the same session I'd say)
You are doing it right. That is, from CDI perspective, you made no mistake and what you want is perfectly legit and should work (assuming you solved the problem of multiple sessions, which you did).
I just tried this with my own piece of code and it works as expected. You can check it on GitHub. The sample is more or less identical to yours.
However, I am running Wildfly 10 and therefore Weld 2.3 which comes with it (Weld being a reference impl of CDI). While you are running TomEE which contains OpenWebBeans (another CDI implementation).
To me it seems like you either missed some TomEE/OWB specific configuration (unrealistic scenario) or, more likely, you found a bug. In any case, if I were you, I would try asking on their forums or creating an issue in their tracking system because, once again, there is imho nothing wrong with your bean/servlet setup.
We have #SessionScope annotation in both JSF & CDI. Please review whether the annotation you are using in your old project is from JSF or from CDI.
Find more on the difference between the annotation from JSF & CDI

How to read spring-application-context.xml and AnnotationConfigWebApplicationContext both in spring mvc

In case I want to read bean definitions from spring-application-context.xml, I would do this in web.xml file.
<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>
In case I want to read bean definitions through Java Configuration Class (AnnotationConfigWebApplicationContext), I would do this in web.xml
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
org.package.MyConfigAnnotatedClass
</param-value>
</init-param>
</servlet>
How do I use both in my application. like reading beans from both configuration xml file and annotated class.
Is there a way to load spring beans in xml file while we are using AppConfigAnnotatedClass to instantiate/use rest of the beans.
This didnt work
Xml file defines bean as
<bean name="mybean" class="org.somepackage.MyBean"/>
Java Class Imports Resources as
#ImportResource(value = {"classpath:some-other-context.xml"})
#Configuration
public class MyConfigAnnotatedClass {
#Inject
MyBean mybean;
}
But mybean value is always null which ofcourse will give nullpointerexception when calling method on mybean.
You can annotate your #Configuration class with
#ImportResource(value = {"classpath:some-other-context.xml"})
#Configuration
public class MyConfigAnnotatedClass {
...
}
to have it import <beans> type xml contexts.
You can do the same thing the other way around. Your #Configuration class is also a #Component. If you have a <component-scan> that includes its package, all its declared beans will be added to the context. Alternatively, you can do
<bean name="myAdditionalConfig" class="org.somepackage.MyConfigAnnotatedClass" />
Note that package cannot be used as a name in the package structure.

#Autowire is not working in Spring security Custom Authentication provider

We have Spring MVC application. We are trying to integrate the Spring security in it.
We have written our custom authentication provider which will do the work of authentication.
Below is the code for my custom authentication provider.
public class CustomAuthenticationProvider extends DaoAuthenticationProvider {
#Autowired
private AuthenticationService authenticationService;
#Override
public Authentication authenticate(Authentication authentication) {
CustomAuthenticationToken auth = (CustomAuthenticationToken) authentication;
String username = String.valueOf(auth.getPrincipal());
String password = String.valueOf(auth.getCredentials());
try {
Users user = new User();
user.setUsername(username);
user.setPassword(PasswordUtil.encrypt(password));
user = authenticationService.validateLogin(user);
return auth;
} catch (Exception e) {
throw new BadCredentialsException("Username/Password does not match for " + username);
}
}
#Override
public boolean supports(Class<? extends Object> authentication) {
return (CustomAuthenticationToken.class.isAssignableFrom(authentication));
}
}
Here i am getting NullpointerException on the following line
user = authenticationService.validateLogin(user);
The authenticationService is not getting autowired in the custom authentication provider. While the same service authenticationService is autowired in the same way in my MVC controller.
Is this because authentication provider is a Spring security component?
Below is a my web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/myApp-security.xml
</param-value>
</context-param>
<servlet>
<servlet-name>myApp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/myApp-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myApp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Edit 1 :-
I have added the following lines in my spring security configuration file.
<beans:bean id="customAuthenticationProvider" class="com.myApp.security.provider.CustomAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService"/>
</beans:bean>
Please help how to autowire my service classes in the Spring security components?
Are you using the <debug/> element? If so, try removing to see if it fixes your problem as SEC-1885 prevents #Autowired from working when using <debug/>.
Perhaps autowiring postprocessor is not enabled in the root application context (but enabled in the DispatcherServlet's context as a side effect of <mvc:annotation-driven> or <context:component-scan>).
You can enable it by adding <context:annotation-config> to myApp-security.xml.
I experienced this issue and came to the conclusion that while autowiring was taking place, the spring security was operating with a completely different instance of the classes. To solve this I imported the security configuration into the spring mvc configuration as below.
This allowed Spring security to share the context with my spring mvc.
<import resource="myapp-security.xml" />
I faced the same issue and fixed it.
The solution is even if u have #Autowired annotation set for Service class.
#Autowired
private AuthenticationService authenticationService;
Removed the bean definition in your dispatcher-servlet.xml and it will work.
<!--
<beans:bean id="customAuthenticationProvider" class="com.myApp.security.provider.CustomAuthenticationProvider">
<beans:property name="userDetailsService" ref="userDetailsService"/>
</beans:bean>
-->
and add it in security context file
if you are using Spring MVC then you have to add both spring-security.xml and dispatcher-servlet.xml in contextConfigLocation.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-security.xml
/WEB-INF/dispatcher-servlet.xml
</param-value>
</context-param>
you need to define your CustomAuthenticationProvider as a spring bean (in applicationContext.xml generally or applicationContext-security.xml if you have one)
You should use
You cannot use because your myApp-security.xml is creating another ApplicationContext which doesnt see all the autowiring from your context created by myApp-servlet.xml

Rest easy and init params - how to access?

I'd like to have some init params in my web.xml and retrieve them later in the application, I know I can do this when I have a normal servlet. However with resteasy I configure HttpServletDispatcher to be my default servlet so I'm not quite sure how I can access this from my rest resource. This might be completely simple or I might need to use a different approach, either way it would be good to know what you guys think. Following is my web.xml,
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>RestEasy sample Web Application</display-name>
<!-- <context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param> -->
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.pravin.sample.YoWorldApplication</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
My question is how do I set something in the init-param and then retrieve it later in a restful resource. Any hints would be appreciated. Thanks guys!
Use the #Context annotation to inject whatever you want into your method:
#GET
public Response getWhatever(#Context ServletContext servletContext) {
String myParm = servletContext.getInitParameter("parmName");
}
With #Context you can inject HttpHeaders, UriInfo, Request, HttpServletRequest, HttpServletResponse, ServletConvig, ServletContext, SecurityContext.
Or anything else if you use this code:
public class MyApplication extends Application {
public MyApplication(#Context Dispatcher dispatcher) {
MyClass myInstance = new MyClass();
dispatcher.getDefautlContextObjects().
put(MyClass.class, myInstance);
}
}
#GET
public Response getWhatever(#Context MyClass myInstance) {
myInstance.doWhatever();
}

Jython and implementing HttpServlet.contextInitialized

I'd like my Jython servlet to implement the HttpServlet.contextInitialized method but I'm not sure how to express this in the web.xml. What I currently have is:
from javax.servlet import ServletContextListener;
from javax.servlet.http import HttpServlet
class JythonServlet1 ( HttpServlet, ServletContextListener ):
def contextInitialized( self, event ):
print "contextInitialized"
context = event.getServletContext()
def contextDestroyed( self, event ):
print "contextDestroyed"
context = event.getServletContext()
def doGet( self, request, response ):
print "doGet"
def doPost( self, request, response ):
print "doPost"
And my web.xml looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>JythonTest</display-name>
<servlet>
<servlet-name>PyServlet</servlet-name>
<servlet-class>org.python.util.PyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>PyServlet</servlet-name>
<url-pattern>*.py</url-pattern>
</servlet-mapping>
<servlet>
<description></description>
<display-name>JythonServlet1</display-name>
<servlet-name>JythonServlet1</servlet-name>
<servlet-class>JythonServlet1</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
</web-app>
As you can see, in the last <servlet> entry I'd like to initialize the servlet with the context (where I can start a scheduler) but it doesn't seem to work the same as with a Java servlet.
I don't do Jython, but there's no means of contextInitialized or contextDestroyed methods in the HttpServlet API. You're probably looking for ServletContextListener interface which is normally to be implemented as the following Java-based example:
package com.example;
import javax.servlet.ServletContextListener;
public class MyServletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
// ...
}
public void contextDestroyed(ServletContextEvent event) {
// ...
}
}
...which is to be definied as <listener> in web.xml as follows:
<listener>
<listener-class>com.example.MyServletContextListener</listener-class>
</listener>
This must give you an idea how to pickup it using Jython.
You can optionally also let your servlet both extend HttpServlet and implement ServletContextListener like follows:
public class MyServlet extends HttpServlet implements ServletContextListener {
// ...
}
so that you can end up with the code you've posted (don't forget to import the particular interface and define your class as both servlet and listener in web.xml). But this is not always considered a good practice.
That said, you should be placing classes in a package to avoid portability problems. It may work in some environments, but not in other. Sun also discourages using packageless classes in non-prototyping environments. They can normally namely not be imported by other classes which are itself in a package.
You really need to write some java bootstrapper like PyServlet that dispatches init() to a pre-defined python script.
Or.. if you want to use the ServletContextListener interface then something like Pyservlet that also implements ServletContextListner and again, dispatches to some python script.
I'm looking for a similar solution and was very disappointed to see that PyServlet doesn't offer anything like this itself.

Resources