uk.ac.open.kmi.iserve.sal.manager.impl.DocumentManagerFileSystem.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.open.kmi.iserve.sal.manager.impl.DocumentManagerFileSystem.java

Source

/*
 * Copyright (c) 2013. Knowledge Media Institute - The Open University
 *
 * 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 uk.ac.open.kmi.iserve.sal.manager.impl;

import com.google.common.collect.ImmutableSet;
import com.google.common.eventbus.EventBus;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.open.kmi.iserve.core.ConfigurationProperty;
import uk.ac.open.kmi.iserve.core.iServeProperty;
import uk.ac.open.kmi.iserve.sal.events.DocumentCreatedEvent;
import uk.ac.open.kmi.iserve.sal.events.DocumentDeletedEvent;
import uk.ac.open.kmi.iserve.sal.events.DocumentsClearedEvent;
import uk.ac.open.kmi.iserve.sal.exception.DocumentException;
import uk.ac.open.kmi.iserve.sal.exception.SalException;
import uk.ac.open.kmi.iserve.sal.manager.DocumentManager;
import uk.ac.open.kmi.iserve.sal.manager.IntegratedComponent;
import uk.ac.open.kmi.iserve.sal.util.UriUtil;
import uk.ac.open.kmi.msm4j.io.util.FileUtil;

import java.io.*;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

@Singleton
public class DocumentManagerFileSystem extends IntegratedComponent implements DocumentManager {

    private static final Logger log = LoggerFactory.getLogger(DocumentManagerFileSystem.class);

    // Default path for service and documents URIs.
    // Note that any change here should also affect the REST API
    // Keep the trailing slash
    private static final String DEFAULT_DOC_URL_PATH = "id/documents/";

    private URI documentsInternalPath;
    private URI documentsPublicUri;

    @Inject
    DocumentManagerFileSystem(EventBus eventBus, @iServeProperty(ConfigurationProperty.ISERVE_URL) String iServeUri,
            @iServeProperty(ConfigurationProperty.DOCUMENTS_FOLDER) String documentsFolderPath)
            throws SalException {

        super(eventBus, iServeUri);

        log.debug("Creating Document Manager. iServe URL - {}\n, Documents Folder - {}", iServeUri,
                documentsFolderPath);

        this.documentsPublicUri = this.getIserveUri().resolve(DEFAULT_DOC_URL_PATH);

        try {
            // Set the internal URI to the docs folder
            this.documentsInternalPath = new URI(
                    documentsFolderPath + (documentsFolderPath.endsWith("/") ? "" : "/")); // Ensure it has a final slash

            // Ensure we get an absolute file:/ URI
            if (!this.documentsInternalPath.isAbsolute()) {
                if (this.documentsInternalPath.toASCIIString().startsWith("/")) {
                    this.documentsInternalPath = this.getClass().getResource("/").toURI()
                            .resolve(this.documentsInternalPath);
                } else {
                    // Obtain absolute URI
                    log.warn(
                            "Configuring document manager with relative path. Documents may be deleted when the application is redeployed.");
                    this.documentsInternalPath = this.getClass().getResource(".").toURI()
                            .resolve(this.documentsInternalPath);
                }
            }

            log.info("Documents internal path set to: {}", this.documentsInternalPath.toASCIIString());

        } catch (URISyntaxException e) {
            log.error("Incorrect iServe URI while initialising " + this.getClass().getSimpleName(), e);
            throw new SalException("Incorrect iServe URI while initialising " + this.getClass().getSimpleName(), e);
        }

        File file = new File(this.documentsInternalPath);
        if (!file.exists()) {
            if (file.mkdirs()) {
                log.info("Created documents folder.");
            } else {
                throw new DocumentException("Unable to create documents folder.");
            }
        } else {
            if (!file.isDirectory()) {
                throw new DocumentException("There already exists a file with the documents folder URI.");
            }
        }
    }

    /**
     * @return
     */
    private URI getDocumentsInternalPath() {
        return this.documentsInternalPath;
    }

    /**
     * Obtain the relative URI of a document
     *
     * @param documentUri
     * @return
     */
    private URI getDocumentRelativeUri(URI documentUri) {
        return documentsPublicUri.relativize(documentUri);
    }

    /**
     * Obtain the Internal URI for a given document.
     *
     * @param documentUri
     * @return
     */
    private URI getDocumentInternalUri(URI documentUri) {
        return this.getDocumentsInternalPath().resolve(this.getDocumentRelativeUri(documentUri));
    }

    /**
     * @param file
     * @return
     */
    private URI getDocumentPublicUri(File file) {
        URI relativeUri = this.getDocumentsInternalPath().relativize(file.toURI());
        return documentsPublicUri.resolve(relativeUri);
    }

    @Override
    public Set<URI> listDocuments() throws DocumentException {
        ImmutableSet.Builder<URI> result = ImmutableSet.builder();
        File documentsFolder = new File(this.getDocumentsInternalPath());
        File[] docsList = documentsFolder.listFiles();
        for (File doc : docsList) {
            if (doc.isDirectory())
                result.add(this.getDocumentPublicUri(doc));
        }
        return result.build();
    }

    /* (non-Javadoc)
     * @see uk.ac.open.kmi.iserve.sal.manager.DocumentManager#getDocument(java.io.InputStream)
     */
    @Override
    public InputStream getDocument(URI documentUri) throws DocumentException {

        if (!documentExists(documentUri)) {
            return null;
        }

        File dir = new File(this.getDocumentInternalUri(documentUri));
        for (File file : dir.listFiles()) {
            if (file.getName().matches("^index\\..*")) {
                InputStream result = null;
                try {
                    result = new FileInputStream(file);
                } catch (IOException e) {
                    throw new DocumentException(e);
                }
                return result;
            }
        }
        return null;
    }

    @Override
    public String getDocumentMediaType(URI documentUri) throws DocumentException {
        if (!documentExists(documentUri)) {
            return null;
        }

        File dir = new File(this.getDocumentInternalUri(documentUri));
        for (File file : dir.listFiles()) {
            if (file.getName().matches("^index\\..*")) {
                return getFileMediaTypeMap().get(file.getAbsolutePath());
            }
        }
        return null;
    }

    //   /* (non-Javadoc)
    //    * @see uk.ac.open.kmi.iserve.sal.manager.DocumentManager#addDocument(java.lang.String, java.lang.String)
    //    */
    //   @Override
    //   public URI addDocumentToService(String fileName, String docContent, String serviceId) throws DocumentException {
    //      
    //      URI folder = this.getDocumentsInternalPath().resolve(serviceId); 
    //      URI fileUri = folder.resolve(fileName);
    //      URI result = null;
    //      try {
    //         FileUtil.createDirIfNotExists(new File(folder));
    //         File file = new File(fileUri);
    //         IOUtil.writeString(docContent, file);
    //         result = this.getDocumentPublicUri(file);
    //      } catch (IOException e) {
    //         throw new DocumentException(e);
    //      }
    //      return result;
    //   }

    @Override
    public URI createDocument(InputStream docContent, String fileExtension, String mediaType)
            throws DocumentException {

        URI newDocUri = this.generateUniqueDocumentUri();
        try {
            URI internalDocUri = this.getDocumentInternalUri(newDocUri);
            File docDir = new File(internalDocUri);
            FileUtil.createDirIfNotExists(docDir);
            File file = new File(
                    new StringBuilder(docDir.getAbsolutePath()).append("/index.").append(fileExtension).toString());
            FileOutputStream out = new FileOutputStream(file);
            FileUtil.createDirIfNotExists(file.getParentFile());
            int size = IOUtils.copy(docContent, out);
            log.info("Document internal URI - " + internalDocUri.toASCIIString() + " - " + size + " bytes.");
            storeMediaType(file.getAbsolutePath(), mediaType);
        } catch (IOException e) {
            throw new DocumentException("Unable to add document to service.", e);
        }

        // Generate Event
        this.getEventBus().post(new DocumentCreatedEvent(new Date(), newDocUri));

        return newDocUri;
    }

    private synchronized void storeMediaType(String absolutePath, String mediaType) {
        log.debug("Storing Media Type for file {}: {}", absolutePath, mediaType);
        Map<String, String> fileMediatypeMap = getFileMediaTypeMap();
        fileMediatypeMap.put(absolutePath, mediaType);
        storeMediaTypeMap(fileMediatypeMap);
    }

    public Map<String, String> getFileMediaTypeMap() {
        File mapFile = new File(URI.create(
                new StringBuilder(documentsInternalPath.toString()).append("/mediaTypeMap.json").toString()));
        if (!mapFile.exists()) {
            return new HashMap<String, String>();
        } else {
            try {
                Gson gson = new GsonBuilder().create();
                Type typeOfHashMap = new TypeToken<Map<String, String>>() {
                }.getType();
                return gson.fromJson(new FileReader(mapFile), typeOfHashMap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private void storeMediaTypeMap(Map<String, String> fileMediatypeMap) {
        try {
            File mapFile = new File(URI.create(
                    new StringBuilder(documentsInternalPath.toString()).append("/mediaTypeMap.json").toString()));
            Gson gson = new GsonBuilder().create();
            String json = gson.toJson(fileMediatypeMap);
            FileOutputStream fos = new FileOutputStream(mapFile);
            fos.write(json.getBytes());
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /* (non-Javadoc)
     * @see uk.ac.open.kmi.iserve.sal.manager.DocumentManager#createDocument(java.io.InputStream, java.lang.String )
     */
    @Override
    public URI createDocument(URI docRemoteLocation, String fileExtension, String mediaType)
            throws DocumentException {

        log.debug("Creating document from {} - File extension: {}", docRemoteLocation, fileExtension);
        URI newDocUri = this.generateUniqueDocumentUri();
        try {
            URI internalDocUri = this.getDocumentInternalUri(newDocUri);
            File docDir = new File(internalDocUri);
            FileUtil.createDirIfNotExists(docDir);
            File file = new File(
                    new StringBuilder(docDir.getAbsolutePath()).append("/index.").append(fileExtension).toString());
            FileOutputStream out = new FileOutputStream(file);
            FileUtil.createDirIfNotExists(file.getParentFile());
            int size = IOUtils.copy(docRemoteLocation.toURL().openStream(), out);
            log.info("Document internal URI - " + internalDocUri.toASCIIString() + " - " + size + " bytes.");
            storeMediaType(file.getAbsolutePath(), mediaType);
            if (fileExtension.equals("json")) {
                storeSwaggerApiDeclarations(file, docDir.getAbsolutePath(), docRemoteLocation, mediaType);
            }
        } catch (IOException e) {
            throw new DocumentException("Unable to add document to service.", e);
        }

        // Generate Event
        this.getEventBus().post(new DocumentCreatedEvent(new Date(), newDocUri));

        return newDocUri;
    }

    private void storeSwaggerApiDeclarations(File rootDoc, String absolutePath, URI docRemoteLocation,
            String mediaType) {
        try {
            JsonElement jelement = new JsonParser().parse(new InputStreamReader(new FileInputStream(rootDoc)));
            JsonObject root = jelement.getAsJsonObject();
            JsonArray apis = root.getAsJsonArray("apis");
            for (JsonElement api : apis) {
                JsonObject apiDeclaration = api.getAsJsonObject();
                String path = apiDeclaration.get("path").toString().replaceAll("\"", "");
                String[] dirs = path.split("/");
                String parentPath = absolutePath;
                for (String dir : dirs) {
                    if (!dir.equals("")) {
                        try {
                            File localDir = new File(
                                    new StringBuilder(parentPath).append("/").append(dir).toString());
                            FileUtil.createDirIfNotExists(localDir);
                            parentPath = localDir.getAbsolutePath();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                File file = new File(new StringBuilder(parentPath).append("/index.json").toString());
                try {
                    URI apiDeclarationLocation = new URI(
                            new StringBuilder(docRemoteLocation.toString()).append(path).toString());
                    FileOutputStream out = new FileOutputStream(file);
                    int size = IOUtils.copy(apiDeclarationLocation.toURL().openStream(), out);
                    log.info("Document internal URI - " + file.getAbsolutePath() + " - " + size + " bytes.");
                    storeMediaType(file.getAbsolutePath(), mediaType);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (URISyntaxException e) {
                    e.printStackTrace();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

    }

    /* (non-Javadoc)
     * @see uk.ac.open.kmi.iserve.sal.manager.DocumentManager#deleteDocument(java.net.URI)
     */
    @Override
    public boolean deleteDocument(URI documentUri) throws DocumentException {
        // delete from hard disk
        URI fileUri = this.getDocumentInternalUri(documentUri);
        File file = new File(fileUri);
        if (file.exists()) {
            try {
                FileUtils.deleteDirectory(file);
                log.info("File deleted: " + file.getName());
                //delete entry in mediatype map
                Map<String, String> mediaTypeMap = getFileMediaTypeMap();
                Set<String> filePaths = new HashSet<String>(mediaTypeMap.keySet());
                for (String filePath : filePaths) {
                    if (filePath.matches(new StringBuilder(file.getAbsolutePath()).append(".*").toString())) {
                        mediaTypeMap.remove(filePath);
                    }
                }
                storeMediaTypeMap(mediaTypeMap);
                // Generate Event
                this.getEventBus().post(new DocumentDeletedEvent(new Date(), documentUri));
                return true;
            } catch (IOException e) {
                log.warn("File does not exist. Unable to delete it: " + file.getAbsolutePath());
                e.printStackTrace();

            }

        }
        return false;
    }

    /**
     * Deletes all the documents in iServe
     *
     * @return
     * @throws uk.ac.open.kmi.iserve.sal.exception.DocumentException
     */
    @Override
    public boolean clearDocuments() throws DocumentException {
        File mapFile = new File(URI.create(
                new StringBuilder(documentsInternalPath.toString()).append("/mediaTypeMap.json").toString()));
        mapFile.delete();
        File internalFolder = new File(this.getDocumentsInternalPath());
        File[] files = internalFolder.listFiles();
        for (File file : files) {
            try {
                FileUtils.deleteDirectory(file);
            } catch (IOException e) {
                e.printStackTrace();
                throw new DocumentException("Unable to delete " + file.getAbsolutePath());
            }
            log.info("File deleted: {}", file.getAbsolutePath());
        }

        // Generate Event
        this.getEventBus().post(new DocumentsClearedEvent(new Date()));

        return true;
    }

    /* (non-Javadoc)
     * @see uk.ac.open.kmi.iserve.sal.manager.DocumentManager#documentExists(java.net.URI)
     */
    @Override
    public boolean documentExists(URI documentUri) throws DocumentException {
        URI fileUri = getDocumentInternalUri(documentUri);
        File file = new File(fileUri);
        return file.exists();
    }

    private URI generateUniqueDocumentUri() {
        String uid = UriUtil.generateUniqueId();
        return documentsPublicUri.resolve(uid);
    }

    /**
     * This method will be called when the server is being shutdown.
     * Ensure a clean shutdown.
     */
    @Override
    public void shutdown() {
        return;
    }

}