diff --git a/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataRequest.java b/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataRequest.java index e8d1d50860e..addd86e031e 100644 --- a/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataRequest.java +++ b/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataRequest.java @@ -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 } /** diff --git a/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataResponse.java b/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataResponse.java index 8d6c422baf7..384f1be42dc 100644 --- a/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataResponse.java +++ b/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/GDataResponse.java @@ -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(); - - 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(); + FormatWriter writer = FormatWriter.getFormatWriter(this,service); + writer.generateOutputFormat(feed,this.response); + } /** @@ -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; + } + } + } + + + } diff --git a/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/ProvidedService.java b/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/ProvidedService.java index 61ab86b7381..2d51f67fe0e 100644 --- a/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/ProvidedService.java +++ b/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/ProvidedService.java @@ -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(); + } \ No newline at end of file diff --git a/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/ProvidedServiceConfig.java b/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/ProvidedServiceConfig.java index 11e7ae800e0..95f3f29fc32 100644 --- a/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/ProvidedServiceConfig.java +++ b/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/ProvidedServiceConfig.java @@ -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 @@ -78,6 +81,9 @@ public class ProvidedServiceConfig implements ProvidedService, ScopeVisitor { private ExtensionProfile extensionProfile; 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); + } + + } + } diff --git a/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/RegistryBuilder.java b/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/RegistryBuilder.java index 3a1e695c53c..146131c6e26 100644 --- a/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/RegistryBuilder.java +++ b/contrib/gdata-server/src/java/org/apache/lucene/gdata/server/registry/RegistryBuilder.java @@ -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 diff --git a/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultGetHandler.java b/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultGetHandler.java index 0bbd74e8688..af77e2b3380 100644 --- a/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultGetHandler.java +++ b/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultGetHandler.java @@ -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) { diff --git a/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultInsertHandler.java b/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultInsertHandler.java index 21d4eb9daf9..ff6eefb7896 100644 --- a/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultInsertHandler.java +++ b/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultInsertHandler.java @@ -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); diff --git a/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultUpdateHandler.java b/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultUpdateHandler.java index 33d980544a2..32ae3b04b21 100644 --- a/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultUpdateHandler.java +++ b/contrib/gdata-server/src/java/org/apache/lucene/gdata/servlet/handler/DefaultUpdateHandler.java @@ -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) { diff --git a/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/TestGDataResponse.java b/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/TestGDataResponse.java index 59750181c28..400ee5a7cdf 100644 --- a/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/TestGDataResponse.java +++ b/contrib/gdata-server/src/test/org/apache/lucene/gdata/server/TestGDataResponse.java @@ -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); diff --git a/contrib/gdata-server/src/test/org/apache/lucene/gdata/utils/ProvidedServiceStub.java b/contrib/gdata-server/src/test/org/apache/lucene/gdata/utils/ProvidedServiceStub.java index 126df36ad1c..c9d8cb58ec2 100644 --- a/contrib/gdata-server/src/test/org/apache/lucene/gdata/utils/ProvidedServiceStub.java +++ b/contrib/gdata-server/src/test/org/apache/lucene/gdata/utils/ProvidedServiceStub.java @@ -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; + } + + + } diff --git a/contrib/gdata-server/webroot/WEB-INF/classes/gdata-config.xml b/contrib/gdata-server/webroot/WEB-INF/classes/gdata-config.xml index 4991ea00e96..a6137e64ccd 100644 --- a/contrib/gdata-server/webroot/WEB-INF/classes/gdata-config.xml +++ b/contrib/gdata-server/webroot/WEB-INF/classes/gdata-config.xml @@ -6,6 +6,7 @@ com.google.gdata.data.ExtensionProfile + transform.xslt diff --git a/contrib/gdata-server/webroot/WEB-INF/classes/gdata-config.xsd b/contrib/gdata-server/webroot/WEB-INF/classes/gdata-config.xsd index 5fb08c05de4..bca82cd8068 100755 --- a/contrib/gdata-server/webroot/WEB-INF/classes/gdata-config.xsd +++ b/contrib/gdata-server/webroot/WEB-INF/classes/gdata-config.xsd @@ -3,6 +3,7 @@ + @@ -27,6 +28,8 @@ minOccurs="1" /> +