org.codice.ddf.landing.LandingPage.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.landing.LandingPage.java

Source

/**
 * Copyright (c) Codice Foundation
 * <p>
 * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or any later version.
 * <p>
 * This program 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
 * Lesser General Public License for more details. A copy of the GNU Lesser General Public License
 * is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.landing;

import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;

import org.apache.commons.lang.StringUtils;
import org.apache.felix.webconsole.BrandingPlugin;
import org.codice.ddf.branding.BrandingResourceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.jknack.handlebars.Context;
import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Template;
import com.github.jknack.handlebars.context.FieldValueResolver;
import com.github.jknack.handlebars.context.MapValueResolver;
import com.github.jknack.handlebars.io.ClassPathTemplateLoader;
import com.github.jknack.handlebars.io.TemplateLoader;

/**
 * A simple HttpServlet to serve up a landing page that displays deployment-specific information such as
 * data source availability and announcements. Announcements are implemented as configurable properties.
 * The Title and Version are retrieved via the {@link BrandingPlugin}. Additionally, a phone number, email address,
 * external website, and description are configurable properties.
 */
public class LandingPage extends HttpServlet {
    private static final long serialVersionUID = 1L;

    private transient BrandingPlugin branding;

    private String title;

    private String version;

    private String description;

    private String phone;

    private String email;

    private String externalUrl;

    private List<String> announcements;

    private static final String LANDING_PAGE_FILE = "index";

    private static final String NO_DATE = "No date specified";

    private static final List<String> ACCEPTED_PATHS = Arrays.asList("/index.html", "/index.htm", "home.html",
            "home.htm", "landing.html", "landing.htm", "/", "");

    private static final Logger LOGGER = LoggerFactory.getLogger(LandingPage.class);

    private String productImage;

    private String favicon;

    private String background;

    private String foreground;

    private String logo;

    private String logoToUse;

    private transient BrandingResourceProvider provider;

    public String getTitle() {
        return title;
    }

    public String getVersion() {
        return version;
    }

    public String getProductImage() {
        return productImage;
    }

    public String getFavicon() {
        return favicon;
    }

    public List<String> getAnnouncements() {
        return announcements;
    }

    public LandingPage(BrandingResourceProvider provider) {
        this.provider = provider;
    }

    private void setVersion() {
        if (branding != null) {
            version = branding.getProductName();
        }
    }

    private void setTitle() {
        if (StringUtils.isNotBlank(version)) {
            // The Product name looks like: "Product 1.0.0.ALPHA12-SNAPSHOT"
            // we want to remove the "version" from the general title.
            String[] productNameParts = version.split(" ");
            productNameParts[productNameParts.length - 1] = "";
            title = StringUtils.join(productNameParts, " ").trim();
        } else {
            title = "DDF";
        }
    }

    public void setBranding(BrandingPlugin branding) throws IOException {
        this.branding = branding;
        setVersion();
        setTitle();
        this.productImage = getBase64(branding.getProductImage());
        this.favicon = getBase64(branding.getFavIcon());
    }

    private String getBase64(String productImage) throws IOException {
        byte[] resourceAsBytes = provider.getResourceAsBytes(productImage);
        if (resourceAsBytes.length > 0) {
            return Base64.getEncoder().encodeToString(resourceAsBytes);
        }
        return "";
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setExternalUrl(String externalUrl) {
        this.externalUrl = externalUrl;
    }

    // Should this be included as a workaround for how the admin UI handles metatype lists?
    public void setAnnouncements(String announcements) {
        setAnnouncements(Arrays.asList(announcements.split(",")));
    }

    public void setAnnouncements(List<String> announcements) {
        this.announcements = announcements;
        sortAnnouncementsByDate();
    }

    public void setBackground(String background) {
        this.background = background;
    }

    public void setForeground(String foreground) {
        this.foreground = foreground;
    }

    public void setLogo(String logo) {
        this.logo = logo;
    }

    private void sortAnnouncementsByDate() {
        Collections.sort(announcements, new Comparator<String>() {
            @Override
            public int compare(String firstAnnouncement, String secondAnnouncement) {
                // Try to extract the dates from the announcements.
                String firstDateString = extractDate(firstAnnouncement, false);
                String secondDateString = extractDate(secondAnnouncement, false);

                final Date firstDate = dateFromString(firstDateString);
                final Date secondDate = dateFromString(secondDateString);
                // Sort the dates in descending order (most recent first).
                return secondDate.compareTo(firstDate);
            }
        });
    }

    private Date dateFromString(String dateString) {
        final DateFormat format = new SimpleDateFormat("MM/dd/yy");
        Date date;
        try {
            date = format.parse(dateString);
        } catch (ParseException e) {
            date = new Date(0L);
        }
        return date;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_HTML);
        if (ACCEPTED_PATHS.contains(req.getRequestURI())) {
            resp.getWriter().write(compileTemplateWithProperties());
        }
    }

    // package-private for unit testing
    String compileTemplateWithProperties() {
        logoToUse = StringUtils.isNotEmpty(logo) ? logo : productImage;
        // FieldValueResolver so this class' fields can be accessed in the template.
        // MapValueResolver so we can access {{@index}} in the #each helper in the template.
        final Context context = Context.newBuilder(this)
                .resolver(FieldValueResolver.INSTANCE, MapValueResolver.INSTANCE).build();
        // The template is "index.handlebars".
        final TemplateLoader templateLoader = new ClassPathTemplateLoader("/", ".handlebars");
        final Handlebars handlebars = new Handlebars(templateLoader);
        // extractDate(), extractAnnouncement(), expanded(), and in() are helper functions used in the template.
        handlebars.registerHelpers(this);
        String landingPageHtml;
        try {
            final Template template = handlebars.compile(LANDING_PAGE_FILE);
            landingPageHtml = template.apply(context);
        } catch (IOException e) {
            LOGGER.info("Unable to compile template.", e);
            landingPageHtml = "<p>We are experiencing some issues. Please contact an administrator.</p>";
        }
        return landingPageHtml;
    }

    // A helper function used in the Handlebars template (index.handlebars). Also used locally.
    public String extractDate(String announcement, boolean reformat) {
        announcement = announcement.trim();

        // Regular expression to (loosely) match a date at the beginning of the string (we don't require leading 0s
        // for the month and day).
        // We expect the date to be at the beginning of the string and be in the format MM/dd/yy (where the month and
        // day can have either 1 or 2 digits).
        final String pattern = "^\\d{1,2}/\\d{1,2}/\\d\\d";
        final Pattern regex = Pattern.compile(pattern);
        final Matcher matcher = regex.matcher(announcement);

        if (matcher.find()) {
            final String matchedText = matcher.group();
            if (reformat) { // Return the date in a nice-looking format. Used for displaying the dates on the Web page.
                final Date date = dateFromString(matchedText);
                final SimpleDateFormat format = new SimpleDateFormat("MMMM dd, yyyy");
                return format.format(date);
            } else { // Just return the date as it was in the announcement.
                return matchedText;
            }
        }

        return NO_DATE;
    }

    // A helper function used in the Handlebars template (index.handlebars).
    public String extractAnnouncement(String announcement) {
        // Try and grab the date from the beginning of the announcement.
        final String date = extractDate(announcement, false);
        if (!date.equals(NO_DATE)) { // There is a valid date, so we need to exclude it from what we display (it's not
            try { // part of the announcement).
                // Ignore the date part of the announcement.
                announcement = announcement.substring(announcement.indexOf(date) + date.length());
            } catch (IndexOutOfBoundsException e) {
                // Do nothing. The announcement is empty.
            }
        }

        return announcement.trim();
    }

    // A helper function used in the Handlebars template (index.handlebars).
    public String expanded(int index) {
        return index == 0 ? "true" : "false";
    }

    // A helper function used in the Handlebars template (index.handlebars).
    public String in(int index) {
        return index == 0 ? "in" : "";
    }

    // A helper function used in the Handlebars template (index.handlebars).
    public String noAnnouncements(List<String> announcements) {
        if (announcements.size() == 0) {
            return "No announcements";
        } else {
            return "";
        }
    }
}