I have a tomcat server with different applications and each one has it's own log4j2 appender which logs to it's own file, each java class logs it's exceptions properly, and for the JSF exceptions such as unclosed tags, wrong class/method/property name I made a defaultexceptionhandler which logs the FacesExceptions, but for some reason I can't find it doesn't log at all.
Here are the specs of the enviroment
Ubuntu 18.04 and 16.04 (tried it on both just in case)
tomcat 8.0.47
oracle jdk 1.8.0_181
myfaces 2.3.1
log4j2 2.11.0
So the thing is that even while debugging the handle() while forcing exceptions by leaving tags open and so on, it does reach the breakpoints on the logger.error() call, but it just doesn't append to the file.
This is the declaration of the handler in the faces-config.xml of the project (I spared all the navigation cases and other junk, all of which works so far, I'll edit it all in if needed)
<faces-config version="2.0"
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-facesconfig_2_0.xsd">
<factory>
<exception-handler-factory>
gescoweb.tools.DefaultExceptionHandlerFactory
</exception-handler-factory>
</factory>
</faces-config>
This is the DefaultExceptionHandler.java
package gescoweb.tools;
import java.util.Iterator;
import javax.faces.FacesException;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerWrapper;
import javax.faces.context.FacesContext;
import javax.faces.event.ExceptionQueuedEvent;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
/**
*
*/
public class DefaultExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
public static final Logger LOGGER = LogManager.getLogger("gescoweb");
public DefaultExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getWrapped() {
return this.wrapped;
}
/** Recibe la lista de excepciones y las procesa segĂșn su tipo.
* #throws FacesException
*/
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
Throwable t = i.next().getContext().getException();
if (t != null) {
LOGGER.error("Error inesperado.", new Exception(t));
} else {
LOGGER.error("", new Exception(t));
}
}
getWrapped().handle();
}
/**
* Tratamiento especial de otras excepciones.
* #param facesContext
* #param t
* #return
*/
protected String handleUnexpected(FacesContext facesContext, final Throwable t) {
if (t instanceof IllegalStateException) {
return "key.exception.IllegalStateException";
} else {
super.handle();
return "";
}
}
}
And this is the handler factory
package gescoweb.tools;
import javax.faces.context.ExceptionHandler;
import javax.faces.context.ExceptionHandlerFactory;
/**
*
*/
public class DefaultExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
public DefaultExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
/**
* Crea los handlers modificados.
*
* #return
*/
#Override
public ExceptionHandler getExceptionHandler() {
ExceptionHandler eh = parent.getExceptionHandler();
eh = new DefaultExceptionHandler(eh);
return eh;
}
}
And finally this is the log4j2.xml config file
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Properties>
<Property name="log-path">${sys:catalina.base}/logs</Property>
<Property name="layout"> [%-5level] %d{yyyy-MM-dd HH:mm:ss} %c{1} - %msg%ex%n </Property>
</Properties>
<Appenders>
<!-- console appender -->
<Console name="console-log" target="SYSTEM_OUT">
<PatternLayout pattern="${layout}"/>
</Console>
<!-- logger gescoweb -->
<RollingFile name="gescoweb-log" fileName="${log-path}/gescoweb.log"
filePattern="${log-path}/gescoweb/$${date:yyyy-MM}/gescoweb-%d{dd}.log.gz">
<!-- log pattern -->
<PatternLayout pattern="${layout}"/>
<!-- set file size policy -->
<CronTriggeringPolicy schedule="0 0 12 */7 * ?"/>
</RollingFile>
<!-- logger catalina -->
<RollingFile name="catalina-log"
fileName="${log-path}/catalina.out"
filePattern="${log-path}/catalina/$${date:yyyy-MM}/catalina-%d{dd}.log.gz">
<PatternLayout pattern="${layout}"/>
<CronTriggeringPolicy schedule="0 0 12 */7 * ?"/>
</RollingFile>
<!-- logger localhost -->
<RollingFile name="localhost-log"
fileName="${log-path}/localhost.log"
filePattern="${log-path}/localhost/$${date:yyyy-MM}/localhost-%d{dd}.log.gz">
<PatternLayout pattern="${layout}"/>
<CronTriggeringPolicy schedule="0 0 12 */7 * ?"/>
</RollingFile>
<!-- logger manager -->
<RollingFile name="manager-log"
fileName="${log-path}/manager.log"
filePattern="${log-path}/manager/$${date:yyyy-MM}/manager-%d{dd}.log.gz">
<PatternLayout pattern="${layout}"/>
<CronTriggeringPolicy schedule="0 0 12 */7 * ?"/>
</RollingFile>
<!-- logger host-manager -->
<RollingFile name="host-manager-log"
fileName="${log-path}/host-manager.log"
filePattern="${log-path}/host-manager/$${date:yyyy-MM}/host-manager-%d{dd}.log.gz">
<PatternLayout pattern="${layout}"/>
<CronTriggeringPolicy schedule="0 0 12 */7 * ?"/>
</RollingFile>
</Appenders>
<Loggers>
<Logger name="gescoweb" level="debug">
<AppenderRef ref="gescoweb-log"/>
</Logger>
<Logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost]"
level="info"
additivity="false">
<AppenderRef ref="localhost-log"/>
</Logger>
<Logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager]"
level="info"
additivity="false">
<AppenderRef ref="manager-log"/>
</Logger>
<Logger name="org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager]"
level="info"
additivity="false">
<AppenderRef ref="host-manager-log"/>
</Logger>
<Root level="info">
<AppenderRef ref="console-log"/>
<AppenderRef ref="catalina-log"/>
</Root>
</Loggers>
</Configuration>
Nevermind it, I imported the wrong log4j libraries in the DefaultExceptionHandler, changed the imports to
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
And everything works now...
Related
I have written a Log4j2 custom Converter plugin in OSGi environment and have used log4j2 with ops4j-pax-logging. The custom field is tenantId and I have referred to it in the log4j2.xml as %tenantId. When I run the program the logs gets printed as {thread-name}enantId.
Plugin class
package com.test.logging.converters;
#Plugin(name = "TenantIdConverter", category = "Converter")
#ConverterKeys({"tenantId"})
public class TenantIdConverter extends LogEventPatternConverter {
public TenantIdConverter(String name, String style) {
super(name, style);
}
public static TenantIdConverter newInstance(String[] options) {
return new TenantIdConverter("tenantId", "tenantId");
}
#Override
public void format(LogEvent event, StringBuilder toAppendTo) {
toAppendTo.append(getTenantID());
}
public String getTenantID() {
String tenantId = "1234";
if (tenantId == null) {
tenantId = "[]";
}
return tenantId;
}
}
pom.xml
<dependency>
<groupId>org.ops4j.pax.logging</groupId>
<artifactId>pax-logging-api</artifactId>
<version>1.10.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.ops4j.pax.logging</groupId>
<artifactId>pax-logging-log4j2</artifactId>
<version>1.10.1</version>
</dependency>
<plugin>
<groupId>org.ops4j</groupId>
<artifactId>maven-pax-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<executions>
<execution>
<id>log4j-plugin-processor</id>
<goals>
<goal>compile</goal>
</goals>
<phase>process-classes</phase>
<configuration>
<proc>only</proc>
<annotationProcessors>
<annotationProcessor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor</annotationProcessor>
</annotationProcessors>
</configuration>
</execution>
</executions>
</plugin>
log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO" name="log-test" packages="com.test.logging.converters">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%-5p %d [%tenantId] %c: %m%n"/>
</Console>
<RollingFile name="RollingFile" fileName="logs/log4j2app.log"
filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout>
<Pattern>%-5p %d [%tenantId] %c: %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
Resolved.
Created an osgi-fragment for org.ops4j.pax.logging.pax-logging-log4j2 and placed the appender in this fragmented bundle.
<Fragment-Host>org.ops4j.pax.logging.pax-logging-log4j2</Fragment-Host>
I am using log4j2 in my web project. I was trying to put logs directly to kafka by extending abstractAppender. As per documentation my understanding is that i can specify patternlayout for a custom appender and with that being set, my logger will send log events to kafka with formatted string but that is not happening. log4j2.xml looks like
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info" packages="com.abc.webservice.log.appender">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L- %X{sessionId}--%X{guid}- %m #]%n</pattern>
</PatternLayout>
</Console>
<Kafka name="kafka" topic="test">
<PatternLayout>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L- %X{sessionId}--%X{guid}- %m #]%n</pattern>
</PatternLayout>
<Property name="metadata.broker.list">127.0.0.1:9092</Property>
<Property name="serializer.class">kafka.serializer.StringEncoder</Property>
</Kafka>
</Appenders>
<Loggers>
<AsyncLogger name="async">
<AppenderRef ref="kafka" />
<AppenderRef ref="console" />
</AsyncLogger>
<Root level="info">
<AppenderRef ref="console" />
<AppenderRef ref="kafka" />
</Root>
<Logger name="com.abc" level="debug">
<!-- <appender-ref ref="console" level="debug"/>-->
<!--<appender-ref ref="kafka" level="debug"/>-->
<!--<appender-ref ref="console" level="error"/>-->
<appender-ref ref="kafka" level="error"/>
</Logger>
<Logger name="org.hibernate.SQL" >
<appender-ref ref="kafka" level="info" />
<appender-ref ref="console" level="info"/>
</Logger>
<Logger name="org.hibernate.type">
<appender-ref ref="console" level="info"/>
<appender-ref ref="kafka" level="info"/>
</Logger>
<Root level="info">
<AppenderRef ref="kafka"/>
<AppenderRef ref="console"/>
</Root>
</Loggers>
</Configuration>
If i use console appender then log comes in proper format but when i use custom appender, log is received without format. How can i send logs to kafka with specified paatternlayout.
Please find my appender implementation
import java.io.Serializable;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.util.Booleans;
import org.apache.logging.log4j.message.Message;
#Plugin(name = "Kafka", category = "Core", elementType = "appender", printObject = true)
public final class KafkaAppender extends AbstractAppender {
private final Lock lock = new ReentrantLock();
private KafkaManager manager;
protected KafkaAppender(String name, Filter filter, Layout layout, boolean ignoreExceptions, KafkaManager manager) {
super(name, filter, layout, ignoreExceptions);
System.err.println("hello world hello");
this.manager = manager;
}
#PluginFactory
public static KafkaAppender createAppender(#PluginAttribute("name") final String name, #PluginElement("Filter") final Filter filter,
#PluginAttribute("ignoreExceptions") final String ignore, #PluginAttribute("topic") final String topic,
#PluginElement("Properties") final Property[] properties, #PluginElement("layout") final Layout layout) {
boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
KafkaManager kafkaManager = KafkaManager.getKafkaManager(name, topic, properties);
if (kafkaManager == null) {
return null;
}
// Layout patternLayout = PatternLayout.createLayout("%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L- %X{sessionId}--%X{guid}- %m #]%n",
// null, null, null, true, false, null, null);
// System.err.println(patternLayout.toString());
return new KafkaAppender(name, filter, layout, ignoreExceptions, kafkaManager);
}
#Override
public final void start() {
if (this.getManager() == null) {
LOGGER.error("No KafkaManager set for the appender named [{}].", this.getName());
}
super.start();
if (this.getManager() != null) {
this.getManager().startup();
}
}
#Override
public final void stop() {
super.stop();
if (this.getManager() != null) {
this.getManager().release();
}
}
public final KafkaManager getManager() {
return this.manager;
}
public void append(LogEvent event) {
this.lock.lock();
try {
String s = event.getMessage().getFormattedMessage();
Message logEvent1 = event.getMessage();
String sp = logEvent1.getFormattedMessage();
this.getManager().send(event.getMessage().getFormattedMessage());
} catch (final Exception e) {
LOGGER.error("Unable to write to kafka [{}] for appender [{}].", this.getManager().getName(), this.getName(), e);
throw new AppenderLoggingException("Unable to write to kafka in appender: " + e.getMessage(), e);
} finally {
this.lock.unlock();
}
}
#Override
public Layout<? extends Serializable> getLayout() {
Layout patternLayout = PatternLayout.createLayout("%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L- %X{sessionId}--%X{guid}- %m #]%n",
null, null, null, true, false, null, null);
return patternLayout;
}
}
In class KafkaAppender, your append method should call getLayout().toByteArray(event) to format the event.
I noticed that the sample code overrides getLayout. I would not recommend this. The AbstractAppender implementation of getLayout returns the configured layout, which allows you to control the layout in configuration without code changes.
#Override
public void append(LogEvent event) {
this.lock.lock();
try {
// let the Layout format the data in the LogEvent object
final byte[] bytes = getLayout().toByteArray(event);
// then pass the byte[] array with the formatted event to the manager
// (I assume that your manager provides this method)
manager.write(bytes, 0, bytes.length);
} catch (Exception e) {
LOGGER.error("Unable to write to kafka [{}] for appender [{}].",
this.getManager().getName(), this.getName(), e);
if (!ignoreExceptions()) {
throw new AppenderLoggingException(
"Unable to write to kafka in appender: " + e.getMessage(), e);
}
} finally {
this.lock.unlock();
}
}
// I would recommend not to override getLayout.
// The AbstractAppender implementation of getLayout returns the configured
// layout, which allows you to control the layout in configuration
// without code changes.
// #Override
// public Layout<? extends Serializable> getLayout() {...
I have tried this example: Richfaces example, the valueChange event only fires if I add a submit button and click it.
I assum one of my configuration is false, as the ajax request not working.
SelectBean class:
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
#ManagedBean(name = "selectsBean")
#RequestScoped
public class SelectsBean
{
private static final String[] FRUITS = { "", "Banana", "Cranberry", "Blueberry", "Orange" };
private static final String[] VEGETABLES = { "", "Potatoes", "Broccoli", "Garlic", "Carrot" };
private String currentItem = "";
private String currentType = "";
private List<SelectItem> firstList = new ArrayList<SelectItem>();
private List<SelectItem> secondList = new ArrayList<SelectItem>();
public SelectsBean()
{
SelectItem item = new SelectItem("", "");
firstList.add(item);
item = new SelectItem("fruits", "Fruits");
firstList.add(item);
item = new SelectItem("vegetables", "Vegetables");
firstList.add(item);
for (int i = 0; i < FRUITS.length; i++)
{
item = new SelectItem(FRUITS[i]);
}
}
public List<SelectItem> getFirstList()
{
return firstList;
}
public List<SelectItem> getSecondList()
{
return secondList;
}
public static String[] getFRUITS()
{
return FRUITS;
}
public static String[] getVEGETABLES()
{
return VEGETABLES;
}
public void valueChanged(ValueChangeEvent event)
{
secondList.clear();
if (null != event.getNewValue())
{
String[] currentItems;
if (((String) event.getNewValue()).equals("fruits"))
{
currentItems = FRUITS;
}
else
{
currentItems = VEGETABLES;
}
for (int i = 0; i < currentItems.length; i++)
{
SelectItem item = new SelectItem(currentItems[i]);
secondList.add(item);
}
}
}
public String getCurrentType()
{
return currentType;
}
public void setCurrentType(String currentType)
{
this.currentType = currentType;
}
public String getCurrentItem()
{
return currentItem;
}
public void setCurrentItem(String currentItem)
{
this.currentItem = currentItem;
}
/**
* #param args
*/
public static void main(String[] args)
{
String webappDirLocation = "WebContent/";
// The port that we should run on can be set into an environment variable
// Look for that variable and default to 8080 if it isn't there.
String webPort = System.getenv("PORT");
if (webPort == null || webPort.isEmpty())
{
webPort = "8080";
}
Server server = new Server(Integer.valueOf(webPort));
WebAppContext root = new WebAppContext();
root.setContextPath("/");
root.setDescriptor(webappDirLocation + "/WEB-INF/web.xml");
root.setResourceBase(webappDirLocation);
// Parent loader priority is a class loader setting that Jetty accepts.
// By default Jetty will behave like most web containers in that it will
// allow your application to replace non-server libraries that are part of the
// container. Setting parent loader priority to true changes this behavior.
// Read more here: http://wiki.eclipse.org/Jetty/Reference/Jetty_Classloading
root.setParentLoaderPriority(true);
server.setHandler(root);
try
{
server.start();
server.join();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
index.xhtml:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich">
<h:form>
<h:selectOneMenu value="#{selectsBean.currentType}" valueChangeListener="#{selectsBean.valueChanged}">
<f:selectItems value="#{selectsBean.firstList}" />
<a4j:ajax event="valueChange" render="second" execute="#this" />
</h:selectOneMenu>
<a4j:outputPanel id="second" layout="block">
<h:selectOneMenu value="#{selectsBean.currentType}" rendered="#{not empty selectsBean.currentType}">
<f:selectItems value="#{selectsBean.secondList}" />
</h:selectOneMenu>
</a4j:outputPanel>
</h:form>
</ui:composition>
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>JSFExamples</groupId>
<artifactId>JSFExamples</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Jetty -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>7.6.0.v20120127</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>7.6.0.v20120127</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jsp-2.1-glassfish</artifactId>
<version>2.1.v20100127</version>
</dependency>
<!-- Servlet Faces API -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>org.primefaces.extensions</groupId>
<artifactId>all-themes</artifactId>
<version>1.0.8</version>
</dependency>
<!-- RichFaces libraries -->
<dependency>
<groupId>org.richfaces.ui</groupId>
<artifactId>richfaces-components-ui</artifactId>
<version>4.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.richfaces.core</groupId>
<artifactId>richfaces-core-impl</artifactId>
<version>4.1.0.Final</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<webXml>WebContent\WEB-INF\web.xml</webXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>JSFExamples</display-name>
<welcome-file-list>
<welcome-file>index.xhtml</welcome-file>
</welcome-file-list>
<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>/faces/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<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>
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
</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">
<managed-bean>
<managed-bean-name>selectsBean</managed-bean-name>
<managed-bean-class>SelectsBean</managed-bean-class>
<managed-bean-scope>none</managed-bean-scope>
</managed-bean>
</faces-config>
Your XHTML produces broken HTML output.
You forgot the <html><h:head><h:body> in XHTML.
Particularly the <h:head> part is mandatory for JSF in order to auto-include JavaScript and CSS files associated with some components, such as <a4j:ajax>.
Fix it accordingly. The index.xhtml should look like this:
<!DOCTYPE html>
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
>
<h:head>
<title>Put your title here.</title>
</h:head>
<h:body>
<h:form>
Put your original form here.
</h:form>
</h:body>
</html>
See also:
How to include another XHTML in XHTML using JSF 2.0 Facelets?
commandButton/commandLink/ajax action/listener method not invoked or input value not updated
Unrelated to the concrete problem, that JSF 1.x way of of <managed-bean> declaration in faces-config.xml is totally unnecessary given that you already have declared it via JSF 2.x flavored #ManagedBean annotation. Make sure that you focus on JSF 2.x targeted resources while learning and seeking for answers, not on JSF 1.x targeted ones, else it will only confuse you.
I have a multi module maven project with follwing child module
Tracker
|--Tracker-core
|--Tracker-dao
| |---src/main/resource/spring-dao-config.xml
|
|--Tracker-services
| |---src/main/resource/spring-service-config.xml
|
|--Tracker-web
In Tracker-dao, I have a spring-context.xml in the resource package. this scans for the dao classes and includes other datasource configuration.
spring-dao-config.xml:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="com.gits.tracker"></context:component-scan>
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:/database.properties" />
</bean>
<import resource="classpath:hibernate-config.xml" />
<!-- Declare a datasource that has pooling capabilities -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate4.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- An AnnotationSessionFactoryBean for loading Hibernate mappings from
annotated domain classes. -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.gits.tracker.core.entity.Address</value>
<value>com.gits.tracker.core.entity.Company</value>
<value>com.gits.tracker.core.entity.Customer</value>
<value>com.gits.tracker.core.entity.User</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
<!-- <prop key="hibernate.query.factory_class">org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory</prop> -->
</props>
</property>
</bean>
</beans>
Junit test for the dao layer alone is working perfectly alright.
In the Tracker-service, I have used this Tracker-core as a dependency.
While running the Junit in Tracker-service, it goes error, saying failed to load Application context, failed to find atleast 1 bean matching the name.
spring-service-config.xml
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- <import resource="classpath:spring-dao-config.xml"/> -->
<context:component-scan base-package="com.gits.tracker.service.services"></context:component-scan>
</beans>
Junit in Tracker-service
Problem is here:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring-service-config.xml" })
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring-service-config.xml" })
public class TestUserService extends
AbstractTransactionalJUnit4SpringContextTests {
private static Logger logger;
private static ApplicationContext ctx;
private UserService userService;
#BeforeClass
public static void init() {
logger = Logger.getLogger(User.class);
if (logger.isDebugEnabled()) {
logger.debug("IN DEBUG MODE");
}
}
#Before
public void localInit() {
logger.info("*************EXECUTING LOCALINIT*************");
ctx = new FileSystemXmlApplicationContext(
"src/main/resources/spring-config.xml");
userService = (UserService) ctx.getBean("userServiceImpl");
// userDao.executeQuery("delete from User where id<>3");
logger.info("*************DELETED ALL COMPANY FROM TABLE*************");
logger.info("*************EXITING OUT OF LOCALINIT*************");
}
#AfterClass
public static void stop() {
logger.debug("TEST COMPLETED");
}
private UserServiceImpl loadUserService() {
return (UserServiceImpl) ctx.getBean("userServiceImpl");
}
private UserDTO createTestUserDTO() {
UserDTO dto = new UserDTO("manoj", "manojpass", "true");
return dto;
}
#Test
public void testCreateUser() {
loadUserService();
UserDTO dto = createTestUserDTO();
Long id = userService.createUser(dto);
dto.setId(id);
UserDTO dto_1 = userService.getUserById(id);
org.junit.Assert.assertEquals(dto.toString(), dto_1.toString());
}
#Test
public void findByCriteriaWithAlias() {
loadUserService();
UserDTO dto = createTestUserDTO();
Long id = userService.createUser(dto);
CriteriaWithAlias criteriaWithAlias = new CriteriaWithAlias();
HashMap<String, String> alias = new HashMap<String, String>();
List<Criterion> criterions = new ArrayList<Criterion>();
Criterion criterion0 = Restrictions.eq("username", dto.getUsername());
criterions.add(criterion0);
criteriaWithAlias.setAlias(alias);
criteriaWithAlias.setCriterions(criterions);
List<UserDTO> users = userService
.findByCriteriaWithAlias(criteriaWithAlias);
for (UserDTO user : users) {
org.junit.Assert.assertFalse(user.getPassword().isEmpty());
org.junit.Assert.assertFalse(user.getId().toString().isEmpty());
org.junit.Assert.assertFalse(user.getUsername().isEmpty());
org.junit.Assert.assertFalse(user.getEnabled().isEmpty());
}
}
#Test
public void findByProjection() {
loadUserService();
UserDTO dto = createTestUserDTO();
userService.createUser(dto);
CriteriaWithAlias criteriaWithAlias = new CriteriaWithAlias();
HashMap<String, String> alias = new HashMap<String, String>();
HashMap<String, String> projections = new HashMap<String, String>();
List<Criterion> criterions = new ArrayList<Criterion>();
projections.put("username", "username");
projections.put("enabled", "enabled");
Criterion criterion0 = Restrictions.ne("username", "syed");
Criterion criterion1 = Restrictions.eq("enabled", "true");
criterions.add(criterion0);
criterions.add(criterion1);
criteriaWithAlias.setAlias(alias);
criteriaWithAlias.setCriterions(criterions);
criteriaWithAlias.setProjections(projections);
List<UserDTO> users = userService
.findByCriterionWithProjection(criteriaWithAlias);
for (UserDTO user : users) {
org.junit.Assert.assertNull(user.getPassword());
org.junit.Assert.assertNull(user.getId());
org.junit.Assert.assertFalse(user.getUsername().isEmpty());
org.junit.Assert.assertFalse(user.getEnabled().isEmpty());
}
}
I also tried importing the spring-dao-config of tracker-core in spring-service-config of the tracker-service module. But, that time, it says, spring-dao-config.xml file not found.
Please let me know whats wrong and what I have missed and suggest a solution for this.
I have added the dependency for each module in their own POM.xml and not all together in parent POM.xml
I found solution to my own question. Correct me if I'm wrong.
Its not possible to perform a JUnit test by accessing a config file from another maven module. JUnit is meant for Unit testing only. and not the integration testing.
The dependent maven modules config file will not be available in the classpath for the actual maven module you want to test.
So, what I did was, copied the config file of the dependent maven modules into the classpath of the actual maven module which I need to test. This might not be the correct way of doing it. but, I'm able to perform the JUnit test successfully.
Other way to perform Junit test in such case is to use tools like MOCKITO : https://code.google.com/p/mockito/
The default behavior of the Channel Processors is to do a sendRedirect (which is redirect temporary with 302 code). I need to change this behavior so that a permanent (301) redirect is done instead of 302 redirect. I tried to do the following:
Create a custom ChannelProcessingFilter by extending the ChannelProcessingFilter:
public class MyChannelProcessingFilter extends ChannelProcessingFilter{
//No implementation, I needed this to just make sure that a custom filter is created and I can configure it as a custom filter in the xml file.
}
Create a custom EntryPoint by extending the AbstractRetryEntryPoint
public class RetryWithHttpsEntryPoint extends org.springframework.security.web.access.channel.AbstractRetryEntryPoint {
private PortResolver portResolver = new PortResolverImpl();
private final String scheme ="https://";
/** The standard port for the scheme (80 for http, 443 for https) */
private final int standardPort = 443;
public RetryWithHttpsEntryPoint() {
super("https://", 443);
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse res) throws IOException, ServletException {
String queryString = request.getQueryString();
String redirectUrl = request.getRequestURI() + ((queryString == null) ? "" : ("?" + queryString));
Integer currentPort = new Integer(portResolver.getServerPort(request));
Integer redirectPort = getMappedPort(currentPort);
if (redirectPort != null) {
boolean includePort = redirectPort.intValue() != standardPort;
redirectUrl = scheme + request.getServerName() + ((includePort) ? (":" + redirectPort) : "") + redirectUrl;
}
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to: " + redirectUrl);
}
res.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
res.setHeader("Location", redirectUrl);
res.setHeader("Connection", "close");
}
protected Integer getMappedPort(Integer mapFromPort) {
return getPortMapper().lookupHttpsPort(mapFromPort);
}
}
Configure the same in the applicationContext-security.xml file. I am putting the complete xml file for your reference (removing the parts that are not needed. If you require the other parts do let me know)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<security:http auto-config="false"
entry-point-ref="authenticationProcessingFilterEntryPoint"
access-decision-manager-ref="accessDecisionManager" >
<security:intercept-url pattern="/activ8/protectedCheckEligibility.html**" access="user" requires-channel="https"/>
<security:intercept-url pattern="/siteMap.html" access="ROLE_ANONYMOUS,user,admin" requires-channel="http"/>
<security:intercept-url pattern="/privacyPolicy.html" access="ROLE_ANONYMOUS,user,admin" requires-channel="http"/>
<!-- other urls configured over here -->
<security:intercept-url pattern="/*.jsp" access="ROLE_ANONYMOUS,admin,user" requires-channel="https"/>
<security:intercept-url pattern="/**/*.html**" access="ROLE_ANONYMOUS,user,admin" requires-channel="https"/>
<security:intercept-url pattern="/fb_activities.html**" access="parent" />
<security:remember-me key="appfuseRocks" />
<security:custom-filter position="SWITCH_USER_FILTER" ref="careSwitchUserProcessingFilter"/>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="myCustomAuthenticationProcessingFilter"/>
<!-- configured the custom channel filter over here -->
<security:custom-filter position="CHANNEL_FILTER" ref="myChannelProcessingFilter"/>
</security:http>
<bean id="myChannelProcessingFilter" class="com.my.webapp.filter.myChannelProcessingFilter">
<property name="channelDecisionManager" ref="channelDecisionManager" />
<property name="securityMetadataSource">
<security:filter-security-metadata-source path-type="ant">
<security:intercept-url pattern="/**" access="REQUIRES_INSECURE_CHANNEL" />
</security:filter-security-metadata-source>
</property>
</bean>
<bean id="channelDecisionManager" class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl">
<property name="channelProcessors">
<list>
<ref bean="secureChannelProcessor"/>
</list>
</property>
</bean>
<bean id="secureChannelProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor">
<property name="entryPoint" ref="secureChannelEntryPoint"/>
<!-- <property name="portMapper" ref="portMapper" /> -->
<property name="secureKeyword" value="REQUIRES_SECURE_CHANNEL"/>
</bean>
<bean id="secureChannelEntryPoint" class="com.my.webapp.filter.RetryWithHttpsEntryPoint"/>
<!-- lot of other configuratons... removed -->
</beans>
I am getting following errors when I try to run my tomcat:
ERROR 2011-12-26 21:13:21,569 [ina].[localhost].[/]]: Exception sending context initialized event to listener instance of class com.kajeet.webapp.listener.StartupListener
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Filter beans '' and 'Root bean: class [org.springframework.security.web.access.channel.ChannelProcessingFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null' have the same 'order' value. When using custom filters, please make sure the positions do not conflict with default filters. Alternatively you can disable the default filters by removing the corresponding child elements from and avoiding the use of .
Offending resource: ServletContext resource [/WEB-INF/applicationContext-security.xml]
at org.springframework.beans.factory.parsing.FailFastProblemReporter.error(FailFastProblemReporter.java:68)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:85)
at org.springframework.beans.factory.parsing.ReaderContext.error(ReaderContext.java:72)
at org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.checkFilterChainOrder(HttpSecurityBeanDefinitionParser.java:196)
at org.springframework.security.config.http.HttpSecurityBeanDefinitionParser.parse(HttpSecurityBeanDefinitionParser.java:132)
at org.springframework.security.config.SecurityNamespaceHandler.parse(SecurityNamespaceHandler.java:86)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1335)
at org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(BeanDefinitionParserDelegate.java:1325)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:135)
at org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.registerBeanDefinitions(DefaultBeanDefinitionDocumentReader.java:93)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.registerBeanDefinitions(XmlBeanDefinitionReader.java:493)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:390)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:334)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:124)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:93)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:467)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:397)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:276)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:197)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
at com.kajeet.webapp.listener.StartupListener.contextInitialized(StartupListener.java:51)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3764)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4216)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:760)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:740)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:544)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:920)
at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:883)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:492)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1138)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:120)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1022)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:736)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1014)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:448)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:700)
at org.apache.catalina.startup.Catalina.start(Catalina.java:552)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:295)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:433)
I have also overridden other filters and it does not complain about those. This application was running perfectly fine before. We had this additional requirement and hence I added the new filter and ran into such errors.
Second approach that I tried is just configuring the default ChannelProcessingFilter in the XML, since in Spring 3.0 the filters are automatically called, I was under impression that I can configure them in XML file and spring will automatically load them, but it didn't:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<security:http auto-config="false"
entry-point-ref="authenticationProcessingFilterEntryPoint"
access-decision-manager-ref="accessDecisionManager" >
<security:intercept-url pattern="/activ8/protectedCheckEligibility.html**" access="user" requires-channel="https"/>
<security:intercept-url pattern="/siteMap.html" access="ROLE_ANONYMOUS,user,admin" requires-channel="http"/>
<security:intercept-url pattern="/privacyPolicy.html" access="ROLE_ANONYMOUS,user,admin" requires-channel="http"/>
<!-- other urls configured over here -->
<security:intercept-url pattern="/*.jsp" access="ROLE_ANONYMOUS,admin,user" requires-channel="https"/>
<security:intercept-url pattern="/**/*.html**" access="ROLE_ANONYMOUS,user,admin" requires-channel="https"/>
<security:intercept-url pattern="/fb_activities.html**" access="parent" />
<security:remember-me key="appfuseRocks" />
<security:custom-filter position="SWITCH_USER_FILTER" ref="careSwitchUserProcessingFilter"/>
<security:custom-filter position="FORM_LOGIN_FILTER" ref="myCustomAuthenticationProcessingFilter"/>
</security:http>
<bean id="channelDecisionManager" class="org.springframework.security.securechannel.ChannelDecisionManagerImpl">
<property name="channelProcessors">
<list>
<ref bean="secureChannelProcessor"/>
<ref bean="insecureChannelProcessor"/>
</list>
</property>
</bean>
<bean id="secureChannelProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor"/>
<bean id="insecureChannelProcessor" class="org.springframework.security.web.access.channel.InsecureChannelProcessor"/>
<!-- lot of other configuratons... removed -->
</beans>
Any help will be definitely appreciated. I am not a Spring pro, but I have done some work on it, a pointer or two may definitely help me to resolve this. Thank you in advance
Solution:
The issue is that we cannot have both the security:http as well as the myChannelProcessingFilter (the one I had overridden) to deal with the access argument of the security:intercept-url, hence I removed the http tag and added the access thing in the myChannelProcessingFilter, where I wanted it to process. The XML that resolved it is follows
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
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-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-3.0.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!--
The http element responsible for creating a FilterChainProxy and the filter beans which it uses.
Common problems like incorrect filter ordering are no longer an issue as the filter positions are predefined.
-->
<security:http auto-config="false"
entry-point-ref="authenticationProcessingFilterEntryPoint"
access-decision-manager-ref="accessDecisionManager" >
<security:custom-filter position="CHANNEL_FILTER" ref="channelProcessingFilter"/>
<security:intercept-url pattern="/*.html*" access="ROLE_ANONYMOUS,admin,user" />
<security:intercept-url pattern="/*.jsp" access="ROLE_ANONYMOUS,admin,user" />
<security:intercept-url pattern="/**/*.html**" access="ROLE_ANONYMOUS,user,admin" />
</security:http>
<bean id="channelProcessingFilter" class="org.springframework.security.web.access.channel.ChannelProcessingFilter">
<property name="channelDecisionManager" ref="channelDecisionManager"/>
<property name="securityMetadataSource">
<security:filter-security-metadata-source path-type="ant">
<security:intercept-url pattern="/*.jsp**" access="REQUIRES_SECURE_CHANNEL" />
<security:intercept-url pattern="/**/*.html**" access="REQUIRES_SECURE_CHANNEL" />
</security:filter-security-metadata-source>
</property>
</bean>
<bean id="channelDecisionManager" class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl">
<property name="channelProcessors">
<list>
<ref bean="secureProcessor"/>
<ref bean="insecureProcessor"/>
</list>
</property>
</bean>
<bean id="secureProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor" >
<property name="entryPoint" ref="retryWithHttps"/>
</bean>
<bean id="insecureProcessor" class="org.springframework.security.web.access.channel.InsecureChannelProcessor">
<property name="entryPoint" ref="retryWithHttp"/>
</bean>
<bean id="retryWithHttps" class="com.my.webapp.filter.RetryWithHttpsEntryPoint" />
<bean id="retryWithHttp" class="com.my.webapp.filter.RetryWithHttpEntryPoint" />
</beans>
I found another way to achieve the same thing with much less code and complexity. You can simply use a BeanPostProcessor to get the SecureChannelProcessor and InsecureChannelProcessor and then set your own entry point on them. That way, you can still use the defaults on everything else.
The BeanPostProcessor:
#Component
public class ChannelProcessorsPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
if (bean instanceof SecureChannelProcessor) ((SecureChannelProcessor)bean).setEntryPoint(new MyEntryRetryPoint("https://", 443));
else if (bean instanceof InsecureChannelProcessor) ((InsecureChannelProcessor)bean).setEntryPoint(new MyEntryRetryPoint("http://", 80));
return bean;
}
#Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
return bean;
}
}
I think it's better to write a redirect strategy:
#Component
public class PermanentRedirectStrategy implements RedirectStrategy {
private boolean contextRelative;
#Override
public void sendRedirect(HttpServletRequest request, HttpServletResponse response, String url) throws IOException {
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", response.encodeRedirectURL(calculateRedirectUrl(request.getContextPath(), url)));
}
/**
* Unfortunately DefaultRedirectStrategy.calculateRedirectUrl is private
* If this weren't the case, we could extend this class from DefaultRedirectStrategy
* to use its method directly without copying it
*/
private String calculateRedirectUrl(String contextPath, String url) {
if (!UrlUtils.isAbsoluteUrl(url)) {
if (contextRelative) {
return url;
} else {
return contextPath + url;
}
}
// Full URL, including http(s)://
if (!contextRelative) {
return url;
}
// Calculate the relative URL from the fully qualified URL, minus the last
// occurence of the scheme and base context
url = url.substring(url.lastIndexOf("://") + 3); // strip off scheme
url = url.substring(url.indexOf(contextPath) + contextPath.length());
if (url.length() > 1 && url.charAt(0) == '/') {
url = url.substring(1);
}
return url;
}
}
and then setting it to the existing entry point:
#Component
public class ChannelProcessorsPostProcessor implements BeanPostProcessor {
#Autowired
private RedirectStrategy permanentRedirectStrategy;
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
ChannelEntryPoint entryPoint = null;
if (bean instanceof SecureChannelProcessor) {
entryPoint = ((SecureChannelProcessor) bean).getEntryPoint();
} else if (bean instanceof InsecureChannelProcessor) {
entryPoint = ((InsecureChannelProcessor) bean).getEntryPoint();
}
if (entryPoint != null && AbstractRetryEntryPoint.class.isAssignableFrom(entryPoint.getClass())) {
((AbstractRetryEntryPoint) entryPoint).setRedirectStrategy(permanentRedirectStrategy);
}
return bean;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}