org.eclipse.dirigible.runtime.registry.RegistryServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.dirigible.runtime.registry.RegistryServlet.java

Source

/*******************************************************************************
 * Copyright (c) 2015 SAP 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:
 * SAP - initial API and implementation
 *******************************************************************************/

package org.eclipse.dirigible.runtime.registry;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.MissingResourceException;

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

import org.apache.commons.lang.StringUtils;
import org.eclipse.dirigible.repository.api.ContentTypeHelper;
import org.eclipse.dirigible.repository.api.ICollection;
import org.eclipse.dirigible.repository.api.IEntity;
import org.eclipse.dirigible.repository.api.IEntityInformation;
import org.eclipse.dirigible.repository.api.IRepository;
import org.eclipse.dirigible.repository.api.IResource;
import org.eclipse.dirigible.repository.logging.Logger;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

/**
 * Servlet implementation class RegistryServlet
 */
public class RegistryServlet extends AbstractRegistryServlet {

    private static final String CONTENT_LENGTH_HEADER = "Content-Length";

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

    private static final String EXPIRES_HEADER = "Expires";

    private static final String IF_MODIFIED_SINCE_HEADER = "If-Modified-Since";

    private static final String ACCEPT_HEADER = "Accept"; //$NON-NLS-1$

    protected static final String INDEX_HTML = "index.html"; //$NON-NLS-1$

    private static final String NODE_MODULES_TABRIS = "node_modules/tabris"; //$NON-NLS-1$

    private static final String TABRIS_JS_MIN = "tabris.min.js"; //$NON-NLS-1$

    private static final String TABRIS_JS = "tabris.js"; //$NON-NLS-1$

    private static final String LISTING_OF_FOLDERS_IS_FORBIDDEN = Messages
            .getString("RegistryServlet.LISTING_OF_FOLDERS_IS_FORBIDDEN"); //$NON-NLS-1$

    private static final String JSON = "json"; //$NON-NLS-1$

    private static final String JSON_FOLDER = "folder"; //$NON-NLS-1$

    private static final String JSON_ROOT = "root"; //$NON-NLS-1$

    private static final String JSON_PATH = "path"; //$NON-NLS-1$

    private static final String JSON_NAME = "name"; //$NON-NLS-1$

    private static final String JSON_FILES = "files"; //$NON-NLS-1$

    private static final String REQUEST_PROCESSING_FAILED_S = ""; //$NON-NLS-1$

    private static final long serialVersionUID = 7435479651482177443L;

    private static final Logger logger = Logger.getLogger(RegistryServlet.class);

    @Override
    public void doGet(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {

        String repositoryPath = null;
        final String requestPath = request.getPathInfo();
        boolean deep = false;
        if (requestPath == null) {
            deep = true;
        }
        final OutputStream out = response.getOutputStream();
        try {
            repositoryPath = extractRepositoryPath(request);
            final IEntity entity = getEntity(repositoryPath, request);
            byte[] data;
            if (entity != null) {
                if (entity instanceof IResource) {
                    data = buildResourceData(entity, request, response);
                } else if (entity instanceof ICollection) {
                    String collectionPath = request.getRequestURI().toString();
                    String acceptHeader = request.getHeader(ACCEPT_HEADER);
                    if ((acceptHeader != null) && acceptHeader.contains(JSON)) {
                        if (!collectionPath.endsWith(IRepository.SEPARATOR)) {
                            collectionPath += IRepository.SEPARATOR;
                        }
                        data = buildCollectionData(deep, entity, collectionPath);
                    } else {
                        // welcome file and tabris.js support
                        IResource index = ((ICollection) entity).getResource(INDEX_HTML);
                        IResource tabrisJs = ((ICollection) entity).getResource(TABRIS_JS);
                        IResource tabrisJsMin = ((ICollection) entity).getResource(TABRIS_JS_MIN);

                        if (index.exists() && (collectionPath.endsWith(IRepository.SEPARATOR))) {
                            data = buildResourceData(index, request, response);
                        } else if ((tabrisJs.exists() || tabrisJsMin.exists())
                                && collectionPath.contains(NODE_MODULES_TABRIS)) {
                            data = buildResourceData(tabrisJs.exists() ? tabrisJs : tabrisJsMin, request, response);
                        } else {
                            // listing of collections is forbidden
                            exceptionHandler(response, repositoryPath, HttpServletResponse.SC_FORBIDDEN,
                                    LISTING_OF_FOLDERS_IS_FORBIDDEN);
                            return;
                        }
                    }
                } else {
                    exceptionHandler(response, repositoryPath, HttpServletResponse.SC_FORBIDDEN,
                            LISTING_OF_FOLDERS_IS_FORBIDDEN);
                    return;
                }
            } else {
                if (requestPath != null) {
                    exceptionHandler(response, repositoryPath, HttpServletResponse.SC_NOT_FOUND,
                            String.format("Resource at [%s] does not exist", requestPath));
                    return;
                }
                // artifact root folder doesn't exist at the public registry - no published artifacts at all
                sendData(out, new byte[] {});
                return;
            }

            if (entity instanceof IResource) {
                final IResource resource = (IResource) entity;
                String mimeType = null;
                String extension = ContentTypeHelper.getExtension(resource.getName());
                if ((mimeType = ContentTypeHelper.getContentType(extension)) != null) {
                    response.setContentType(mimeType);
                } else {
                    response.setContentType(resource.getContentType());
                }

                // encoding
                String acceptLang = request.getHeader("Accept-Language");
                String contentLang = acceptLang;
                if ((acceptLang != null) && (acceptLang.indexOf(",") > 0)) {
                    contentLang = acceptLang.substring(0, acceptLang.indexOf(","));
                    if (contentLang.indexOf("-") > 0) {
                        contentLang = contentLang.substring(0, contentLang.indexOf("-"));
                    }
                }

                if (contentLang != null) {
                    response.setHeader("Content-Language", contentLang);
                }
            }
            sendData(out, data);
            setContentLengthHeader(entity, data.length, request, response);
        } catch (final IllegalArgumentException ex) {
            exceptionHandler(response, repositoryPath, HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
        } catch (final MissingResourceException ex) {
            exceptionHandler(response, repositoryPath, HttpServletResponse.SC_NO_CONTENT, ex.getMessage());
        } catch (final RuntimeException ex) {
            exceptionHandler(response, repositoryPath, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    ex.getMessage());
        } finally {
            out.flush();
            out.close();
        }
    }

    private void exceptionHandler(final HttpServletResponse response, final String repositoryPath,
            final int errorMessage, final String exceptionMessage) throws IOException {
        logger.error(String.format(REQUEST_PROCESSING_FAILED_S, repositoryPath) + exceptionMessage);
        response.sendError(errorMessage, exceptionMessage);
    }

    protected byte[] buildCollectionData(final boolean deep, final IEntity entity, final String collectionPath)
            throws IOException {
        byte[] data;
        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
        final PrintWriter writer = new PrintWriter(baos);

        final JsonObject rootObject = new JsonObject();
        rootObject.addProperty(JSON_NAME, JSON_ROOT);
        rootObject.addProperty(JSON_PATH, IRepository.SEPARATOR);
        rootObject.add(JSON_FILES, enumerateCollectionData(collectionPath, (ICollection) entity, deep));

        writer.println(new Gson().toJson(rootObject));
        writer.flush();
        data = baos.toByteArray();
        return data;
    }

    protected byte[] buildResourceData(final IEntity entity, final HttpServletRequest request,
            final HttpServletResponse response) throws IOException {
        byte[] data = new byte[] {};
        if (!setCacheHeaders(entity, request, response)) {
            data = readResourceData((IResource) entity);
        }
        return data;
    }

    private void setContentLengthHeader(IEntity entity, int contentLength, HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        response.setHeader(CONTENT_LENGTH_HEADER, Integer.toString(contentLength));
    }

    private boolean setCacheHeaders(IEntity entity, HttpServletRequest request, HttpServletResponse response)
            throws IOException {

        boolean cached = false;
        IEntityInformation entityInformation = entity.getInformation();
        String modifiedSinceHeader = request.getHeader(IF_MODIFIED_SINCE_HEADER);

        if ((entityInformation != null)) {
            Calendar lastModified = getCalendar(entityInformation.getModifiedAt());

            if ((!StringUtils.isEmpty(modifiedSinceHeader))) {
                Calendar modifiedSince = getCalendar(parseDate(modifiedSinceHeader));

                if (lastModified.getTimeInMillis() <= modifiedSince.getTimeInMillis()) {

                    Calendar expires = getCalendar(lastModified);
                    expires.add(Calendar.MONTH, 1);

                    response.setDateHeader(EXPIRES_HEADER, expires.getTimeInMillis());
                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);

                    cached = true;
                }
            }
            response.setDateHeader(LAST_MODIFIED_HEADER, lastModified.getTimeInMillis());
        }
        return cached;
    }

    // --------------------------------------------------------------------
    // lifted from org.apache.http.client.utils.DateUtils
    // --------------------------------------------------------------------

    public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
    public static final String PATTERN_RFC1036 = "EEE, dd-MMM-yy HH:mm:ss zzz";
    public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy";

    public static final String[] DATE_FORMATS = new String[] { PATTERN_RFC1123, PATTERN_RFC1036, PATTERN_ASCTIME };

    private static final String SINGLE_QUOTE = "'";

    private Date parseDate(String modifiedSinceHeader) {
        final Calendar calendar = Calendar.getInstance();
        if ((modifiedSinceHeader.length() > 1) && modifiedSinceHeader.startsWith(SINGLE_QUOTE)
                && modifiedSinceHeader.endsWith(SINGLE_QUOTE)) {
            modifiedSinceHeader = modifiedSinceHeader.substring(1, modifiedSinceHeader.length() - 1);
        }
        for (String format : DATE_FORMATS) {
            SimpleDateFormat dateParser = new SimpleDateFormat(format);
            dateParser.set2DigitYearStart(calendar.getTime());
            final ParsePosition pos = new ParsePosition(0);
            final Date result = dateParser.parse(modifiedSinceHeader, pos);
            if (pos.getIndex() != 0) {
                return result;
            }
        }
        return null;
    }

    // --------------------------------------------------------------------

    private Calendar getCalendar(Calendar calendar) {
        return getCalendar(calendar.getTime());
    }

    private Calendar getCalendar(Date time) {
        Calendar calendar = Calendar.getInstance();
        if (time == null) {
            return calendar;
        }
        calendar.setTime(time);
        calendar.clear(Calendar.MILLISECOND);
        return calendar;
    }

    private JsonArray enumerateCollectionData(final String collectionPath, final ICollection collection,
            final boolean deep) throws IOException {
        final JsonArray arr = new JsonArray();
        childItterate(arr, collectionPath, collection, deep, collection.getCollectionsNames(), true);
        childItterate(arr, collectionPath, collection, deep, collection.getResourcesNames(), false);
        return arr;
    }

    private void childItterate(final JsonArray arr, final String collectionPath, final ICollection collection,
            final boolean deep, final List<String> collections, final Boolean isFolder) throws IOException {
        for (final String collectionName : collections) {

            final String path = collectionPath + collectionName + (isFolder ? IRepository.SEPARATOR : ""); //$NON-NLS-1$
            final JsonObject elementObject = new JsonObject();
            elementObject.addProperty(JSON_NAME, collectionName);
            elementObject.addProperty(JSON_PATH, path);
            if (isFolder) {
                elementObject.addProperty(JSON_FOLDER, isFolder);
            }
            if (deep && isFolder) {
                final JsonArray children = enumerateCollectionData(path, collection.getCollection(collectionName),
                        deep);
                // We don't care about empty folders
                if (children.size() != 0) {
                    elementObject.add(JSON_FILES, children);
                }
            }
            arr.add(elementObject);
        }
    }

}