com.vmware.appfactory.common.base.AbstractController.java Source code

Java tutorial

Introduction

Here is the source code for com.vmware.appfactory.common.base.AbstractController.java

Source

/* ***********************************************************************
 * VMware ThinApp Factory
 * Copyright (c) 2009-2013 VMware, Inc. All Rights Reserved.
 *
 * 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 com.vmware.appfactory.common.base;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.HttpStatus;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import com.vmware.appfactory.application.model.Application;
import com.vmware.appfactory.build.model.Build;
import com.vmware.appfactory.common.AppFactory;
import com.vmware.appfactory.common.dao.AfDaoFactory;
import com.vmware.appfactory.common.exceptions.AfBadRequestException;
import com.vmware.appfactory.common.exceptions.AfConflictException;
import com.vmware.appfactory.common.exceptions.AfForbiddenException;
import com.vmware.appfactory.common.exceptions.AfNotFoundException;
import com.vmware.appfactory.common.exceptions.AfServerErrorException;
import com.vmware.appfactory.common.exceptions.InvalidDataException;
import com.vmware.appfactory.config.ConfigRegistry;
import com.vmware.appfactory.cws.CwsClientService;
import com.vmware.appfactory.cws.exception.CwsException;
import com.vmware.appfactory.datasource.model.DataSource;
import com.vmware.appfactory.datastore.DatastoreClientService;
import com.vmware.appfactory.datastore.exception.DsException;
import com.vmware.appfactory.feed.dao.FeedDao;
import com.vmware.appfactory.feed.model.Feed;
import com.vmware.appfactory.taskqueue.tasks.TaskQueue;
import com.vmware.appfactory.taskqueue.tasks.state.TaskState;
import com.vmware.appfactory.taskqueue.tasks.state.tasks.TaskFactory;
import com.vmware.appfactory.workpool.VIClientService;
import com.vmware.appfactory.workpool.WorkpoolClientService;
import com.vmware.thinapp.common.util.I18N;
import com.vmware.thinapp.common.workpool.exception.WpException;

/**
 * All controllers inherit from this in order to gain some basic functionality.
 * The BaseController provides global data to a common model, including the
 * current user, the page name (functional area of the application), etc.
 */
public abstract class AbstractController implements ApplicationEventPublisherAware {
    @Resource
    protected AppFactory _af;

    @Resource
    protected AfDaoFactory _daoFactory;

    @Resource(name = "conversionsQueue")
    protected TaskQueue _conversionsQueue;

    @Resource(name = "scanningQueue")
    protected TaskQueue _scanningQueue;

    @Resource
    protected ConfigRegistry _config;

    @Resource
    protected CwsClientService _cwsClient;

    @Resource
    protected DatastoreClientService _dsClient;

    @Resource
    protected WorkpoolClientService _wpClient;

    @Resource
    protected VIClientService _viClient;

    @Resource
    protected TaskFactory _taskFactory;

    protected Logger _log;

    @Nullable
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * Instantiate this base class.
     * Configures a logger specific to the most-derived child class.
     */
    protected AbstractController() {
        _log = LoggerFactory.getLogger(getClass());
        applicationEventPublisher = null;
    }

    /**
     * Convert all thrown AfBadRequestException exceptions
     * into a 400 error code.
     * @param ex
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({ AfBadRequestException.class, InvalidDataException.class })
    public String handleBadRequestException(AfBadRequestException ex) {
        _log.error("Bad request!", ex);
        return ex.getMessage();
    }

    /**
     * Convert all thrown AfForbiddenException exceptions
     * into a 403 error code.
     * @param ex
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(AfForbiddenException.class)
    public String handleForbiddenException(Exception ex) {
        _log.error("Internal server error!", ex);
        return ex.getMessage();
    }

    /**
     * Convert all thrown AfNotFoundException exceptions
     * into a 404 error code.
     * @param ex
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler({ AfNotFoundException.class, FileNotFoundException.class })
    public String handleNotFoundException(Exception ex) {
        _log.error("Resource not found!", ex);
        return ex.getMessage();
    }

    /**
     * Convert all thrown AfConflictException exceptions
     * into a 409 error code.
     * @param ex
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.CONFLICT)
    @ExceptionHandler(AfConflictException.class)
    public String handleConflictException(Exception ex) {
        _log.error("Resource conflict!", ex);
        return ex.getMessage();
    }

    /**
     * Convert all thrown AfServerErrorException exceptions
     * into a 500 error code.
     * @param ex
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler({ AfServerErrorException.class, RuntimeException.class, CwsException.class, WpException.class,
            DsException.class })
    public String handleServerErrorException(Exception ex) {
        _log.error("Internal server error!", ex);
        return ex.getMessage();
    }

    /**
     * Get a required "long" parameter from a request. If the parameter is
     * missing or is not a long, an exception is thrown.
     */
    protected long getLongFromRequest(HttpServletRequest request, String paramName)
            throws IllegalArgumentException, NumberFormatException {
        String str = request.getParameter(paramName);
        if (str == null || str.isEmpty()) {
            throw new IllegalArgumentException("Missing required parameter " + paramName + ".");
        }

        return Long.parseLong(str);
    }

    /**
     * Get an optional "long" parameter from a request. If the parameter is
     * missing the default is returned.
     */
    protected long getLongFromRequest(HttpServletRequest request, String paramName, long def)
            throws NumberFormatException {
        String str = request.getParameter(paramName);
        return (str == null ? def : Long.parseLong(str));
    }

    /**
     * Get a required "string" parameter from a request. If the parameter is
     * missing or is not a long, an exception is thrown.
     */
    protected String getStringFromRequest(HttpServletRequest request, String paramName)
            throws IllegalArgumentException {
        String str = request.getParameter(paramName);
        if (str == null) {
            throw new IllegalArgumentException("Missing required parameter " + paramName + ".");
        }

        return str;
    }

    /**
     * Get an optional "string" parameter from a request. If the parameter is
     * missing the default is returned.
     */
    protected String getStringFromRequest(HttpServletRequest request, String paramName, String def) {
        String str = request.getParameter(paramName);
        return (str == null ? def : str);
    }

    /**
     * Get an optional "boolean" parameter from a request. If the parameter is
     * missing, the default is returned. Otherwise, values of "true" or "on"
     * return true and others return false.
     */
    protected boolean getBoolFromRequest(HttpServletRequest request, String paramName) {
        String str = request.getParameter(paramName);
        if (str == null) {
            return false;
        }
        return (str.equals("on") || str.equals("true"));
    }

    /**
     * Set HTTP headers on an HTTP response to hopefully force browsers to
     * prevent the response being cached. Especially important for dynamic
     * content updates. We're talking to you, Internet Explorer.
     */
    protected void setNoCacheHeaders(HttpServletResponse response) {
        response.addHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
        response.setDateHeader("Expires", 0);
    }

    /**
     * Read the body from an HTTP request.
     */
    protected String readRequest(HttpServletRequest request) throws IOException {
        StringBuffer sb = new StringBuffer();
        String line;

        /* Read the entire message */
        while ((line = request.getReader().readLine()) != null) {
            sb.append(line);
        }

        String s = sb.toString();
        return s;
    }

    protected String getContextPath(HttpServletRequest request) {
        return request.getSession().getServletContext().getContextPath();
    }

    /**
     * Convert a list of builds into a map, grouping the builds by
     * application category.
     *
     * TODO: Multiple categories per app
     */
    protected Map<String, Collection<Build>> createBuildCategoryMap(List<Build> allBuilds) {
        Map<String, Collection<Build>> map = new TreeMap<String, Collection<Build>>();

        for (Build build : allBuilds) {
            String categoryName = "Uncategorized";

            Set<String> cats = build.getCategories();
            if (cats != null && !cats.isEmpty()) {
                // XXX only using first category here
                categoryName = cats.iterator().next();
            }

            Collection<Build> thesePkgs = map.get(categoryName);
            if (thesePkgs == null) {
                thesePkgs = new TreeSet<Build>();
                map.put(categoryName, thesePkgs);
            }
            thesePkgs.add(build);
        }

        return map;
    }

    /**
     * Deletes all the other stuff that points back to it.
     *
     * This will abort related tasks.
     */
    protected void deleteAppSourceRelated(DataSource appSource) {
        if (appSource == null) {
            throw new IllegalArgumentException("appSource must NOT be null!");
        }
        /*
         * Deleting an application source will delete the apps automatically, since there is a
         * direct parent-child mapping, but we need to clean up the task
         * queue first.
         */
        for (Application app : appSource.getApplications()) {
            for (TaskState state : _conversionsQueue.findActiveTasksForApp(app.getId())) {
                _conversionsQueue.abortTask(state.getId());
            }
        }
    }

    /**
     * This method provides the lastModifiedRecord for a given collection
     * of type AbstractRecord. If no records exist, then 0 is returned.
     *
     * @param list
     * @return
     */
    protected long getLastModifiedRecord(Collection<? extends AbstractRecord> list) {
        long lastModified = 0;
        if (CollectionUtils.isEmpty(list)) {
            return lastModified;
        }
        for (AbstractRecord ar : list) {
            if (lastModified < ar.getModified()) {
                lastModified = ar.getModified();
            }
        }
        return lastModified;
    }

    /**
     * Get a full server path url including current web app context.
     * e.g. http(s)://(hostname)(:optional_port)/(web_context)/
     *
     * @param request - a HttpServletRequest.
     * @return a server path url
     */
    protected String getFullContextPath(HttpServletRequest request) {
        if (request == null) {
            throw new IllegalArgumentException("request must NOT be null!");
        }
        int port = request.getLocalPort();
        final StringBuilder url = new StringBuilder();
        /* http(s)://hostname */
        url.append(request.getScheme()).append("://").append(request.getServerName());
        if (port != 80) {
            /* http(s)://hostname:xxxx */
            url.append(":" + port);
        }
        /* http(s)://hostname(:xxxx)/web_context/ */
        url.append(request.getContextPath()).append("/");

        return url.toString();
    }

    /**
     * For the convenience of subclasses, provide a shorthand notation for
     * translating string.
     */
    protected static String tr(Locale locale, String key) {
        return I18N.translate(locale, key);
    }

    /**
     * For the convenience of subclasses, provide a shorthand notation for
     * translating string.
     * @param locale
     * @param key
     * @param args
     * @return A translation of 'key'
     */
    protected static String fr(Locale locale, String key, String... args) {
        return I18N.format(locale, key, (Object[]) args);
    }

    /**
     * Get all feeds list
     * @return a list of feeds.
     */
    protected List<Feed> getFeedsList() {
        FeedDao feedDao = _daoFactory.getFeedDao();

        return feedDao.findAll();
    }

    /**
     * Apply Etag caching to the request. In the request header,
     * if the 'If-None-Match' field is either empty or does not match
     * with the new token; it then set current date in the 'Last-Modified'
     * field. Otherwise, it sends 304 code back in the response.
     *
     * @param request - a HttpServletRequest.
     * @param response - a HttpServletResponse.
     * @param newToken - a new token to be set in the response's Etag header.
     * @return it always returns previous token from the request's
     * 'If-None-Match' header.
     * @throws IOException if any error raised while setting
     * Etag caching related Http headers.
     */
    protected String applyETagCache(HttpServletRequest request, HttpServletResponse response, String newToken,
            boolean setExpires) throws IOException {
        if (!StringUtils.hasLength(newToken)) {
            throw new IllegalArgumentException("Invalid newToken - " + newToken);
        }

        // Get the current date/time
        final Calendar cal = Calendar.getInstance();
        cal.set(Calendar.MILLISECOND, 0);
        final Date lastModified = cal.getTime();

        response.setHeader("Pragma", "cache");
        response.setHeader("Cache-Control", "public, must-revalidate");

        // Set the Expires and Cache-Control: max-age headers to 1 year in the future
        if (setExpires) {
            // Get the date/time 1 year from now
            cal.add(Calendar.YEAR, 1);
            final Date expires = cal.getTime();

            response.addHeader("Cache-Control", "max-age=3153600");
            response.setDateHeader("Expires", expires.getTime());
        }

        // Always store new token in the ETag header.
        response.setHeader("ETag", newToken);
        String previousToken = request.getHeader("If-None-Match");

        if (newToken.equals(previousToken)) {
            response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
            response.setHeader("Last-Modified", request.getHeader("If-Modified-Since"));
        } else {
            response.setDateHeader("Last-Modified", lastModified.getTime());
        }
        return previousToken;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    protected void publishEvent(ApplicationEvent event) {
        if (null != applicationEventPublisher) {
            applicationEventPublisher.publishEvent(event);
        }
    }
}