fr.cnes.sitools.ext.jeobrowser.upload.JeoUploadResource.java Source code

Java tutorial

Introduction

Here is the source code for fr.cnes.sitools.ext.jeobrowser.upload.JeoUploadResource.java

Source

/*******************************************************************************
 * Copyright 2011, 2012 CNES - CENTRE NATIONAL d'ETUDES SPATIALES
 * 
 * This file is part of SITools2.
 * 
 * SITools2 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.
 * 
 * SITools2 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 SITools2.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package fr.cnes.sitools.ext.jeobrowser.upload;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.restlet.Request;
import org.restlet.data.ClientInfo;
import org.restlet.data.Form;
import org.restlet.data.LocalReference;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Preference;
import org.restlet.data.Reference;
import org.restlet.data.ReferenceList;
import org.restlet.data.Status;
import org.restlet.engine.util.DateUtils;
import org.restlet.ext.fileupload.RestletFileUpload;
import org.restlet.ext.jackson.JacksonRepresentation;
import org.restlet.ext.xstream.XstreamRepresentation;
import org.restlet.representation.ObjectRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.representation.Variant;
import org.restlet.resource.ClientResource;
import org.restlet.resource.Post;

import fr.cnes.sitools.common.SitoolsSettings;
import fr.cnes.sitools.common.application.SitoolsApplication;
import fr.cnes.sitools.common.model.Response;
import fr.cnes.sitools.common.resource.SitoolsParameterizedResource;
import fr.cnes.sitools.dataset.DataSetApplication;
import fr.cnes.sitools.dataset.dto.DictionaryMappingDTO;
import fr.cnes.sitools.dataset.model.BehaviorEnum;
import fr.cnes.sitools.dataset.model.Column;
import fr.cnes.sitools.dataset.model.ColumnRenderer;
import fr.cnes.sitools.dataset.model.DataSet;
import fr.cnes.sitools.datasource.jdbc.model.AttributeValue;
import fr.cnes.sitools.datasource.jdbc.model.Record;
import fr.cnes.sitools.ext.jeobrowser.upload.model.Entry;
import fr.cnes.sitools.ext.jeobrowser.upload.model.JsonEntry;
import fr.cnes.sitools.ext.jeobrowser.upload.model.MediaDetails;
import fr.cnes.sitools.plugins.resources.model.ResourceModel;
import fr.cnes.sitools.server.Consts;
import fr.cnes.sitools.tasks.TaskUtils;
import fr.cnes.sitools.util.RIAPUtils;

/**
 * Upload a Zip containing a JSON object and some medias object Insert the
 * records contained in the JSON object and copy the medias object to a
 * directory Also works with a single JSON file
 * 
 * @author m.gond
 */
public class JeoUploadResource extends SitoolsParameterizedResource {
    /** The name of the concept to look for in the dictionary mapping */
    private static final String CONCEPT_NAME = "commonGeoWKTField";
    /** The zip file Path */
    private String filePath;
    /** The dataset */
    private DataSet dataset;
    /** The url of the datastorage */
    private String datastorageUrl;
    /** The SitoolsSettings */
    private SitoolsSettings settings;
    /** The clientInfo */
    private ClientInfo clientInfo;
    /** The dataset application */
    private DataSetApplication application;

    /** The columnAlias of the geometry column */
    private String columnAliasColGeometry = null;
    /** The url of the JeoInsertResource on the Entry dataset */
    private String entryUrlInsertResource;
    /** The url of the JeoInsertResource on the Media dataset */
    private String mediaUrlInsertResource;

    @Override
    public void doInit() {
        super.doInit();
        settings = ((SitoolsApplication) getApplication()).getSettings();
        clientInfo = getRequest().getClientInfo();
        setNegotiated(true);

        Form query = this.getRequest().getResourceRef().getQueryAsForm();

        datastorageUrl = query.getFirstValue(JeoUploadResourceModel.PARAM_DATASTORAGE_URL);
        if (datastorageUrl == null || datastorageUrl.equals("")) {
            datastorageUrl = getOverrideParameterValue(JeoUploadResourceModel.PARAM_DATASTORAGE_URL);
        }
        ResourceModel model = this.getModel();
        String dicoName = model.getParameterByName("dictionary_name").getValue();
        application = (DataSetApplication) this.getApplication();
        dataset = application.getDataSet();
        if (dicoName != null) {
            DictionaryMappingDTO dico = application.getColumnConceptMappingDTO(dicoName);
            if (dico != null) {
                List<String> columnsAlias = dico.getListColumnAliasMapped(CONCEPT_NAME);
                if (columnsAlias.size() == 0) {
                    getContext().getLogger().log(Level.INFO,
                            dataset.getName() + " no column mapped for concept " + CONCEPT_NAME);
                } else if (columnsAlias.size() > 1) {
                    getContext().getLogger().log(Level.INFO,
                            dataset.getName() + " too many columns mapped for concept " + CONCEPT_NAME);
                } else {
                    columnAliasColGeometry = columnsAlias.get(0);
                }

            }
        }
        entryUrlInsertResource = model.getParameterByName("entryUrlInsertResource").getValue();
        mediaUrlInsertResource = model.getParameterByName("mediaUrlInsertResource").getValue();

    }

    @Override
    public void sitoolsDescribe() {
        setName("JeoBrowserUploadResource");
        setDescription("Resource plugin for jeoBrowser to upload data");
    }

    /**
     * Upload a Zip containing a JSON object and some medias object Insert the
     * records contained in the JSON object and copy the medias object to a
     * directory Also works with a single JSON file
     * 
     * @param representation
     *          the Entity sent
     * @param variant
     *          the variant needed
     * @return a Response
     */
    @Post
    public Representation upload(Representation representation, Variant variant) {
        fr.cnes.sitools.common.model.Response response = null;
        this.getRequestAttributes().keySet();
        if (!getApplication().getClass().equals(DataSetApplication.class)) {
            response = new fr.cnes.sitools.common.model.Response(false, "not attached to a dataset");
            return getRepresentation(response, variant);
        }
        dataset = ((DataSetApplication) getApplication()).getDataSet();

        if (representation.getMediaType().isCompatible(MediaType.APPLICATION_JSON)) {
            JsonEntry jsonEntry = new JacksonRepresentation<JsonEntry>(representation, JsonEntry.class).getObject();
            Entry entry = jsonEntry.getEntry();

            response = insertEntryMetaData(entry);
        } else {

            // generate directory name
            String directoryName = DateUtils.format(new Date(), TaskUtils.getTimestampPattern());

            Representation rep = uploadFile(representation, directoryName);

            // if there was an error during the upload
            if (getResponse().getStatus().isError()) {
                return rep;
            }

            // get the zip
            if (filePath != null) {
                LocalReference fr = LocalReference.createFileReference(filePath);
                Reference zr = new Reference("zip:" + fr.toString());
                ClientResource crFile = new ClientResource(zr);
                Representation zipDetails = crFile.get();

                try {
                    ReferenceList refList = new ReferenceList(zipDetails);
                    // loop through the files of the zip
                    Reference jsonRef = null;
                    for (Iterator<Reference> iterator = refList.iterator(); iterator.hasNext()
                            && jsonRef == null;) {
                        Reference reference = iterator.next();
                        if ("json".equals(reference.getExtensions())) {
                            jsonRef = reference;
                            iterator.remove();
                        }
                        if ("txt".equals(reference.getExtensions())) {
                            jsonRef = reference;
                            iterator.remove();
                        }
                    }
                    crFile.release();
                    crFile = new ClientResource(jsonRef);
                    Representation json = crFile.get();

                    JsonEntry jsonEntry = new JacksonRepresentation<JsonEntry>(json, JsonEntry.class).getObject();
                    Entry entry = jsonEntry.getEntry();

                    response = insertEntryMetaData(entry);
                    if (entry.getMedia() != null) {
                        if (response.isSuccess()) {
                            response = insertMedia(entry);
                        }
                        if (response.isSuccess()) {
                            response = copyMedia(entry, refList, datastorageUrl);
                            if (response.isSuccess()) {
                                response = new Response(true, "Zip successfully uploaded");
                            }
                        }
                    }

                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }

        // boolean folderDeleted = deleteTemporaryFolder(directoryName);
        // if (!folderDeleted) {
        // response = new Response(false, "temporary folder not deleted");
        // }
        if (!response.getSuccess()) {
            getResponse().setStatus(Status.SUCCESS_OK);
        } else {
            getResponse().setStatus(Status.SUCCESS_CREATED);
        }
        return getRepresentation(response, variant);
    }

    /**
     * Copy a set of media files contained in the zip to the specified
     * datastorageUrl
     * 
     * @param entry
     *          the Entry
     * @param refList
     *          the list of media files
     * @param datastorageUrl
     *          the storage url
     * @return a Response
     */
    private Response copyMedia(Entry entry, ReferenceList refList, String datastorageUrl) {
        Response response = null;
        if (entry == null || refList == null) {
            response = new Response(false, "No entry specified or reference list specified");
        }

        for (Iterator<Reference> iterator = refList.iterator(); iterator.hasNext();) {
            Reference reference = iterator.next();

            ClientResource crFile = new ClientResource(reference);
            Representation fileToCopy = crFile.get();

            int indexExcla = reference.getPath().lastIndexOf("!/" + entry.getIdentifier());
            String fileUrl = reference.getPath().substring(indexExcla + 2 + entry.getIdentifier().length());

            Request reqPOST = new Request(Method.PUT, RIAPUtils.getRiapBase()
                    + settings.getString(Consts.APP_DATASTORAGE_URL) + datastorageUrl + fileUrl, fileToCopy);

            reqPOST.setClientInfo(clientInfo);
            org.restlet.Response r = null;
            try {
                r = getContext().getClientDispatcher().handle(reqPOST);

                if (r == null) {
                    response = new Response(false, "ERROR COPYING FILE : " + reference);
                    break;
                } else if (Status.CLIENT_ERROR_FORBIDDEN.equals(r.getStatus())) {
                    response = new Response(false, "CLIENT_ERROR_FORBIDDEN : " + reference);
                    break;
                } else if (Status.CLIENT_ERROR_UNAUTHORIZED.equals(r.getStatus())) {
                    response = new Response(false, "CLIENT_ERROR_UNAUTHORIZED : " + reference);
                    break;
                } else if (Status.isError(r.getStatus().getCode())) {
                    response = new Response(false,
                            "ERROR : " + r.getStatus().getName() + " getting file : " + reference);
                    break;
                } else {
                    response = new Response(true, "file " + reference + " copied");
                }
            } finally {
                if (r != null) {
                    RIAPUtils.exhaust(r);
                }
            }
        }

        return response;
    }

    /**
     * Insert the record contained in the following entry
     * 
     * @param entry
     *          the Entry
     * @return a Response
     */
    private Response insertEntryMetaData(Entry entry) {
        Response response = null;
        if (entry == null) {
            response = new Response(false, "No entry specified");
            return response;
        }

        // Entry
        Record recordEntry = getRecordFromEntry(entry);
        return insertRecord(recordEntry, dataset.getSitoolsAttachementForUsers(), entryUrlInsertResource);
    }

    /**
     * Insert the medias contained in the following entry
     * 
     * @param entry
     *          the entry
     * @return a Response
     */
    private Response insertMedia(Entry entry) {
        Response response = null;
        if (entry == null) {
            response = new Response(false, "No entry specified");
        }

        String datasetMediaUrl = null;
        List<Column> columns = dataset.getColumnModel();
        for (Iterator<Column> iterator = columns.iterator(); iterator.hasNext();) {
            Column column = iterator.next();
            ColumnRenderer columnRenderer = column.getColumnRenderer();
            if (columnRenderer != null && columnRenderer.getBehavior() != null
                    && (columnRenderer.getBehavior() == BehaviorEnum.datasetIconLink
                            || columnRenderer.getBehavior() == BehaviorEnum.datasetLink)) {
                datasetMediaUrl = columnRenderer.getDatasetLinkUrl();
            }
        }

        if (datasetMediaUrl != null) {
            do {
                // Medias
                // Video
                if (entry.getMedia().getVideo().size() > 0) {
                    response = insertMediaRecord(entry.getMedia().getVideo(), datasetMediaUrl, "video", entry);
                    if (response == null) {
                        response = new Response(false, "Error while inserting media video");
                        break;
                    } else if (!response.isSuccess()) {
                        break;
                    }
                }
                // Photo
                if (entry.getMedia().getPhoto().size() > 0) {
                    response = insertMediaRecord(entry.getMedia().getPhoto(), datasetMediaUrl, "photo", entry);
                    if (response == null) {
                        response = new Response(false, "Error while inserting media photo");
                        break;
                    } else if (!response.isSuccess()) {
                        break;
                    }
                }
                // audio
                if (entry.getMedia().getAudio().size() > 0) {
                    response = insertMediaRecord(entry.getMedia().getAudio(), datasetMediaUrl, "audio", entry);
                    if (response == null) {
                        response = new Response(false, "Error while inserting media audio");
                        break;
                    } else if (!response.isSuccess()) {
                        break;
                    }
                }
            } while (false);
        }

        return response;
    }

    /**
     * Create a Record from an Entry
     * 
     * @param entry
     *          the Entry
     * @return a Record Object
     */
    private Record getRecordFromEntry(Entry entry) {
        Record record = new Record();
        record.setId(entry.getIdentifier());

        AttributeValue attrIdentifier = new AttributeValue("identifier", entry.getIdentifier());
        record.getAttributeValues().add(attrIdentifier);

        AttributeValue attrDate = new AttributeValue("date", entry.getDate());
        record.getAttributeValues().add(attrDate);

        AttributeValue attrCoord = new AttributeValue(columnAliasColGeometry,
                "POINT(" + entry.getLongitude() + " " + entry.getLatitude() + ")");
        record.getAttributeValues().add(attrCoord);

        AttributeValue attrEle = new AttributeValue("ele", entry.getEle());
        record.getAttributeValues().add(attrEle);

        AttributeValue attrBuildingId = new AttributeValue("building_identifier",
                entry.getBuilding().getIdentifier());
        record.getAttributeValues().add(attrBuildingId);

        AttributeValue attrBuildingPeopleNb = new AttributeValue("building_peoplenb",
                entry.getBuilding().getPeoplesNb());
        record.getAttributeValues().add(attrBuildingPeopleNb);

        AttributeValue attrBuildingState = new AttributeValue("building_state", entry.getBuilding().getState());
        record.getAttributeValues().add(attrBuildingState);

        AttributeValue attrNote = new AttributeValue("notes", entry.getNotes());
        record.getAttributeValues().add(attrNote);

        return record;

    }

    /**
     * Create a Record from a MediaDetails
     * 
     * @param media
     *          the MediaDetails
     * @param type
     *          the type of the media (audio, video, photo)
     * @param entryId
     *          the id of the parent entry
     * @return a Record Object
     */
    private Record getRecordFromMedia(MediaDetails media, String type, String entryId) {
        Record record = new Record();
        record.setId(type + media.getIdentifier() + entryId);

        AttributeValue attrIdentifier = new AttributeValue("identifier", media.getIdentifier());
        record.getAttributeValues().add(attrIdentifier);

        AttributeValue attrName = new AttributeValue("name", media.getName());
        record.getAttributeValues().add(attrName);

        AttributeValue attrDirectory = new AttributeValue("directory", media.getDirectory());
        record.getAttributeValues().add(attrDirectory);

        AttributeValue attrType = new AttributeValue("type", type);
        record.getAttributeValues().add(attrType);

        AttributeValue attrEntryId = new AttributeValue("entry_id", entryId);
        record.getAttributeValues().add(attrEntryId);

        AttributeValue attrExtension = new AttributeValue("extension", media.getExtension());
        record.getAttributeValues().add(attrExtension);

        return record;

    }

    /**
     * Insert the given record in the given dataset
     * 
     * @param record
     *          the Record
     * @param datasetUrl
     *          the datasetUrl
     * @param resourceUrl
     *          the Url of the resource to call
     * @return the Response of the call
     */
    private Response insertRecord(Record record, String datasetUrl, String resourceUrl) {
        Request reqPUT = new Request(Method.PUT, RIAPUtils.getRiapBase() + datasetUrl + resourceUrl,
                new ObjectRepresentation<Record>(record));

        ArrayList<Preference<MediaType>> objectMediaType = new ArrayList<Preference<MediaType>>();
        objectMediaType.add(new Preference<MediaType>(MediaType.APPLICATION_JSON));
        reqPUT.getClientInfo().setAcceptedMediaTypes(objectMediaType);

        org.restlet.Response r = null;
        Response resp = null;
        try {
            r = getContext().getClientDispatcher().handle(reqPUT);

            @SuppressWarnings("unchecked")
            XstreamRepresentation<Response> repr = (XstreamRepresentation<Response>) r.getEntity();
            resp = (Response) repr.getObject();

        } finally {
            RIAPUtils.exhaust(r);
        }
        return resp;
    }

    /**
     * Insert a List of Media to the given dataset. Medias are of the following
     * type and correspond to the given entry
     * 
     * @param medias
     *          the List of medias
     * @param datasetUrl
     *          the dataset
     * @param type
     *          the type of medias
     * @param entry
     *          the Entry
     * @return a Response
     */
    private Response insertMediaRecord(List<MediaDetails> medias, String datasetUrl, String type, Entry entry) {
        Response response = null;
        for (Iterator<MediaDetails> iterator = medias.iterator(); iterator.hasNext();) {
            MediaDetails details = iterator.next();
            Record rec = getRecordFromMedia(details, type, entry.getIdentifier());
            response = insertRecord(rec, datasetUrl, mediaUrlInsertResource);
            if (!response.isSuccess()) {
                break;
            }
        }
        return response;
    }

    // /**
    // * Delete the temporary folder created
    // *
    // * @param directoryName
    // * the directory to delete
    // * @return true if the directory has been deleted, false otherwise
    // */
    // private boolean deleteTemporaryFolder(String directoryName) {
    //
    // File storeDirectory = new File(settings.getTmpFolderUrl() + "/" +
    // directoryName);
    // boolean fileDeleted = false;
    // if (storeDirectory.exists()) {
    // try {
    // fileDeleted = FileUtils.cleanDirectory(storeDirectory);
    // }
    // catch (IOException e) {
    // // TODO Auto-generated catch block
    // e.printStackTrace();
    // }
    // }
    //
    // return fileDeleted;
    //
    // }

    /**
     * Upload a File contained in the given entity to the temporary folder of the
     * Sitools Server at the given directory name
     * 
     * @param entity
     *          the file to upload
     * @param directoryName
     *          the directory
     * @return a Representation
     * 
     */
    private Representation uploadFile(Representation entity, String directoryName) {
        Representation rep = null;
        if (entity != null) {
            settings = ((SitoolsApplication) getApplication()).getSettings();
            File storeDirectory = new File(settings.getTmpFolderUrl() + "/" + directoryName);
            storeDirectory.mkdirs();

            if (MediaType.MULTIPART_FORM_DATA.equals(entity.getMediaType(), true)) {
                // storeDirectory = "c:\\temp\\";

                // The Apache FileUpload project parses HTTP requests which
                // conform to RFC 1867, "Form-based File Upload in HTML". That
                // is, if an HTTP request is submitted using the POST method,
                // and with a content type of "multipart/form-data", then
                // FileUpload can parse that request, and get all uploaded files
                // as FileItem.

                // 1/ Create a factory for disk-based file items
                DiskFileItemFactory factory = new DiskFileItemFactory();
                factory.setSizeThreshold(1000240);

                // 2/ Create a new file upload handler based on the Restlet
                // FileUpload extension that will parse Restlet requests and
                // generates FileItems.
                RestletFileUpload upload = new RestletFileUpload(factory);

                List<FileItem> items;

                // 3/ Request is parsed by the handler which generates a
                // list of FileItems
                try {
                    items = upload.parseRequest(getRequest());
                } catch (FileUploadException e1) {
                    e1.printStackTrace();
                    items = new ArrayList<FileItem>();
                }

                // Says if at least one file has been handled
                boolean found = false;
                // List the files that haven't been uploaded
                List<String> oops = new ArrayList<String>();
                // count the number files
                int nbFiles = 0;

                for (final Iterator<FileItem> it = items.iterator(); it.hasNext();) {
                    FileItem fi = it.next();
                    // Process only the items that *really* contains an uploaded
                    // file and save them on disk
                    if (fi.getName() != null) {
                        found = true;
                        nbFiles++;
                        File file = new File(storeDirectory, fi.getName());
                        try {
                            fi.write(file);
                            filePath = file.getAbsolutePath();
                        } catch (Exception e) {
                            System.out.println(
                                    "Can't write the content of " + file.getPath() + " due to " + e.getMessage());
                            oops.add(file.getName());
                        }
                    } else {
                        // This is a simple text form input.
                        System.out.println(fi.getFieldName() + " " + fi.getString());
                    }
                }

                // Once handled, you can send a report to the user.
                StringBuilder sb = new StringBuilder();
                if (found) {
                    sb.append(nbFiles);
                    if (nbFiles > 1) {
                        sb.append(" files sent");
                    } else {
                        sb.append(" file sent");
                    }
                    if (!oops.isEmpty()) {
                        sb.append(", ").append(oops.size());
                        if (oops.size() > 1) {
                            sb.append(" files in error:");
                        } else {
                            sb.append(" file in error:");
                        }
                        for (int i = 0; i < oops.size(); i++) {
                            if (i > 0) {
                                sb.append(",");
                            }
                            sb.append(" \"").append(oops.get(i)).append("\"");
                        }
                    }
                    sb.append(".");
                    rep = new StringRepresentation(sb.toString(), MediaType.TEXT_PLAIN);
                } else {
                    // Some problem occurs, sent back a simple line of text.
                    rep = new StringRepresentation("no file uploaded", MediaType.TEXT_PLAIN);
                }
            } else {
                String fileName = "tmp_zip.zip";
                String resourceRef = RIAPUtils.getRiapBase() + settings.getString(Consts.APP_TMP_FOLDER_URL) + "/"
                        + directoryName + "/" + fileName;
                filePath = settings.getTmpFolderUrl() + "/" + directoryName + "/" + fileName;
                // Transfer of PUT calls is only allowed if the readOnly flag is
                // not set.
                Request contextRequest = new Request(Method.PUT, resourceRef);

                // Add support of partial PUT calls.
                contextRequest.getRanges().addAll(getRanges());
                contextRequest.setEntity(entity);

                org.restlet.Response contextResponse = new org.restlet.Response(contextRequest);
                contextRequest.setResourceRef(resourceRef);
                getContext().getClientDispatcher().handle(contextRequest, contextResponse);
                setStatus(contextResponse.getStatus());

            }
        } else {
            // POST request with no entity.
            getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
        }
        return rep;
    }

}