/*
Copyright (C) 2016 maik.jablonski@jease.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package jease.cms.web.servlet;
import java.io.IOException;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import javax.servlet.DispatcherType;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jease.Names;
import jease.Registry;
import jease.cmf.domain.Node;
import jease.cmf.service.Nodes;
import jease.cms.domain.Redirect;
import jfix.db4o.Database;
import jfix.servlet.ResponseRewriter;
import jfix.servlet.Servlets;
import org.apache.commons.lang3.StringUtils;
@WebFilter(urlPatterns = { "/*" }, dispatcherTypes = { DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.INCLUDE, DispatcherType.ERROR })
public class JeaseController implements javax.servlet.Filter {
private static Supplier<List<Redirect>> redirectSupplier = () -> {
List<Redirect> redirects = Database.query(Redirect.class);
redirects.sort(Comparator.comparing(Redirect::getTimestamp));
return redirects;
};
private static Supplier<Function<String, String>> rewriterSupplier = () -> {
try {
String jeaseSiteRewriter = Registry.getParameter(Names.JEASE_SITE_REWRITER);
return (Function<String, String>) Class.forName(jeaseSiteRewriter).newInstance();
} catch (ReflectiveOperationException e) {
e.printStackTrace();
return null;
}
};
private static String contextPath;
private static String dispatcher;
private static Pattern servlets;
private static Set<String> locales;
public static String getContextPath() {
return contextPath;
}
public void init(FilterConfig config) throws ServletException {
contextPath = config.getServletContext().getContextPath();
dispatcher = config.getServletContext().getInitParameter(Names.JEASE_SITE_DISPATCHER);
servlets = Pattern.compile(String.format("/(%s).*", config.getServletContext().getInitParameter(Names.JEASE_SITE_SERVLETS)));
locales = new HashSet<>();
for (Locale locale : Locale.getAvailableLocales()) {
locales.add(locale.getLanguage());
}
}
public void destroy() {
}
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
String uri = request.getRequestURI();
// Strip jsessionid from URI.
int jsessionidIndex = uri.indexOf(";jsessionid=");
if (jsessionidIndex != -1) {
uri = uri.substring(0, jsessionidIndex);
}
// Process internal context prefix
int tilde = uri.indexOf("~");
if (tilde != -1) {
String path = uri.substring(tilde + 1);
response.sendRedirect(response.encodeRedirectURL(buildURI(
request.getContextPath() + path, request.getQueryString())));
return;
}
// Try to resolve node from URI (without context path).
String nodePath = uri.substring(request.getContextPath().length());
Node node = Nodes.getByPath(nodePath);
// Process redirect rules
if (node == null && !servlets.matcher(nodePath).matches()) {
String sourceURI = buildURI(nodePath, request.getQueryString());
String targetURI = rewriteURI(sourceURI);
if (!targetURI.equals(sourceURI)) {
if (targetURI.contains("://")) {
response.sendRedirect(response.encodeRedirectURL(targetURI));
} else {
response.sendRedirect(response.encodeRedirectURL(request
.getContextPath() + targetURI));
}
return;
}
}
// Save "virtual" root node. Per default it is the absolute root of the
// instance.
// If a node with the server name exists, this node is used as virtual
// root.
if (request.getAttribute("Root") == null) {
String server = Servlets.getServerName(request).replaceFirst(
"www.", "");
Node root = Nodes.getRoot() != null ? Nodes.getRoot().getChild(
server) : null;
if (root == null) {
root = Nodes.getRoot();
}
if (node != null) {
if (node.getParent() == root && locales.contains(node.getId())) {
root = node;
} else {
for (Node parent : node.getParents()) {
if (parent.getParent() == root
&& locales.contains(parent.getId())) {
root = parent;
break;
}
}
}
}
request.setAttribute("Root", root);
if (node != null && root.getParent() == node) {
response.sendRedirect(response.encodeRedirectURL(request
.getContextPath() + root.getPath()));
return;
}
}
// If no node is found, process filter chain.
if (node == null) {
chain.doFilter(request, response);
return;
}
// Redirect if trailing slash is missing for containers.
if (node.isContainer() && !uri.endsWith("/")) {
response.sendRedirect(response.encodeRedirectURL(buildURI(uri + "/", request.getQueryString())));
} else {
// Set node into request scope and forward to dispatcher
request.setAttribute(Node.class.getSimpleName(), node);
request.setAttribute(Names.JEASE_SITE_DISPATCHER, dispatcher);
Function<String, String> rewriter = StringUtils.isNotBlank(Registry
.getParameter(Names.JEASE_SITE_REWRITER)) ? Database
.query(rewriterSupplier) : null;
request.getRequestDispatcher(dispatcher).forward(
request,
rewriter != null ? new ResponseRewriter(response, rewriter)
: response);
}
}
private String buildURI(String uri, String query) {
if (query != null) {
return String.format("%s%s%s", uri, uri.contains("?") ? "&" : "?",
query);
} else {
return uri;
}
}
private String rewriteURI(String uri) {
for (Redirect redirect : Database.query(redirectSupplier)) {
String output = redirect.transform(uri);
if (!output.equals(uri)) {
return output;
}
}
return uri;
}
}