PL-9055 Single Uber JAR

This commit is contained in:
Andrey Subbotin 2017-05-25 17:49:24 +04:00
parent c4166e1902
commit ad1a9e9134
7 changed files with 327 additions and 219 deletions

View File

@ -908,7 +908,6 @@ configure(uberJarModule) {
compile(bom['org.eclipse.jetty.websocket:websocket-server'])
compile(bom['commons-cli:commons-cli'])
compile(bom['commons-io:commons-io'])
compile(bom['com.google.guava:guava'])
compile(bom['org.apache.commons:commons-dbcp2'])
}
}

View File

@ -0,0 +1,173 @@
/*
* Copyright (c) 2008-2017 Haulmont.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.haulmont.cuba.uberjar;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebXmlConfiguration;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import static com.haulmont.cuba.uberjar.CubaJettyUtils.*;
public class CubaJettyServer {
protected int port;
protected String contextPath;
protected String portalContextPath;
protected String frontContextPath;
protected URL jettyEnvPathUrl;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getContextPath() {
return contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
public String getFrontContextPath() {
return frontContextPath;
}
public void setFrontContextPath(String frontContextPath) {
this.frontContextPath = frontContextPath;
}
public String getPortalContextPath() {
return portalContextPath;
}
public void setPortalContextPath(String portalContextPath) {
this.portalContextPath = portalContextPath;
}
public URL getJettyEnvPathUrl() {
return jettyEnvPathUrl;
}
public void setJettyEnvPathUrl(URL jettyEnvPathUrl) {
this.jettyEnvPathUrl = jettyEnvPathUrl;
}
public void start() {
String appHome = System.getProperty("app.home");
if (appHome == null || appHome.length() == 0) {
System.setProperty("app.home", System.getProperty("user.dir"));
}
try {
Server server = createServer();
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
protected Server createServer() throws Exception {
ClassLoader serverClassLoader = Thread.currentThread().getContextClassLoader();
ClassLoader sharedClassLoader = new URLClassLoader(pathsToURLs(serverClassLoader, SHARED_CLASS_PATH_IN_JAR), serverClassLoader);
Server server = new Server(port);
List<Handler> handlers = new ArrayList<>();
if (CubaJettyUtils.hasCoreApp(serverClassLoader)) {
String coreContextPath = contextPath;
if (isSingleJar(serverClassLoader)) {
if (PATH_DELIMITER.equals(contextPath)) {
coreContextPath = PATH_DELIMITER + "app-core";
} else {
coreContextPath = contextPath + "-core";
}
}
handlers.add(createAppContext(serverClassLoader, sharedClassLoader, CORE_PATH_IN_JAR, coreContextPath));
}
if (hasWebApp(serverClassLoader)) {
handlers.add(createAppContext(serverClassLoader, sharedClassLoader, WEB_PATH_IN_JAR, contextPath));
}
if (hasPortalApp(serverClassLoader)) {
String portalContextPath = contextPath;
if (isSingleJar(serverClassLoader)) {
portalContextPath = this.portalContextPath;
}
handlers.add(createAppContext(serverClassLoader, sharedClassLoader, PORTAL_PATH_IN_JAR, portalContextPath));
}
if (hasFrontApp(serverClassLoader)) {
handlers.add(createFrontAppContext(serverClassLoader));
}
HandlerCollection handlerCollection = new HandlerCollection();
handlerCollection.setHandlers(handlers.toArray(new Handler[handlers.size()]));
server.setHandler(handlerCollection);
return server;
}
protected WebAppContext createAppContext(ClassLoader serverClassLoader, ClassLoader sharedClassLoader,
String appPathInJar, String contextPath) throws URISyntaxException {
ClassLoader appClassLoader = new URLClassLoader(pathsToURLs(serverClassLoader, getAppClassesPath(appPathInJar)), sharedClassLoader);
WebAppContext appContext = new WebAppContext();
appContext.setConfigurations(new Configuration[]{new WebXmlConfiguration(), createEnvConfiguration()});
appContext.setContextPath(contextPath);
appContext.setClassLoader(appClassLoader);
setResourceBase(serverClassLoader, appContext, appPathInJar);
return appContext;
}
protected WebAppContext createFrontAppContext(ClassLoader serverClassLoader) throws URISyntaxException {
URL frontContentUrl = serverClassLoader.getResource(FRONT_PATH_IN_JAR);
WebAppContext frontContext = new WebAppContext();
frontContext.setContextPath(frontContextPath);
frontContext.setClassLoader(serverClassLoader);
frontContext.setResourceBase(frontContentUrl.toURI().toString());
return frontContext;
}
protected void setResourceBase(ClassLoader serverClassLoader, WebAppContext appContext, String appPath) throws URISyntaxException {
URL resourceBaseUrl = serverClassLoader.getResource(appPath);
if (resourceBaseUrl != null) {
appContext.setResourceBase(resourceBaseUrl.toURI().toString());
}
}
protected EnvConfiguration createEnvConfiguration() {
EnvConfiguration envConfiguration = new EnvConfiguration();
if (jettyEnvPathUrl != null) {
envConfiguration.setJettyEnvXml(jettyEnvPathUrl);
}
return envConfiguration;
}
}

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2008-2017 Haulmont.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.haulmont.cuba.uberjar;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class CubaJettyUtils {
public static final String CLASSES_PATH = "WEB-INF/classes";
public static final String SHARED_CLASS_PATH_IN_JAR = "LIB-INF/shared";
public static final String CORE_PATH_IN_JAR = "LIB-INF/app-core";
public static final String WEB_PATH_IN_JAR = "LIB-INF/app";
public static final String PORTAL_PATH_IN_JAR = "LIB-INF/app-portal";
public static final String FRONT_PATH_IN_JAR = "LIB-INF/app-front";
public static final String APP_PROPERTIES_PATH_IN_JAR = "WEB-INF/local.app.properties";
public static final String PATH_DELIMITER = "/";
private CubaJettyUtils() {
}
public static URL[] pathsToURLs(ClassLoader classLoader, String... paths) {
if (paths != null) {
List<URL> urls = new ArrayList<>();
for (String path : paths) {
urls.add(classLoader.getResource(path + PATH_DELIMITER));
}
return urls.toArray(new URL[urls.size()]);
}
return new URL[0];
}
public static String getAppClassesPath(String appPathInJar) {
if (appPathInJar.endsWith(PATH_DELIMITER)) {
return appPathInJar + CLASSES_PATH;
} else {
return appPathInJar + PATH_DELIMITER + CLASSES_PATH;
}
}
public static boolean isSingleJar(ClassLoader serverClassLoader) {
return hasCoreApp(serverClassLoader) && hasWebApp(serverClassLoader);
}
public static boolean hasCoreApp(ClassLoader serverClassLoader) {
return serverClassLoader.getResource(CORE_PATH_IN_JAR) != null;
}
public static boolean hasWebApp(ClassLoader serverClassLoader) {
return serverClassLoader.getResource(WEB_PATH_IN_JAR) != null;
}
public static boolean hasPortalApp(ClassLoader classLoader) {
return classLoader.getResource(PORTAL_PATH_IN_JAR) != null;
}
public static boolean hasFrontApp(ClassLoader classLoader) {
return classLoader.getResource(FRONT_PATH_IN_JAR) != null;
}
}

View File

@ -1,66 +0,0 @@
/*
* Copyright (c) 2008-2017 Haulmont.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.haulmont.cuba.uberjar;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import java.net.MalformedURLException;
import java.util.Set;
public class CubaJettyWebAppContext extends WebAppContext {
protected Set<String> staticContents;
public Set<String> getStaticContents() {
return staticContents;
}
public void setStaticContents(Set<String> staticContents) {
this.staticContents = staticContents;
}
@Override
public Resource getResource(String uriInContext) throws MalformedURLException {
if (uriInContext != null && getClassLoader() != null) {
boolean isWebInf = isWebInf(uriInContext);
boolean isStatic = isStatic(uriInContext);
if (isWebInf || isStatic) {
if (uriInContext.startsWith("/")) {
uriInContext = uriInContext.substring(1);
}
if (isStatic) {
uriInContext = ServerRunner.STATIC_CONTENT_PATH_IN_JAR + "/" + uriInContext;
}
return Resource.newResource(getClassLoader().getResource(uriInContext));
}
}
return super.getResource(uriInContext);
}
protected boolean isWebInf(String uriInContext) {
return uriInContext.startsWith("/WEB-INF/");
}
protected boolean isStatic(String uriInContext) {
if (staticContents != null) {
return staticContents.stream()
.anyMatch(it -> uriInContext.startsWith("/" + it + "/"));
}
return false;
}
}

View File

@ -1,35 +0,0 @@
/*
* Copyright (c) 2008-2017 Haulmont.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.haulmont.cuba.uberjar;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebXmlConfiguration;
import java.io.IOException;
public class CubaJettyWebXmlConfiguration extends WebXmlConfiguration {
@Override
protected Resource findWebXml(WebAppContext context) throws IOException {
Resource webXml = super.findWebXml(context);
if ((webXml == null || !webXml.exists()) && context.getClassLoader() != null) {
webXml = Resource.newResource(
context.getClassLoader().getResource(context.getDescriptor()));
}
return webXml;
}
}

View File

@ -16,38 +16,19 @@
package com.haulmont.cuba.uberjar;
import com.google.common.reflect.ClassPath;
import org.apache.commons.cli.*;
import org.apache.commons.io.FilenameUtils;
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.CodeSource;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import static com.haulmont.cuba.uberjar.CubaJettyUtils.*;
import static java.lang.String.format;
public class ServerRunner {
public static final String STATIC_CONTENT_PATH_IN_JAR = "ubercontent";
public static final String FRONT_CONTENT_PATH_IN_JAR = "uberfront";
public static final String APP_PROPERTIES_PATH_IN_JAR = "WEB-INF/local.app.properties";
protected static final int DEFAULT_PORT = 8080;
protected static final String CONTEXT_PATH_DELIMITER = "/";
protected int port;
protected String contextPath;
protected String frontContextPath;
protected URL jettyEnvPathUrl;
public static void main(String[] args) {
ServerRunner runner = new ServerRunner();
@ -69,6 +50,10 @@ public class ServerRunner {
.hasArg()
.desc("front application context name").argName("frontContextName").build();
Option portalContextPathOption = Option.builder("portalContextName")
.hasArg()
.desc("portal application context name for single jar application").argName("portalContextName").build();
Option jettyEnvPathOption = Option.builder("jettyEnvPath")
.hasArg()
.desc("jetty resource xml path").argName("jettyEnvPath").build();
@ -80,6 +65,7 @@ public class ServerRunner {
cliOptions.addOption(portOption);
cliOptions.addOption(contextPathOption);
cliOptions.addOption(frontContextPathOption);
cliOptions.addOption(portalContextPathOption);
cliOptions.addOption(jettyEnvPathOption);
CommandLineParser parser = new DefaultParser();
@ -95,52 +81,82 @@ public class ServerRunner {
if (cmd.hasOption("help"))
printHelp(formatter, cliOptions);
else {
CubaJettyServer jettyServer = new CubaJettyServer();
if (cmd.hasOption(portOption.getOpt())) {
try {
port = Integer.parseInt(cmd.getOptionValue(portOption.getOpt()));
jettyServer.setPort(Integer.parseInt(cmd.getOptionValue(portOption.getOpt())));
} catch (NumberFormatException e) {
System.out.println("port has to be number");
printHelp(formatter, cliOptions);
return;
}
} else if (port == 0) {
port = getWebPort();
} else {
jettyServer.setPort(getDefaultWebPort());
}
String contextPath = null;
String frontContextPath = null;
String portalContextPath = null;
if (cmd.hasOption(contextPathOption.getOpt())) {
String contextName = cmd.getOptionValue(contextPathOption.getOpt());
if (contextName != null && !contextName.isEmpty()) {
if (CONTEXT_PATH_DELIMITER.equals(contextName)) {
contextPath = CONTEXT_PATH_DELIMITER;
if (PATH_DELIMITER.equals(contextName)) {
contextPath = PATH_DELIMITER;
} else {
contextPath = PATH_DELIMITER + contextName;
}
contextPath = CONTEXT_PATH_DELIMITER + contextName;
}
}
if (cmd.hasOption(frontContextPathOption.getOpt())) {
String contextName = cmd.getOptionValue(frontContextPathOption.getOpt());
if (contextName != null && !contextName.isEmpty()) {
if (CONTEXT_PATH_DELIMITER.equals(contextName)) {
frontContextPath = CONTEXT_PATH_DELIMITER;
if (PATH_DELIMITER.equals(contextName)) {
frontContextPath = PATH_DELIMITER;
} else {
frontContextPath = PATH_DELIMITER + contextName;
}
}
}
if (cmd.hasOption(portalContextPathOption.getOpt())) {
String contextName = cmd.getOptionValue(portalContextPathOption.getOpt());
if (contextName != null && !contextName.isEmpty()) {
if (PATH_DELIMITER.equals(contextName)) {
portalContextPath = PATH_DELIMITER;
} else {
portalContextPath = PATH_DELIMITER + contextName;
}
frontContextPath = CONTEXT_PATH_DELIMITER + contextName;
}
}
if (contextPath == null) {
String jarName = getJarName();
if (jarName != null) {
contextPath = CONTEXT_PATH_DELIMITER + FilenameUtils.getBaseName(jarName);
jettyServer.setContextPath(PATH_DELIMITER + FilenameUtils.getBaseName(jarName));
} else {
contextPath = CONTEXT_PATH_DELIMITER;
jettyServer.setContextPath(PATH_DELIMITER);
}
} else {
jettyServer.setContextPath(contextPath);
}
if (frontContextPath == null) {
if (CONTEXT_PATH_DELIMITER.equals(contextPath)) {
frontContextPath = CONTEXT_PATH_DELIMITER + "app-front";
if (PATH_DELIMITER.equals(contextPath)) {
jettyServer.setFrontContextPath(PATH_DELIMITER + "app-front");
} else {
frontContextPath = contextPath + "-front";
jettyServer.setFrontContextPath(jettyServer.getContextPath() + "-front");
}
} else {
jettyServer.setFrontContextPath(frontContextPath);
}
if (portalContextPath == null) {
if (PATH_DELIMITER.equals(contextPath)) {
jettyServer.setPortalContextPath(PATH_DELIMITER + "app-portal");
} else {
jettyServer.setPortalContextPath(jettyServer.getContextPath() + "-portal");
}
} else {
jettyServer.setPortalContextPath(frontContextPath);
}
if (cmd.hasOption(jettyEnvPathOption.getOpt())) {
String jettyEnvPath = cmd.getOptionValue(jettyEnvPathOption.getOpt());
if (jettyEnvPath != null && !jettyEnvPath.isEmpty()) {
@ -151,70 +167,17 @@ public class ServerRunner {
return;
}
try {
jettyEnvPathUrl = file.toURI().toURL();
jettyServer.setJettyEnvPathUrl(file.toURI().toURL());
} catch (MalformedURLException e) {
throw new RuntimeException("Unable to create jettyEnvPathUrl", e);
}
}
}
startServer();
System.out.println(format("Starting Jetty server on port: %s and contextPath: %s", jettyServer.getPort(), jettyServer.getContextPath()));
jettyServer.start();
}
}
protected void startServer() {
System.out.println(format("Starting Jetty server on port: %s and contextPath: %s", port, contextPath));
System.setProperty("app.home", System.getProperty("user.dir"));
try {
Server server = createServer();
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
protected Server createServer() throws Exception {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Server server = new Server(port);
CubaJettyWebAppContext cubaContext = new CubaJettyWebAppContext();
cubaContext.setStaticContents(getStaticContents(classLoader));
cubaContext.setConfigurations(new Configuration[]{new CubaJettyWebXmlConfiguration(), createEnvConfiguration(classLoader)});
cubaContext.setDescriptor("WEB-INF/web.xml");
cubaContext.setContextPath(contextPath);
cubaContext.setParentLoaderPriority(true);
cubaContext.setClassLoader(classLoader);
URL frontContentUrl = classLoader.getResource(FRONT_CONTENT_PATH_IN_JAR);
WebAppContext frontContext = null;
if (frontContentUrl != null) {
frontContext = new WebAppContext();
frontContext.setContextPath(frontContextPath);
frontContext.setParentLoaderPriority(true);
frontContext.setClassLoader(classLoader);
frontContext.setResourceBase(frontContentUrl.toURI().toString());
}
if (frontContext != null) {
HandlerCollection handlerCollection = new HandlerCollection();
handlerCollection.setHandlers(new Handler[]{cubaContext, frontContext});
server.setHandler(handlerCollection);
} else {
server.setHandler(cubaContext);
}
return server;
}
protected EnvConfiguration createEnvConfiguration(ClassLoader classLoader) {
EnvConfiguration envConfiguration = new EnvConfiguration();
if (jettyEnvPathUrl != null) {
envConfiguration.setJettyEnvXml(jettyEnvPathUrl);
} else {
envConfiguration.setJettyEnvXml(classLoader.getResource("WEB-INF/jetty-env.xml"));
}
return envConfiguration;
}
protected void printHelp(HelpFormatter formatter, Options cliOptions) {
String jarName = getJarName();
formatter.printHelp(String.format("java -jar %s", jarName == null ? "jar-file" : jarName), cliOptions);
@ -229,36 +192,29 @@ public class ServerRunner {
return null;
}
protected Set<String> getStaticContents(ClassLoader classLoader) {
Set<String> contents = new HashSet<>();
try {
ClassPath.from(classLoader).getResources().forEach(it -> {
if (it.getResourceName() != null && it.getResourceName().startsWith(STATIC_CONTENT_PATH_IN_JAR)) {
String[] paths = it.getResourceName().split("/");
//extract only first level dirs
if (paths.length > 2) {
contents.add(paths[1]);
}
}
});
} catch (IOException e) {
e.printStackTrace(System.out);
}
return contents;
}
protected int getWebPort() {
protected int getDefaultWebPort() {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
Properties properties = new Properties();
properties.load(classLoader.getResourceAsStream(APP_PROPERTIES_PATH_IN_JAR));
properties.load(classLoader.getResourceAsStream(getPropertiesForPort(classLoader)));
String webPort = (String) properties.get("cuba.webPort");
if (webPort != null && !webPort.isEmpty()) {
return Integer.parseInt(webPort);
}
} catch (IOException | NumberFormatException e) {
} catch (Exception e) {
System.out.println("Error while parsing port, use default port");
}
return DEFAULT_PORT;
return 8080;
}
protected String getPropertiesForPort(ClassLoader classLoader) {
if (isSingleJar(classLoader) || hasWebApp(classLoader)) {
return WEB_PATH_IN_JAR + PATH_DELIMITER + APP_PROPERTIES_PATH_IN_JAR;
} else if (hasCoreApp(classLoader)) {
return CORE_PATH_IN_JAR + PATH_DELIMITER + APP_PROPERTIES_PATH_IN_JAR;
} else if (hasPortalApp(classLoader)) {
return PORTAL_PATH_IN_JAR + PATH_DELIMITER + APP_PROPERTIES_PATH_IN_JAR;
}
return null;
}
}

View File

@ -21,6 +21,7 @@ import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.Configuration;
import com.haulmont.cuba.core.global.GlobalConfig;
import com.haulmont.cuba.core.global.Resources;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.web.AppUI;
import com.haulmont.cuba.web.WebConfig;
import com.haulmont.cuba.web.app.WebStatisticsAccumulator;
@ -320,10 +321,13 @@ public class CubaApplicationServlet extends VaadinServlet {
@Override
protected boolean isAllowedVAADINResourceUrl(HttpServletRequest request,
URL resourceUrl) {
String resourcePath = resourceUrl.getPath();
if ("jar".equals(resourceUrl.getProtocol())) {
if (resourcePath.contains("!/ubercontent/VAADIN/")) {
return true;
boolean isUberJar = Boolean.parseBoolean(AppContext.getProperty("cuba.uberJar"));
if (isUberJar) {
String resourcePath = resourceUrl.getPath();
if ("jar".equals(resourceUrl.getProtocol())) {
if (resourcePath.contains("!/LIB-INF/app/VAADIN/")) {
return true;
}
}
}
return super.isAllowedVAADINResourceUrl(request, resourceUrl);