Merged in feature/ui-for-production (pull request #70)

Feature/ui for production
This commit is contained in:
Philipp Czora
2018-09-12 08:36:19 +00:00
28 changed files with 546 additions and 250 deletions

View File

@@ -1,24 +0,0 @@
package sonia.scm;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* This dispatcher forwards every request to the index.html of the application.
*
* @since 2.0.0
*/
public class ForwardingPushStateDispatcher implements PushStateDispatcher {
@Override
public void dispatch(HttpServletRequest request, HttpServletResponse response, String uri) throws IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher("/index.html");
try {
dispatcher.forward(request, response);
} catch (ServletException e) {
throw new IOException("failed to forward request", e);
}
}
}

View File

@@ -3,12 +3,13 @@ package sonia.scm;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import javax.inject.Inject;
import javax.inject.Provider;
/**
* Injection Provider for the {@link PushStateDispatcher}. The provider will return a {@link ProxyPushStateDispatcher}
* if the system property {@code PushStateDispatcherProvider#PROPERTY_TARGET} is set to a proxy target url, otherwise
* a {@link ForwardingPushStateDispatcher} is used.
* a {@link TemplatingPushStateDispatcher} is used.
*
* @since 2.0.0
*/
@@ -17,11 +18,18 @@ public class PushStateDispatcherProvider implements Provider<PushStateDispatcher
@VisibleForTesting
static final String PROPERTY_TARGET = "sonia.scm.ui.proxy";
private Provider<TemplatingPushStateDispatcher> templatingPushStateDispatcherProvider;
@Inject
public PushStateDispatcherProvider(Provider<TemplatingPushStateDispatcher> templatingPushStateDispatcherProvider) {
this.templatingPushStateDispatcherProvider = templatingPushStateDispatcherProvider;
}
@Override
public PushStateDispatcher get() {
String target = System.getProperty(PROPERTY_TARGET);
if (Strings.isNullOrEmpty(target)) {
return new ForwardingPushStateDispatcher();
return templatingPushStateDispatcherProvider.get();
}
return new ProxyPushStateDispatcher(target);
}

View File

@@ -0,0 +1,61 @@
package sonia.scm;
import com.google.common.annotations.VisibleForTesting;
import sonia.scm.template.Template;
import sonia.scm.template.TemplateEngine;
import sonia.scm.template.TemplateEngineFactory;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
/**
* This dispatcher renders the /index.mustache template, which is merged in from the scm-ui package.
*
* @since 2.0.0
*/
public class TemplatingPushStateDispatcher implements PushStateDispatcher {
@VisibleForTesting
static final String TEMPLATE = "/index.mustache";
private final TemplateEngine templateEngine;
@Inject
public TemplatingPushStateDispatcher(TemplateEngineFactory templateEngineFactory) {
this(templateEngineFactory.getDefaultEngine());
}
@VisibleForTesting
TemplatingPushStateDispatcher(TemplateEngine templateEngine) {
this.templateEngine = templateEngine;
}
@Override
public void dispatch(HttpServletRequest request, HttpServletResponse response, String uri) throws IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
Template template = templateEngine.getTemplate(TEMPLATE);
try (Writer writer = response.getWriter()) {
template.execute(writer, new IndexHtmlModel(request));
}
}
@VisibleForTesting
static class IndexHtmlModel {
private final HttpServletRequest request;
private IndexHtmlModel(HttpServletRequest request) {
this.request = request;
}
public String getContextPath() {
return request.getContextPath();
}
}
}

View File

@@ -1,7 +1,7 @@
package sonia.scm;
import com.github.sdorra.webresources.WebResourceSender;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.Resources;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.filter.WebElement;
@@ -15,7 +15,6 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
/**
@@ -27,6 +26,7 @@ import java.net.URL;
@WebElement(value = WebResourceServlet.PATTERN, regex = true)
public class WebResourceServlet extends HttpServlet {
/**
* exclude api requests and the old frontend servlets.
*
@@ -37,6 +37,11 @@ public class WebResourceServlet extends HttpServlet {
private static final Logger LOG = LoggerFactory.getLogger(WebResourceServlet.class);
private final WebResourceSender sender = WebResourceSender.create()
.withGZIP()
.withGZIPMinLength(512)
.withBufferSize(16384);
private final UberWebResourceLoader webResourceLoader;
private final PushStateDispatcher pushStateDispatcher;
@@ -53,7 +58,7 @@ public class WebResourceServlet extends HttpServlet {
LOG.trace("try to load {}", uri);
URL url = webResourceLoader.getResource(uri);
if (url != null) {
serveResource(response, url);
serveResource(request, response, url);
} else {
dispatch(request, response, uri);
}
@@ -72,10 +77,9 @@ public class WebResourceServlet extends HttpServlet {
return HttpUtil.getStrippedURI(request);
}
private void serveResource(HttpServletResponse response, URL url) {
// TODO lastModifiedDate, if-... ???
try (OutputStream output = response.getOutputStream()) {
Resources.copy(url, output);
private void serveResource(HttpServletRequest request, HttpServletResponse response, URL url) {
try {
sender.resource(url).send(request, response);
} catch (IOException ex) {
LOG.warn("failed to serve resource: {}", url);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);

View File

@@ -31,18 +31,11 @@
package sonia.scm.plugin;
//~--- non-JDK imports --------------------------------------------------------
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//~--- JDK imports ------------------------------------------------------------
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -55,47 +48,27 @@ import java.nio.file.Path;
public class PathWebResourceLoader implements WebResourceLoader
{
/** Field description */
private static final String DEFAULT_SEPARATOR = "/";
private static final String SEPARATOR = "/";
/**
* the logger for PathWebResourceLoader
*/
private static final Logger logger =
private static final Logger LOG =
LoggerFactory.getLogger(PathWebResourceLoader.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param directory
*/
public PathWebResourceLoader(Path directory)
{
this.directory = directory;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param path
*
* @return
*/
@Override
public URL getResource(String path)
{
public URL getResource(String path) {
URL resource = null;
Path file = directory.resolve(filePath(path));
if (Files.exists(file) && ! Files.isDirectory(file))
{
logger.trace("found path {} at {}", path, file);
LOG.trace("found path {} at {}", path, file);
try
{
@@ -103,56 +76,20 @@ public class PathWebResourceLoader implements WebResourceLoader
}
catch (MalformedURLException ex)
{
logger.error("could not transform path to url", ex);
LOG.error("could not transform path to url", ex);
}
} else {
LOG.trace("could not find file {}", file);
}
return resource;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param path
*
* @return
*/
private String filePath(String path)
{
// TODO handle illegal path parts, such as ..
String filePath = filePath(DEFAULT_SEPARATOR, path);
if (!DEFAULT_SEPARATOR.equals(File.separator))
{
filePath = filePath(File.separator, path);
private String filePath(String path) {
if (path.startsWith(SEPARATOR)) {
return path.substring(1);
}
return filePath;
}
/**
* Method description
*
*
* @param separator
* @param path
*
* @return
*/
private String filePath(String separator, String path)
{
String filePath = path;
if (filePath.startsWith(separator))
{
filePath = filePath.substring(separator.length());
}
return filePath;
return path;
}
//~--- fields ---------------------------------------------------------------