de.iai.ilcd.services.AbstractDataSetResource.java Source code

Java tutorial

Introduction

Here is the source code for de.iai.ilcd.services.AbstractDataSetResource.java

Source

/*******************************************************************************
 * Copyright (c) 2011 Karlsruhe Institute of Technology (KIT) - Institute for
 * Applied Computer Science (IAI).
 * 
 * This file is part of soda4LCA - the Service-Oriented Life Cycle Data Store.
 * 
 * soda4LCA is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * soda4LCA is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with soda4LCA. If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package de.iai.ilcd.services;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBException;

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.tools.view.ParameterTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.jersey.core.header.ContentDisposition;
import com.sun.jersey.multipart.FormDataParam;

import de.fzk.iai.ilcd.api.binding.helper.DatasetHelper;
import de.fzk.iai.ilcd.api.dataset.ILCDTypes;
import de.fzk.iai.ilcd.service.client.impl.DatasetTypes;
import de.fzk.iai.ilcd.service.client.impl.ServiceDAO;
import de.fzk.iai.ilcd.service.client.impl.vo.DataStockVO;
import de.fzk.iai.ilcd.service.client.impl.vo.DatasetVODAO;
import de.fzk.iai.ilcd.service.client.impl.vo.dataset.DataSetList;
import de.fzk.iai.ilcd.service.client.impl.vo.dataset.DataSetVO;
import de.fzk.iai.ilcd.service.model.IContactListVO;
import de.fzk.iai.ilcd.service.model.IDataSetListVO;
import de.fzk.iai.ilcd.service.model.IFlowListVO;
import de.fzk.iai.ilcd.service.model.IFlowPropertyListVO;
import de.fzk.iai.ilcd.service.model.ILCIAMethodListVO;
import de.fzk.iai.ilcd.service.model.IProcessListVO;
import de.fzk.iai.ilcd.service.model.ISourceListVO;
import de.fzk.iai.ilcd.service.model.IUnitGroupListVO;
import de.iai.ilcd.configuration.ConfigurationService;
import de.iai.ilcd.model.adapter.DataSetListAdapter;
import de.iai.ilcd.model.adapter.DataStockListAdapter;
import de.iai.ilcd.model.common.DataSet;
import de.iai.ilcd.model.common.DataSetType;
import de.iai.ilcd.model.common.DataSetVersion;
import de.iai.ilcd.model.common.GlobalRefUriAnalyzer;
import de.iai.ilcd.model.common.exception.FormatException;
import de.iai.ilcd.model.dao.CommonDataStockDao;
import de.iai.ilcd.model.dao.DataSetDao;
import de.iai.ilcd.model.datastock.AbstractDataStock;
import de.iai.ilcd.model.datastock.IDataStockMetaData;
import de.iai.ilcd.model.datastock.RootDataStock;
import de.iai.ilcd.security.SecurityUtil;
import de.iai.ilcd.webgui.controller.DirtyFlagBean;
import de.iai.ilcd.webgui.controller.ui.AvailableStockHandler;

/**
 * REST web service for data sets
 */
public abstract class AbstractDataSetResource<T extends DataSet> {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDataSetResource.class);

    /**
     * URL parameter key for view type
     */
    public final static String PARAM_VIEW = "view";

    /**
     * URL parameter key for format type
     */
    public final static String PARAM_FORMAT = "format";

    /**
     * URL parameter key for version
     */
    public final static String PARAM_VERSION = "version";

    /**
     * URL parameter value for HTML format type
     */
    public final static String FORMAT_HTML = "html";

    /**
     * URL parameter value for XML format type
     */
    public final static String FORMAT_XML = "xml";

    /**
     * URL parameter value for overview view type
     */
    public final static String VIEW_OVERVIEW = "overview";

    /**
     * URL parameter value for full view type
     */
    public final static String VIEW_FULL = "full";

    /**
     * URL parameter value for meta data view type
     */
    public final static String VIEW_METADATA = "metadata";

    /**
     * Context, required for the velocity rendering
     */
    @Context
    private UriInfo context;

    /**
     * Headers, currently unused
     */
    @SuppressWarnings("unused")
    @Context
    private HttpHeaders headers;

    /**
     * The request, required for request parameter evaluation
     */
    @Context
    private HttpServletRequest request;

    /**
     * The type in API definition, required for {@link #generateFullDataSetAsHtml(DataSet)}
     */
    private final ILCDTypes apiType;

    /**
     * The type in model definition, required for
     */
    private final DataSetType modelType;

    /**
     * Create an abstract data set resource
     * 
     * @param dao
     *            the data access object to work with
     */
    public AbstractDataSetResource(DataSetType modelType, ILCDTypes apiType) {
        this.apiType = apiType;
        this.modelType = modelType;
    }

    /**
     * Create a fresh data access object to work with
     * 
     * @return fresh data access object
     */
    protected abstract DataSetDao<T, ?, ?> getFreshDaoInstance();

    /**
     * Get the path to the template for the XML data set list view This is typically something like
     * <code>/xml/<i>$datasettype$</i>s.vm</code>
     * 
     * @deprecated
     * @return path to the template for the XML data set list view
     */
    @Deprecated
    protected abstract String getXMLListTemplatePath();

    /**
     * Get the path to the template for the XML single data set view This is typically something like
     * <code>/xml/<i>$datasettype$</i>.vm</code>
     * 
     * @deprecated
     * @return path to the template for the XML single data set view
     */
    @Deprecated
    protected abstract String getXMLTemplatePath();

    /**
     * Get the path to the template for the HTML full view page This is typically something like
     * <code>/html/<i>$datasettype$</i>.vm</code>
     * 
     * @return path to the template for the HTML full view page
     */
    protected abstract String getHTMLFullViewTemplatePath();

    /**
     * Get the path to the template for the HTML overview page This is typically something like
     * <code>/html/<i>$datasettype$</i>_overview.vm</code>
     * 
     * @return path to the template for the HTML overview page
     */
    protected abstract String getHTMLOverviewTemplatePath();

    /**
     * Get the name of the data set type for the creation of error/info messages
     * 
     * @return name of the data set type for the creation of error/info messages
     */
    protected abstract String getDataSetTypeName();

    /**
     * Flag to set, if full view rights shall be checked in user bean
     * 
     * @return <code>true</code> if full view rights shall be checked, else <code>false</code>
     */
    protected abstract boolean userRequiresFullViewRights();

    /**
     * Retrieves representation of an instance of de.iai.ilcd.services.ContactResource
     * 
     * @return an instance of javax.ws.rs.core.StreamingOutput
     */
    @GET
    @Produces("application/xml")
    public StreamingOutput getDataSets(@DefaultValue("false") @QueryParam("search") final boolean search,
            @DefaultValue("0") @QueryParam("startIndex") final int startIndex,
            @DefaultValue("500") @QueryParam("pageSize") final int pageSize) {

        List<? extends IDataSetListVO> dataSets;
        int count;

        DataSetDao<T, ?, ?> daoObject = this.getFreshDaoInstance();

        if (search) {
            ParameterTool params = new ParameterTool(this.request);

            AvailableStockHandler availableStocksHandler = new AvailableStockHandler();
            availableStocksHandler.setDirty(new DirtyFlagBean());

            IDataStockMetaData[] availableStocks = availableStocksHandler.getAllStocksMeta()
                    .toArray(new IDataStockMetaData[0]);

            count = (int) daoObject.searchResultCount(params, true, availableStocks);
            dataSets = daoObject.search(params, startIndex, pageSize, true, availableStocks);
        } else {
            count = daoObject.getAllCount().intValue();
            dataSets = daoObject.getDataSets(startIndex, pageSize);
        }

        return this.getStreamingOutputForDatasetListVOList(dataSets, count, startIndex, pageSize);
    }

    /**
     * Create a streaming output for a list of data sets
     * 
     * @param list
     *            the list with the data
     * @param totalCount
     *            the total count of result items (for pagination)
     * @param startIndex
     *            index of the first item in the passed list in the total data (for pagination)
     * @param pageSize
     *            pagination page size
     * @return output for the client (which will be marshaled via JAXB)
     */
    protected StreamingOutput getStreamingOutputForDatasetListVOList(final List<? extends IDataSetListVO> list,
            final int totalCount, final int startIndex, final int pageSize) {
        return new StreamingOutput() {

            @Override
            public void write(OutputStream out) throws IOException, WebApplicationException {
                DataSetList resultList = new DataSetListAdapter(list);

                String baseUrl = ConfigurationService.INSTANCE.getNodeInfo().getBaseURL();

                // set xlink:href attribute for each dataset
                for (DataSetVO ds : resultList.getDataSet()) {
                    StringBuffer buf = new StringBuffer(baseUrl);
                    buf.append(AbstractDataSetResource.this.getURLSuffix(ds));
                    buf.append("/");
                    buf.append(ds.getUuidAsString());
                    ds.setHref(buf.toString());
                }

                resultList.setTotalSize(totalCount);
                resultList.setPageSize(pageSize);
                resultList.setStartIndex(startIndex);

                DatasetVODAO dao = new DatasetVODAO();

                try {
                    dao.marshal(resultList, out);
                } catch (JAXBException e) {
                    if (e.getCause().getCause() instanceof SocketException) {
                        LOGGER.warn("exception occurred during marshalling - " + e);
                    } else {
                        LOGGER.error("error marshalling data", e);
                    }
                }
            }
        };
    }

    /**
     * Get a data set by UUID string
     * 
     * @param uuid
     *            UUID string
     * @return XML or HTML response for client
     */
    @GET
    @Path("{uuid}")
    @Produces("application/xml, text/html")
    public Response getDataSet(@PathParam("uuid") String uuid) {

        try {

            MultivaluedMap<String, String> queryParams = this.context.getQueryParameters(true);
            String view = queryParams.getFirst(AbstractDataSetResource.PARAM_VIEW);
            if (view == null) {
                view = AbstractDataSetResource.VIEW_FULL;
            }
            String format = queryParams.getFirst(AbstractDataSetResource.PARAM_FORMAT);
            if (format == null) {
                format = AbstractDataSetResource.FORMAT_HTML;
            }

            DataSetVersion version = null;
            String versionStr = queryParams.getFirst(AbstractDataSetResource.PARAM_VERSION);
            if (versionStr != null) {
                try {
                    version = DataSetVersion.parse(versionStr);
                } catch (FormatException e) {
                    // nothing
                }
            }

            // fix uuid, if not in the right format
            GlobalRefUriAnalyzer analyzer = new GlobalRefUriAnalyzer(uuid);
            uuid = analyzer.getUuidAsString();

            DataSetDao<T, ?, ?> daoObject = this.getFreshDaoInstance();

            T dataset;
            if (version != null) {
                dataset = daoObject.getByUuidAndVersion(uuid, version);
            } else {
                dataset = daoObject.getByUuid(uuid);
            }
            if (dataset == null) {
                String errorTitle = this.getDataSetTypeName() + " data set not found";
                String errorMessage = "A " + this.getDataSetTypeName() + " data set with the uuid " + uuid
                        + " cannot be found in the database";
                if (format.equals(AbstractDataSetResource.FORMAT_HTML)) {
                    return Response.status(Response.Status.NOT_FOUND)
                            .entity(VelocityUtil.errorPage(this.context, errorTitle, errorMessage))
                            .type("text/html").build();
                } else {
                    return Response.status(Response.Status.NOT_FOUND).entity(errorMessage).type("text/plain")
                            .build();
                }
            }

            // handle full and metadata mode by returning the XML file itself
            if (view.equals(AbstractDataSetResource.VIEW_FULL)
                    || view.equals(AbstractDataSetResource.VIEW_METADATA)) {
                if (format.equals("html")) {
                    SecurityUtil.assertCanRead(dataset);
                    return Response.ok(this.generateFullDataSetAsHtml(dataset), MediaType.TEXT_HTML_TYPE).build();
                }
                // return the xml file itself
                else {
                    SecurityUtil.assertCanExport(dataset);
                    ContentDisposition cd = ContentDisposition.type("attachment").fileName(uuid + ".xml").build();
                    return Response.ok(dataset.getXmlFile().getContent(), MediaType.APPLICATION_XML)
                            .header("Content-Disposition", cd).build();
                    // replaceFirst("<\\?xml-stylesheet.*\\?>", "")
                }
            }

            SecurityUtil.assertCanRead(dataset);
            // in all other cases we return the overview view as XML file
            return Response.ok(this.generateOverview(dataset, AbstractDataSetResource.FORMAT_XML),
                    MediaType.APPLICATION_XML).build();
        } catch (AuthorizationException e) {
            return Response.status(Response.Status.UNAUTHORIZED).entity(e.getMessage()).type("text/plain").build();
        }
    }

    protected String getURLSuffix(DataSetVO vo) {
        if (vo instanceof IProcessListVO) {
            return DatasetTypes.PROCESSES.getValue();
        } else if (vo instanceof IFlowListVO) {
            return DatasetTypes.FLOWS.getValue();
        } else if (vo instanceof IFlowPropertyListVO) {
            return DatasetTypes.FLOWPROPERTIES.getValue();
        } else if (vo instanceof IUnitGroupListVO) {
            return DatasetTypes.UNITGROUPS.getValue();
        } else if (vo instanceof ILCIAMethodListVO) {
            return DatasetTypes.LCIAMETHODS.getValue();
        } else if (vo instanceof ISourceListVO) {
            return DatasetTypes.SOURCES.getValue();
        } else if (vo instanceof IContactListVO) {
            return DatasetTypes.CONTACTS.getValue();
        } else {
            return null;
        }
    }

    /**
     * Generate overview. currently called statically with <code>type == xml</code>.
     * 
     * @param dataset
     *            data set to generate overview for
     * @param type
     *            type of data set
     * @return overview source to return to client
     */
    private String generateOverview(T dataset, String type) {

        VelocityContext velocityContext = VelocityUtil.getServicesContext(this.context);
        velocityContext.put("dataset", dataset);

        String template = "";
        if (type.equals(AbstractDataSetResource.FORMAT_XML)) {
            template = this.getXMLTemplatePath();
        } else {
            template = this.getHTMLOverviewTemplatePath();
        }

        return VelocityUtil.parseTemplate(template, velocityContext);
    }

    /**
     * Generate fill data set as HTML view
     * 
     * @param dataset
     *            data set to generate HTML view for
     * @return HTML code to return to client
     */
    private String generateFullDataSetAsHtml(T dataset) {
        VelocityContext velocityContext = VelocityUtil.getServicesContext(this.context);
        velocityContext.put("dataset", dataset);

        de.fzk.iai.ilcd.api.dataset.DataSet xmlDataset = null;
        try {
            DatasetHelper helper = new DatasetHelper();
            String xmlFile = dataset.getXmlFile().getContent();
            InputStream bais = new ByteArrayInputStream(xmlFile.getBytes("UTF-8"));
            xmlDataset = helper.unmarshal(bais, this.apiType);
        } catch (JAXBException ex) {
            AbstractDataSetResource.LOGGER
                    .error("cannot unmarshal xml information from " + this.getDataSetTypeName());
            AbstractDataSetResource.LOGGER.error("stack trace is: ", ex);
        } catch (UnsupportedEncodingException ex) {
            AbstractDataSetResource.LOGGER
                    .error("cannot unmarshal xml information from " + this.getDataSetTypeName());
            AbstractDataSetResource.LOGGER.error("stack trace is: ", ex);
        }
        velocityContext.put("xmlDataset", xmlDataset);

        return VelocityUtil.parseTemplate(this.getHTMLFullViewTemplatePath(), velocityContext);
    }

    /**
     * PUT method for updating or creating an instance of ContactResource
     * 
     * @param content
     *            representation for the resource
     * @return an HTTP response with content of the updated or created resource.
     */
    @PUT
    @Consumes("application/xml")
    public void putXml(String content) {
    }

    /**
     * POST method for importing new process data set sent by form
     * 
     * @param fileInputStream
     *            XML data to import (from form)
     * @param stockUuid
     *            UUID of the root stock to import to (or <code>null</code> for default)
     * @return response for client
     */
    @POST
    @Produces("text/plain")
    @Consumes("multipart/form-data")
    public Response importByFileUpload(@FormDataParam("file") InputStream fileInputStream,
            @FormDataParam("stock") String stockUuid) {
        return this.importXml(fileInputStream, stockUuid);
    }

    /**
     * POST method for importing new process data set sent directly
     * 
     * @param fileInputStream
     *            XML data to import
     * @param stockUuid
     *            UUID of the root stock to import to (or <code>null</code> for default
     * @return response for client
     */
    @POST
    @Produces("text/plain")
    @Consumes("application/xml")
    public Response importByXml(InputStream fileInputStream, @HeaderParam("stock") String stockUuid) {
        return this.importXml(fileInputStream, stockUuid);
    }

    /**
     * Process uploaded XML as input stream
     * 
     * @param inputStream
     *            stream with XML
     * @param stockUuid
     *            UUID of the root stock to import to (or <code>null</code> for default
     * @return response for client
     */
    private Response importXml(InputStream inputStream, String stockUuid) {
        try {
            final CommonDataStockDao dao = new CommonDataStockDao();
            AbstractDataStock ads;
            if (StringUtils.isBlank(stockUuid)) {
                ads = dao.getById(IDataStockMetaData.DEFAULT_DATASTOCK_ID);
            } else {
                ads = dao.getDataStockByUuid(stockUuid);
                if (!(ads instanceof RootDataStock)) {
                    return Response.status(Response.Status.PRECONDITION_FAILED)
                            .entity("Data sets can only be imported into root data stocks!").type("text/plain")
                            .build();
                }
            }
            if (ads == null) {
                return Response.status(Response.Status.BAD_REQUEST).entity("Invalid root data stock UUID specified")
                        .type("text/plain").build();
            }
            SecurityUtil.assertCanImport(ads);

            PostResourceHelper postHelper = new PostResourceHelper();
            final Response resp = postHelper.importByFileUpload(this.modelType, inputStream, (RootDataStock) ads);
            if (resp.getStatus() != Status.OK.getStatusCode()) {
                return resp;
            } else {
                final DataStockVO dsVo = DataStockListAdapter.getServiceApiVo(ads);
                final StreamingOutput sout = new StreamingOutput() {

                    @Override
                    public void write(OutputStream out) throws IOException, WebApplicationException {
                        ServiceDAO sDao = new ServiceDAO();
                        try {
                            sDao.marshal(dsVo, out);
                        } catch (JAXBException e) {
                            if (e.getCause().getCause() instanceof SocketException) {
                                AbstractDataSetResource.LOGGER.warn("exception occurred during marshalling - " + e);
                            } else {
                                AbstractDataSetResource.LOGGER.error("error marshalling data", e);
                            }
                        }
                    }
                };
                return Response.ok(sout).type("application/xml").build();
            }

        } catch (IllegalArgumentException e) {
            return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).type("text/plain").build();
        } catch (AuthorizationException e) {
            return Response.status(Response.Status.FORBIDDEN).entity(e.getMessage()).type("text/plain").build();
        }
    }

}