Package io.conducive.server.resources

Source Code of io.conducive.server.resources.ContentResource

package io.conducive.server.resources;

import com.google.common.collect.Lists;
import com.google.common.io.Files;
import org.markdown4j.Markdown4jProcessor;

import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.*;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Serves templated html (or markdown) from an arbitrary file system location. Intended for simple
* text & images. (Also supports CSS, PDF).
*
* Intended to allow content to be separately managed from the application; it's common for clients to
* want to be able to edit content.
*
* @author Reuben Firmin
*/
@Path("/content")
public class ContentResource {

    private final static Pattern tokenPattern = Pattern.compile("\\{\\{([^}]+)\\}\\}");
    private final static String PNG = "image/png";
    private final static String JPG = "image/jpg";
    private final static String GIF = "image/gif";
    private final static String CSS = "text/css";
    private final static String PDF = "application/pdf";
    private final File contentRoot;

    @Inject
    public ContentResource(@Named("contentRoot") String contentRoot) {
        this.contentRoot = new File(contentRoot);
        if (!this.contentRoot.exists()) {
            throw new RuntimeException(contentRoot + " doesn't exist");
        }
    }

    @GET
    @Path("{resource}")
    @Produces({MediaType.TEXT_HTML, JPG, PNG, GIF, CSS, PDF})
    public Response getResource(@Context UriInfo uriInfo, @PathParam("resource") String resource) {
        File file;
        String name = resource.endsWith("/") ? resource.substring(0, resource.length() - 1) : resource;
        if (name.contains("/")) {
            String[] path = name.split("/");
            StringBuilder br = new StringBuilder().append(contentRoot).append("/");
            for (int i = 0; i < path.length - 1; i++) {
                br.append(path[i]).append("/");
            }
            file = new File(br.toString(), path[path.length - 1]);
        } else {
            file = new File(contentRoot, name);
        }
        return returnFile(uriInfo, file);
    }

    private Response returnFile(UriInfo uriInfo, File file) {
        if (!file.exists()) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }

        // make sure content root is in hierarchy
        {
            File walk = file;
            while ((walk = walk.getParentFile()) != null) {
                if (walk.getAbsolutePath().equals(contentRoot.getAbsolutePath())) {
                    break;
                }
            }
            // we hit the root of the filesystem
            if (walk == null) {
                return Response.status(Response.Status.BAD_REQUEST).entity("Bad path").build();
            }
        }

        try {
            Date fileDate = new Date(file.lastModified());
            String type;
            switch (Files.getFileExtension(file.getName())) {
                case "md":
                    return parseMarkdown(uriInfo, file);
                case "htm":
                case "html":
                    return parseHtml(uriInfo, file);
                case "png":
                    type = PNG;
                    break;
                case "jpg":
                    type = JPG;
                    break;
                case "gif":
                    type = GIF;
                    break;
                case "css":
                    type = CSS;
                    break;
                case "pdf":
                    type = PDF;
                    break;
                // no js, this is a simple content server.
                default:
                    return Response.status(Response.Status.BAD_REQUEST).entity("Unknown file type").build();
            }

            return Response.ok(new FileInputStream(file)).lastModified(fileDate).type(type).build();
        } catch (IOException e) {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
    }

    /**
     * Replaces resource tokens in the file with absolute url to this api.
     * @param uriInfo
     * @param file
     * @return
     * @throws IOException
     */
    private Response parseHtml(UriInfo uriInfo, File file) throws IOException {
        return Response.ok().entity(
                parseHtml(uriInfo, Files.readLines(file, Charset.defaultCharset())))
                .type(MediaType.TEXT_HTML_TYPE)
                .build();
    }

    /**
     * Converts markdown to HTML, and then parses resource tokens from the HTML.
     * @param uriInfo
     * @param file
     * @return
     * @throws IOException
     */
    private Response parseMarkdown(UriInfo uriInfo, File file) throws IOException {
        return Response.ok().entity(
                parseHtml(uriInfo, toList(new Markdown4jProcessor().process(file))))
                .type(MediaType.TEXT_HTML_TYPE)
                .build();
    }

    private List<String> toList(String string) {
        return Lists.newArrayList(string.split("\\n"));
    }

    private String parseHtml(UriInfo uriInfo, List<String> input) {
        String baseUri = uriInfo.getBaseUri().toString() + uriInfo.getPathSegments().get(0).getPath() + "/";
        StringBuilder output = new StringBuilder();
        for (String line : input) {
            int cursor = 0;
            Matcher tokenMatcher = tokenPattern.matcher(line);

            while (tokenMatcher.find()) {
                int tokenStart = tokenMatcher.start();
                int tokenEnd = tokenMatcher.end();
                int keyStart = tokenMatcher.start(1);
                int keyEnd = tokenMatcher.end(1);
                output.append(line.substring(cursor, tokenStart));

                String key = line.substring(keyStart, keyEnd);
                // "encode" path
                key = key.replace("/", "%2F");
                output.append(baseUri).append(key);
                cursor = tokenEnd;
            }
            output.append(line.substring(cursor)).append("\n");
        }
        return output.toString();
    }

}
TOP

Related Classes of io.conducive.server.resources.ContentResource

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.