net.cloudfree.apps.shop.internal.app.JsonListingServlet.java Source code

Java tutorial

Introduction

Here is the source code for net.cloudfree.apps.shop.internal.app.JsonListingServlet.java

Source

/*******************************************************************************
 * Copyright (c) 2008,2010 Gunnar Wagenknecht and others.
 * All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html.
 *
 * Contributors:
 *     Gunnar Wagenknecht - initial API and implementation
 *******************************************************************************/
package net.cloudfree.apps.shop.internal.app;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.gyrex.cds.model.IListing;
import org.eclipse.gyrex.cds.model.IListingAttribute;
import org.eclipse.gyrex.cds.model.documents.Document;
import org.eclipse.gyrex.cds.service.IListingService;
import org.eclipse.gyrex.cds.service.query.ListingQuery;
import org.eclipse.gyrex.cds.service.query.ListingQuery.ResultDimension;
import org.eclipse.gyrex.cds.service.query.ListingQuery.SortDirection;
import org.eclipse.gyrex.cds.service.result.IListingResult;
import org.eclipse.gyrex.cds.service.result.IListingResultFacet;
import org.eclipse.gyrex.cds.service.result.IListingResultFacetValue;
import org.eclipse.gyrex.context.IRuntimeContext;
import org.eclipse.gyrex.http.application.ApplicationException;
import org.eclipse.gyrex.services.common.ServiceUtil;

import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.ObjectMapper;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;

import com.ibm.icu.text.MeasureFormat;
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.ULocale;

public class JsonListingServlet extends HttpServlet {

    static interface Enhancer {
        void enhanceWithinObject(JsonGenerator json) throws IOException;
    }

    private static final String ID_PATH_PREFIX = "/_id/";

    /** serialVersionUID */
    private static final long serialVersionUID = 1L;

    private static StringBuilder getBaseUrl(final HttpServletRequest req) {
        final StringBuilder builder = new StringBuilder(50);
        builder.append(req.getScheme());
        builder.append("://");
        builder.append(req.getServerName());
        if ((req.getScheme().equals("http") && (req.getServerPort() != 80))
                || (req.getScheme().equals("https") && (req.getServerPort() != 443))) {
            builder.append(":");
            builder.append(req.getServerPort());
        }
        builder.append(req.getContextPath());
        builder.append(req.getServletPath());
        builder.append("/");
        return builder;
    }

    private final IRuntimeContext context;

    /**
     * Creates a new instance.
     * 
     * @param context
     */
    public JsonListingServlet(final IRuntimeContext context) {
        this.context = context;
    }

    @Override
    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp)
            throws ServletException, IOException {
        if (req.getParameter("help") != null) {
            doHelp(req, resp);
            return;
        }

        final IListingService listingService = ServiceUtil.getService(IListingService.class, getContext());
        final ListingQuery query = new ListingQuery();
        boolean isSingleListing = false;

        final String path = req.getPathInfo();
        if ((null != path) && (path.length() > 1)) {
            if (path.startsWith(ID_PATH_PREFIX)) {
                // ID path
                query.setFilterQueries(Document.ID + ":" + path.substring(ID_PATH_PREFIX.length()));
            } else {
                // URI path
                query.setFilterQueries(Document.URI_PATH + ":" + ListingQuery.escapeQueryChars(path.substring(1)));
            }
            query.setResultDimension(ResultDimension.FULL);
            query.setMaxResults(1);
            isSingleListing = true;
        } else {
            final String q = req.getParameter("q");
            if (StringUtils.isNotBlank(q)) {
                query.setQuery(q);
            }

            // ignore variations
            query.addFilterQuery("-type:variation");

            // add filters
            final String[] f = req.getParameterValues("f");
            if ((null != f) && (f.length > 0)) {
                for (final String filter : f) {
                    if (StringUtils.isNotBlank(filter)) {
                        query.addFilterQuery(filter);
                    }
                }
            }

            // simple category selection
            final String[] categories = req.getParameterValues("c");
            if ((null != categories) && (categories.length > 0)) {
                for (final String cat : categories) {
                    if (StringUtils.isNotBlank(cat)) {
                        query.addFilterQuery("+category:" + ListingQuery.escapeQueryChars(cat));
                    }
                }
            }

            // simple tags selection
            final String[] tags = req.getParameterValues("t");
            if ((null != tags) && (tags.length > 0)) {
                for (final String tag : tags) {
                    if (StringUtils.isNotBlank(tag)) {
                        query.addFilterQuery("+tags:" + ListingQuery.escapeQueryChars(tag));
                    }
                }
            }

            // start offset
            final String start = req.getParameter("s");
            if (null != start) {
                final long startIndex = NumberUtils.toLong(start);
                if (startIndex < 0) {
                    throw new ApplicationException(400, "startIndex must be greater than or equal to zero");
                }
                query.setStartIndex(startIndex);
            }

            // start offset
            final String rows = req.getParameter("r");
            if (null != rows) {
                final int maxResults = NumberUtils.toInt(rows);
                if ((maxResults <= 0) || (maxResults > 100)) {
                    throw new ApplicationException(400,
                            "rows must be greater than zero and less than or equal to 100");
                }

                query.setMaxResults(maxResults);
            }
        }

        final IListingResult result = listingService.findListings(query);

        //      final Future<IListingResult> findListings = listingService.findListings(query, null);
        //      IListingResult result;
        //      try {
        //         result = findListings.get(8, TimeUnit.SECONDS);
        //      } catch (final InterruptedException e) {
        //         Thread.currentThread().interrupt();
        //         throw new ApplicationException(503, "Server Too Busy");
        //      } catch (final TimeoutException e) {
        //         throw new ApplicationException(503, "Server Too Busy");
        //      } catch (final Exception e) {
        //         throw new ApplicationException(e);
        //      }
        if (null == result) {
            resp.sendError(404);
            return;
        }

        //resp.setContentType("application/json");
        if (req.getParameter("text") != null) {
            resp.setContentType("text/plain");
        } else {
            resp.setContentType("application/json");
        }
        resp.setCharacterEncoding("UTF-8");

        final PrintWriter writer = resp.getWriter();
        final JsonGenerator json = new JsonFactory().createJsonGenerator(writer);
        if (req.getParameter("text") != null) {
            json.useDefaultPrettyPrinter();
        }

        if (isSingleListing) {
            writeSingleProductResult(result, json, req);
        } else {
            writeProductsResult(result, json, req);
        }

        json.close();
    }

    /**
     * Prints a help text.
     * 
     * @param req
     * @param resp
     * @throws IOException
     */
    private void doHelp(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
        resp.setContentType("text/plain");
        resp.setCharacterEncoding("UTF-8");
        final PrintWriter writer = resp.getWriter();
        writer.println("JSON Servlet Usage");
        writer.println("==================");
        writer.println();
        writer.print("    Retrieve all products: ");
        writer.println(getBaseUrl(req));
        writer.print("Retrieve a single product: ");
        writer.println(getBaseUrl(req).append("<uripath>"));
        writer.print("                       or: ");
        writer.println(getBaseUrl(req).append(ID_PATH_PREFIX.substring(1)).append("<id>"));
        writer.println();
        writer.println();
        writer.println("Search/Guided Navigation Parameters");
        writer.println("-----------------------------------");
        writer.println();
        writer.println("q ... the query string");
        writer.println("      (see JavaDoc of org.eclipse.gyrex.cds.service.query.ListingQuery#setQuery(String))");
        writer.println("f ... a filter query (multiple possible, will be interpreted as AND; ..&f=..&f=..)");
        writer.println("      (eg. the facet 'filter' attribute from the result set)");
        writer.println(
                "c ... easy retrieval of a category (multiple possible, will be interpreted as AND; ..&c=shirts&c=underwear)");
        writer.println(
                "      (to filter for categories using OR use a filter \"..&f=+category:(shirts underwear)\")");
        writer.println("s ... start index (zero-based, used for paging)");
        writer.println("r ... rows to return (defaults to 10, used for paging)");
        writer.println(
                "t ... easy retrieval of a tag (multiple possible, will be interpreted as AND; ..&t=shirts&t=cool)");
        writer.println("      (to filter for tags using OR use a filter \"..&f=+tags:(shirts cool)\")");
        writer.println();
        writer.println();
        writer.println("Variations");
        writer.println("----------");
        writer.println();
        writer.println(
                "To keep things simple, variations are not searched by default. They should only be relevant");
        writer.println("on product details pages. Thus, they are available in the product details information.");
        writer.println();
        writer.println();
        writer.println("Debug Parameters");
        writer.println("----------------");
        writer.println();
        writer.println("help ... print this help text");
        writer.println("text ... send response as plain/text");
        writer.flush();
    }

    /**
     * Returns the context.
     * 
     * @return the context
     */
    public IRuntimeContext getContext() {
        return context;
    }

    private void writeFacet(final IListingResultFacet facet, final JsonGenerator json) throws IOException {
        if (null == facet) {
            return;
        }
        json.writeStartObject();

        json.writeFieldName("label");
        json.writeString(facet.getLabel());

        json.writeFieldName("id");
        json.writeString(facet.getId());

        json.writeFieldName("values");
        json.writeStartArray();
        final IListingResultFacetValue[] values = facet.getValues();
        for (final IListingResultFacetValue value : values) {
            json.writeStartObject();
            writeValue("value", value.getValue(), json);
            writeValue("count", value.getCount(), json);
            writeValue("filter", value.toFilterQuery(), json);
            json.writeEndObject();
        }
        json.writeEndArray();

        json.writeEndObject();
    }

    private void writeProduct(final IListing listing, final JsonGenerator json, final HttpServletRequest req,
            final Enhancer enhancer) throws IOException {
        if (null == listing) {
            return;
        }
        json.writeStartObject();

        writeValue("id", listing.getId(), json);
        writeValue("name", listing.getName(), json);
        writeValue("title", listing.getTitle(), json);
        writeValue("description", listing.getDescription(), json);
        writeValue("uri", getBaseUrl(req).append(listing.getUriPath()).toString(), json);
        writeValue("uripath", listing.getUriPath(), json);

        final IListingAttribute categoryAttribute = listing.getAttribute("category");
        if ((null != categoryAttribute) && (categoryAttribute.getValues().length > 0)) {
            writeValue("category", categoryAttribute.getValues()[0].toString(), json);
        }

        final IListingAttribute priceAttribute = listing.getAttribute("price");
        if ((null != priceAttribute) && (priceAttribute.getValues().length > 0)) {
            // the first price the formated store price
            writeValue("shopPrice",
                    MeasureFormat.getCurrencyFormat(ULocale.GERMANY).format(new CurrencyAmount(
                            (Double) priceAttribute.getValues()[0], com.ibm.icu.util.Currency.getInstance("EUR"))),
                    json);
        }

        final IListingAttribute typeAttribute = listing.getAttribute("type");
        if ((null != typeAttribute) && (typeAttribute.getValues().length > 0)) {
            writeValue("type", typeAttribute.getValues()[0].toString(), json);
        }

        final IListingAttribute parentIdAttribute = listing.getAttribute("parentid");
        if ((null != parentIdAttribute) && (parentIdAttribute.getValues().length > 0)) {
            writeValue("parentid", parentIdAttribute.getValues()[0].toString(), json);
        }

        final IListingAttribute[] attributes = listing.getAttributes();
        if (attributes.length > 0) {
            json.writeFieldName("attributes");
            json.writeStartObject();
            final ObjectMapper javaTypeMapper = new ObjectMapper();
            for (final IListingAttribute attribute : attributes) {
                json.writeFieldName(attribute.getName());
                json.writeStartArray();
                for (final Object object : attribute.getValues()) {
                    javaTypeMapper.writeValue(json, object);
                }
                json.writeEndArray();
            }
            json.writeEndObject();
        }

        if (null != enhancer) {
            enhancer.enhanceWithinObject(json);
        }

        json.writeEndObject();
    }

    private void writeProductsResult(final IListingResult result, final JsonGenerator json,
            final HttpServletRequest req) throws IOException {
        json.writeStartObject();

        writeValue("version", "1.0", json);
        writeValue("type", "application/x-gyrex-fanshop-products-json", json);

        json.writeFieldName("query");
        writeQuery(result.getQuery(), json);

        writeValue("queryTime", result.getQueryTime(), json);
        writeValue("numFound", result.getNumFound(), json);
        writeValue("startOffset", result.getStartOffset(), json);

        json.writeFieldName("facets");
        json.writeStartArray();
        for (final IListingResultFacet facet : result.getFacets()) {
            writeFacet(facet, json);
        }
        json.writeEndArray();

        json.writeFieldName("products");
        json.writeStartArray();
        for (final IListing listing : result.getListings()) {
            writeProduct(listing, json, req, null);
        }
        json.writeEndArray();

        json.writeEndObject();
    }

    private void writeQuery(final ListingQuery query, final JsonGenerator json) throws IOException {
        if (null == query) {
            return;
        }
        json.writeStartObject();

        if (null != query.getAdvancedQuery()) {
            writeValue("advancedQuery", query.getAdvancedQuery(), json);
        } else {
            writeValue("query", query.getQuery(), json);
        }

        final List<String> filterQueries = query.getFilterQueries();
        if (!filterQueries.isEmpty()) {
            json.writeFieldName("filters");
            json.writeStartArray();
            for (final String filter : filterQueries) {
                json.writeString(filter);
            }
            json.writeEndArray();
        }

        final Map<String, SortDirection> sortFields = query.getSortFields();
        if (!sortFields.isEmpty()) {
            json.writeFieldName("sortFields");
            json.writeStartObject();
            for (final Entry<String, SortDirection> entry : sortFields.entrySet()) {
                json.writeFieldName(entry.getKey());
                switch (entry.getValue()) {
                case DESCENDING:
                    json.writeString("desc");
                    break;
                case ASCENDING:
                default:
                    json.writeString("asc");
                    break;
                }
            }
            json.writeEndObject();
        }

        json.writeFieldName("dimension");
        switch (query.getResultDimension()) {
        case FULL:
            json.writeString("full");
            break;

        case COMPACT:
        default:
            json.writeString("compact");
            break;
        }

        json.writeEndObject();
    }

    private void writeSingleProductResult(final IListingResult result, final JsonGenerator json,
            final HttpServletRequest req) throws IOException {
        json.writeStartObject();

        writeValue("version", "1.0", json);
        writeValue("type", "application/x-gyrex-fanshop-product-json", json);

        json.writeFieldName("query");
        writeQuery(result.getQuery(), json);

        writeValue("queryTime", result.getQueryTime(), json);
        //writeValue("numFound", result.getNumFound(), json);
        //writeValue("startOffset", result.getStartOffset(), json);

        final IListing[] listings = result.getListings();
        if (listings.length == 1) {
            json.writeFieldName("product");
            final IListing product = listings[0];
            writeProduct(product, json, req, new Enhancer() {

                @Override
                public void enhanceWithinObject(final JsonGenerator json) throws IOException {
                    //               final String productType = (String) product.getAttribute("type").getValues()[0];
                    //               if ("variable-product".equals(productType)) {
                    //                  final Object[] variationids = product.getAttribute("variationids").getValues();
                    //                  json.writeFieldName("variations");
                    //                  json.writeStartObject();
                    //                  final IListingManager manager = ModelUtil.getManager(IListingManager.class, getContext());
                    //                  for (final Object variationId : variationids) {
                    //                     final IListing variation = manager.findById((String) variationId);
                    //                     if (null != variation) {
                    //                        json.writeFieldName((String) variationId);
                    //                        writeProduct(variation, json, req, null);
                    //                     }
                    //                  }
                    //                  json.writeEndObject();
                    //               } else if ("variation".equals(productType)) {
                    //                  final String masterid = (String) product.getAttribute("parentid").getValues()[0];
                    //                  final IListingManager manager = ModelUtil.getManager(IListingManager.class, getContext());
                    //                  final IListing master = manager.findById(masterid);
                    //                  if (null != master) {
                    //                     json.writeFieldName("master");
                    //                     writeProduct(master, json, req, null);
                    //                  }
                    //               }
                }
            });
        }

        json.writeEndObject();
    }

    private void writeValue(final String name, final long value, final JsonGenerator json) throws IOException {
        json.writeFieldName(name);
        json.writeNumber(value);
    }

    private void writeValue(final String name, final String value, final JsonGenerator json)
            throws IOException, JsonGenerationException {
        if (StringUtils.isNotBlank(value)) {
            json.writeFieldName(name);
            json.writeString(value);
        }
    }
}