blob: f43328bff6178bdac8bb773d837787bbc90425bc [file] [log] [blame]
/*
* Copyright 2009 Niclas Hedhman.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.qi4j.sample.rental.web;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.TreeMap;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.qi4j.api.Qi4j;
import org.qi4j.api.composite.Composite;
import org.qi4j.api.service.ServiceFinder;
import org.qi4j.api.service.ServiceReference;
import org.qi4j.api.structure.Application;
import org.qi4j.api.structure.Module;
import org.qi4j.bootstrap.ApplicationAssembler;
import org.qi4j.bootstrap.Energy4Java;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.EntityResolver;
import org.xml.sax.SAXException;
import static org.qi4j.functional.Iterables.first;
public class QuikitServlet
extends HttpServlet
{
private Application application;
private ServiceFinder finder;
private UrlService urlService;
private DocumentBuilderFactory documentFactory;
private TreeMap<String, Page> mountPoints;
@Override
public void init( ServletConfig config )
throws ServletException
{
try
{
mountPoints = new TreeMap<String, Page>();
documentFactory = DocumentBuilderFactory.newInstance();
documentFactory.setNamespaceAware( true );
ClassLoader cl = getClass().getClassLoader();
SchemaFactory schemaFactory = SchemaFactory.newInstance( "http://www.w3.org/2001/XMLSchema" );
Source[] schemaSources = new Source[ 2 ];
schemaSources[ 0 ] = new StreamSource( cl.getResourceAsStream( "xhtml1-strict.xsd" ) );
schemaSources[ 1 ] = new StreamSource( cl.getResourceAsStream( "xml.xsd" ) );
Schema schema = schemaFactory.newSchema( schemaSources );
documentFactory.setSchema( schema );
ApplicationAssembler assembler = createApplicationAssembler( config );
Energy4Java qi4j = new Energy4Java();
application = qi4j.newApplication( assembler );
application.activate();
Module module = application.findModule( "WebLayer", "PagesModule" );
finder = module;
if( application.mode() == Application.Mode.development )
{
DataInitializer initializer = module.newTransient( DataInitializer.class );
initializer.initialize();
}
Iterable<ServiceReference<Page>> iterable = finder.findServices( Page.class );
for( ServiceReference<Page> page : iterable )
{
PageMetaInfo pageMetaInfo = page.metaInfo( PageMetaInfo.class );
String mountPoint = pageMetaInfo.mountPoint();
mountPoints.put( mountPoint, page.get() );
}
}
catch( Exception e )
{
throw new ServletException( "Can not initialize Zest.", e );
}
}
private ApplicationAssembler createApplicationAssembler( ServletConfig config )
throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException
{
String assemblerClassname = config.getInitParameter( "qi4j-assembler" );
ClassLoader loader = getClass().getClassLoader();
Class<?> assemblerClass = loader.loadClass( assemblerClassname );
ApplicationAssembler assembler;
Constructor cons = assemblerClass.getConstructor( Application.Mode.class );
if( cons == null )
{
assembler = (ApplicationAssembler) assemblerClass.newInstance();
}
else
{
Application.Mode mode;
String modeSetting = config.getInitParameter( "qi4j-application-mode" );
if( modeSetting == null )
{
mode = Application.Mode.development;
}
else
{
mode = Application.Mode.valueOf( modeSetting );
}
assembler = (ApplicationAssembler) cons.newInstance( mode );
}
return assembler;
}
@Override
public void destroy()
{
try
{
application.passivate();
}
catch( Exception e )
{
throw new RuntimeException( "Problem to passivate Zest", e );
}
}
@Override
protected void doGet( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse )
throws ServletException, IOException
{
String path = httpServletRequest.getPathInfo();
if( urlService == null )
{
urlService = finder.findService( UrlService.class ).get();
String uri = httpServletRequest.getRequestURI();
String basePath = uri.substring( 0, uri.length() - path.length() );
urlService.registerBaseUri( basePath );
}
PrintWriter output = httpServletResponse.getWriter();
while( path.startsWith( "//" ) )
{
path = path.substring( 1 );
}
for( Map.Entry<String, Page> entry : mountPoints.entrySet() )
{
if( path.startsWith( entry.getKey() ) )
{
Page page = entry.getValue();
String subPath = path.substring( entry.getKey().length() );
try
{
renderPage( page, subPath, output, httpServletRequest );
}
catch( Exception e )
{
throw new ServletException( e );
}
break;
}
}
output.flush();
}
private void renderPage( Page page, String path, PrintWriter output, HttpServletRequest httpRequest )
throws ParserConfigurationException, SAXException, IOException, RenderException, TransformerException
{
Class<? extends Composite> pageClass = (Class<Composite>) first( Qi4j.FUNCTION_DESCRIPTOR_FOR
.map( page ).types() );
String pageName = pageClass.getSimpleName() + ".html";
DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();
documentBuilder.setEntityResolver( createEntityResolver( documentBuilder ) );
InputStream pageResource = pageClass.getResourceAsStream( pageName );
Document dom = documentBuilder.parse( pageResource );
try
{
Context context = new Context( dom, page, path, httpRequest );
page.render( context );
}
catch( Throwable e )
{
error( dom, e );
}
DOMSource source = new DOMSource( dom );
StreamResult result = new StreamResult( output );
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform( source, result );
output.flush();
}
private EntityResolver createEntityResolver( DocumentBuilder builder )
throws SAXException
{
QuikitResolver quickitResolver = new QuikitResolver( builder );
SchemaFactory schemaFactory = SchemaFactory.newInstance( "http://www.w3.org/2001/XMLSchema" );
schemaFactory.setResourceResolver( quickitResolver );
return quickitResolver;
}
private void error( Document dom, Throwable exception )
{
Element root = dom.getDocumentElement();
dom.removeChild( root );
Element preElement = (Element) dom.appendChild( dom.createElementNS( Page.XHTML, "html" ) )
.appendChild( dom.createElementNS( Page.XHTML, "body" ) )
.appendChild( dom.createElementNS( Page.XHTML, "code" ) )
.appendChild( dom.createElementNS( Page.XHTML, "pre" ) );
StringWriter stringWriter = new StringWriter( 2000 );
PrintWriter writer = new PrintWriter( stringWriter );
exception.printStackTrace( writer );
writer.close();
String content = stringWriter.getBuffer().toString();
preElement.setTextContent( content );
}
static class Context
implements QuikitContext
{
private final Page page;
private String methodName;
private Element element;
private Element parentElement;
private final Document dom;
private final String path;
private final HttpServletRequest request;
private byte[] data = null;
public Context( Document dom, Page page, String path, HttpServletRequest httpServletRequest )
{
this.dom = dom;
this.page = page;
this.path = path;
this.request = httpServletRequest;
}
public Page page()
{
return page;
}
public String methodName()
{
return methodName;
}
public Document dom()
{
return dom;
}
public Element element()
{
return element;
}
public Element parentElement()
{
return parentElement;
}
public String path()
{
return path;
}
public String queryString()
{
return request.getQueryString();
}
public String getHeader( String headerKey )
{
return request.getHeader( headerKey );
}
public byte[] data()
throws RenderException
{
if( data == null )
{
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
InputStream in = request.getInputStream();
int i;
while( ( i = in.read() ) != -1 )
{
baos.write( i );
}
data = baos.toByteArray();
}
catch( IOException e )
{
throw new RenderException( "I/O problems.", e );
}
}
return data;
}
public void setDynamic( String method, Element element, Element parent )
{
this.methodName = method;
this.element = element;
this.parentElement = parent;
}
}
}