GData html render preview: LUCENE-660

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@433051 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yonik Seeley 2006-08-20 21:20:08 +00:00
parent 9763d03f48
commit 8c0d242750
12 changed files with 238 additions and 55 deletions

View File

@ -58,6 +58,7 @@ public class GDataRequest {
private static final String RESPONSE_FORMAT_PARAMETER = "alt";
private static final String RESPONSE_FORMAT_PARAMETER_RSS = "rss";
private static final String RESPONSE_FORMAT_PARAMETER_HTML = "html";
private static final int DEFAULT_ITEMS_PER_PAGE = 25;
@ -276,6 +277,8 @@ public class GDataRequest {
return;
if (formatParameter.equalsIgnoreCase(RESPONSE_FORMAT_PARAMETER_RSS))
this.responseFormat = OutputFormat.RSS;
if (formatParameter.equalsIgnoreCase(RESPONSE_FORMAT_PARAMETER_HTML))
this.responseFormat = OutputFormat.HTML;
}
@ -439,7 +442,11 @@ public class GDataRequest {
/**
* Output format RSS
*/
RSS
RSS,
/**
* Output format html if user defined xsl style sheet is present
*/
HTML
}
/**

View File

@ -17,14 +17,23 @@
package org.apache.lucene.gdata.server;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Date;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.server.GDataRequest.OutputFormat;
import org.apache.lucene.gdata.server.registry.ProvidedService;
import org.apache.lucene.gdata.utils.DateFormater;
import com.google.gdata.data.BaseEntry;
@ -104,7 +113,7 @@ public class GDataResponse {
public static final int UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED;
private static final Log LOG = LogFactory.getLog(GDataResponse.class);
static final Log LOG = LogFactory.getLog(GDataResponse.class);
private int error;
private boolean isError = false;
@ -119,11 +128,6 @@ public class GDataResponse {
protected static final String XMLMIME_RSS = "text/xml";
private static final String DEFAUL_NAMESPACE_URI = "http://www.w3.org/2005/Atom";
private static final Namespace DEFAULT_NAMESPACE = new Namespace("",
DEFAUL_NAMESPACE_URI);
private static final String HEADER_LASTMODIFIED = "Last-Modified";
/**
@ -188,33 +192,26 @@ public class GDataResponse {
*
* @param feed -
* the feed to respond to the client
* @param profile -
* the extension profile for the feed to write
* @param service - the service to render the feed
*
* @throws IOException -
* if an I/O exception occurs, often caused by an already
* closed Writer or OutputStream
*
*/
public void sendResponse(BaseFeed feed, ExtensionProfile profile)
public void sendResponse(final BaseFeed feed, final ProvidedService service)
throws IOException {
if (feed == null)
throw new IllegalArgumentException("feed must not be null");
if (profile == null)
if (service == null)
throw new IllegalArgumentException(
"extension profile must not be null");
"provided service must not be null");
DateTime time = feed.getUpdated();
if (time != null)
setLastModifiedHeader(time.getValue());
XmlWriter writer = createWriter();
FormatWriter writer = FormatWriter.getFormatWriter(this,service);
writer.generateOutputFormat(feed,this.response);
if (this.outputFormat.equals(OutputFormat.ATOM)) {
this.response.setContentType(XMLMIME_ATOM);
feed.generateAtom(writer, profile);
} else {
this.response.setContentType(XMLMIME_RSS);
feed.generateRss(writer, profile);
}
writer.close();
}
/**
@ -226,38 +223,27 @@ public class GDataResponse {
*
* @param entry -
* the modified / created entry to send
* @param profile -
* the entries extension profile
* @param service - the service to render the feed
* @throws IOException -
* if an I/O exception occurs, often caused by an already
* closed Writer or OutputStream
*/
public void sendResponse(BaseEntry entry, ExtensionProfile profile)
public void sendResponse(BaseEntry entry, ProvidedService service)
throws IOException {
if (entry == null)
throw new IllegalArgumentException("entry must not be null");
if (profile == null)
if (service == null)
throw new IllegalArgumentException(
"extension profile must not be null");
"service must not be null");
DateTime time = entry.getUpdated();
if (time != null)
setLastModifiedHeader(time.getValue());
XmlWriter writer = createWriter();
if (this.outputFormat.equals(OutputFormat.ATOM))
entry.generateAtom(writer, profile);
else
entry.generateRss(writer, profile);
writer.close();
FormatWriter writer = FormatWriter.getFormatWriter(this,service);
writer.generateOutputFormat(entry,this.response);
}
private XmlWriter createWriter() throws IOException {
XmlWriter writer = new XmlWriter(getWriter(), this.encoding);
// set the default namespace to Atom if Atom is the response format
if (this.outputFormat.equals(OutputFormat.ATOM))
writer.setDefaultNamespace(DEFAULT_NAMESPACE);
return writer;
}
/**
* This encoding will be used to encode the xml representation of feed or
@ -326,4 +312,138 @@ public class GDataResponse {
this.response.setStatus(status);
}
private static abstract class FormatWriter{
static FormatWriter getFormatWriter(final GDataResponse response, final ProvidedService service ){
OutputFormat format = response.getOutputFormat();
if(format == OutputFormat.HTML){
return new HTMLFormatWriter(service);
}
return new SyndicateFormatWriter(service,format,response.getEncoding());
}
abstract void generateOutputFormat(final BaseFeed feed, final HttpServletResponse response) throws IOException;
abstract void generateOutputFormat(final BaseEntry entry, final HttpServletResponse response) throws IOException;
private static class HTMLFormatWriter extends FormatWriter{
private static final String CONTENT_TYPE = "text/html";
private final ProvidedService service;
HTMLFormatWriter(final ProvidedService service){
this.service = service;
}
@Override
void generateOutputFormat(BaseFeed feed, final HttpServletResponse response) throws IOException {
Templates template = this.service.getTransformTemplate();
response.setContentType(CONTENT_TYPE);
if(template == null){
sendNotAvailable(response);
return;
}
StringWriter writer = new StringWriter();
XmlWriter xmlWriter = new XmlWriter(writer);
feed.generateAtom(xmlWriter,this.service.getExtensionProfile());
try {
writeHtml(template,response.getWriter(),writer);
} catch (TransformerException e) {
LOG.error("Can not transform feed for service "+this.service.getName(),e);
sendNotAvailable(response);
}
}
@Override
void generateOutputFormat(BaseEntry entry, final HttpServletResponse response) throws IOException{
Templates template = this.service.getTransformTemplate();
response.setContentType(CONTENT_TYPE);
if(template == null){
sendNotAvailable(response);
return;
}
StringWriter writer = new StringWriter();
XmlWriter xmlWriter = new XmlWriter(writer);
entry.generateAtom(xmlWriter,this.service.getExtensionProfile());
try {
writeHtml(template,response.getWriter(),writer);
} catch (TransformerException e) {
LOG.error("Can not transform feed for service "+this.service.getName(),e);
sendNotAvailable(response);
}
}
private void writeHtml(final Templates template, final Writer writer, final StringWriter source ) throws TransformerException{
Transformer transformer = template.newTransformer();
Source tranformSource = new StreamSource(new StringReader(source.toString()));
transformer.transform(tranformSource,new StreamResult(writer));
}
private void sendNotAvailable(final HttpServletResponse response) throws IOException{
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "No transformation stylesheet available");
}
}
private static class SyndicateFormatWriter extends FormatWriter{
private static final String DEFAUL_NAMESPACE_URI = "http://www.w3.org/2005/Atom";
private static final Namespace DEFAULT_NAMESPACE = new Namespace("",
DEFAUL_NAMESPACE_URI);
private final ProvidedService service;
private final String encoding;
private final OutputFormat format;
SyndicateFormatWriter(final ProvidedService service,final OutputFormat format, String encoding){
this.service = service;
this.format = format;
this.encoding = encoding;
}
@Override
void generateOutputFormat(final BaseFeed feed, final HttpServletResponse response) throws IOException {
XmlWriter writer = null;
try{
writer = createWriter(response.getWriter());
if (this.format == OutputFormat.ATOM) {
response.setContentType(XMLMIME_ATOM);
feed.generateAtom(writer, this.service.getExtensionProfile());
} else {
response.setContentType(XMLMIME_RSS);
feed.generateRss(writer, this.service.getExtensionProfile());
}
}finally{
if(writer != null)
writer.close();
}
}
@Override
void generateOutputFormat(final BaseEntry entry, final HttpServletResponse response) throws IOException {
XmlWriter writer = null;
try{
writer = createWriter(response.getWriter());
if (this.format == OutputFormat.ATOM) {
response.setContentType(XMLMIME_ATOM);
entry.generateAtom(writer, this.service.getExtensionProfile());
} else {
response.setContentType(XMLMIME_RSS);
entry.generateRss(writer, this.service.getExtensionProfile());
}
}finally{
if(writer != null)
writer.close();
}
}
private XmlWriter createWriter(final Writer target) throws IOException {
XmlWriter writer = new XmlWriter(target, this.encoding);
// set the default namespace to Atom if Atom is the response format
if (this.format == OutputFormat.ATOM)
writer.setDefaultNamespace(DEFAULT_NAMESPACE);
return writer;
}
}
}
}

View File

@ -15,6 +15,8 @@
*/
package org.apache.lucene.gdata.server.registry;
import javax.xml.transform.Templates;
import org.apache.lucene.gdata.search.config.IndexSchema;
import com.google.gdata.data.ExtensionProfile;
@ -55,4 +57,9 @@ public interface ProvidedService {
* @return the index schema configuration for this service
*/
public abstract IndexSchema getIndexSchema();
/**
* @return the compiled xslt stylesheet to transform the feed / entry for preview
*/
public abstract Templates getTransformTemplate();
}

View File

@ -17,6 +17,11 @@ package org.apache.lucene.gdata.server.registry;
import java.lang.reflect.Constructor;
import javax.xml.transform.Templates;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.gdata.search.config.IndexSchema;
@ -26,9 +31,7 @@ import org.apache.lucene.gdata.utils.SimpleObjectPool;
import com.google.gdata.data.BaseEntry;
import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.Entry;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.data.Feed;
/**
* Standard implementation of
@ -79,6 +82,9 @@ public class ProvidedServiceConfig implements ProvidedService, ScopeVisitor {
private int poolSize = DEFAULT_POOL_SIZE;
private Templates transformerTemplate;
/**
* @return Returns the poolSize.
*/
@ -326,4 +332,33 @@ public class ProvidedServiceConfig implements ProvidedService, ScopeVisitor {
this.indexSchema.setName(this.serviceName);
}
/**
* @see org.apache.lucene.gdata.server.registry.ProvidedService#getTransformTemplate()
*/
public Templates getTransformTemplate() {
return this.transformerTemplate;
}
/**
* Sets and creates the preview transformer xslt template to provide a html formate for feeds and entries.
* The given file name must be available in the classpath.
* @param filename - the name of the file in the classpath
*/
public void setXsltStylesheet(String filename){
if(filename == null || filename.length() == 0){
LOG.info("No preview stylesheet configured for service "+this.serviceName);
return;
}
TransformerFactory factory = TransformerFactory.newInstance();
try {
this.transformerTemplate = factory.newTemplates(new StreamSource(ProvidedServiceConfig.class.getResourceAsStream(filename.startsWith("/")?filename:"/"+filename)));
} catch (TransformerConfigurationException e) {
throw new RuntimeException("Can not compile xslt stylesheet path: "+filename,e);
}
}
}

View File

@ -77,6 +77,7 @@ class RegistryBuilder {
digester.addBeanPropertySetter("gdata/service/entry-class", "entryType");
digester.addBeanPropertySetter("gdata/service/extension-profile",
"extensionProfileClass");
digester.addBeanPropertySetter("gdata/service/previewStyleSheet","xsltStylesheet");
addIndexRule(digester);
/*
* load components and configurations

View File

@ -90,12 +90,12 @@ public class DefaultGetHandler extends AbstractGdataRequestHandler {
this.feedResponse);
this.feedResponse.sendResponse(feed, this.feedRequest
.getConfigurator().getExtensionProfile());
.getConfigurator());
} else {
BaseEntry entry = this.service.getSingleEntry(this.feedRequest,
this.feedResponse);
this.feedResponse.sendResponse(entry, this.feedRequest
.getConfigurator().getExtensionProfile());
.getConfigurator());
}
} catch (ServiceException e) {

View File

@ -74,7 +74,7 @@ public class DefaultInsertHandler extends AbstractGdataRequestHandler {
BaseEntry entry = this.service.createEntry(this.feedRequest,this.feedResponse);
setFeedResponseFormat();
setFeedResponseStatus(GDataResponse.CREATED);
this.feedResponse.sendResponse(entry, this.feedRequest.getConfigurator().getExtensionProfile());
this.feedResponse.sendResponse(entry, this.feedRequest.getConfigurator());
}catch (ServiceException e) {
LOG.error("Could not process GetFeed request - "+e.getMessage(),e);

View File

@ -75,7 +75,7 @@ public class DefaultUpdateHandler extends AbstractGdataRequestHandler {
BaseEntry entry = this.service.updateEntry(this.feedRequest,
this.feedResponse);
setFeedResponseFormat();
this.feedResponse.sendResponse(entry, this.feedRequest.getConfigurator().getExtensionProfile());
this.feedResponse.sendResponse(entry, this.feedRequest.getConfigurator());
}
catch (ServiceException e) {

View File

@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletResponse;
import junit.framework.TestCase;
import org.apache.lucene.gdata.server.GDataRequest.OutputFormat;
import org.apache.lucene.gdata.utils.ProvidedServiceStub;
import org.easymock.MockControl;
import com.google.gdata.data.Entry;
@ -69,7 +70,7 @@ public class TestGDataResponse extends TestCase {
public void testSendResponseBaseFeedExtensionProfile() throws IOException {
try{
Feed f = null;
this.response.sendResponse(f,new ExtensionProfile());
this.response.sendResponse(f, new ProvidedServiceStub());
fail("Exception expected");
}catch (IllegalArgumentException e) {
//
@ -90,7 +91,7 @@ public class TestGDataResponse extends TestCase {
this.response.setOutputFormat(OutputFormat.ATOM);
this.control.replay();
this.response.sendResponse(createFeed(),new ExtensionProfile());
this.response.sendResponse(createFeed(), new ProvidedServiceStub());
assertEquals("Simple XML representation",stringWriter.toString(),generatedFeedAtom);
this.control.reset();
@ -102,8 +103,7 @@ public class TestGDataResponse extends TestCase {
this.httpResponse.setContentType(GDataResponse.XMLMIME_RSS);
this.control.replay();
this.response.sendResponse(createFeed(),new ExtensionProfile
());
this.response.sendResponse(createFeed(), new ProvidedServiceStub());
assertEquals("Simple XML representation",stringWriter.toString(),generatedFeedRSS);
@ -117,7 +117,7 @@ public class TestGDataResponse extends TestCase {
public void testSendResponseBaseEntryExtensionProfile() throws IOException {
try{
Entry e = null;
this.response.sendResponse(e,new ExtensionProfile());
this.response.sendResponse(e, new ProvidedServiceStub());
fail("Exception expected");
}catch (IllegalArgumentException e) {
//
@ -134,11 +134,11 @@ public class TestGDataResponse extends TestCase {
PrintWriter writer = new PrintWriter(stringWriter);
this.control.expectAndReturn(this.httpResponse.getWriter(),writer);
this.httpResponse.setContentType(GDataResponse.XMLMIME_ATOM);
this.response.setOutputFormat(OutputFormat.ATOM);
this.control.replay();
this.response.sendResponse(createEntry(),new ExtensionProfile
());
this.response.sendResponse(createEntry(), new ProvidedServiceStub());
assertEquals("Simple XML representation ATOM",stringWriter.toString(),generatedEntryAtom);
// test rss output
@ -147,11 +147,11 @@ public class TestGDataResponse extends TestCase {
writer = new PrintWriter(stringWriter);
this.control.expectAndReturn(this.httpResponse.getWriter(),writer);
this.httpResponse.setContentType(GDataResponse.XMLMIME_RSS);
this.response.setOutputFormat(OutputFormat.RSS);
this.control.replay();
this.response.sendResponse(createEntry(),new ExtensionProfile
());
this.response.sendResponse(createEntry(), new ProvidedServiceStub());
assertEquals("Simple XML representation RSS",stringWriter.toString(),generatedEntryRSS);

View File

@ -15,6 +15,8 @@
*/
package org.apache.lucene.gdata.utils;
import javax.xml.transform.Templates;
import org.apache.lucene.gdata.search.config.IndexSchema;
import org.apache.lucene.gdata.server.registry.ProvidedService;
@ -63,4 +65,11 @@ public class ProvidedServiceStub implements ProvidedService {
return this.indexSchema;
}
public Templates getTransformTemplate() {
return null;
}
}

View File

@ -6,6 +6,7 @@
<extension-profile>
com.google.gdata.data.ExtensionProfile
</extension-profile>
<previewStyleSheet>transform.xslt</previewStyleSheet>
<index-schema defaultSearchField="content">
<index useTimedIndexer="true" indexerIdleTime="120" optimizeAfterCommit="5" commitAfterDocuments="10">
<defaultAnalyzer>

View File

@ -3,6 +3,7 @@
<xs:element name="feed-class" type="xs:string" />
<xs:element name="entry-class" type="xs:string" />
<xs:element name="extension-profile" type="xs:string" />
<xs:element name="previewStyleSheet" type="xs:string" />
<xs:element name="class" type="xs:string" />
<xs:annotation>
<xs:documentation xml:lang="en">
@ -27,6 +28,8 @@
minOccurs="1" />
<xs:element ref="extension-profile" maxOccurs="1"
minOccurs="1" />
<xs:element ref="previewStyleSheet" maxOccurs="1"
minOccurs="0" />
<xs:element ref="index-schema" maxOccurs="1"
minOccurs="1" />
</xs:sequence>