fr.cnes.sitools.proxy.AbstractDirectoryServerResource.java Source code

Java tutorial

Introduction

Here is the source code for fr.cnes.sitools.proxy.AbstractDirectoryServerResource.java

Source

/*******************************************************************************
* Copyright 2010-2016 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.proxy;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
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.Client;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.CacheDirective;
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.local.Entity;
import org.restlet.ext.fileupload.RestletFileUpload;
import org.restlet.representation.Representation;
import org.restlet.representation.Variant;
import org.restlet.resource.Directory;
import org.restlet.resource.ServerResource;

import fr.cnes.sitools.util.FileCopyUtils;

/**
 * Base class for Sitools Directory resources
 * 
 * Additional methods :
 * 
 * @see {@link #upload(Representation)}
 * 
 * @see Original class in Restlet : {@link #DirectoryServerResource()}
 * 
 * @author m.marseille (AKKA Technologies)
 */

public abstract class AbstractDirectoryServerResource extends ServerResource {

    /**
     * If the resource is a directory, the non-trailing slash character leads to redirection.
     */
    private volatile boolean directoryRedirection;

    /** The original target URI, in case of extensions tunneling. */
    private volatile Reference originalRef;

    /**
     * The local base name of the resource. For example, "foo.en" and "foo.en-GB.html" return "foo".
     */
    private volatile String baseName;

    /** The parent directory handler. */
    private volatile DirectoryProxy directory;

    /** If the resource is a directory, this contains its content. */
    private volatile ReferenceList directoryContent;

    /** The context's directory URI (file, clap URI). */
    private volatile String directoryUri;

    /** The context's target URI (file, clap URI). */
    private volatile String targetUri;

    /** Indicates if the target resource is a directory. */
    private volatile boolean directoryTarget;

    /** Indicates if the target resource is a file. */
    private volatile boolean fileTarget;

    /** The unique representation of the target URI, if it exists. */
    private volatile Reference uniqueReference;

    /** The prototype variant. */
    private volatile Variant protoVariant;

    /** The base variant. */
    private volatile Variant baseVariant;

    /** The list of variants for the GET method. */
    private volatile List<Variant> variantsGet;

    /** If the resource is a file, this contains its content. */
    private volatile Representation fileContent;

    /** The resource path relative to the directory URI. */
    private volatile String relativePart;

    /** To override directory indexName and prevent automatic index.html response */
    private volatile String indexName;

    /**
     * Gets the variantsGet value
     * 
     * @return the variantsGet
     */
    public final List<Variant> getVariantsGet() {
        return variantsGet;
    }

    /**
     * Sets the value of variantsGet
     * 
     * @param variantsGet
     *          the variantsGet to set
     */
    public final void setVariantsGet(List<Variant> variantsGet) {
        this.variantsGet = variantsGet;
    }

    /**
     * Gets the fileContent value
     * 
     * @return the fileContent
     */
    public final Representation getFileContent() {
        return fileContent;
    }

    /**
     * Sets the value of fileContent
     * 
     * @param fileContent
     *          the fileContent to set
     */
    public final void setFileContent(Representation fileContent) {
        this.fileContent = fileContent;
    }

    /**
     * Gets the relativePart value
     * 
     * @return the relativePart
     */
    public final String getRelativePart() {
        return relativePart;
    }

    /**
     * Sets the value of relativePart
     * 
     * @param relativePart
     *          the relativePart to set
     */
    public final void setRelativePart(String relativePart) {
        this.relativePart = relativePart;
    }

    @Override
    public final Representation get() {
        // Content negotiation has been disabled
        // The variant that may need to meet the request conditions
        Representation result = null;

        List<Variant> variants = getVariants(Method.GET);
        if ((variants == null) || (variants.isEmpty())) {
            // Resource not found
            getResponse().setStatus(Status.CLIENT_ERROR_NOT_FOUND);
        } else {
            if (variants.size() == 1) {
                result = (Representation) variants.get(0);
            } else {
                ReferenceList variantRefs = new ReferenceList();

                for (Variant variant : variants) {
                    if (variant.getLocationRef() != null) {
                        variantRefs.add(variant.getLocationRef());
                    } else {
                        getLogger().warning(
                                "A resource with multiple variants should provide a location for each variant when content negotiation is turned off");
                    }
                }

                if (variantRefs.size() > 0) {
                    // Return the list of variants
                    setStatus(Status.REDIRECTION_MULTIPLE_CHOICES);
                    result = variantRefs.getTextRepresentation();
                } else {
                    setStatus(Status.CLIENT_ERROR_NOT_FOUND);
                }
            }
        }

        return result;
    }

    /**
     * Returns the local base name of the file. For example, "foo.en" and "foo.en-GB.html" return "foo".
     * 
     * @return The local name of the file.
     */
    public final String getBaseName() {
        return this.baseName;
    }

    /**
     * Set the base name
     * 
     * @param base
     *          the base name to set
     */
    public final void setBaseName(String base) {
        this.baseName = base;
    }

    /**
     * Returns a client dispatcher.
     * 
     * @return A client dispatcher.
     */
    public final Client getClientDispatcher() {
        return getDirectory().getContext() == null ? null : getDirectory().getContext().getClientDispatcher();
    }

    /**
     * Returns the parent directory handler.
     * 
     * @return The parent directory handler.
     */
    public final Directory getDirectory() {
        return this.directory;
    }

    /**
     * Set the parent directory handler.
     * 
     * @param dir
     *          the directory to set
     */
    public final void setDirectory(DirectoryProxy dir) {
        this.directory = dir;
    }

    /**
     * If the resource is a directory, this returns its content.
     * 
     * @return The directory content.
     */
    public final ReferenceList getDirectoryContent() {
        return directoryContent;
    }

    /**
     * If the resource is a directory, this returns its content.
     * 
     * @param dirContent
     *          the directory content
     */
    public final void setDirectoryContent(ReferenceList dirContent) {
        directoryContent = dirContent;
    }

    /**
     * Returns the context's directory URI (file, clap URI).
     * 
     * @return The context's directory URI (file, clap URI).
     */
    public final String getDirectoryUri() {
        return this.directoryUri;
    }

    /**
     * Returns the context's directory URI (file, clap URI).
     * 
     * @param dirUri
     *          set the directory URI
     */
    public final void setDirectoryUri(String dirUri) {
        this.directoryUri = dirUri;
    }

    /**
     * Sets the context's target URI (file, clap URI).
     * 
     * @param targetUri
     *          The context's target URI.
     */
    public final void setTargetUri(String targetUri) {
        this.targetUri = targetUri;
    }

    /**
     * Returns a representation of the resource at the target URI. Leverages the client dispatcher of the parent
     * directory's context.
     * 
     * @param resourceUri
     *          The URI of the target resource.
     * @return A response with the representation if success.
     */
    public final Response getRepresentation(String resourceUri) {
        return getClientDispatcher().handle(new Request(Method.GET, resourceUri));
    }

    /**
     * Returns a representation of the resource at the target URI. Leverages the client dispatcher of the parent
     * directory's context.
     * 
     * @param resourceUri
     *          The URI of the target resource.
     * @param acceptedMediaType
     *          The accepted media type or null.
     * @return A response with the representation if success.
     */
    public final Response getRepresentation(String resourceUri, MediaType acceptedMediaType) {
        if (acceptedMediaType == null) {
            return getClientDispatcher().handle(new Request(Method.GET, resourceUri));
        }

        Request request = new Request(Method.GET, resourceUri);
        request.getClientInfo().getAcceptedMediaTypes().add(new Preference<MediaType>(acceptedMediaType));
        return getClientDispatcher().handle(request);
    }

    /**
     * Allows to sort the list of representations set by the resource.
     * 
     * @return A Comparator instance imposing a sort order of representations or null if no special order is wanted.
     */
    public final Comparator<Representation> getRepresentationsComparator() {
        // Sort the list of representations by their identifier.
        Comparator<Representation> identifiersComparator = new Comparator<Representation>() {
            public int compare(Representation rep0, Representation rep1) {
                boolean bRep0Null = (rep0.getLocationRef() == null);
                boolean bRep1Null = (rep1.getLocationRef() == null);

                if (bRep0Null && bRep1Null) {
                    return 0;
                }
                if (bRep0Null) {
                    return -1;
                }

                if (bRep1Null) {
                    return 1;
                }

                return rep0.getLocationRef().getLastSegment().compareTo(rep1.getLocationRef().getLastSegment());
            }
        };
        return identifiersComparator;
    }

    /**
     * Returns the context's target URI (file, clap URI).
     * 
     * @return The context's target URI (file, clap URI).
     */
    public final String getTargetUri() {
        return this.targetUri;
    }

    @Override
    public final List<Variant> getVariants() {
        return getVariants(getMethod());
    }

    /**
     * Indicates if the target resource is a directory.
     * 
     * @param isDirTarget
     *          true if is a directory target
     */
    public final void setIfDirectoryTarget(boolean isDirTarget) {
        this.directoryTarget = isDirTarget;
    }

    /**
     * Indicates if the target resource is a file.
     * 
     * @param isFileTarget
     *          true if is a file target
     */
    public final void setIfFileTarget(boolean isFileTarget) {
        this.fileTarget = isFileTarget;
    }

    /**
     * Indicates if the target resource is a directory.
     * 
     * @return True if the target resource is a directory.
     */
    public final boolean isDirectoryTarget() {
        return this.directoryTarget;
    }

    /**
     * Indicates if the target resource is a file.
     * 
     * @return True if the target resource is a file.
     */
    public final boolean isFileTarget() {
        return this.fileTarget;
    }

    @Override
    public final Representation post(Representation entity) {
        return upload(entity);
    }

    @Override
    public final Representation put(Representation entity) {
        return upload(entity);
    }

    /**
     * upload
     * 
     * @param entity
     *          Representation
     * @return Representation
     */
    public final Representation upload(Representation entity) {
        boolean unzip = false;

        if (this.getDirectory().isModifiable()) {
            if ((entity != null) && (entity.getMediaType() != null)
                    && (entity.getMediaType().getName().startsWith(MediaType.MULTIPART_FORM_DATA.getName()))) {
                // 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;
                ArrayList<File> zipFiles = new ArrayList<File>();

                try {
                    // 3/ Request is parsed by the handler which generates a
                    // list of FileItems
                    items = upload.parseRequest(getRequest());

                    String path = getDirectory().getRootRef().getPath() + getRelativePart();
                    String repertoire = Reference.decode(path);

                    // Process only the uploaded item called "fileToUpload" and
                    // save it on disk
                    for (final Iterator<FileItem> it = items.iterator(); it.hasNext();) {
                        FileItem fi = (FileItem) it.next();
                        if ((fi != null) && (fi.getName() != null)) {
                            // Adding url relative part to post at the real url (not only root directory)

                            File file = new File(repertoire, fi.getName());
                            fi.write(file);

                            if (file.getName().matches("([^\\s]+(\\.(?i)(zip|ZIP))$)")) {
                                zipFiles.add(file);
                            }
                        } else {
                            if (fi.getFieldName().equals("wantUnzipFile")) {
                                unzip = true;
                            }
                        }
                    }

                    // 4 unzip files if a part is wantUnzipFile
                    if (unzip) {
                        for (File file : zipFiles) {
                            String fileName = file.getName();
                            int end = fileName.lastIndexOf(".");
                            String name = fileName.substring(0, end);

                            FileCopyUtils.unzipAFile(file.getAbsolutePath(), repertoire + name);
                        }
                    }
                } catch (FileUploadException e) {
                    getLogger().log(Level.INFO, null, e);
                    getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
                } catch (Exception e) {
                    getLogger().log(Level.INFO, null, e);
                    getResponse().setStatus(Status.SERVER_ERROR_INTERNAL);
                }

                // String redirectUrl = getReference().toString(); // address of newly created resource
                // getResponse().redirectSeeOther(redirectUrl);

                setStatus(Status.SUCCESS_CREATED);
            }

            else {

                // Transfer of PUT calls is only allowed if the readOnly flag is
                // not set.
                Request contextRequest = new Request(Method.PUT, this.getTargetUri());

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

                Response contextResponse = new Response(contextRequest);
                contextRequest.setResourceRef(this.getTargetUri());
                getClientDispatcher().handle(contextRequest, contextResponse);
                setStatus(contextResponse.getStatus());
            }
        } else {
            setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED, "The directory is not modifiable.");
        }

        return null;
    }

    @Override
    public Representation handle() {
        Representation result = null;

        if (this.directoryRedirection) {
            if (this.originalRef != null) {
                if (this.originalRef.hasQuery()) {
                    redirectSeeOther(this.originalRef.getPath() + "/?" + this.originalRef.getQuery());
                } else {
                    redirectSeeOther(this.originalRef.getPath() + "/");
                }
            } else {
                if (getReference().hasQuery()) {
                    redirectSeeOther(getReference().getPath() + "/?" + getReference().getQuery());
                } else {
                    redirectSeeOther(getReference().getPath() + "/");
                }
            }
        } else {
            result = super.handle();
        }

        // fix no cache
        if (((DirectoryProxy) getDirectory()).isNocache()) {
            getResponse().getCacheDirectives().add(CacheDirective.noCache());
        }
        // end fix no cache

        return result;
    }

    /**
     * Gets the directoryRedirection value
     * 
     * @return the directoryRedirection
     */
    public final boolean isDirectoryRedirection() {
        return directoryRedirection;
    }

    /**
     * Sets the value of directoryRedirection
     * 
     * @param directoryRedirection
     *          the directoryRedirection to set
     */
    public final void setDirectoryRedirection(boolean directoryRedirection) {
        this.directoryRedirection = directoryRedirection;
    }

    /**
     * Gets the originalRef value
     * 
     * @return the originalRef
     */
    public final Reference getOriginalRef() {
        return originalRef;
    }

    /**
     * Sets the value of originalRef
     * 
     * @param originalRef
     *          the originalRef to set
     */
    public final void setOriginalRef(Reference originalRef) {
        this.originalRef = originalRef;
    }

    /**
     * Handling the call for delete method using base handle
     * 
     * @return the representation according to the call
     */
    public final Representation handleDelete() {
        return super.handle();
    }

    /**
     * Returns the references of the representations of the target resource according to the directory handler property
     * 
     * @return The list of variants references
     */
    public final ReferenceList getVariantsReferences() {
        ReferenceList result = new ReferenceList(0);

        try {
            this.setUniqueReference(null);

            // Ask for the list of all variants of this resource
            Response contextResponse = getRepresentation(this.getTargetUri(), MediaType.TEXT_URI_LIST);
            if (contextResponse.getEntity() != null) {
                // Test if the given response is the list of all variants for
                // this resource
                if (MediaType.TEXT_URI_LIST.equals(contextResponse.getEntity().getMediaType())) {
                    ReferenceList listVariants = new ReferenceList(contextResponse.getEntity());
                    String entryUri;
                    String fullEntryName;
                    String baseEntryName;
                    int lastSlashIndex;
                    int firstDotIndex;

                    for (Reference ref : listVariants) {
                        entryUri = ref.toString();
                        lastSlashIndex = entryUri.lastIndexOf('/');
                        fullEntryName = (lastSlashIndex == -1) ? entryUri : entryUri.substring(lastSlashIndex + 1);
                        baseEntryName = fullEntryName;

                        // Remove the extensions from the base name
                        firstDotIndex = fullEntryName.indexOf('.');
                        if (firstDotIndex != -1) {
                            baseEntryName = fullEntryName.substring(0, firstDotIndex);
                        }

                        // Check if the current file is a valid variant
                        if (baseEntryName.equals(this.getBaseName())) {
                            // Test if the variant is included in the base
                            // prototype variant
                            Variant variant = new Variant();
                            Entity.updateMetadata(fullEntryName, variant, true, getMetadataService());
                            if (this.getProtoVariant().includes(variant)) {
                                result.add(ref);
                            }

                            // Test if the variant is equal to the base variant
                            if (this.getBaseVariant().equals(variant)) {
                                // The unique reference has been found.
                                this.setUniqueReference(ref);
                            }
                        }
                    }
                } else {
                    result.add(contextResponse.getEntity().getLocationRef());
                }
            }
        } catch (IOException ioe) {
            getLogger().log(Level.WARNING, "Unable to get resource variants", ioe);
        }

        return result;

    }

    /**
     * Sets the value of uniqueReference
     * 
     * @param uniqueReference
     *          the uniqueReference to set
     */
    public final void setUniqueReference(Reference uniqueReference) {
        this.uniqueReference = uniqueReference;
    }

    /**
     * Gets the uniqueReference value
     * 
     * @return the uniqueReference
     */
    public final Reference getUniqueReference() {
        return uniqueReference;
    }

    /**
     * Sets the value of baseVariant
     * 
     * @param baseVariant
     *          the baseVariant to set
     */
    public final void setBaseVariant(Variant baseVariant) {
        this.baseVariant = baseVariant;
    }

    /**
     * Gets the baseVariant value
     * 
     * @return the baseVariant
     */
    public final Variant getBaseVariant() {
        return baseVariant;
    }

    /**
     * Sets the value of protoVariant
     * 
     * @param protoVariant
     *          the protoVariant to set
     */
    public final void setProtoVariant(Variant protoVariant) {
        this.protoVariant = protoVariant;
    }

    /**
     * Gets the protoVariant value
     * 
     * @return the protoVariant
     */
    public final Variant getProtoVariant() {
        return protoVariant;
    }

    /**
     * Returns the list of variants for the given method.
     * 
     * @param method
     *          The related method.
     * @return The list of variants for the given method.
     * 
     * @see DirectoryServerResource.getVariants(Method method)
     */
    public List<Variant> getVariants(Method method) {

        List<Variant> result = null;

        if ((Method.GET.equals(method) || Method.HEAD.equals(method))) {
            if (variantsGet != null) {
                result = variantsGet;
            } else {
                getLogger().fine("Getting variants for : " + getTargetUri());

                if ((this.getDirectoryContent() != null) && (getReference() != null)
                        && (getReference().getBaseRef() != null)) {

                    // Allows to sort the list of representations
                    SortedSet<Representation> resultSet = new TreeSet<Representation>(
                            getRepresentationsComparator());

                    // Compute the base reference (from a call's client point of
                    // view)
                    String baseRef = getReference().getBaseRef().toString(false, false);

                    if (!baseRef.endsWith("/")) {
                        baseRef += "/";
                    }

                    int lastIndex = this.relativePart.lastIndexOf("/");

                    if (lastIndex != -1) {
                        baseRef += this.relativePart.substring(0, lastIndex);
                    }

                    int rootLength = getDirectoryUri().length();

                    if (this.getBaseName() != null) {
                        String filePath;
                        for (Reference ref : getVariantsReferences()) {
                            // Add the new variant to the result list
                            Response contextResponse = getRepresentation(ref.toString());
                            if (contextResponse.getStatus().isSuccess() && (contextResponse.getEntity() != null)) {
                                filePath = ref.toString(false, false).substring(rootLength);
                                Representation rep = contextResponse.getEntity();

                                if (filePath.startsWith("/")) {
                                    rep.setLocationRef(baseRef + filePath);
                                } else {
                                    rep.setLocationRef(baseRef + "/" + filePath);
                                }

                                resultSet.add(rep);
                            }
                        }
                    }

                    if (!resultSet.isEmpty()) {
                        result = new ArrayList<Variant>(resultSet);
                    }

                    if (resultSet.isEmpty()) {
                        if (isDirectoryTarget() && getDirectory().isListingAllowed()) {
                            ReferenceFileList userList = new ReferenceFileList(this.getDirectoryContent().size());
                            // Set the list identifier
                            userList.setIdentifier(baseRef);
                            userList.setRegexp(directory.getRegexp());

                            SortedSet<Reference> sortedSet = new TreeSet<Reference>(getDirectory().getComparator());
                            sortedSet.addAll(this.getDirectoryContent());

                            for (Reference ref : sortedSet) {
                                String filePart = ref.toString(false, false).substring(rootLength);
                                StringBuilder filePath = new StringBuilder();
                                if ((!baseRef.endsWith("/")) && (!filePart.startsWith("/"))) {
                                    filePath.append('/');
                                }
                                filePath.append(filePart);

                                // SITOOLS2 - ReferenceFileList = reference and File
                                String path = ref.getPath();
                                String repertoire = Reference.decode(path);
                                File file = new File(repertoire);

                                userList.addFileReference(baseRef + filePath, file);
                                // SITOOLS2 instead of simple reference : userList.add(baseRef + filePath);
                            }
                            List<Variant> list = getDirectory().getIndexVariants(userList);
                            for (Variant variant : list) {
                                if (result == null) {
                                    result = new ArrayList<Variant>();
                                }

                                result.add(getDirectory().getIndexRepresentation(variant, userList));
                            }

                        }
                    }
                } else if (isFileTarget() && (this.fileContent != null)) {
                    // Sets the location of the target representation.
                    if (getOriginalRef() != null) {
                        this.fileContent.setLocationRef(getRequest().getOriginalRef());
                    } else {
                        this.fileContent.setLocationRef(getReference());
                    }

                    result = new ArrayList<Variant>();
                    result.add(this.fileContent);
                }

                this.variantsGet = result;
            }
        }

        return result;
    }

    /**
     * Gets the indexName value
     * @return the indexName
     */
    public String getIndexName() {
        return indexName;
    }

    /**
     * Sets the value of indexName
     * @param indexName the indexName to set
     */
    public void setIndexName(String indexName) {
        this.indexName = indexName;
    }

}