diff --git a/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmApiProxyDelegate.java b/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmApiProxyDelegate.java index 527202d..a3d2284 100755 --- a/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmApiProxyDelegate.java +++ b/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmApiProxyDelegate.java @@ -492,7 +492,7 @@ class IdleConnectionMonitorThread extends Thread { public IdleConnectionMonitorThread(ClientConnectionManager connectionManager) { super("IdleApiConnectionMontorThread"); this.connectionManager = connectionManager; - this.setDaemon(false); + this.setDaemon(true); } @Override diff --git a/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmRuntimeFileLogHandler.java b/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmRuntimeFileLogHandler.java index ee42207..47a1622 100755 --- a/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmRuntimeFileLogHandler.java +++ b/appengine-managed-runtime/src/main/java/com/google/apphosting/vmruntime/VmRuntimeFileLogHandler.java @@ -31,11 +31,14 @@ * */ public class VmRuntimeFileLogHandler extends FileHandler { + // This exists for testing purposes only. If set, the cloud logger may lose logs. - private static final String LOG_PATTERN_CONFIG_PROPERTY = + public static final String LOG_DIRECTORY_PROPERTY = "com.google.apphosting.logs"; + public static final String LOG_PATTERN_CONFIG_PROPERTY = "com.google.apphosting.vmruntime.VmRuntimeFileLogHandler.pattern"; // Log files to /var/log/app_engine/app.[0-2].log.json - private static final String DEFAULT_LOG_PATTERN = "/var/log/app_engine/app.%g.log.json"; + private static final String DEFAULT_LOG_DIRECTORY = "/var/log/app_engine"; + private static final String DEFAULT_LOG_PATTERN = "app.%g.log.json"; private static final String APP_ENGINE_LOG_CONFIG_PATTERN_ENV = "APP_ENGINE_LOG_CONFIG_PATTERN"; private static final int LOG_MAX_SIZE = 100 * 1024 * 1024; @@ -53,11 +56,17 @@ private static String fileLogPattern() { if (pattern != null) { return pattern; } + + String directory = System.getProperty(LOG_DIRECTORY_PROPERTY,DEFAULT_LOG_DIRECTORY); + pattern = System.getProperty(LOG_PATTERN_CONFIG_PROPERTY); if (pattern != null) { - return pattern; + if (pattern.startsWith("/")) + return pattern; + return directory+"/"+pattern; } - return DEFAULT_LOG_PATTERN; + + return directory+"/"+DEFAULT_LOG_PATTERN; } /** diff --git a/jetty9-compat-base/README.md b/jetty9-compat-base/README.md new file mode 100644 index 0000000..fbb83da --- /dev/null +++ b/jetty9-compat-base/README.md @@ -0,0 +1,29 @@ +jetty9-compat-base +================== + +This module builds a [jetty base](https://www.eclipse.org/jetty/documentation/current/startup-base-and-home.html) directory +that contains the Google App Engine runtime environment. This base is used by the docker image built to run the +Google App Engine on Google Container Engine in compatibility mode. + +The base can also be run directly for local testing with: +```shell +java -Droot.webapp=../webapps/testwebapp/ \ + -Dcom.google.apphosting.logs=/tmp \ + -jar ../jetty-distribution-9.3.5.v20151012/start.jar \ + --module=testMetadataServer +``` +This example command uses: + * the system property `root.webapp` to point to an exploded WAR file suitable for GAE deployment (default `webapps/root`) + * the system property `com.google.apphosting.logs` to configure a directory to log into (default /var/log/appi\_engine) + * the normal jetty start.jar + * A testMetadataServer module that locally handles GAE rest calls + +The system property `quickstart_web_xml` may also be set to a file location to trigger the generation of a quickstart-web.xml +that can be used to quickstart the context if deployed in WEB-INF/quickstart-web.xml + +## Artefacts +This module builds 3 maven artefacts: + * jetty9-compat-base-@VERSION@.jar - The classes built in this module that addapt the generic appengine-managed-runtime module to the Jetty9 container. + * jetty9-compat-base-@VERSION@.tar.gz - A tar archive of the jar file deployed with all it's dependencies as a runnable [jetty base](https://www.eclipse.org/jetty/documentation/current/startup-base-and-home.html). + * jetty9-compat-base-@VERSION@.zip - A zip archive of the jar file deployed with all it's dependencies as a runnable [jetty base](https://www.eclipse.org/jetty/documentation/current/startup-base-and-home.html). +Enjoy... diff --git a/jetty9-compat-base/pom.xml b/jetty9-compat-base/pom.xml new file mode 100644 index 0000000..8581746 --- /dev/null +++ b/jetty9-compat-base/pom.xml @@ -0,0 +1,312 @@ + + + + 4.0.0 + + com.google.appengine + appengine-java-vm-runtime + 1.9.30-SNAPSHOT + ../pom.xml + + Google App Engine Container with GAE Compatibility (jetty9-compat) Jetty Base + jetty9-compat-base + jar + + + + com.google.appengine + appengine-api-1.0-sdk + ${appengine.api.version} + jar + + + com.google.appengine + appengine-managed-runtime + ${project.version} + jar + + + org.eclipse.jetty + jetty-security + ${jetty.version} + jar + + + org.eclipse.jetty + jetty-webapp + ${jetty.version} + jar + + + org.eclipse.jetty + jetty-quickstart + ${jetty.version} + jar + + + com.google.appengine + appengine-java-logging + ${project.version} + jar + + + + + org.eclipse.jetty + apache-jsp + ${jetty.version} + jar + test + + + + junit + junit + 4.12 + test + + + org.hamcrest + hamcrest-library + 1.3 + test + + + org.hamcrest + hamcrest-core + 1.3 + test + + + com.google.appengine + appengine-testing + ${appengine.api.version} + test + jar + + + org.easymock + easymock + 3.1 + test + jar + + + me.alexpanov + free-port-finder + 1.0 + test + + + com.google.appengine + appengine-api-stubs + ${appengine.api.version} + test + jar + + + org.mockito + mockito-core + 2.0.31-beta + test + + + org.apache.geronimo.bundles + commons-httpclient + 3.1_2 + test + jar + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.5 + + + + https://github.com/GoogleCloudPlatform/appengine-runtime-java-docker-image + com/google/apphosting/vmruntime/ + Google + ${maven.build.timestamp} + ${project.version} + com.google + Google + + + + + + + maven-dependency-plugin + + + test-webapp + generate-test-resources + + copy + + + + + com.google.appengine.demos + testwebapp + 1.0-SNAPSHOT + war + true + ** + ${project.build.directory}/webapps/ + testwebapp.war + + + + + + jetty-home + generate-resources + + unpack + + + + + org.eclipse.jetty + jetty-distribution + ${jetty.version} + tar.gz + true + ** + ${project.build.directory} + + + + + + gae-jars + generate-resources + + copy-dependencies + + + ${project.build.directory}/jetty-base/lib/gae + false + true + false + org.eclipse.jetty,org.ow2.asm,javax.annotation,javax.servlet,javax.transaction + runtime + + + + + + + maven-antrun-plugin + 1.8 + + + generate-jetty-base + generate-resources + + + + + + + + + run + + + + generate-test-war + generate-test-resources + + + + + + + run + + + + move-artefact + package + + + + + + + run + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.4.0 + + + add-jetty-modules + generate-resources + + exec + + + + + java + ${project.build.directory}/jetty-base + + -jar + ../jetty-distribution-${jetty.version}/start.jar + --add-to-startd=http,jsp,jstl,gae,gae-deploy + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + src/main/assembly/assembly.xml + + gnu + false + + + + package + + single + + + + + + + + diff --git a/jetty9-compat-base/src/main/assembly/assembly.xml b/jetty9-compat-base/src/main/assembly/assembly.xml new file mode 100644 index 0000000..ffd325a --- /dev/null +++ b/jetty9-compat-base/src/main/assembly/assembly.xml @@ -0,0 +1,22 @@ + + assembly + + tar.gz + zip + + false + + + ${project.build.directory}/jetty-base + jetty-base + + ** + + + **/META-INF/** + + + + diff --git a/jetty9-compat/src/main/java/com/google/apphosting/runtime/jetty9/SaveSessionFilter.java b/jetty9-compat-base/src/main/java/com/google/apphosting/runtime/jetty9/SaveSessionFilter.java similarity index 100% rename from jetty9-compat/src/main/java/com/google/apphosting/runtime/jetty9/SaveSessionFilter.java rename to jetty9-compat-base/src/main/java/com/google/apphosting/runtime/jetty9/SaveSessionFilter.java diff --git a/jetty9-compat/src/main/java/com/google/apphosting/runtime/jetty9/SessionManager.java b/jetty9-compat-base/src/main/java/com/google/apphosting/runtime/jetty9/SessionManager.java similarity index 100% rename from jetty9-compat/src/main/java/com/google/apphosting/runtime/jetty9/SessionManager.java rename to jetty9-compat-base/src/main/java/com/google/apphosting/runtime/jetty9/SessionManager.java diff --git a/jetty9-compat/src/main/java/com/google/apphosting/vmruntime/jetty9/AppEngineAuthentication.java b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/AppEngineAuthentication.java similarity index 100% rename from jetty9-compat/src/main/java/com/google/apphosting/vmruntime/jetty9/AppEngineAuthentication.java rename to jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/AppEngineAuthentication.java diff --git a/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/TestMetadataServer.java b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/TestMetadataServer.java new file mode 100644 index 0000000..95cba7d --- /dev/null +++ b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/TestMetadataServer.java @@ -0,0 +1,234 @@ +/** + * Copyright 2015 Google Inc. All Rights Reserved. + * + * 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.google.apphosting.vmruntime.jetty9; + +import static com.google.apphosting.vmruntime.VmApiProxyEnvironment.AFFINITY_ATTRIBUTE; +import static com.google.apphosting.vmruntime.VmApiProxyEnvironment.APPENGINE_HOSTNAME_ATTRIBUTE; +import static com.google.apphosting.vmruntime.VmApiProxyEnvironment.BACKEND_ATTRIBUTE; +import static com.google.apphosting.vmruntime.VmApiProxyEnvironment.INSTANCE_ATTRIBUTE; +import static com.google.apphosting.vmruntime.VmApiProxyEnvironment.PARTITION_ATTRIBUTE; +import static com.google.apphosting.vmruntime.VmApiProxyEnvironment.PROJECT_ATTRIBUTE; +import static com.google.apphosting.vmruntime.VmApiProxyEnvironment.USE_MVM_AGENT_ATTRIBUTE; +import static com.google.apphosting.vmruntime.VmApiProxyEnvironment.VERSION_ATTRIBUTE; +import static com.google.apphosting.vmruntime.VmMetadataCache.DEFAULT_META_DATA_SERVER; +import static com.google.apphosting.vmruntime.VmMetadataCache.META_DATA_PATTERN; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.HttpURLConnection; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URL; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.eclipse.jetty.util.component.AbstractLifeCycle; + +/** + * Minimal implementation of the metadata server running inside each VM. + * + */ +public class TestMetadataServer extends AbstractLifeCycle implements Runnable { + + protected static final Logger logger = Logger.getLogger(TestMetadataServer.class.getName()); + + private static final String PATH_PREFIX = "/computeMetadata/v1/instance/"; + public static final String PROJECT = "google.com:test-project"; + public static final String PARTITION = "testpartition"; + public static final String VERSION = "testversion"; + public static final String BACKEND = "testbackend"; + public static final String INSTANCE = "frontend1"; + public static final String AFFINITY = "true"; + public static final String APPENGINE_HOSTNAME = "testhostname"; + + + public static void main(String... args) throws Exception + { + TestMetadataServer test = new TestMetadataServer(); + test.start(); + Thread.sleep(5000); + test.stop(); + } + + private ServerSocket serverSocket; + private HashMap responses = new HashMap(); + private boolean run = true; + + public TestMetadataServer() { + addMetadata("STOP", "STOP"); + addMetadata(PROJECT_ATTRIBUTE, PROJECT); + addMetadata(PARTITION_ATTRIBUTE, PARTITION); + addMetadata(BACKEND_ATTRIBUTE, BACKEND); + addMetadata(VERSION_ATTRIBUTE, VERSION); + addMetadata(INSTANCE_ATTRIBUTE, INSTANCE); + addMetadata(AFFINITY_ATTRIBUTE, AFFINITY); + addMetadata(APPENGINE_HOSTNAME_ATTRIBUTE, APPENGINE_HOSTNAME); + addMetadata(USE_MVM_AGENT_ATTRIBUTE, Boolean.toString(false)); + } + + public void setUseMVM(boolean useMVM) + { + addMetadata(USE_MVM_AGENT_ATTRIBUTE, Boolean.toString(useMVM)); + } + + @Override + public void doStart() throws Exception + { + serverSocket = new ServerSocket(0); + int metadataPort = serverSocket.getLocalPort(); + System.setProperty("metadata_server", "127.0.0.1:" + metadataPort); + logger.fine("Listening for metadata requests at port: " + metadataPort); + + Thread metadataThread = new Thread(this); + metadataThread.setName("Metadata server"); + metadataThread.setDaemon(true); + metadataThread.start(); + } + + @Override + public void doStop() throws IOException { + String path="STOP"; + BufferedReader reader = null; + HttpURLConnection connection = null; + try { + String server = System.getProperty("metadata_server", DEFAULT_META_DATA_SERVER); + URL url = new URL(String.format(META_DATA_PATTERN, server, path)); + connection = (HttpURLConnection) url.openConnection(); + connection.setRequestProperty("Metadata-Flavor", "Google"); + + connection.setConnectTimeout(120000); + connection.setReadTimeout(120000); + reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + StringBuffer result = new StringBuffer(); + char[] buffer = new char[4096]; + int read; + while ((read = reader.read(buffer)) != -1) { + result.append(buffer, 0, read); + } + if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { + return; + } + throw new IOException("Meta-data request for '" + path + "' failed with error: " + + connection.getResponseCode()+" "+connection.getResponseMessage()); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + logger.info("Error closing connection for " + path + ": " + e.getMessage()); + } + } + if (connection != null) { + connection.disconnect(); + } + } + } + + /** + * Adds a new metadata value to the server. + * + * @param path The path where the value is stored. + * @param value The value to return. + */ + public void addMetadata(String path, String value) { + responses.put(PATH_PREFIX + path, value); + } + + + + /** + * Starts a single threaded metadata server. + */ + @Override + public void run() { + try { + while (run) { + final Socket clientSocket = serverSocket.accept(); + BufferedWriter responseWriter = null; + try { + BufferedReader requestDataReader + = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + responseWriter + = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); + String httpGetLine = requestDataReader.readLine(); + String[] getLineSplit = httpGetLine.split(" "); + if (getLineSplit.length < 2) { + responseWriter.write("HTTP/1.0 400 Bad Request\r\n\r\n"); + return; + } + // Seek to the end of the header. + if (!verifyHeader(requestDataReader, "Metadata-Flavor: Google")) { + responseWriter.write("HTTP/1.0 403 Access Denied\r\n\r\n"); + return; + } + // Check if we have content mapped to the requested path. + String requestedPath = getLineSplit[1]; + String returnData = responses.get(requestedPath); + if (returnData == null) { + responseWriter.write("HTTP/1.0 404 Not Found\r\n\r\n"); + return; + } + responseWriter.write("HTTP/1.0 200 OK\r\n"); + responseWriter.write("Content-Type: text/plain\r\n\r\n"); + responseWriter.write(returnData + "\r\n"); + if (requestedPath.endsWith("/STOP")) { + run = false; + } + } finally { + if (responseWriter != null) { + responseWriter.close(); + } + clientSocket.close(); + } + } + } catch (IOException e) { + logger.log(Level.WARNING, "Exception in TestMetadataServer: ", e); + } finally { + try { + if (serverSocket != null) { + serverSocket.close(); + logger.fine("CLOSING metadata requests at port: " + serverSocket.getLocalPort()); + + } + } catch (IOException e) { + logger.log(Level.WARNING, "got Exception when closing the server socket.", e); + } + } + } + + /** + * Advance the buffer just past the next empty line, or to the end of the + * buffer if no empty line exists. + * + * @param requestDataReader + * @throws IOException + */ + private boolean verifyHeader(BufferedReader requestData, String header) throws IOException { + boolean found = false; + String line; + do { + line = requestData.readLine(); + if (line != null && header.equals(line.trim())) { + found = true; + } + } while (line != null && line.trim().length() > 0); + return found; + } +} diff --git a/jetty9-compat/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeTrustedAddressChecker.java b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeTrustedAddressChecker.java similarity index 100% rename from jetty9-compat/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeTrustedAddressChecker.java rename to jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeTrustedAddressChecker.java diff --git a/jetty9-compat/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppContext.java b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppContext.java similarity index 87% rename from jetty9-compat/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppContext.java rename to jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppContext.java index 3dcbf07..f56be2b 100755 --- a/jetty9-compat/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppContext.java +++ b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppContext.java @@ -44,16 +44,21 @@ import com.google.apphosting.vmruntime.VmTimer; import org.eclipse.jetty.http.HttpScheme; +import org.eclipse.jetty.quickstart.PreconfigureDescriptorProcessor; +import org.eclipse.jetty.quickstart.QuickStartDescriptorGenerator; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.session.AbstractSessionManager; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppContext; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.util.Arrays; import java.util.Collection; @@ -94,6 +99,8 @@ public class VmRuntimeWebAppContext private final Timer wallclockTimer; private VmApiProxyEnvironment defaultEnvironment; private DatastoreService datastoreService; + private String quickstartWebXml; + private PreconfigureDescriptorProcessor preconfigProcessor; // Indicates if the context is running via the Cloud SDK, or the real runtime. @@ -129,6 +136,22 @@ public class VmRuntimeWebAppContext org.eclipse.jetty.annotations.AnnotationConfiguration.class.getCanonicalName() }; + public String getQuickstartWebXml() { + return quickstartWebXml; + } + + /** Set the quickstart WebXml + *

If set, this context will not start, rather it will generate the + * quickstart-web.xml file and then stop the server. + * If not set, the context will start normally

+ * @param quickstartWebXml The location of the quickstart web.xml to generate + */ + public void setQuickstartWebXml(String quickstartWebXml) { + if (quickstartWebXml!=null && quickstartWebXml.length()==0) + quickstartWebXml=null; + this.quickstartWebXml = quickstartWebXml; + } + @Override protected void doStart() throws Exception { // unpack and Adjust paths. @@ -152,11 +175,45 @@ protected void doStart() throws Exception { datastoreService = getDatastoreService(); - addEventListener(new ContextListener()); + if (quickstartWebXml==null) + addEventListener(new ContextListener()); + else + getMetaData().addDescriptorProcessor(preconfigProcessor=new PreconfigureDescriptorProcessor()); super.doStart(); } + @Override + protected void startWebapp() throws Exception + { + if (quickstartWebXml==null) + super.startWebapp(); + else + { + LOG.info("Generating quickstart web.xml: {}", quickstartWebXml); + Resource descriptor = Resource.newResource(quickstartWebXml); + if (descriptor.exists()) + descriptor.delete(); + descriptor.getFile().createNewFile(); + QuickStartDescriptorGenerator generator = new QuickStartDescriptorGenerator(this, preconfigProcessor.getXML()); + try (FileOutputStream fos = new FileOutputStream(descriptor.getFile())) + { + generator.generateQuickStartWebXml(fos); + } + finally + { + System.exit(0); + } + } + } + + + @Override + protected void stopWebapp() throws Exception + { + if (quickstartWebXml==null) + super.stopWebapp(); + } /** * Broken out to facilitate testing. @@ -256,6 +313,9 @@ public void init(String appengineWebXmlFile) appEngineWebXml = appEngineWebXmlReader.readAppEngineWebXml(); } VmRuntimeUtils.installSystemProperties(defaultEnvironment, appEngineWebXml); + String logConfig = System.getProperty("java.util.logging.config.file"); + if (logConfig!=null && logConfig.startsWith("WEB-INF/")) + System.setProperty("java.util.logging.config.file", URIUtil.addPaths(appDir, logConfig)); VmRuntimeLogHandler.init(); VmRuntimeFileLogHandler.init(); @@ -304,7 +364,7 @@ VmApiProxyEnvironment getRequestSpecificEnvironment() { } } - class ContextListener implements ContextHandler.ContextScopeListener, ServletRequestListener { + public class ContextListener implements ContextHandler.ContextScopeListener, ServletRequestListener { @Override public void enterScope(org.eclipse.jetty.server.handler.ContextHandler.Context context, Request baseRequest, Object reason) { RequestContext requestContext = getRequestContext(baseRequest); diff --git a/jetty9-compat/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppDeployer.java b/jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppDeployer.java similarity index 100% rename from jetty9-compat/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppDeployer.java rename to jetty9-compat-base/src/main/java/com/google/apphosting/vmruntime/jetty9/VmRuntimeWebAppDeployer.java diff --git a/jetty9-compat-base/src/main/jetty-base/LICENSE b/jetty9-compat-base/src/main/jetty-base/LICENSE new file mode 100644 index 0000000..e06d208 --- /dev/null +++ b/jetty9-compat-base/src/main/jetty-base/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + diff --git a/jetty9-compat/src/main/docker/etc/gae-deploy.xml b/jetty9-compat-base/src/main/jetty-base/etc/gae-deploy.xml similarity index 100% rename from jetty9-compat/src/main/docker/etc/gae-deploy.xml rename to jetty9-compat-base/src/main/jetty-base/etc/gae-deploy.xml diff --git a/jetty9-compat/src/main/docker/etc/gae.xml b/jetty9-compat-base/src/main/jetty-base/etc/gae.xml similarity index 96% rename from jetty9-compat/src/main/docker/etc/gae.xml rename to jetty9-compat-base/src/main/jetty-base/etc/gae.xml index 1cdaabf..232c0cd 100644 --- a/jetty9-compat/src/main/docker/etc/gae.xml +++ b/jetty9-compat-base/src/main/jetty-base/etc/gae.xml @@ -54,7 +54,7 @@ - /request.yyyy_mm_dd.log + /request.yyyy_mm_dd.log 2 true true diff --git a/jetty9-compat-base/src/main/jetty-base/etc/testMetadataServer.xml b/jetty9-compat-base/src/main/jetty-base/etc/testMetadataServer.xml new file mode 100644 index 0000000..2dc1fa5 --- /dev/null +++ b/jetty9-compat-base/src/main/jetty-base/etc/testMetadataServer.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/jetty9-compat/src/main/docker/etc/webdefault.xml b/jetty9-compat-base/src/main/jetty-base/etc/webdefault.xml similarity index 100% rename from jetty9-compat/src/main/docker/etc/webdefault.xml rename to jetty9-compat-base/src/main/jetty-base/etc/webdefault.xml diff --git a/jetty9-compat/src/main/docker/modules/gae-deploy.mod b/jetty9-compat-base/src/main/jetty-base/modules/gae-deploy.mod similarity index 100% rename from jetty9-compat/src/main/docker/modules/gae-deploy.mod rename to jetty9-compat-base/src/main/jetty-base/modules/gae-deploy.mod diff --git a/jetty9-compat/src/main/docker/modules/gae.mod b/jetty9-compat-base/src/main/jetty-base/modules/gae.mod similarity index 96% rename from jetty9-compat/src/main/docker/modules/gae.mod rename to jetty9-compat-base/src/main/jetty-base/modules/gae.mod index 3c6d44b..0d2f7ba 100644 --- a/jetty9-compat/src/main/docker/modules/gae.mod +++ b/jetty9-compat-base/src/main/jetty-base/modules/gae.mod @@ -11,6 +11,9 @@ quickstart jsp jstl +[optional] +testMetadataServer + [xml] etc/gae.xml diff --git a/jetty9-compat-base/src/main/jetty-base/modules/testMetadataServer.mod b/jetty9-compat-base/src/main/jetty-base/modules/testMetadataServer.mod new file mode 100644 index 0000000..1b5cb41 --- /dev/null +++ b/jetty9-compat-base/src/main/jetty-base/modules/testMetadataServer.mod @@ -0,0 +1,9 @@ +# +# Test GAE metadata server +# + +[depend] +server + +[xml] +etc/testMetadataServer.xml diff --git a/jetty9-compat/src/main/docker/resources/jetty-logging.properties b/jetty9-compat-base/src/main/jetty-base/resources/jetty-logging.properties similarity index 100% rename from jetty9-compat/src/main/docker/resources/jetty-logging.properties rename to jetty9-compat-base/src/main/jetty-base/resources/jetty-logging.properties diff --git a/jetty9-compat/src/main/docker/webapps/root.xml b/jetty9-compat-base/src/main/jetty-base/webapps/root.xml similarity index 78% rename from jetty9-compat/src/main/docker/webapps/root.xml rename to jetty9-compat-base/src/main/jetty-base/webapps/root.xml index 1ab1aaf..9ea593c 100644 --- a/jetty9-compat/src/main/docker/webapps/root.xml +++ b/jetty9-compat-base/src/main/jetty-base/webapps/root.xml @@ -7,11 +7,14 @@ / - /webapps/root + + + + org.apache.commons.codec. org.apache.commons.logging. @@ -22,9 +25,10 @@ - /etc/webdefault.xml + /etc/webdefault.xml - + + - - org.eclipse.jetty - apache-jsp - ${jetty.version} - jar - test - - - - junit - junit - 4.12 - test - - - org.hamcrest - hamcrest-library - 1.3 - test - - - org.hamcrest - hamcrest-core - 1.3 - test - - - com.google.appengine - appengine-testing - ${appengine.api.version} - test - jar - - - org.easymock - easymock - 3.1 - test - jar - - - me.alexpanov - free-port-finder - 1.0 - test - - - com.google.appengine - appengine-api-stubs - ${appengine.api.version} - test - jar - - - org.mockito - mockito-core - 2.0.31-beta - test - - - org.apache.geronimo.bundles - commons-httpclient - 3.1_2 - test - jar - - - - org.apache.maven.plugins - maven-jar-plugin - 2.5 - - - - https://github.com/GoogleCloudPlatform/appengine-runtime-java-docker-image - com/google/apphosting/vmruntime/ - Google - ${maven.build.timestamp} - ${project.version} - com.google - Google - - - - - maven-dependency-plugin - test-webapp - generate-test-resources + jetty-base + generate-resources - copy + unpack - com.google.appengine.demos - testwebapp - 1.0-SNAPSHOT - war + com.google.appengine + jetty9-compat-base + ${project.version} + zip true ** - ${project.build.directory}/webapps/ - testwebapp.war + ${project.build.directory}/docker/ - - - gae-jars - generate-resources - - copy-dependencies - - - ${project.build.directory}/docker/lib/gae - false - true - false - org.eclipse.jetty,org.ow2.asm,javax.annotation,javax.servlet,javax.transaction - runtime - - + @@ -214,14 +73,7 @@ latest - ${project.basedir}/target/generated-sources/docker - - - /lib/gae - ${project.build.directory} - ${project.build.finalName}.jar - - + ${project.basedir}/target/docker @@ -249,23 +101,10 @@ generate-sources - + - - - - - run - - - - generate-test-war - generate-test-resources - - - + diff --git a/jetty9-compat/src/main/docker/Dockerfile b/jetty9-compat/src/main/docker/Dockerfile index 07567f3..e460bf6 100644 --- a/jetty9-compat/src/main/docker/Dockerfile +++ b/jetty9-compat/src/main/docker/Dockerfile @@ -14,15 +14,19 @@ FROM jetty9:@@image.label@@ -ADD . $JETTY_BASE +# Remove stock Jetty Base +RUN rm -fr $JETTY_BASE \ + && mkdir $JETTY_BASE + +# add jetty9 compat base +ADD jetty-base jetty_run.sh $JETTY_BASE/ WORKDIR $JETTY_BASE RUN sed -i 's/^\([a-zA-Z\.]*=\).*/#\1(see gae.ini)/' start.d/server.ini \ - && rm -f start.d/setuid.ini start.d/deploy.ini \ - && java -jar $JETTY_HOME/start.jar --add-to-startd=gae,gae-deploy \ && java -jar $JETTY_HOME/start.jar --dry-run \ | sed 's/^.*java /& ${ALPN_BOOT} ${DBG_AGENT} ${PROF_AGENT} -Xms${HEAP_SIZE} -Xmx${HEAP_SIZE} ${JAVA_OPTS} /' > jetty_cmd.sh \ - && chmod +x /var/lib/jetty/jetty_run.sh + && chown -R jetty:jetty $JETTY_BASE \ + && chmod +x $JETTY_BASE/jetty_run.sh WORKDIR /app diff --git a/jetty9-compat/src/test/java/com/google/apphosting/vmruntime/jetty9/TestMetadataServer.java b/jetty9-compat/src/test/java/com/google/apphosting/vmruntime/jetty9/TestMetadataServer.java deleted file mode 100644 index 62613fc..0000000 --- a/jetty9-compat/src/test/java/com/google/apphosting/vmruntime/jetty9/TestMetadataServer.java +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Copyright 2015 Google Inc. All Rights Reserved. - * - * 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.google.apphosting.vmruntime.jetty9; - -import static com.google.apphosting.vmruntime.jetty9.VmRuntimeTestBase.logger; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.HashMap; -import java.util.logging.Level; - -/** - * Minimal implementation of the metadata server running inside each VM. - * - */ -public class TestMetadataServer implements Runnable { - - private static final String PATH_PREFIX = "/computeMetadata/v1/instance/"; - private final int metadataPort; - private HashMap responses = new HashMap(); - private boolean run = true; - - /** - * Constructor. - * - * @param metadataPort The port the server should bind to. - */ - public TestMetadataServer(int metadataPort) { - this.metadataPort = metadataPort; - } - - /** - * Adds a new metadata value to the server. - * - * @param path The path where the value is stored. - * @param value The value to return. - */ - public void addMetadata(String path, String value) { - responses.put(PATH_PREFIX + path, value); - } - - /** - * Starts a single threaded metadata server. - */ - @Override - public void run() { - ServerSocket serverSocket = null; - try { - logger.fine("TRYING TO Listen for metadata requests at port: " + metadataPort); - serverSocket = new ServerSocket(metadataPort); - logger.fine("Listening for metadata requests at port: " + metadataPort); - while (run) { - final Socket clientSocket = serverSocket.accept(); - BufferedWriter responseWriter = null; - try { - BufferedReader requestDataReader - = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); - responseWriter - = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream())); - String httpGetLine = requestDataReader.readLine(); - String[] getLineSplit = httpGetLine.split(" "); - if (getLineSplit.length < 2) { - responseWriter.write("HTTP/1.0 400 Bad Request\r\n\r\n"); - return; - } - // Seek to the end of the header. - if (!verifyHeader(requestDataReader, "Metadata-Flavor: Google")) { - responseWriter.write("HTTP/1.0 403 Access Denied\r\n\r\n"); - return; - } - // Check if we have content mapped to the requested path. - String requestedPath = getLineSplit[1]; - String returnData = responses.get(requestedPath); - if (returnData == null) { - responseWriter.write("HTTP/1.0 404 Not Found\r\n\r\n"); - return; - } - responseWriter.write("HTTP/1.0 200 OK\r\n"); - responseWriter.write("Content-Type: text/plain\r\n\r\n"); - responseWriter.write(returnData + "\r\n"); - if (requestedPath.endsWith("/STOP")) { - run = false; - } - } finally { - if (responseWriter != null) { - responseWriter.close(); - } - clientSocket.close(); - } - } - } catch (IOException e) { - logger.log(Level.WARNING, "Exception in TestMetadataServer: ", e); - } finally { - try { - if (serverSocket != null) { - serverSocket.close(); - logger.fine("CLOSING metadata requests at port: " + metadataPort); - - } - } catch (IOException e) { - logger.log(Level.WARNING, "got Exception when closing the server socket.", e); - } - } - } - - /** - * Advance the buffer just past the next empty line, or to the end of the - * buffer if no empty line exists. - * - * @param requestDataReader - * @throws IOException - */ - private boolean verifyHeader(BufferedReader requestData, String header) throws IOException { - boolean found = false; - String line; - do { - line = requestData.readLine(); - if (line != null && header.equals(line.trim())) { - found = true; - } - } while (line != null && line.trim().length() > 0); - return found; - } -} diff --git a/pom.xml b/pom.xml index 566b52f..e631cbb 100644 --- a/pom.xml +++ b/pom.xml @@ -81,6 +81,7 @@ openjdk8 jetty9 testwebapp + jetty9-compat-base jetty9-compat