FileDocCategorySizeDatePackage
GDataResponse.javaAPI DocApache Lucene 2.1.016455Wed Feb 14 10:46:06 GMT 2007org.apache.lucene.gdata.server

GDataResponse.java

/** 
 * Copyright 2004 The Apache Software Foundation 
 * 
 * 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.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;
import com.google.gdata.data.BaseFeed;
import com.google.gdata.data.DateTime;
import com.google.gdata.data.ExtensionProfile;
import com.google.gdata.util.common.xml.XmlWriter;
import com.google.gdata.util.common.xml.XmlWriter.Namespace;

/**
 * The FeedRequest Class wraps the current HttpServletResponse. Any action on the
 * HttpServletRequest will be executed via this class. This represents an
 * abstraction on the plain {@link HttpServletResponse}. Any action which has
 * to be performed on the underlying {@link HttpServletResponse} will be
 * executed within this class.
 * <p>
 * The GData basically writes two different kinds of response to the output
 * stream.
 * <ol>
 * <li>update, delete or insert requests will respond with a status code and if
 * successful the feed entry modified or created</li>
 * <li>get requests will respond with a status code and if successful the
 * requested feed</li>
 * </ol>
 * 
 * For this purpose the {@link GDataResponse} class provides the overloaded
 * method
 * {@link org.apache.lucene.gdata.server.GDataResponse#sendResponse(BaseEntry, ExtensionProfile)}
 * which sends the entry e.g feed to the output stream.
 * </p>
 * <p>
 * This class will set the HTTP <tt>Last-Modified</tt> Header to enable
 * clients to send <tt>If-Modified-Since</tt> request header to avoid
 * retrieving the content again if it hasn't changed. If the content hasn't
 * changed since the If-Modified-Since time, then the GData service returns a
 * 304 (Not Modified) HTTP response.
 * </p>
 * 
 * 
 * 
 * 
 * @author Simon Willnauer
 * 
 */
public class GDataResponse {
    /**
     * Response code bad request
     */
    public static final int BAD_REQUEST = HttpServletResponse.SC_BAD_REQUEST;
    /**
     * Response code version conflict
     */
    public static final int CONFLICT = HttpServletResponse.SC_CONFLICT;
    /**
     * Response code forbidden access
     */
    public static final int FORBIDDEN = HttpServletResponse.SC_FORBIDDEN;
    /**
     * Response code internal server error
     */
    public static final int SERVER_ERROR = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
    /**
     * Response code not found
     */
    public static final int NOT_FOUND = HttpServletResponse.SC_NOT_FOUND;
    /**
     * Response code not modified since
     */
    public static final int NOT_MODIFIED = HttpServletResponse.SC_NOT_MODIFIED;
    /**
     * Response code created
     */
    public static final int CREATED = HttpServletResponse.SC_CREATED;
    /**
     * Response code unauthorized access
     */
    public static final int UNAUTHORIZED = HttpServletResponse.SC_UNAUTHORIZED;
    
    
    static final Log LOG = LogFactory.getLog(GDataResponse.class);
    private int error;

    private boolean isError = false;

    private String encoding;

    private OutputFormat outputFormat;

    private final HttpServletResponse response;

    protected static final String XMLMIME_ATOM = "text/xml";

    protected static final String XMLMIME_RSS = "text/xml";

    private static final String HEADER_LASTMODIFIED = "Last-Modified";

    /**
     * Creates a new GDataResponse
     * 
     * @param response -
     *            The underlying {@link HttpServletResponse}
     */
    public GDataResponse(HttpServletResponse response) {
        if (response == null)
            throw new IllegalArgumentException("response must not be null");
        this.response = response;

    }

    /**
     * Sets an error code to this FeedResponse.
     * 
     * @param errorCode -
     *            {@link HttpServletResponse} error code
     */
    public void setError(int errorCode) {
        this.isError = true;
        this.error = errorCode;
    }

    /**
     * Sets the status of the underlying response
     * 
     * @see HttpServletResponse
     * @param responseCode -
     *            the status of the response
     */
    public void setResponseCode(int responseCode) {
        this.response.setStatus(responseCode);
    }

    /**
     * This method sends the specified error to the user if set
     * 
     * @throws IOException -
     *             if an I/O Exception occurs
     */
    public void sendError() throws IOException {
        if (this.isError)
            this.response.sendError(this.error);
        
    }

    /**
     * @return - the {@link HttpServletResponse} writer
     * @throws IOException -
     *             If an I/O exception occurs
     */
    public Writer getWriter() throws IOException {
        return this.response.getWriter();
    }

    /**
     * Sends a response for a get e.g. query request. This method must not
     * invoked in a case of an error performing the requested action.
     * 
     * @param feed -
     *            the feed to respond to the client
     * @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(final BaseFeed feed, final ProvidedService service)
            throws IOException {
        if (feed == null)
            throw new IllegalArgumentException("feed must not be null");
        if (service == null)
            throw new IllegalArgumentException(
                    "provided service must not be null");
        DateTime time = feed.getUpdated();
        if (time != null)
            setLastModifiedHeader(time.getValue());
        FormatWriter writer = FormatWriter.getFormatWriter(this,service);
        writer.generateOutputFormat(feed,this.response);

    }

    /**
     * 
     * Sends a response for an update, insert or delete request. This method
     * must not invoked in a case of an error performing the requested action. If
     * the specified response format is ATOM the default namespace will be set
     * to ATOM.
     * 
     * @param entry -
     *            the modified / created entry to send
     * @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, ProvidedService service)
            throws IOException {
        if (entry == null)
            throw new IllegalArgumentException("entry must not be null");
        if (service == null)
            throw new IllegalArgumentException(
                    "service must not be null");
        DateTime time = entry.getUpdated();
        if (time != null)
            setLastModifiedHeader(time.getValue());
        FormatWriter writer = FormatWriter.getFormatWriter(this,service);
        writer.generateOutputFormat(entry,this.response);

        
    }


    /**
     * This encoding will be used to encode the xml representation of feed or
     * entry written to the {@link HttpServletResponse} output stream.
     * 
     * @return - the entry / feed encoding
     */
    public String getEncoding() {
        return this.encoding;
    }

    /**
     * This encoding will be used to encode the xml representation of feed or
     * entry written to the {@link HttpServletResponse} output stream. <i>UTF-8</i>
     * <i>ISO-8859-1</i>
     * 
     * @param encoding -
     *            string represents the encoding
     */
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    /**
     * @return - the response
     *         {@link org.apache.lucene.gdata.server.GDataRequest.OutputFormat}
     */
    public OutputFormat getOutputFormat() {
        return this.outputFormat;
    }

    /**
     * @param outputFormat -
     *            the response
     *            {@link org.apache.lucene.gdata.server.GDataRequest.OutputFormat}
     */
    public void setOutputFormat(OutputFormat outputFormat) {
        this.outputFormat = outputFormat;
    }

    /**
     * @see Object#toString()
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder(" GDataResponse: ");
        builder.append("Error: ").append(this.error);
        builder.append(" outputFormat: ").append(getOutputFormat());
        builder.append(" encoding: ").append(this.encoding);

        return builder.toString();

    }

    protected void setLastModifiedHeader(long lastModified) {
        String lastMod = DateFormater.formatDate(new Date(lastModified),
                DateFormater.HTTP_HEADER_DATE_FORMAT);
        this.response.setHeader(HEADER_LASTMODIFIED, lastMod);
    }
    
    /**
     * @see HttpServletResponse#setStatus(int)
     * @param status - the request status code
     */
    public void setStatus(int status){
        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;
            }
        }
    }
    
    
    
}