io.personium.core.rs.cell.LogResource.java Source code

Java tutorial

Introduction

Here is the source code for io.personium.core.rs.cell.LogResource.java

Source

/**
 * personium.io
 * Copyright 2014 FUJITSU LIMITED
 *
 * 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 io.personium.core.rs.cell;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.util.Date;
import java.util.List;

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
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.Response;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.core.StreamingOutput;
import javax.ws.rs.core.UriInfo;

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpStatus;
import org.apache.wink.webdav.WebDAVMethod.PROPFIND;
import org.apache.wink.webdav.model.Creationdate;
import org.apache.wink.webdav.model.Getcontentlength;
import org.apache.wink.webdav.model.Getcontenttype;
import org.apache.wink.webdav.model.Getlastmodified;
import org.apache.wink.webdav.model.Multistatus;
import org.apache.wink.webdav.model.ObjectFactory;
import org.apache.wink.webdav.model.Propfind;
import org.apache.wink.webdav.model.Resourcetype;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.personium.common.utils.PersoniumCoreUtils;
import io.personium.core.PersoniumCoreException;
import io.personium.core.auth.AccessContext;
import io.personium.core.auth.CellPrivilege;
import io.personium.core.event.EventUtils;
import io.personium.core.eventlog.ArchiveLogCollection;
import io.personium.core.eventlog.ArchiveLogFile;
import io.personium.core.model.Cell;
import io.personium.core.model.DavRsCmp;
import io.personium.core.utils.ResourceUtils;

/**
 * JAX-RS Resource.
 */
public class LogResource {

    /** archive??. */
    public static final String ARCHIVE_COLLECTION = "archive";
    /** current??. */
    public static final String CURRENT_COLLECTION = "current";

    private static final String DEFAULT_LOG = "default.log";

    Cell cell;
    AccessContext accessContext;
    DavRsCmp davRsCmp;

    static Logger log = LoggerFactory.getLogger(LogResource.class);

    /**
     * constructor.
     * @param cell Cell
     * @param accessContext AccessContext
     * @param davRsCmp DavRsCmp
     */
    public LogResource(final Cell cell, final AccessContext accessContext, final DavRsCmp davRsCmp) {
        this.accessContext = accessContext;
        this.cell = cell;
        this.davRsCmp = davRsCmp;
    }

    /**
     * ????.
     * @return JAX-RS Response Object
     */
    @Path(CURRENT_COLLECTION)
    @PROPFIND
    public final Response currentPropfind() {
        // ?????????501??
        throw PersoniumCoreException.Misc.METHOD_NOT_IMPLEMENTED;
    }

    /**
     * ????.
     * @param requestBodyXml Request Body
     * @param uriInfo URL
     * @param contentLength contentlength?
     * @param transferEncoding Transfer-Encoding?
     * @param depth Depth?
     * @return JAX-RS Response Object
     */
    @Path(ARCHIVE_COLLECTION)
    @PROPFIND
    public final Response archivePropfind(final Reader requestBodyXml, @Context UriInfo uriInfo,
            @HeaderParam(HttpHeaders.CONTENT_LENGTH) final Long contentLength,
            @HeaderParam("Transfer-Encoding") final String transferEncoding,
            @HeaderParam(PersoniumCoreUtils.HttpHeaders.DEPTH) final String depth) {

        // 
        this.davRsCmp.checkAccessContext(this.davRsCmp.getAccessContext(), CellPrivilege.LOG_READ);

        // Depth??? 0, 1
        // infinity?????????403??
        if ("infinity".equals(depth)) {
            throw PersoniumCoreException.Dav.PROPFIND_FINITE_DEPTH;
        } else if (depth == null) {
            throw PersoniumCoreException.Dav.INVALID_DEPTH_HEADER.params("null");
        } else if (!("0".equals(depth) || "1".equals(depth))) {
            throw PersoniumCoreException.Dav.INVALID_DEPTH_HEADER.params(depth);
        }

        // ?? pf??
        // ????allprop?????????
        Propfind propfind = null;
        if (ResourceUtils.hasApparentlyRequestBody(contentLength, transferEncoding)) {
            BufferedReader br = null;
            try {
                br = new BufferedReader(requestBodyXml);
                propfind = Propfind.unmarshal(br);
            } catch (Exception e) {
                throw PersoniumCoreException.Dav.XML_ERROR.reason(e);
            }
        }
        if (null != propfind && !propfind.isAllprop()) {
            throw PersoniumCoreException.Dav.XML_CONTENT_ERROR;
        }

        // archive??????
        ArchiveLogCollection archiveLogCollection = new ArchiveLogCollection(this.cell, uriInfo);
        archiveLogCollection.createFileInformation();

        // ??
        final Multistatus multiStatus = createMultiStatus(depth, archiveLogCollection);
        StreamingOutput str = new StreamingOutput() {
            @Override
            public void write(final OutputStream os) throws IOException, WebApplicationException {
                Multistatus.marshal(multiStatus, os);
            }
        };
        return Response.status(HttpStatus.SC_MULTI_STATUS).entity(str)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_XML).build();
    }

    private Multistatus createMultiStatus(final String depth, ArchiveLogCollection archiveLogCollection) {
        ObjectFactory of = new ObjectFactory();
        final Multistatus multiStatus = of.createMultistatus();
        List<org.apache.wink.webdav.model.Response> responseList = multiStatus.getResponse();

        // Archive????
        org.apache.wink.webdav.model.Response collectionResponse = this.createPropfindResponse(
                archiveLogCollection.getCreated(), archiveLogCollection.getUpdated(), archiveLogCollection.getUrl(),
                null);
        responseList.add(collectionResponse);

        // Depth?1????????
        if ("1".equals(depth)) {
            for (ArchiveLogFile archiveFile : archiveLogCollection.getArchivefileList()) {
                org.apache.wink.webdav.model.Response fileResponse = this.createPropfindResponse(
                        archiveFile.getCreated(), archiveFile.getUpdated(), archiveFile.getUrl(),
                        archiveFile.getSize());
                responseList.add(fileResponse);
            }
        }

        return multiStatus;
    }

    /**
     * PROPFIND????.
     */
    org.apache.wink.webdav.model.Response createPropfindResponse(long created, long updated, String href,
            Long size) {
        // href
        ObjectFactory of = new ObjectFactory();
        org.apache.wink.webdav.model.Response ret = of.createResponse();
        ret.getHref().add(href);

        // creationdate
        Creationdate cd = of.createCreationdate();
        cd.setValue(new Date(created));
        ret.setPropertyOk(cd);

        // getlastmodified
        Getlastmodified lm = of.createGetlastmodified();
        lm.setValue(new Date(updated));
        ret.setPropertyOk(lm);

        if (size != null) {
            // ??
            // getcontentlength
            Getcontentlength contentLength = of.createGetcontentlength();
            contentLength.setValue(String.valueOf(size));
            ret.setPropertyOk(contentLength);

            // getcontenttype?"text/csv"?
            Getcontenttype contentType = of.createGetcontenttype();
            contentType.setValue(EventUtils.TEXT_CSV);
            ret.setPropertyOk(contentType);

            // ?resourcetype
            Resourcetype colRt = of.createResourcetype();
            ret.setPropertyOk(colRt);
        } else {
            // ??
            // resourcetype?WebDav?
            Resourcetype colRt = of.createResourcetype();
            colRt.setCollection(of.createCollection());
            ret.setPropertyOk(colRt);
        }

        return ret;
    }

    /**
     * ??.
     * @param ifNoneMatch If-None-Match
     * @param logCollection Collection??
     * @param fileName fileName
     * @return JAXRS Response
     */
    @Path("{logCollection}/{filename}")
    @GET
    public final Response getLogFile(@HeaderParam(HttpHeaders.IF_NONE_MATCH) final String ifNoneMatch,
            @PathParam("logCollection") final String logCollection, @PathParam("filename") final String fileName) {

        // 
        this.davRsCmp.checkAccessContext(this.davRsCmp.getAccessContext(), CellPrivilege.LOG_READ);

        // ?Collection????
        if (!isValidLogCollection(logCollection)) {
            throw PersoniumCoreException.Dav.RESOURCE_NOT_FOUND;
        }

        // ???default.log???404?
        if (!isValidLogFile(logCollection, fileName)) {
            throw PersoniumCoreException.Dav.RESOURCE_NOT_FOUND;
        }

        String cellId = davRsCmp.getCell().getId();
        String owner = davRsCmp.getCell().getOwner();

        // ??
        StringBuilder logFileName = EventUtils.getEventLogDir(cellId, owner);
        logFileName.append(logCollection);
        logFileName.append(File.separator);
        logFileName.append(fileName);
        return getLog(logCollection, logFileName.toString());
    }

    private Response getLog(final String logCollection, String logFileName) {
        if (CURRENT_COLLECTION.equals(logCollection)) {
            File logFile = new File(logFileName);
            if (!logFile.isFile() || !logFile.canRead()) {
                // ????????????????SC_OK?
                return getEmptyResponse();
            }
            try {
                final InputStream isInvariable = new FileInputStream(logFile);
                return createResponse(isInvariable);
            } catch (FileNotFoundException e) {
                throw PersoniumCoreException.Dav.RESOURCE_NOT_FOUND;
            }
        } else {
            ZipArchiveInputStream zipArchiveInputStream = null;
            BufferedInputStream bis = null;
            String archiveLogFileName = logFileName + ".zip";

            try {
                log.info("EventLog file path : " + archiveLogFileName);
                zipArchiveInputStream = new ZipArchiveInputStream(new FileInputStream(archiveLogFileName));
                bis = new BufferedInputStream(zipArchiveInputStream);

                // ?entry??
                // ??1???????????????
                ZipArchiveEntry nextZipEntry = zipArchiveInputStream.getNextZipEntry();
                if (nextZipEntry == null) {
                    IOUtils.closeQuietly(bis);
                    throw PersoniumCoreException.Event.ARCHIVE_FILE_CANNOT_OPEN;
                }
                return createResponse(bis);
            } catch (FileNotFoundException e1) {
                // ??????404?
                throw PersoniumCoreException.Dav.RESOURCE_NOT_FOUND;
            } catch (IOException e) {
                log.info("Failed to read archive entry : " + e.getMessage());
                throw PersoniumCoreException.Event.ARCHIVE_FILE_CANNOT_OPEN;
            }
        }
    }

    private Response createResponse(final InputStream isInvariable) {
        // 
        ResponseBuilder res = Response.status(HttpStatus.SC_OK);
        res.header(HttpHeaders.CONTENT_TYPE, EventUtils.TEXT_CSV);
        res.entity(isInvariable);
        return res.build();
    }

    /**
     * ???????????.
     * @return ?
     */
    private Response getEmptyResponse() {
        // ???
        ResponseBuilder res = Response.status(HttpStatus.SC_OK);
        res.header(HttpHeaders.CONTENT_TYPE, EventUtils.TEXT_CSV);

        res.entity("");
        log.debug("main thread end.");
        return res.build();
    }

    /**
     * .
     * @return ?
     */
    @Path("{logCollection}/{filename}")
    @DELETE
    public final Response deleteLogFile() {
        throw PersoniumCoreException.Misc.METHOD_NOT_IMPLEMENTED;
    }

    /**
     * ?Collection???.
     * @param collectionName Collection?? ( "current" or "archive" )
     * @return true: ???false: 
     */
    protected boolean isValidLogCollection(String collectionName) {
        return CURRENT_COLLECTION.equals(collectionName) || ARCHIVE_COLLECTION.equals(collectionName);
    }

    /**
     * ????.
     * <ul>
     * <li>current: "default.log" 
     * <li>archive: "default.log." ?????????404????????????)
     * </ul>
     * @param collectionName Collection?? ( "current" or "archive" )
     * @param fileName ?? ( "default.log" or "default.log.*" )
     * @return true: ???false: 
     */
    protected boolean isValidLogFile(String collectionName, String fileName) {
        if (CURRENT_COLLECTION.equals(collectionName)) {
            return DEFAULT_LOG.equals(fileName);
        } else { // ?????
            return fileName != null && fileName.startsWith(DEFAULT_LOG + ".");
        }
    }
}