org.haiku.haikudepotserver.support.web.FallbackController.java Source code

Java tutorial

Introduction

Here is the source code for org.haiku.haikudepotserver.support.web.FallbackController.java

Source

/*
 * Copyright 2018, Andrew Lindesay
 * Distributed under the terms of the MIT License.
 */

package org.haiku.haikudepotserver.support.web;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import com.google.common.net.HttpHeaders;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.configuration.server.ServerRuntime;
import org.haiku.haikudepotserver.dataobjects.Pkg;
import org.haiku.haikudepotserver.dataobjects.PkgVersion;
import org.haiku.haikudepotserver.dataobjects.Repository;
import org.haiku.haikudepotserver.pkg.model.PkgService;
import org.haiku.haikudepotserver.repository.model.RepositoryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.Optional;

/**
 * <p>If somebody makes a query such as "/apr" then the system should search for that as a package
 * and navigate to the user interface for it.</p>
 */

@Controller
public class FallbackController {

    private enum FallbackType {
        FAVICON, APPLETOUCH, PKG
    }

    protected static Logger LOGGER = LoggerFactory.getLogger(FallbackController.class);

    private final static String KEY_TERM = "term";

    private final static String VALUE_FAVICON = "favicon";

    private final ServerRuntime serverRuntime;
    private final PkgService pkgService;
    private final RepositoryService repositoryService;
    private final String baseUrl;

    @Autowired(required = false)
    private ServletContext servletContext;

    public FallbackController(ServerRuntime serverRuntime, PkgService pkgService,
            RepositoryService repositoryService, @Value("${baseurl}") String baseUrl) {
        this.serverRuntime = serverRuntime;
        this.pkgService = pkgService;
        this.repositoryService = repositoryService;
        this.baseUrl = baseUrl;
    }

    private String termDebug(String term) {
        if (term.length() > 64) {
            return term.substring(0, 64) + "...";
        }

        return term;
    }

    private void redirectToPkg(HttpServletResponse response, String term) throws IOException {
        ObjectContext context = serverRuntime.newContext();

        Optional<PkgVersion> pkgVersionOptional = tryGetPkgVersion(context, term);

        if (pkgVersionOptional.isPresent()) {
            UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUrl).pathSegment("#!", "pkg");
            pkgVersionOptional.get().appendPathSegments(builder);
            UriComponents uriComponents = builder.build();

            response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
            response.setHeader(HttpHeaders.LOCATION, uriComponents.toUriString());

            PrintWriter w = response.getWriter();
            w.format("redirecting to; %s", uriComponents.toUriString());
            w.flush();

            LOGGER.info("did redirect to a package for; {}", term);
        } else {
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);

            PrintWriter w = response.getWriter();
            w.format("unable to find an entity for; %s", termDebug(term));
            w.flush();

            LOGGER.info("did not find a package for; {}", termDebug(term));
        }
    }

    private void streamFavicon(RequestMethod method, HttpServletResponse response) throws IOException {
        Preconditions.checkState(null != servletContext, "the servlet context must be supplied");
        response.setContentType(com.google.common.net.MediaType.ICO.toString());

        try (InputStream inputStream = servletContext.getResourceAsStream("/img/favicon.ico")) {
            if (null == inputStream) {
                throw new IllegalStateException("unable to find the favicon resource");
            }

            if (method != RequestMethod.HEAD) {
                ByteStreams.copy(inputStream, response.getOutputStream());
            }
        }
    }

    private FallbackType getFallbackType(String term) {
        if (term.equals(VALUE_FAVICON)) {
            return FallbackType.FAVICON;
        }

        if (term.startsWith("apple-touch-icon")) {
            return FallbackType.APPLETOUCH;
        }

        return FallbackType.PKG;
    }

    private Optional<PkgVersion> tryGetPkgVersion(ObjectContext context, String term) {
        if (!Strings.isNullOrEmpty(term)) {
            Optional<Pkg> pkgOptional = Pkg.tryGetByName(context, term);

            if (pkgOptional.isPresent()) {
                List<Repository> repositories = repositoryService.getRepositoriesForPkg(context, pkgOptional.get());

                if (!repositories.isEmpty()) {
                    return pkgService.getLatestPkgVersionForPkg(context, pkgOptional.get(), repositories.get(0));
                }
            }
        }

        return Optional.empty();
    }

    @RequestMapping(value = "/{" + KEY_TERM + "}", method = { RequestMethod.GET, RequestMethod.HEAD })
    public void fallback(RequestMethod method, HttpServletResponse response,
            @PathVariable(value = KEY_TERM) String term) throws IOException {

        switch (getFallbackType(term)) {

        case APPLETOUCH:
            LOGGER.debug("unhandled apple touch icon -> 404; {}", term);
            response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            break;

        case FAVICON:
            streamFavicon(method, response);
            break;

        case PKG:
            redirectToPkg(response, term);
            break;

        default:
            LOGGER.error("unable to handle the fallback; {}", term);

        }

    }

}