* @version $Id: ServiceServlet.java 2161 2011-02-02 00:16:13Z pboy $
*/
public class ServiceServlet extends BaseApplicationServlet {
/**Error logging */
private static Logger s_log = Logger
.getLogger(ServiceServlet.class.getName());
/** The path of the file that maps resources (relative urls - corresponding
* class names). */
private final static String MAP_FILE = "WEB-INF/resources/cms-service-map.xml";
/** Mapping between a relative URL and the class name of a service. */
private static HashMap s_serviceClasses = new HashMap();
/** Instantiated services cache. This allows for lazy loading of the class
* (i.e. irs ResourceHandler) for each service. */
private static SimpleCache s_services = new SimpleCache();
/** Path to directory containg ccm-cms template files, used in case of fall
* back, when no service class is found in s_serviceClasses rsp. MAP_FILE */
private String m_templatePath;
/** Resolvers to find templages (JSP) and other stuff stored in file system.*/
private ApplicationFileResolver m_resolver;
/**
* Use parent's class initialization extension point to perform additional
* initialisation tasks.
*/
@Override
protected void doInit() {
if (s_log.isDebugEnabled()) {
s_log.info("starting doInit method");
}
/* Process mapping file and fill up s_serviceClasses. */
readFromFile(MAP_FILE);
/** Set Template base path for JSP's */
m_templatePath = ContentSection.getConfig().getTemplateRoot();
Assert.exists(m_templatePath, String.class);
Assert.isTrue(m_templatePath.startsWith("/"),
"template-path must start with '/'");
Assert.isTrue(!m_templatePath.endsWith("/"),
"template-path must not end with '/'");
/** Set TemplateResolver class */
m_resolver = Web.getConfig().getApplicationFileResolver();
}
/**
* Implements the (abstract) doService method of BaseApplicationServlet to
* perform the services.
* @param sreq
* @param sresp
* @param app
* @throws ServletException
* @throws IOException
* @see com.arsdigita.web.BaseApplicationServlet#doService
* (HttpServletRequest, HttpServletResponse, Application)
*/
protected void doService( HttpServletRequest sreq,
HttpServletResponse sresp,
Application app)
throws ServletException, IOException {
if (s_log.isDebugEnabled()) {
s_log.info("starting doService method");
}
DeveloperSupport.startStage("ServiceServlet.doService");
/* Developer's Note:
* Legacy context, established by BaseApplicationServlet, currently
* KernelContext. Not used in ServiceServlet, but required to invoke
* the Resource to provide the service by interface definition.
* Cuurently (version 6.6.8) not used in any service class.
*/
RequestContext ctx = DispatcherHelper.getRequestContext();
/* Get the service being requested, i.e. the remaining URL following
* the servlet address ( ccm/cms-service by default) */
String url = sreq.getPathInfo();
if (url.length() > 1 && url.endsWith("/")) {
/* NOTE: ServletAPI specifies, pathInfo may be empty or will
* start with a '/' character. It currently carries a
* trailing '/' if a "virtual" serviceResource, i.e. not a real jsp, but
* result of a servlet mapping. But Application requires url
* NOT to end with a trailing '/' for legacy free applications.
* The service classes are currently not real applications, so no
* adaptation required here. */
// url = url.substring(0, url.length() - 1);
}
// User access can not be checked here, but has to be checked by each
// service!
/* Determine the service requested by url */
ResourceHandler serviceResource = getResource(url);
if ( serviceResource != null ) {
// Serve the serviceResource.
serviceResource.init();
serviceResource.dispatch(sreq, sresp, ctx);
} else {
// Fall back on the JSP application dispatcher.
if (s_log.isInfoEnabled()) {
s_log.info("NO page registered to serve the requst url.");
}
RequestDispatcher rd = m_resolver.resolve(m_templatePath,
sreq, sresp, app);
if (rd != null) {
if (s_log.isDebugEnabled()) {
s_log.debug("Got dispatcher " + rd);
}
sreq = DispatcherHelper.restoreOriginalRequest(sreq);
rd.forward(sreq,sresp);
} else {
sresp.sendError(404, sreq.getRequestURI() + " not found on this server.");
}
}
DeveloperSupport.endStage("ServiceServlet.doService");
if (s_log.isDebugEnabled()) {
s_log.info("doService method completed");
}
}
/**
* Determines the Resource (ie class) to serve a requested service based
* on its url.
*
* Returns a RecourceHandler for the requested service if it could be found,
* i.e. a dispatcher class whose dispatch method invokes the service.
*
* @param url The URL stub following the site-node URL
* @return A ResourceHandler or null if none exists.
* @pre (url != null)
*/
private ResourceHandler getResource(String url) throws ServletException {
// First check the pages cache for existing pages.
ResourceHandler page = (ResourceHandler) s_services.get(url);
if ( page == null ) {
// Next check if the URL maps to a serviceResource class.
String pageClassName = (String) s_serviceClasses.get(url);
if ( pageClassName != null ) {
Class pageClass;
try {
pageClass = Class.forName(pageClassName);
} catch (ClassNotFoundException e) {
s_log.error("error fetching class for ResourceHandler", e);
throw new ServletException(e);
}
// Try and instantiate the serviceResource.
try {
page = (ResourceHandler) pageClass.newInstance();
} catch (InstantiationException e) {
s_log.error("error instantiating a ResourceHandler", e);
throw new ServletException(e);
} catch (IllegalAccessException e) {
s_log.error("error instantiating a ResourceHandler", e);
throw new ServletException(e);
}
page.init();
s_services.put(url, page);
}
}
return page;
}
/**
*
* Initializes URL-to-Page (class) mappings from a file.
*
* Format of the file is XML:
*
* <dispatcher-configuration>
* <url-mapping
* <url>my-serviceResource</url>
* OR <serviceResource-class>com.arsdigita.Page</page-class>
* <url-mapping
* </dispatcher-configuration>
*
*/
private void readFromFile(final String file) {
XML.parseResource(file, new PageClassConfigHandler(s_serviceClasses));
}
/**
*
* SAX event handler class for parsing configuration file.
*/
protected static class PageClassConfigHandler extends DefaultHandler {
private Map m_map;
private StringBuffer m_buffer;
private String m_url;
private String m_className;
public PageClassConfigHandler(Map map) {
m_map = map;
m_buffer = new StringBuffer();
}
@Override
public void characters(char[] ch, int start, int len) {
for (int i = 0; i < len; i++) {
m_buffer.append(ch[start + i]);
}
}
@Override
public void endElement(String uri, String localName, String qn) {
if ( qn.equals("url") ) {
m_url = m_buffer.toString().trim();
} else if ( qn.equals("page-class") ) {
m_className = m_buffer.toString().trim();
} else if ( qn.equals("url-mapping") ) {
m_map.put(m_url, m_className);
}
m_buffer = new StringBuffer();
}
}
}