Skip to Main Content

Java EE (Java Enterprise Edition) General Discussion

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Simple Tiles Servlet I wrote to allow better tile+JSF integration

843842Jul 7 2004 — edited Apr 7 2005
Here is a simple tiles servlet I wrote to help my tiles and jsf integration. I got sick of writing 2 jsps for each body so this is the result. I'm not much of a tiles or jsf expert but this servlet seems to work for my simple cases.

Here is the servlet.

DispatchTilesServlet:
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.tiles.ComponentContext;
import org.apache.struts.tiles.ComponentDefinition;
import org.apache.struts.tiles.Controller;
import org.apache.struts.tiles.DefinitionsFactoryConfig;
import org.apache.struts.tiles.DefinitionsFactoryException;
import org.apache.struts.tiles.FactoryNotFoundException;
import org.apache.struts.tiles.TilesUtil;

public class DispatchTilesServlet extends HttpServlet {
	/** Commons Logging instance. */
	protected static Log log = LogFactory.getLog(DispatchTilesServlet.class);
	
	public static final String FALLBACK_EXTENTION_ATTR = "DispatchTilesServlet.FALLBACK_EXTENTION";
	public static final String TILE_DEFINITION_DELIMITER = "DispatchTilesServlet.TILE_DEFINITION_DELIMITER";
	public static final String DEFAULT_FALLBACK_EXTENTION_ATTR = ".jsp";
	public static final String DEFAULT_TILE_DEFINITION_DELIMITER = ".";
	
	public String tileDefinitionDelimiter = DEFAULT_TILE_DEFINITION_DELIMITER;
	public String fallbackExtention = DEFAULT_FALLBACK_EXTENTION_ATTR;

	/**
	 * Initialize this servlet
	 * 
	 * @exception ServletException
	 *                if we cannot configure ourselves correctly
	 */
	public void init(ServletConfig config) throws ServletException {
		super.init(config);
		if(config.getServletContext().getAttribute(FALLBACK_EXTENTION_ATTR) != null) {
			fallbackExtention = config.getServletContext().getAttribute(FALLBACK_EXTENTION_ATTR).toString();
		}
		if(config.getServletContext().getAttribute(TILE_DEFINITION_DELIMITER) != null) {
			tileDefinitionDelimiter = config.getServletContext().getAttribute(TILE_DEFINITION_DELIMITER).toString();
		}
		
		if (log.isInfoEnabled())
			log.debug("Start Tiles initialization");

		// Create tiles definitions config object
		DefinitionsFactoryConfig factoryConfig = new DefinitionsFactoryConfig();
		// Get init parameters from web.xml files
		try {
			Enumeration enum = config.getInitParameterNames();
			HashMap map = new HashMap();
			while (enum.hasMoreElements()) {
				String key = (String) enum.nextElement();
				map.put(key, config.getInitParameter(key));
			}
			factoryConfig.populate(map);
		} catch (Exception ex) {
			String msg = "Can't populate DefinitionsFactoryConfig class from 'web.xml'";
			if (log.isErrorEnabled()) log.error(msg, ex);
			throw new ServletException(msg + ex.getMessage());
		}

		try {
			if (log.isInfoEnabled())
				log.debug("Try to load Tiles factory");
			TilesUtil.createDefinitionsFactory(getServletContext(), factoryConfig);
			if (log.isInfoEnabled())
				log.debug("Tiles Factory successfully loaded");
		} catch (DefinitionsFactoryException ex) {
			if (log.isErrorEnabled())
				log.error("Tiles Factory load fail !", ex);
			throw new ServletException(ex);
		}

	}

	/**
	 * @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest,
	 *      javax.servlet.http.HttpServletResponse)
	 */
	public void service(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {

		try {
			//obtain the request uri and eliminate the context path and any extention it may have plus leading '/'
			String uri = StringUtils.substringAfter(request.getRequestURI(), request.getContextPath()+"/");
            StringBuffer buffer = new StringBuffer(uri);
            //eliminate the extention
            int extIdx = uri.lastIndexOf('.');
            if (extIdx != -1) {
                buffer.replace(extIdx, uri.length(), "");
            } 
            uri = buffer.toString();
            //find a component definition from the remaining uri replacing the '/' with the specified delimiter
			ComponentDefinition definition = TilesUtil.getDefinition(StringUtils.replace(uri, "/", tileDefinitionDelimiter), request, getServletContext());
			boolean setContext = false;
			if(definition == null) {
				//if definition not found then replace delimiter and append fallback extention
				uri = "/"+uri+fallbackExtention; 
			} else {
				//get the path of the found definition
				uri = definition.getPath();
				ComponentContext tileContext = ComponentContext.getContext(request);
				if (tileContext == null) {
					//if no current context then create a new one
					tileContext = new ComponentContext(definition.getAttributes());
					ComponentContext.setContext(tileContext, request);
					setContext = true;
				}
				//get and execute a controller if it exists
				Controller controller = definition.getOrCreateController();
				if (controller != null)
					controller.perform(tileContext, request, response, getServletContext());
			}
			//forward to the tile path specified for it's descriptor or forward to the fallbackurl if it doesn't exist
			RequestDispatcher dispatcher = request.getRequestDispatcher(uri);
			dispatcher.forward(request, response);
			if(setContext) {
				//clear tile context if we created one.
				ComponentContext.setContext(null, request);
			}
		} catch (FactoryNotFoundException e) {
			throw new ServletException(e.getMessage(), e);
		} catch (DefinitionsFactoryException e) {
			throw new ServletException(e.getMessage(), e);
		} catch (InstantiationException e) {
			throw new ServletException(e.getMessage(), e);
		}
	}
}
Here is an example web.xml configuration
	<context-param>
		<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
		<param-value>.tile</param-value>
	</context-param>
	<servlet>
		<servlet-name>FacesServlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet>
		<servlet-name>TilesServlet</servlet-name>
		<servlet-class>DispatchTilesServlet</servlet-class>
		<init-param>
			<param-name>definitions-config</param-name>
			<param-value>/WEB-INF/tiles-defs.xml</param-value>
		</init-param>
		<load-on-startup>2</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>TilesServlet</servlet-name>
		<url-pattern>*.tile</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>FacesServlet</servlet-name>
		<url-pattern>*.jsf</url-pattern>
	</servlet-mapping>
Example tile-def.xml
<tiles-definitions>
	<definition name="secure.index" path="/WEB-INF/jsps/layout/main.jsp">
          <put name="title" value="Main Home" />
          <put name="body" value="/secure/index.jsp" />
	</definition>
</tiles-definitions>
So if you go to the url "/secure/index.tile" then the tile "secure.index" will be displayed.

Or if you go to "/secure/index.jsf" then the tile will be displayed within the scope of a FacesContext.

I also added some context parameters:

DispatchTilesServlet.TILE_DEFINITION_DELIMITER To identify a new delimiter char. I chose '.' for the default

DispatchTilesServlet.FALLBACK_EXTENTION To identify the extention of a fallback url that /secure/index.tile will revert to if a tile 'secure.index' does not exist. The default is .jsp.

This servlet will probably not work for people using a /faces/* servlet mapping for the FacesServlet.

This solution seems too simple to be true....so if anyone knows of a problem with my using tiles in this way please let me know.

Regards,
Mike
Comments
Locked Post
New comments cannot be posted to this locked post.
Post Details
Locked on May 5 2005
Added on Jul 7 2004
4 comments
441 views