├── .gitignore
├── .travis.yml
├── CONTRIBUTING.md
├── DEVELOPING.md
├── LICENSE
├── README.md
├── checkstyle
├── custom-checks.xml
└── java.header
├── pom.xml
├── scripts
├── build.sh
├── ci
│ ├── build.sh
│ ├── gcloud-init.sh
│ ├── release-cloudbuild.yaml
│ └── release.sh
├── integration_test.sh
└── utils
│ ├── gcloud.sh
│ ├── maven.sh
│ └── structure_test.sh
├── tests
├── test-distributed-war
│ ├── .gitignore
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── appengine
│ │ └── app.yaml
│ │ ├── docker
│ │ ├── Dockerfile
│ │ └── credential.json
│ │ ├── java
│ │ └── com
│ │ │ └── google
│ │ │ └── cloud
│ │ │ └── runtimes
│ │ │ └── tomcat
│ │ │ └── test
│ │ │ └── distributed
│ │ │ └── SessionServlet.java
│ │ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
├── test-war-integration
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── cloudbuild
│ │ └── integration.yaml
│ │ ├── docker
│ │ ├── Dockerfile
│ │ ├── Dockerfile.in
│ │ └── app.yaml
│ │ ├── java
│ │ └── com
│ │ │ └── google
│ │ │ └── cloud
│ │ │ └── runtimes
│ │ │ └── tomcat
│ │ │ └── test
│ │ │ └── integration
│ │ │ ├── CustomLoggingServlet.java
│ │ │ ├── CustomServlet.java
│ │ │ ├── DumpServlet.java
│ │ │ ├── ExceptionServlet.java
│ │ │ ├── HelloServlet.java
│ │ │ ├── MonitoringServlet.java
│ │ │ ├── SecureTestServlet.java
│ │ │ └── StandardLoggingServlet.java
│ │ ├── resources
│ │ ├── custom-test-specification.json
│ │ └── logging.properties
│ │ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
├── test-war-simple
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── docker
│ │ └── Dockerfile
│ │ ├── java
│ │ └── com
│ │ │ └── google
│ │ │ └── cloud
│ │ │ └── runtimes
│ │ │ └── tomcat
│ │ │ └── test
│ │ │ └── integration
│ │ │ └── HelloServlet.java
│ │ └── webapp
│ │ ├── WEB-INF
│ │ └── web.xml
│ │ └── index.html
└── test-war-spring-boot
│ ├── pom.xml
│ └── src
│ └── main
│ ├── docker
│ └── Dockerfile
│ └── java
│ └── com.google.cloud.runtimes
│ └── spring
│ ├── Application.java
│ └── controllers
│ └── HelloController.java
├── tomcat-gcp-lib
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── google
│ │ └── cloud
│ │ └── runtimes
│ │ └── tomcat
│ │ ├── session
│ │ ├── DatastoreManager.java
│ │ ├── DatastoreSession.java
│ │ ├── DatastoreStore.java
│ │ └── DatastoreValve.java
│ │ └── trace
│ │ ├── HttpLabels.java
│ │ └── TraceValve.java
│ └── test
│ └── java
│ └── com
│ └── google
│ └── cloud
│ └── runtimes
│ └── tomcat
│ ├── session
│ ├── DatastoreManagerTest.java
│ ├── DatastoreSessionTest.java
│ ├── DatastoreStoreTest.java
│ ├── DatastoreValveTest.java
│ └── integration
│ │ └── DatastoreStoreIT.java
│ └── trace
│ └── TraceValveTest.java
└── tomcat
├── pom.xml
└── src
├── cloudbuild
└── build.yaml
├── main
├── docker
│ ├── 15-debug-env-tomcat.bash
│ ├── 50-tomcat.bash
│ └── Dockerfile
├── resources
│ └── config
│ │ ├── distributed-sessions.xml
│ │ ├── gcp.xml
│ │ └── stackdriver-trace.xml
└── tomcat-base
│ └── conf
│ ├── catalina.properties
│ ├── context.xml
│ ├── distributed-sessions.xml
│ ├── gcp.xml
│ ├── logging.properties
│ ├── server.xml
│ └── stackdriver-trace.xml
└── test
├── resources
└── structure.yaml
└── workspace
├── tomcat-gae.bash
└── tomcat-unpack.bash
/.gitignore:
--------------------------------------------------------------------------------
1 | # maven
2 | target
3 |
4 | # intellij / idea
5 | *.iml
6 | .idea
7 |
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 |
3 | languages: java
4 |
5 | services:
6 | - docker
7 |
8 | jdk:
9 | - oraclejdk8
10 |
11 | branches:
12 | only:
13 | - master
14 |
15 | script:
16 | - mvn clean verify -B -V -q
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to become a contributor and submit your own code
2 |
3 | ## Contributor License Agreements
4 |
5 | We'd love to accept your patches! Before we can take them, we
6 | have to jump a couple of legal hurdles.
7 |
8 | Please fill out either the individual or corporate Contributor License Agreement
9 | (CLA).
10 |
11 | * If you are an individual writing original source code and you're sure you
12 | own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual).
13 | * If you work for a company that wants to allow you to contribute your work,
14 | then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate).
15 |
16 | Follow either of the two links above to access the appropriate CLA and
17 | instructions for how to sign and return it. Once we receive it, we'll be able to
18 | accept your pull requests.
19 |
20 | ## Contributing A Patch
21 |
22 | 1. Submit an issue describing your proposed change to the repo in question.
23 | 1. The repo owner will respond to your issue promptly.
24 | 1. If your proposed change is accepted, and you haven't already done so, sign a
25 | Contributor License Agreement (see details above).
26 | 1. Fork the desired repo, develop and test your code changes.
27 | 1. Ensure that your code adheres to the existing style in the sample to which
28 | you are contributing. Refer to the
29 | [Google Cloud Platform Samples Style Guide](https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the
30 | recommended coding standards for this organization.
31 | 1. Ensure that your code has an appropriate set of unit tests which all pass.
32 | 1. Submit a pull request.
--------------------------------------------------------------------------------
/DEVELOPING.md:
--------------------------------------------------------------------------------
1 | # Development Guide
2 |
3 | ## Building the image
4 |
5 | Make sure you have Docker and Maven installed and that the Docker daemon is running.
6 | Then run the following command:
7 |
8 | ```bash
9 | mvn clean install
10 | ```
11 |
12 | This will add the Tomcat runtime image to your local docker repository. You can now use the
13 | newly created image as a base in your Dockerfile.
14 |
15 | ## Testing the image
16 |
17 | ### JUnit Units tests
18 |
19 | You can quickly tests the project locally using the JUnit tests, to do so run:
20 | ```bash
21 | mvn clean test
22 | ```
23 |
24 | ### JUnit GCP Integration tests
25 |
26 | This tests the interaction of the different components with the GCP Services.
27 | The tests are run outside of a Docker container.
28 |
29 | In order to run the tests you need to install the [Cloud SDK](https://cloud.google.com/sdk/docs/)
30 | and select an active project with `gcloud init`.
31 |
32 | You can run these tests with maven using the profile `gcp-integration-test`, for example:
33 | ```bash
34 | mvn clean verify -P gcp-integration-test
35 | ```
36 |
37 | ### Runtimes common Structure tests
38 |
39 | Specification: [Runtime common - Structure tests](https://github.com/GoogleCloudPlatform/runtimes-common/tree/master/structure_tests)
40 |
41 | These tests inspect the content of the Docker image to ensure the presence of specific files and environment variables.
42 |
43 | They are automatically run when the image is built with Maven.
44 | You can find the details of the tests in [structure.yaml](tomcat/src/test/resources/structure.yaml)
45 |
46 | ### Runtimes common Integration tests
47 |
48 | Specification: [Runtimes common - Integration tests](https://github.com/GoogleCloudPlatform/runtimes-common/tree/master/integration_tests)
49 |
50 | The integration tests will deploy a sample application to App Engine Flex and run remote tests against this application to ensure
51 | that the standard requirements of the [language runtime image](https://github.com/GoogleCloudPlatform/runtimes-common/tree/master/integration_tests#tests) are respected.
52 |
53 | Before running these tests ensure that:
54 | * [Maven](https://maven.apache.org/download.cgi) is installed
55 | * [Google Cloud SDK](https://cloud.google.com/sdk) is installed
56 |
57 | When running those tests you need to indicate which image needs to be tested.
58 | As the tests will be executed remotely, the image needs to be pushed to a gcr.io repository.
59 |
60 | A script is available to run those tests:
61 | ```bash
62 | RUNTIME_IMAGE=gcr.io/my-project-id/tomcat:tag
63 | gcloud docker -- push $RUNTIME_IMAGE
64 | ./scripts/integration_test.sh $RUNTIME_IMAGE
65 | ```
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Google Cloud Platform Tomcat Runtime Image
2 |
3 | [](http://github.com/badges/stability-badges)
4 |
5 | **This Project is Archived**
6 |
7 | This repository contains the source for the Google-maintained Tomcat [docker](https://docker.com) image.
8 |
9 | ## Using the Tomcat image
10 |
11 |
12 |
13 | ### Running on App Engine Flexible
14 |
15 | #### Cloud SDK
16 |
17 | Simply choose the Java runtime with Tomcat as the server preference in `app.yaml`.
18 |
19 | ```yaml
20 | runtime: java
21 | env: flex
22 | runtime_config:
23 | server: tomcat8
24 | ```
25 |
26 | Then you can deploy using the `gcloud beta app deploy` [command](https://cloud.google.com/sdk/gcloud/reference/beta/app/deploy). Note that the "beta" command is currently required.
27 |
28 | #### Using custom Dockerfile
29 |
30 | Specify a custom runtime in `app.yaml`.
31 |
32 | ```yaml
33 | runtime: custom
34 | env: flex
35 | ```
36 |
37 | Then create a `Dockerfile` that uses the Tomcat runtime image as specified in [Other platforms](#other-platforms).
38 |
39 | ### Other platforms
40 |
41 | Create a `Dockerfile` based on the image and add your application WAR:
42 |
43 | ```dockerfile
44 | FROM gcr.io/google-appengine/tomcat
45 | COPY your-application.war $APP_DESTINATION_WAR
46 | ```
47 |
48 | You can also use an exploded-war:
49 |
50 | ```dockerfile
51 | COPY your-application $APP_DESTINATION_EXPLODED_WAR
52 | ```
53 |
54 | ## Configuring Tomcat
55 |
56 | ### Tomcat properties
57 | The Tomcat instance can be configured through the environment variable `TOMCAT_PROPERTIES` which is
58 | a comma-separated list of `name=value` pairs appended to `catalina.properties`.
59 |
60 | ### Security best practices
61 |
62 | #### Execute tomcat with a non-root user
63 | For security purposes it is recommended to start the Tomcat instance using the `tomcat` user.
64 |
65 | You can do so by adding `USER tomcat` at the end of your Dockerfile.
66 |
67 | ```dockerfile
68 | FROM gcr.io/google-appengine/tomcat
69 | COPY your-application.war $APP_DESTINATION_WAR
70 |
71 | RUN chown tomcat:tomcat $APP_DESTINATION_WAR
72 | USER tomcat
73 | ```
74 |
75 | If you are using an exploded-war, then use the `$APP_DESTINATION_EXPLODED_WAR` environment variable instead.
76 |
77 | ## Optional Features
78 | ### Distributed sessions
79 | This image can be configured to store Tomcat sessions in the [Google Cloud Datastore](https://cloud.google.com/datastore/docs) which allows
80 | multiple instances of Tomcat to share sessions.
81 |
82 | You can enable this feature by adding `distributed-sessions` to the list of optional modules, which is specified in the `TOMCAT_MODULES_ENABLE` environment variable.
83 |
84 | The distributed sessions module can be configured through the environment variable `TOMCAT_PROPERTIES`.
85 |
86 | | Property | Description | Default |
87 | |---|---|---|
88 | | gcp.distributed-sessions.namespace | Namespace to use in the Datastore. | tomcat-gcp-persistent-session |
89 | | gcp.distributed-sessions.sessionKind | Name of the entity used to store sessions in the Datastore. | TomcatGCloudSession |
90 | | gcp.distributed-sessions.uriExcludePattern | [Pattern](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) specifying which URI to ignore when persisting sessions. | null |
91 | | gcp.distributed-sessions.enableTrace | Register the operations of the module in Stackdriver Trace. (The [Trace module](#stackdriver-trace) must also be active) | false |
92 |
93 | For example on Google App Engine:
94 |
95 | ```yaml
96 | env_variables:
97 | TOMCAT_MODULES_ENABLE: distributed-sessions
98 | TOMCAT_PROPERTIES: gcp.distributed-sessions.uriExcludePattern=^/_ah/.*
99 | ```
100 |
101 | #### Usage outside of Google Cloud Platform
102 | If you are using the runtime outside of GCP, you will want to make sure that your application has access to
103 | the Datastore. In this case, check out the [Google Cloud Authentication](https://developers.google.com/identity/protocols/application-default-credentials) guide.
104 |
105 | ### Stackdriver Trace
106 | The trace module sends information about requests (such as latency) to the [Stackdriver Trace service](https://cloud.google.com/trace/docs/).
107 |
108 | To enable this module add `stackdriver-trace` to the list of enabled modules.
109 |
110 | ```yaml
111 | env_variables:
112 | TOMCAT_MODULES_ENABLE: stackdriver-trace
113 | ```
114 |
115 | The following configuration is available through the the environment variable `TOMCAT_PROPERTIES`.
116 |
117 | | Property | Description | Default |
118 | |---|---|---|
119 | | gcp.stackdriver-trace.scheduledDelay | The traces are grouped before being sent to the Stackdriver service, this is the maximum time in seconds a trace can be buffered| 15 |
120 |
121 | #### Usage outside of Google Cloud Platform
122 | When you are using this module outside of GCP you need to provide credentials through [Google Cloud Authentication](https://developers.google.com/identity/protocols/application-default-credentials).
123 |
124 | ### Stackdriver Logging
125 | When the Tomcat runtime is running on Google App Engine flexible environment all output to stdout/stderr is forwarded to Stackdriver Logging
126 | and available in the Cloud Console Log Viewer.
127 |
128 | However more detailed and integrated logs are available if the [Stackdriver Logging](https://cloud.google.com/logging/) mechanism is used directly.
129 |
130 | To take advantage of this integration, add the [Google Cloud Java Client for Logging](https://github.com/GoogleCloudPlatform/google-cloud-java/tree/master/google-cloud-logging)
131 | to your dependencies and provide a Java Util Logging configuration file (`logging.properties`) as part of the resources of the application (`classes/logging.properties`) with the following content:
132 |
133 | ```properties
134 | handlers=com.google.cloud.logging.LoggingHandler
135 |
136 | # Optional configuration
137 | .level=FINE
138 | com.google.cloud.logging.LoggingHandler.level=FINE
139 | com.google.cloud.logging.LoggingHandler.log=gae_app.log
140 | com.google.cloud.logging.LoggingHandler.formatter=java.util.logging.SimpleFormatter
141 | java.util.logging.SimpleFormatter.format=%3$s: %5$s%6$s
142 | ```
143 |
144 | ### Enabling gzip compression
145 | Tomcat offers the possibility to use GZIP compression.
146 |
147 | To take advantage of this feature set the property `tomcat.server.connector.compression` to `on`:
148 | ```yaml
149 | env_variables:
150 | TOMCAT_PROPERTIES: tomcat.server.connector.compression=on
151 | ```
152 |
153 | You can also specify a numerical value indicating the minimal amount of data (in bytes) before using compression.
154 |
155 | Detailed documentation can be found in the [Tomcat documentation](http://tomcat.apache.org/tomcat-8.5-doc/config/http.html),
156 | at the attribute `compression`.
157 |
158 | ## Development Guide
159 |
160 | * See [instructions](DEVELOPING.md) on how to build and test this image.
161 |
162 | ### Contributing changes
163 |
164 | * See [CONTRIBUTING.md](CONTRIBUTING.md)
165 |
166 | ## Licensing
167 |
168 | * See [LICENSE](LICENSE)
169 |
--------------------------------------------------------------------------------
/checkstyle/custom-checks.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/checkstyle/java.header:
--------------------------------------------------------------------------------
1 | ^/\*$
2 | ^ \* Copyright \(C\) \d\d\d\d Google Inc\.$
3 | ^ \*$
4 | ^ \* Licensed under the Apache License, Version 2\.0 \(the "License"\);$
5 | ^ \* you may not use this file except in compliance with the License\.$
6 | ^ \* You may obtain a copy of the License at$
7 | ^ \*$
8 | ^ \* http://www.apache.org/licenses/LICENSE-2.0$
9 | ^ \*$
10 | ^ \* Unless required by applicable law or agreed to in writing, software$
11 | ^ \* distributed under the License is distributed on an "AS IS" BASIS,$
12 | ^ \* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied\.$
13 | ^ \* See the License for the specific language governing permissions and$
14 | ^ \* limitations under the License\.$
15 | ^ \*/$
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
20 | 4.0.0
21 |
22 | com.google.cloud.runtimes
23 | tomcat-parent
24 | 0.1.0-SNAPSHOT
25 | pom
26 | Google Tomcat Runtime Parent
27 |
28 |
29 | UTF-8
30 | UTF-8
31 | yyyy-MM-dd-HH-mm
32 | 1.0.1
33 | 8
34 | 5
35 | 15
36 | ${tomcat.major.version}.${tomcat.minor.version}.${tomcat.dot.version}
37 |
38 | ${tomcat.major.version}
39 | ${tomcat.major.version}.${tomcat.minor.version}-${maven.build.timestamp}
40 |
41 | tomcat
42 | gcr.io/google-appengine/openjdk:8
43 | gcr.io/$PROJECT_ID
44 | ${docker.project.namespace}/${docker.image.name}:${docker.tag.long}
45 |
46 |
47 |
48 | tomcat-gcp-lib
49 | tomcat
50 | tests/test-war-simple
51 | tests/test-war-integration
52 | tests/test-war-spring-boot
53 | tests/test-distributed-war
54 |
55 |
56 |
57 |
58 |
59 | maven-checkstyle-plugin
60 | 2.17
61 |
62 | false
63 | true
64 | warning
65 |
66 |
67 |
68 | com.puppycrawl.tools
69 | checkstyle
70 | 8.1
71 |
72 |
73 |
74 |
75 | validate-google-style
76 | validate
77 |
78 | check
79 |
80 |
81 | google_checks.xml
82 |
83 |
84 |
85 | validate-file-header
86 | validate
87 |
88 | check
89 |
90 |
91 | checkstyle/java.header
92 | checkstyle/custom-checks.xml
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | org.apache.maven.plugins
102 | maven-enforcer-plugin
103 | 1.4.1
104 |
105 |
106 | enforce-maven
107 |
108 | enforce
109 |
110 |
111 |
112 |
113 | [3.0,)
114 |
115 |
116 | [1.8,)
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 | org.apache.maven.plugins
125 | maven-dependency-plugin
126 | 3.0.1
127 |
128 |
129 | org.apache.maven.plugins
130 | maven-resources-plugin
131 | 3.0.1
132 |
133 |
134 | org.apache.maven.plugins
135 | maven-compiler-plugin
136 | 3.6.1
137 |
138 | 1.8
139 | 1.8
140 |
141 |
142 |
143 | io.fabric8
144 | docker-maven-plugin
145 | 0.21.0
146 |
147 |
148 |
149 |
150 |
151 |
152 |
--------------------------------------------------------------------------------
/scripts/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright 2017 Google Inc. All rights reserved.
4 |
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 |
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | #
18 | # All the arguments passed to this script are transferred to the maven command
19 | #
20 |
21 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
22 | projectRoot=${dir}/..
23 |
24 | source ${projectRoot}/scripts/utils/maven.sh
25 |
26 | pushd ${projectRoot}
27 | maven_utils::execute clean install
28 | popd
29 |
30 | gcloud builds submit --config ${projectRoot}/tomcat/target/cloudbuild/build.yaml ${projectRoot}
31 |
--------------------------------------------------------------------------------
/scripts/ci/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright 2017 Google Inc. All rights reserved.
4 |
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 |
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 | set -e
17 |
18 | readonly dir=$(dirname $0)
19 | readonly projectRoot=$dir/../..
20 |
21 | # Load the library with Maven utilities
22 | source ${projectRoot}/scripts/utils/maven.sh
23 | source ${projectRoot}/scripts/utils/gcloud.sh
24 |
25 | # If no namespace is specified deduct it from the gcloud CLI
26 | if [ -z "$DOCKER_NAMESPACE" ]; then
27 | export DOCKER_NAMESPACE="gcr.io/$(gcloud_utils::get_project_name)"
28 | fi
29 |
30 | # If we are in a gcloud environment we want to initialize the gcloud CLI
31 | if [ -n "$GCLOUD_FILE" ]; then
32 | source ${dir}/gcloud-init.sh
33 | fi
34 |
35 | # Generate the Docker image tag from the git tag and generate the complete image name
36 | pushd ${projectRoot}
37 | export DOCKER_TAG_LONG=$(git rev-parse --short HEAD)
38 | readonly IMAGE=$(maven_utils::get_property cloudbuild.tomcat.image)
39 | popd
40 |
41 | echo "Building $IMAGE and running structure tests"
42 | ${projectRoot}/scripts/build.sh
43 |
44 | echo "Running integration tests on $IMAGE"
45 | ${projectRoot}/scripts/integration_test.sh ${IMAGE}
46 |
--------------------------------------------------------------------------------
/scripts/ci/gcloud-init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright 2016 Google Inc. All rights reserved.
4 |
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 |
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 |
18 | # Set up gcloud and auth
19 | set -ex
20 |
21 | DIR=$(pwd)
22 |
23 | if [ -z $GCLOUD_FILE ]; then
24 | echo '$GCLOUD_FILE environment variable must be set.'
25 | exit 1
26 | fi
27 |
28 | if [ -z $KEYFILE ]; then
29 | echo '$KEYFILE environment variable must be set.'
30 | exit 1
31 | fi
32 |
33 | if [ -z $GCP_PROJECT ]; then
34 | echo '$GCP_PROJECT environment variable must be set.'
35 | exit 1
36 | fi
37 |
38 | LOCAL_GCLOUD_FILE=gcloud.tar.gz
39 | cp $GCLOUD_FILE $LOCAL_GCLOUD_FILE
40 |
41 | # Hide the output here, it's long.
42 | tar -xzf $LOCAL_GCLOUD_FILE
43 | export PATH=$DIR/google-cloud-sdk/bin:$PATH
44 |
45 | gcloud auth activate-service-account --key-file=$KEYFILE
46 | gcloud config set project $GCP_PROJECT
47 | export GOOGLE_APPLICATION_CREDENTIALS=$KEYFILE
48 |
49 | gcloud components install beta -q
--------------------------------------------------------------------------------
/scripts/ci/release-cloudbuild.yaml:
--------------------------------------------------------------------------------
1 | steps:
2 | # Perform maven build, omitting local docker operations
3 | - name: 'gcr.io/cloud-builders/mvn:3.5.0-jdk-8'
4 | args:
5 | - '--batch-mode'
6 | - '-P-local-docker-build'
7 | - '-P-test.local'
8 | - '-Ddocker.tag.long=${_DOCKER_TAG}'
9 | - 'clean'
10 | - 'install'
11 |
12 | # Build the runtime container
13 | - name: 'gcr.io/cloud-builders/docker'
14 | args: ['build', '--tag=${_IMAGE}', '--no-cache', 'tomcat/target/docker-src']
15 |
16 | # Runtimes-common structure tests
17 | # See https://github.com/GoogleCloudPlatform/runtimes-common/tree/master/structure_tests
18 | - name: 'gcr.io/gcp-runtimes/structure_test'
19 | args: ['--image', '${_IMAGE}', '-v', '--config', '/workspace/tomcat/target/test-classes/structure.yaml']
20 |
21 | images: ['${_IMAGE}']
22 |
--------------------------------------------------------------------------------
/scripts/ci/release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright 2017 Google Inc. All rights reserved.
4 |
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 |
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
18 | projectRoot=${dir}/../..
19 |
20 | source ${projectRoot}/scripts/utils/gcloud.sh
21 |
22 | RUNTIME_NAME='tomcat'
23 | RUNTIME_VERSION='8.5'
24 |
25 | if [ -z "${DOCKER_TAG}" ]; then
26 | DOCKER_TAG="${RUNTIME_VERSION}-$(date -u +%Y-%m-%d_%H_%M)"
27 | fi
28 |
29 | if [ -z "$DOCKER_NAMESPACE" ]; then
30 | DOCKER_NAMESPACE="gcr.io/$(gcloud_utils::get_project_name)"
31 | fi
32 |
33 | IMAGE="${DOCKER_NAMESPACE}/${RUNTIME_NAME}:${DOCKER_TAG}"
34 |
35 | gcloud builds submit \
36 | --config ${dir}/release-cloudbuild.yaml \
37 | --substitutions="_IMAGE=$IMAGE,_DOCKER_TAG=$DOCKER_TAG" \
38 | ${projectRoot}
39 |
40 | # Allow external script to reference the image tag
41 | export TAG=${DOCKER_TAG}
--------------------------------------------------------------------------------
/scripts/integration_test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright 2016 Google Inc. All rights reserved.
4 |
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 |
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | set -e
18 |
19 | # Runs integration tests on a given runtime image
20 |
21 | readonly dir=$(dirname $0)
22 | readonly projectRoot=${dir}/..
23 | readonly testAppDir=${projectRoot}/tests/test-war-integration
24 | readonly deployDir=${testAppDir}/target/docker-src
25 | readonly service="tomcat-runtime-integration"
26 |
27 | readonly imageUnderTest=$1
28 | if [ -z "${imageUnderTest}" ]; then
29 | echo "Usage: ${0} "
30 | exit 1
31 | fi
32 |
33 | # build the test app
34 | pushd ${testAppDir}
35 | mvn -B -P-local-docker-build -P-local.test clean install
36 | popd
37 |
38 | # deploy to app engine
39 | pushd $deployDir
40 | export STAGING_IMAGE=$imageUnderTest
41 | envsubst '$STAGING_IMAGE' < Dockerfile.in > Dockerfile
42 | echo "Deploying to App Engine..."
43 | gcloud app deploy -q
44 | popd
45 |
46 | readonly DEPLOYED_APP_URL="https://${service}-dot-$(gcloud app describe | grep defaultHostname | awk '{print $2}')"
47 | echo "Running integration tests on application that is deployed at $DEPLOYED_APP_URL"
48 |
49 | # run in cloud container builder
50 | gcloud builds submit \
51 | --config $testAppDir/target/cloudbuild/integration.yaml \
52 | --substitutions "_DEPLOYED_APP_URL=$DEPLOYED_APP_URL" \
53 | --timeout=25m \
54 | ${dir}
--------------------------------------------------------------------------------
/scripts/utils/gcloud.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright 2017 Google Inc. All rights reserved.
4 |
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 |
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | function gcloud_utils::get_project_name () {
18 | echo $(gcloud info \
19 | | awk '/^Project: / { print $2 }' \
20 | | sed 's/\[//' \
21 | | sed 's/\]//')
22 | }
23 |
--------------------------------------------------------------------------------
/scripts/utils/maven.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright 2017 Google Inc. All rights reserved.
4 |
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 |
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | # Execute an arbitrary maven command
18 | # Arguments:
19 | # - All the arguments are passed to the maven command (Example: clean install)
20 | #
21 | function maven_utils::execute () {
22 | if [ -n "$DOCKER_NAMESPACE" ]; then
23 | set -- $@ "-Ddocker.project.namespace=$DOCKER_NAMESPACE"
24 | fi
25 |
26 | if [ -n "$DOCKER_TAG_LONG" ]; then
27 | set -- $@ "-Ddocker.tag.long=$DOCKER_TAG_LONG"
28 | fi
29 |
30 | mvn -P-local-docker-build -P-test.local "$@"
31 | }
32 |
33 | # Return a maven property
34 | # Arguments:
35 | # - The name of the property (Example: docker.tag.long)
36 | #
37 | function maven_utils::get_property () {
38 | echo $(maven_utils::execute org.codehaus.mojo:exec-maven-plugin:1.6.0:exec \
39 | --non-recursive -q \
40 | -Dexec.executable="echo" \
41 | -Dexec.args="\${$1}" )
42 | }
--------------------------------------------------------------------------------
/scripts/utils/structure_test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright 2017 Google Inc. All rights reserved.
4 |
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 |
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | #
18 | # Fetch and execute the structure test framework run script.
19 | #
20 | dir=`dirname $0`
21 | scriptPath=https://raw.githubusercontent.com/GoogleCloudPlatform/runtimes-common/a5efef7f1f2cfd60814641fcff8239ea301e661d/structure_tests/ext_run.sh
22 | destDir=$dir/../target
23 | fileName=$destDir/run_structure_tests.sh
24 |
25 | if [ ! -d $destDir ]
26 | then
27 | mkdir -p $destDir
28 | fi
29 |
30 | curl $scriptPath > $fileName
31 | bash $fileName "$@"
--------------------------------------------------------------------------------
/tests/test-distributed-war/.gitignore:
--------------------------------------------------------------------------------
1 | credential.json
--------------------------------------------------------------------------------
/tests/test-distributed-war/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | tomcat-parent
7 | com.google.cloud.runtimes
8 | 0.1.0-SNAPSHOT
9 | ../../pom.xml
10 |
11 | 4.0.0
12 |
13 | test-distributed-war
14 | Tomcat-Runtime :: Tests :: Distributed Tests
15 | war
16 |
17 |
18 | ${docker.image.name}:${docker.tag.long}
19 |
20 |
21 |
22 |
23 | javax.servlet
24 | javax.servlet-api
25 | 3.1.0
26 | provided
27 |
28 |
29 |
30 |
31 |
32 | local-docker-build
33 |
34 | true
35 |
36 |
37 |
38 |
39 | io.fabric8
40 | docker-maven-plugin
41 |
42 |
43 | build
44 | package
45 |
46 | build
47 |
48 |
49 |
50 |
51 | ${project.artifactId}
52 |
53 | ${project.build.directory}/docker-src
54 |
55 | ${project.version}
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | maven-resources-plugin
73 |
74 |
75 | copy-dockerfile
76 | generate-resources
77 |
78 | copy-resources
79 |
80 |
81 | ${project.build.directory}/docker-src
82 |
83 |
84 | ${basedir}/src/main/docker
85 | true
86 |
87 |
88 |
89 |
90 |
91 | copy-appengine-file
92 | generate-resources
93 |
94 | copy-resources
95 |
96 |
97 | ${project.build.directory}/docker-src
98 |
99 |
100 | ${basedir}/src/main/appengine
101 | true
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | org.apache.maven.plugins
110 | maven-war-plugin
111 | 3.1.0
112 |
113 | ${project.build.directory}/docker-src
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/tests/test-distributed-war/src/main/appengine/app.yaml:
--------------------------------------------------------------------------------
1 | runtime: custom
2 | env: flex
3 | service: tomcat-distributed-test
4 |
5 | manual_scaling:
6 | instances: 1
7 |
8 | handlers:
9 | - url: /.*
10 | script: ignored
11 | secure: optional
--------------------------------------------------------------------------------
/tests/test-distributed-war/src/main/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Google Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | FROM ${test.docker.image}
15 | ADD ${project.build.finalName}.war $APP_DESTINATION
16 |
17 | COPY credential.json /credentials/key.json
18 | ENV GOOGLE_APPLICATION_CREDENTIALS /credentials/key.json
19 |
20 | ENV TOMCAT_MODULES_ENABLE distributed-sessions,stackdriver-trace
21 | ENV TOMCAT_LOGGING_PROPERTIES com.google.cloud.runtimes.tomcat.session.level=ALL,gcp.distributed-sessions.enableTrace=true
--------------------------------------------------------------------------------
/tests/test-distributed-war/src/main/docker/credential.json:
--------------------------------------------------------------------------------
1 | /* If you are using the test-distributed-war outside of Google Cloud, you must replace this file
2 | * with your crendentials in order to access the datastore service.
3 | * You can find the procedure to generate credential.json at:
4 | * https://developers.google.com/identity/protocols/application-default-credentials
5 | */
--------------------------------------------------------------------------------
/tests/test-distributed-war/src/main/java/com/google/cloud/runtimes/tomcat/test/distributed/SessionServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.test.distributed;
18 |
19 | import java.io.IOException;
20 | import java.util.Arrays;
21 | import java.util.HashMap;
22 | import java.util.Map;
23 | import java.util.stream.DoubleStream;
24 | import javax.servlet.ServletException;
25 | import javax.servlet.annotation.WebServlet;
26 | import javax.servlet.http.HttpServlet;
27 | import javax.servlet.http.HttpServletRequest;
28 | import javax.servlet.http.HttpServletResponse;
29 |
30 | /**
31 | * This servlet demonstrates the usage of distributed sessions by adding session parameters and
32 | * modifying their values at each requests.
33 | */
34 | @WebServlet(urlPatterns = {"/session"})
35 | public class SessionServlet extends HttpServlet {
36 |
37 | @Override
38 | protected void doGet(HttpServletRequest req, HttpServletResponse resp)
39 | throws ServletException, IOException {
40 | resp.setContentType("text/plain");
41 |
42 | int count = 0;
43 | Object sessionValue = req.getSession().getAttribute("count");
44 | resp.getWriter().println("Max inactive time: " + req.getSession().getMaxInactiveInterval());
45 |
46 | if (sessionValue != null) {
47 | count = (int)sessionValue;
48 | count++;
49 | resp.getWriter().println("Session parameter: " + count);
50 | } else {
51 | resp.getWriter().println("No session parameter found, reload the page");
52 | }
53 |
54 | Map map = new HashMap<>();
55 | map.put("First entry", Arrays.asList(1,2,3,4,5));
56 | map.put("Second entry", DoubleStream.generate(() -> Math.random() * 10000)
57 | .limit(5000)
58 | .toArray());
59 |
60 | req.getSession().setAttribute("count", count);
61 | if (Math.random() > 0.7) {
62 | resp.getWriter().println("Modified map");
63 | req.getSession().setAttribute("map", map);
64 | }
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/tests/test-distributed-war/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 | Web Application for Servlet 3.1
8 |
9 | 1
10 |
11 |
--------------------------------------------------------------------------------
/tests/test-war-integration/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
20 |
21 | tomcat-parent
22 | com.google.cloud.runtimes
23 | 0.1.0-SNAPSHOT
24 | ../../pom.xml
25 |
26 | 4.0.0
27 |
28 | test-war-integration
29 | Tomcat-Runtime :: Tests :: Integration Tests
30 | war
31 |
32 |
33 | ${docker.image.name}:${docker.tag.long}
34 |
35 |
36 |
37 |
38 | javax.servlet
39 | javax.servlet-api
40 | 3.1.0
41 | provided
42 |
43 |
44 | com.fasterxml.jackson.core
45 | jackson-databind
46 | 2.8.7
47 |
48 |
49 | com.google.cloud
50 | google-cloud-logging
51 | 1.4.0
52 |
53 |
54 | com.google.cloud
55 | google-cloud-monitoring
56 | 0.22.0-alpha
57 |
58 |
59 |
60 |
61 |
62 | local-docker-build
63 |
64 | true
65 |
66 |
67 |
68 |
69 | io.fabric8
70 | docker-maven-plugin
71 |
72 |
73 | build
74 | package
75 |
76 | build
77 |
78 |
79 |
80 |
81 | ${project.artifactId}
82 |
83 | ${project.build.directory}/docker-src
84 |
85 | ${project.version}
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 | maven-resources-plugin
102 |
103 |
104 | copy-dockerfile
105 | generate-resources
106 |
107 | copy-resources
108 |
109 |
110 | ${project.build.directory}/docker-src
111 |
112 |
113 | ${basedir}/src/main/docker
114 | true
115 |
116 |
117 |
118 |
119 |
120 | copy-cloudbuild-configuration
121 | generate-resources
122 |
123 | copy-resources
124 |
125 |
126 | ${project.build.directory}/cloudbuild
127 |
128 |
129 | ${basedir}/src/main/cloudbuild
130 | true
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | org.apache.maven.plugins
139 | maven-war-plugin
140 | 3.1.0
141 |
142 | ${project.build.directory}/docker-src
143 |
144 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/cloudbuild/integration.yaml:
--------------------------------------------------------------------------------
1 | # Cloud Builder pipeline for running integration tests
2 | # https://cloud.google.com/container-builder/docs/overview
3 | steps:
4 | # Runtimes-common integration tests
5 | # See https://github.com/GoogleCloudPlatform/runtimes-common/tree/master/integration_tests
6 | - name: 'gcr.io/gcp-runtimes/integration_test:2017-09-18-104536'
7 | args:
8 | - '--url=${_DEPLOYED_APP_URL}'
9 | - '--verbose'
10 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Google Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | FROM ${test.docker.image}
15 | COPY ${project.build.finalName}.war $CATALINA_BASE/webapps/ROOT.war
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/docker/Dockerfile.in:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Google Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | FROM ${STAGING_IMAGE}
15 | ADD ${project.build.finalName}.war $CATALINA_BASE/webapps/ROOT.war
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/docker/app.yaml:
--------------------------------------------------------------------------------
1 | runtime: custom
2 | env: flex
3 | service: tomcat-runtime-integration
4 |
5 | manual_scaling:
6 | instances: 1
7 |
8 | env_variables:
9 | TOMCAT_MODULES_ENABLE: stackdriver-trace
10 |
11 | handlers:
12 | - url: /.*
13 | script: ignored
14 | secure: optional
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/java/com/google/cloud/runtimes/tomcat/test/integration/CustomLoggingServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.test.integration;
18 |
19 | import com.fasterxml.jackson.databind.JsonNode;
20 | import com.fasterxml.jackson.databind.ObjectMapper;
21 | import com.google.cloud.MonitoredResource;
22 | import com.google.cloud.logging.LogEntry;
23 | import com.google.cloud.logging.Logging;
24 | import com.google.cloud.logging.LoggingOptions;
25 | import com.google.cloud.logging.Payload;
26 | import com.google.cloud.logging.Severity;
27 | import java.io.IOException;
28 | import java.net.URLEncoder;
29 | import java.util.Collections;
30 | import javax.servlet.ServletException;
31 | import javax.servlet.annotation.WebServlet;
32 | import javax.servlet.http.HttpServlet;
33 | import javax.servlet.http.HttpServletRequest;
34 | import javax.servlet.http.HttpServletResponse;
35 |
36 | @WebServlet(urlPatterns = "/logging_custom")
37 | public class CustomLoggingServlet extends HttpServlet {
38 |
39 | private static final ObjectMapper objectMapper = new ObjectMapper();
40 |
41 | private final Logging logging = LoggingOptions.getDefaultInstance().getService();
42 |
43 | @Override
44 | protected void doPost(HttpServletRequest req, HttpServletResponse resp)
45 | throws ServletException, IOException {
46 |
47 | JsonNode body = objectMapper.readTree(req.getReader());
48 | String logName = body.path("log_name").asText();
49 | String token = body.path("token").asText();
50 | Severity severity = Severity.valueOf(body.path("level").asText());
51 |
52 | LogEntry logEntry = LogEntry.newBuilder(Payload.StringPayload.of(token))
53 | .setLogName(logName)
54 | .setSeverity(severity)
55 | .setResource(MonitoredResource.newBuilder("global").build())
56 | .build();
57 |
58 | logging.write(Collections.singletonList(logEntry));
59 |
60 | resp.setContentType("text/plain");
61 | resp.getWriter().println(URLEncoder.encode(logName, "UTF-8"));
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/java/com/google/cloud/runtimes/tomcat/test/integration/CustomServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.test.integration;
18 |
19 | import com.google.common.io.CharStreams;
20 | import java.io.IOException;
21 | import java.io.InputStreamReader;
22 | import javax.servlet.ServletException;
23 | import javax.servlet.annotation.WebServlet;
24 | import javax.servlet.http.HttpServlet;
25 | import javax.servlet.http.HttpServletRequest;
26 | import javax.servlet.http.HttpServletResponse;
27 |
28 | /**
29 | * Reference the custom tests for the integration framework
30 | * (https://github.com/GoogleCloudPlatform/runtimes-common/tree/master/integration_tests)
31 | */
32 | @WebServlet(urlPatterns = {"/custom"})
33 | public class CustomServlet extends HttpServlet {
34 |
35 | @Override
36 | protected void doGet(HttpServletRequest req, HttpServletResponse resp)
37 | throws ServletException, IOException {
38 |
39 | String configuration = CharStreams.toString(
40 | new InputStreamReader(getClass().getResourceAsStream("/custom-test-specification.json")));
41 |
42 | resp.setContentType("application/json");
43 | resp.getWriter().print(configuration);
44 | }
45 | }
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/java/com/google/cloud/runtimes/tomcat/test/integration/DumpServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.test.integration;
18 |
19 | import java.io.IOException;
20 | import java.io.PrintWriter;
21 | import java.util.Collections;
22 | import java.util.logging.Logger;
23 | import javax.servlet.ServletException;
24 | import javax.servlet.annotation.WebServlet;
25 | import javax.servlet.http.HttpServlet;
26 | import javax.servlet.http.HttpServletRequest;
27 | import javax.servlet.http.HttpServletResponse;
28 |
29 | @WebServlet(urlPatterns = {"/dump/*"})
30 | public class DumpServlet extends HttpServlet {
31 |
32 | private static final Logger log = Logger.getLogger(DumpServlet.class.getName());
33 |
34 | @Override
35 | protected void doGet(HttpServletRequest request, HttpServletResponse response)
36 | throws ServletException, IOException {
37 | log.info("JUL.info:" + request.getRequestURI());
38 | getServletContext().log("ServletContext.log:" + request.getRequestURI());
39 | if (Boolean.parseBoolean(request.getParameter("throw"))) {
40 | throw new ServletException("Test Exception");
41 | }
42 |
43 | response.setContentType("text/html");
44 | response.setStatus(HttpServletResponse.SC_OK);
45 |
46 | PrintWriter out = response.getWriter();
47 |
48 | out.println("
");
110 | for (String n : System.getenv().keySet()) {
111 | out.printf("%s=%s%n", n, System.getenv(n));
112 | }
113 | out.println("
");
114 |
115 | out.println("
System Properties:
");
116 | out.println("
");
117 | for (Object n : System.getProperties().keySet()) {
118 | out.printf("%s=%s%n", n, System.getProperty(String.valueOf(n)));
119 | }
120 | out.println("
");
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/java/com/google/cloud/runtimes/tomcat/test/integration/ExceptionServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.test.integration;
18 |
19 | import com.fasterxml.jackson.databind.ObjectMapper;
20 |
21 | import java.io.IOException;
22 | import javax.servlet.ServletException;
23 | import javax.servlet.annotation.WebServlet;
24 | import javax.servlet.http.HttpServlet;
25 | import javax.servlet.http.HttpServletRequest;
26 | import javax.servlet.http.HttpServletResponse;
27 |
28 | @WebServlet(urlPatterns = {"/exception"})
29 | public class ExceptionServlet extends HttpServlet {
30 |
31 | private static final ObjectMapper objectMapper = new ObjectMapper();
32 |
33 | static class ExceptionRequest {
34 | public String token;
35 | }
36 |
37 | @Override
38 | protected void doPost(HttpServletRequest req, HttpServletResponse resp)
39 | throws ServletException, IOException {
40 | ExceptionRequest exceptionRequest
41 | = objectMapper.readValue(req.getReader(), ExceptionRequest.class);
42 |
43 | // Print an exception stack trace containing the provided token. This should be picked up by
44 | // Stackdriver exception monitoring.
45 | new RuntimeException(String.format(
46 | "Sample runtime exception for integration test. Token is %s", exceptionRequest.token))
47 | .printStackTrace();
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/java/com/google/cloud/runtimes/tomcat/test/integration/HelloServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.test.integration;
18 |
19 | import java.io.IOException;
20 | import javax.servlet.ServletException;
21 | import javax.servlet.annotation.WebServlet;
22 | import javax.servlet.http.HttpServlet;
23 | import javax.servlet.http.HttpServletRequest;
24 | import javax.servlet.http.HttpServletResponse;
25 |
26 | @WebServlet(urlPatterns = {"/"})
27 | public class HelloServlet extends HttpServlet {
28 |
29 | @Override
30 | protected void doGet(HttpServletRequest req, HttpServletResponse resp)
31 | throws ServletException, IOException {
32 | resp.setContentType("text.plain");
33 | resp.getWriter().print("Hello World!");
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/java/com/google/cloud/runtimes/tomcat/test/integration/MonitoringServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.test.integration;
18 |
19 | import com.fasterxml.jackson.databind.JsonNode;
20 | import com.fasterxml.jackson.databind.ObjectMapper;
21 | import com.google.api.Metric;
22 | import com.google.cloud.ServiceOptions;
23 | import com.google.cloud.Timestamp;
24 | import com.google.cloud.monitoring.v3.MetricServiceClient;
25 | import com.google.monitoring.v3.Point;
26 | import com.google.monitoring.v3.ProjectName;
27 | import com.google.monitoring.v3.TimeInterval;
28 | import com.google.monitoring.v3.TimeSeries;
29 | import com.google.monitoring.v3.TypedValue;
30 | import java.io.IOException;
31 | import java.util.Collections;
32 | import java.util.logging.Logger;
33 | import javax.servlet.ServletException;
34 | import javax.servlet.annotation.WebServlet;
35 | import javax.servlet.http.HttpServlet;
36 | import javax.servlet.http.HttpServletRequest;
37 | import javax.servlet.http.HttpServletResponse;
38 |
39 | @WebServlet(urlPatterns = "/monitoring")
40 | public class MonitoringServlet extends HttpServlet {
41 |
42 | private static final ObjectMapper objectMapper = new ObjectMapper();
43 |
44 | private static final Logger logger = Logger.getLogger(MonitoringServlet.class.getName());
45 |
46 | @Override
47 | protected void doPost(HttpServletRequest req, HttpServletResponse resp)
48 | throws ServletException, IOException {
49 | JsonNode body = objectMapper.readTree(req.getReader());
50 | String name = body.path("name").asText();
51 | long token = body.path("token").asLong();
52 |
53 | logger.info("Creating Time series with name " + name + " and token " + token);
54 |
55 | MetricServiceClient serviceClient = MetricServiceClient.create();
56 |
57 | TimeSeries timeSeries =
58 | TimeSeries.newBuilder()
59 | .addPoints(Point.newBuilder()
60 | .setValue(TypedValue.newBuilder().setInt64Value(token))
61 | .setInterval(TimeInterval.newBuilder()
62 | .setEndTime(Timestamp.now().toProto())))
63 | .setMetric(Metric.newBuilder().setType(name))
64 | .build();
65 |
66 | serviceClient.createTimeSeries(
67 | ProjectName.create(ServiceOptions.getDefaultProjectId()),
68 | Collections.singletonList(timeSeries));
69 |
70 | resp.setContentType("text/plain");
71 | resp.getWriter().println("OK");
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/java/com/google/cloud/runtimes/tomcat/test/integration/SecureTestServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.test.integration;
18 |
19 | import java.io.IOException;
20 | import javax.servlet.ServletException;
21 | import javax.servlet.annotation.WebServlet;
22 | import javax.servlet.http.HttpServlet;
23 | import javax.servlet.http.HttpServletRequest;
24 | import javax.servlet.http.HttpServletResponse;
25 |
26 | /**
27 | * This class is used by the the runtime common testing framework as a custom test to ensure
28 | * that when x-forwarded-proto is specified and equals to https
29 | * (e.g the application is served by a load balancer)
30 | * the connection is considered as secure.
31 | *
32 | *
33 | * Note:
34 | *
35 | *
If the request with the header x-forwarded-proto does not come from a local ip the
36 | * connection will not be considered as secure.
37 | *
38 | *
In Tomcat this header is interpreted by the valve:
39 | * https://tomcat.apache.org/tomcat-9.0-doc/config/valve.html#Remote_IP_Valve
40 | * See tomcat-base/server.xml for the configuration.
41 | *
42 | *
43 | */
44 | @WebServlet(urlPatterns = "/custom/tests/secure")
45 | public class SecureTestServlet extends HttpServlet {
46 |
47 | @Override
48 | protected void doGet(HttpServletRequest req, HttpServletResponse resp)
49 | throws ServletException, IOException {
50 |
51 | resp.setContentType("plain/text");
52 |
53 | if (req.getHeader("x-forwarded-proto") != null) {
54 |
55 | if (req.getHeader("x-forwarded-proto").equals("http") && req.isSecure()) {
56 | resp.setStatus(500);
57 | resp.getWriter().println(
58 | "Error: x-forwarded-proto is set to http and the connection is considered secure");
59 | } else if (req.getHeader("x-forwarded-proto").equals("https") && !req.isSecure()) {
60 | resp.setStatus(500);
61 | resp.getWriter().println(
62 | "Error: x-forwarded-proto is set to https but the connection is not considered secure");
63 | }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/java/com/google/cloud/runtimes/tomcat/test/integration/StandardLoggingServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.test.integration;
18 |
19 | import com.fasterxml.jackson.databind.JsonNode;
20 | import com.fasterxml.jackson.databind.ObjectMapper;
21 | import java.io.IOException;
22 | import java.net.URLEncoder;
23 | import java.util.logging.Level;
24 | import java.util.logging.Logger;
25 | import javax.servlet.ServletException;
26 | import javax.servlet.annotation.WebServlet;
27 | import javax.servlet.http.HttpServlet;
28 | import javax.servlet.http.HttpServletRequest;
29 | import javax.servlet.http.HttpServletResponse;
30 |
31 | @WebServlet(urlPatterns = "/logging_standard")
32 | public class StandardLoggingServlet extends HttpServlet {
33 |
34 | private static final Logger logger = Logger.getLogger(StandardLoggingServlet.class.getName());
35 |
36 | private static final ObjectMapper objectMapper = new ObjectMapper();
37 |
38 | @Override
39 | protected void doPost(HttpServletRequest req, HttpServletResponse resp)
40 | throws ServletException, IOException {
41 | JsonNode body = objectMapper.readTree(req.getReader());
42 | String token = body.path("token").asText();
43 | String level = convertStackdriverSeverityToLoggingLevel(body.path("level").asText());
44 |
45 | logger.log(Level.parse(level), token);
46 |
47 | resp.setContentType("text/plain");
48 | resp.getWriter().println(URLEncoder.encode("appengine.googleapis.com/stdout", "UTF-8"));
49 | }
50 |
51 | private String convertStackdriverSeverityToLoggingLevel(String severity) {
52 | String level;
53 | switch (severity) {
54 | case "DEBUG":
55 | level = "FINE";
56 | break;
57 | case "ERROR":
58 | case "CRITICAL":
59 | case "ALERT":
60 | level = "SEVERE";
61 | break;
62 | default:
63 | level = severity;
64 | break;
65 | }
66 |
67 | return level;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/resources/custom-test-specification.json:
--------------------------------------------------------------------------------
1 | [{
2 | "name": "test-gzip",
3 | "steps": [{
4 | "name": "test-gzip-header",
5 | "configuration": {
6 | "headers": { "Accept-Encoding": "gzip" },
7 | "method": "GET"
8 | },
9 | "path": "/dump/all"
10 | }],
11 | "validation": {
12 | "match": [{
13 | "key": "test-gzip-header.response.headers.Content-Encoding",
14 | "pattern": "gzip"
15 | }]
16 | }
17 | },{
18 | "name": "test-x-forwarded-proto-header",
19 | "path": "custom/tests/secure"
20 | }]
21 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/resources/logging.properties:
--------------------------------------------------------------------------------
1 | com.google.cloud.runtimes.level = FINE
2 | # Ideally we would prefer to set the handler for a specific class however this is not functional yet.
3 | # com.google.cloud.runtimes.tomcat.test.handlers = com.google.cloud.logging.LoggingHandler
4 | handlers = com.google.cloud.logging.LoggingHandler
5 |
6 | com.google.cloud.logging.LoggingHandler.level = FINE
7 | com.google.cloud.logging.LoggingHandler.log = appengine.googleapis.com%2Fstdout
8 |
--------------------------------------------------------------------------------
/tests/test-war-integration/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 | Web Application for Servlet 3.1
8 |
9 |
--------------------------------------------------------------------------------
/tests/test-war-simple/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
20 |
21 | tomcat-parent
22 | com.google.cloud.runtimes
23 | 0.1.0-SNAPSHOT
24 | ../../pom.xml
25 |
26 | 4.0.0
27 |
28 | test-war-simple
29 | Tomcat-Runtime :: Tests :: Simple Tests
30 | war
31 |
32 |
33 | ${docker.image.name}:${docker.tag.long}
34 |
35 |
36 |
37 |
38 | javax.servlet
39 | javax.servlet-api
40 | 3.1.0
41 | provided
42 |
43 |
44 |
45 |
46 |
47 | local-docker-build
48 |
49 | true
50 |
51 |
52 |
53 |
54 | io.fabric8
55 | docker-maven-plugin
56 |
57 |
58 | build
59 | package
60 |
61 | build
62 |
63 |
64 |
65 |
66 | ${project.artifactId}
67 |
68 | ${project.build.directory}/docker-src
69 |
70 | ${project.version}
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | maven-resources-plugin
87 |
88 |
89 | copy-dockerfile
90 | generate-resources
91 |
92 | copy-resources
93 |
94 |
95 | ${project.build.directory}/docker-src
96 |
97 |
98 | ${basedir}/src/main/docker
99 | true
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | org.apache.maven.plugins
108 | maven-war-plugin
109 | 3.1.0
110 |
111 | ${project.build.directory}/docker-src
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/tests/test-war-simple/src/main/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Google Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | FROM ${test.docker.image}
15 | ADD ${project.build.finalName}.war $CATALINA_BASE/webapps/ROOT.war
--------------------------------------------------------------------------------
/tests/test-war-simple/src/main/java/com/google/cloud/runtimes/tomcat/test/integration/HelloServlet.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.test.integration;
18 |
19 | import java.io.IOException;
20 | import javax.servlet.ServletException;
21 | import javax.servlet.annotation.WebServlet;
22 | import javax.servlet.http.HttpServlet;
23 | import javax.servlet.http.HttpServletRequest;
24 | import javax.servlet.http.HttpServletResponse;
25 |
26 | @WebServlet(urlPatterns = {"/hello/*"})
27 | public class HelloServlet extends HttpServlet {
28 |
29 | @Override
30 | protected void doGet(HttpServletRequest req, HttpServletResponse resp)
31 | throws ServletException, IOException {
32 | resp.setContentType("text.plain");
33 | resp.getWriter().println("Hello from the simple war application");
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/tests/test-war-simple/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 | Web Application for Servlet 3.1
8 |
9 |
--------------------------------------------------------------------------------
/tests/test-war-simple/src/main/webapp/index.html:
--------------------------------------------------------------------------------
1 | Welcome to the Simple Test.
--------------------------------------------------------------------------------
/tests/test-war-spring-boot/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
20 | 4.0.0
21 |
22 |
23 | tomcat-parent
24 | com.google.cloud.runtimes
25 | 0.1.0-SNAPSHOT
26 | ../../pom.xml
27 |
28 |
29 | test-war-spring-boot
30 | Tomcat-Runtime :: Tests :: Spring boot tests
31 | war
32 |
33 |
34 | ${docker.image.name}:${docker.tag.long}
35 |
36 |
37 |
38 |
39 | local-docker-build
40 |
41 | true
42 |
43 |
44 |
45 |
46 | io.fabric8
47 | docker-maven-plugin
48 |
49 |
50 | build
51 | package
52 |
53 | build
54 |
55 |
56 |
57 |
58 | ${project.artifactId}
59 |
60 | ${project.build.directory}/docker-src
61 |
62 | ${project.version}
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | org.springframework.boot
80 | spring-boot-dependencies
81 | 1.5.3.RELEASE
82 | pom
83 | import
84 |
85 |
86 |
87 |
88 |
89 |
90 | org.springframework.boot
91 | spring-boot-starter-web
92 |
93 |
94 | org.springframework.boot
95 | spring-boot-starter-tomcat
96 | provided
97 |
98 |
99 |
100 |
101 |
102 |
103 | org.springframework.boot
104 | spring-boot-maven-plugin
105 |
106 |
107 | org.apache.maven.plugins
108 | maven-war-plugin
109 | 3.1.0
110 |
111 | ${project.build.directory}/docker-src
112 |
113 |
114 |
115 | maven-resources-plugin
116 |
117 |
118 | copy-dockerfile
119 | generate-resources
120 |
121 | copy-resources
122 |
123 |
124 | ${project.build.directory}/docker-src
125 |
126 |
127 | ${basedir}/src/main/docker
128 | true
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/tests/test-war-spring-boot/src/main/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Google Inc.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | FROM ${test.docker.image}
15 | ADD ${project.build.finalName}.war $CATALINA_BASE/webapps/ROOT.war
--------------------------------------------------------------------------------
/tests/test-war-spring-boot/src/main/java/com.google.cloud.runtimes/spring/Application.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.spring;
18 |
19 | import org.springframework.boot.SpringApplication;
20 | import org.springframework.boot.autoconfigure.SpringBootApplication;
21 | import org.springframework.boot.builder.SpringApplicationBuilder;
22 | import org.springframework.boot.web.support.SpringBootServletInitializer;
23 |
24 | @SpringBootApplication
25 | public class Application extends SpringBootServletInitializer {
26 |
27 | @Override
28 | protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
29 | return builder.sources(Application.class);
30 | }
31 |
32 | public static void main(String[] args) throws Exception {
33 | SpringApplication.run(Application.class, args);
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/tests/test-war-spring-boot/src/main/java/com.google.cloud.runtimes/spring/controllers/HelloController.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.spring.controllers;
18 |
19 | import org.springframework.web.bind.annotation.RequestMapping;
20 | import org.springframework.web.bind.annotation.RestController;
21 |
22 | @RestController
23 | public class HelloController {
24 |
25 | @RequestMapping("/")
26 | public String hello() {
27 | return "Hello World!";
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/tomcat-gcp-lib/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | tomcat-parent
7 | com.google.cloud.runtimes
8 | 0.1.0-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | tomcat-gcp-lib
13 |
14 |
15 |
16 | org.apache.tomcat
17 | tomcat-catalina
18 | 8.5.15
19 | provided
20 |
21 |
22 | com.google.cloud
23 | google-cloud-datastore
24 | 1.2.1
25 |
26 |
27 | com.google.guava
28 | guava
29 | 23.0
30 |
31 |
32 |
34 |
35 | io.grpc
36 | grpc-all
37 | 1.4.0
38 |
39 |
41 |
42 | io.netty
43 | netty-tcnative-boringssl-static
44 | 2.0.3.Final
45 |
46 |
47 |
48 | com.google.cloud.trace
49 | core
50 | 0.4.0
51 |
52 |
53 | com.google.cloud.trace
54 | trace-grpc-api-service
55 | 0.4.0
56 |
57 |
58 | io.grpc
59 | grpc-all
60 |
61 |
62 | com.google.auth
63 | google-auth-library-oauth2-http
64 |
65 |
66 |
67 |
68 |
69 | junit
70 | junit
71 | 4.12
72 | test
73 |
74 |
75 | org.mockito
76 | mockito-inline
77 | 2.8.47
78 | test
79 |
80 |
81 |
82 |
83 |
84 |
85 | maven-assembly-plugin
86 | 3.0.0
87 |
88 |
89 | jar-with-dependencies
90 |
91 |
92 |
93 |
94 | make-assembly
95 | package
96 |
97 | single
98 |
99 |
100 |
101 |
102 |
103 | org.apache.maven.plugins
104 | maven-surefire-plugin
105 | 2.20
106 |
107 |
108 | org.apache.maven.plugins
109 | maven-compiler-plugin
110 | 3.6.1
111 |
112 | javac-with-errorprone
113 | true
114 | 1.8
115 | 1.8
116 |
117 |
118 |
119 | org.codehaus.plexus
120 | plexus-compiler-javac-errorprone
121 | 2.8.2
122 |
123 |
124 | com.google.errorprone
125 | error_prone_core
126 | 2.0.21
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 | gcp-integration-test
136 |
137 |
138 |
139 | org.apache.maven.plugins
140 | maven-failsafe-plugin
141 | 2.20
142 |
143 |
144 |
145 | integration-test
146 | verify
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/tomcat-gcp-lib/src/main/java/com/google/cloud/runtimes/tomcat/session/DatastoreManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.session;
18 |
19 | import java.io.IOException;
20 | import java.util.Arrays;
21 | import java.util.HashSet;
22 | import java.util.Set;
23 |
24 | import org.apache.catalina.Lifecycle;
25 | import org.apache.catalina.LifecycleException;
26 | import org.apache.catalina.LifecycleState;
27 | import org.apache.catalina.Session;
28 | import org.apache.catalina.Store;
29 | import org.apache.catalina.StoreManager;
30 | import org.apache.catalina.session.ManagerBase;
31 | import org.apache.catalina.session.StandardSession;
32 | import org.apache.catalina.session.StoreBase;
33 | import org.apache.juli.logging.Log;
34 | import org.apache.juli.logging.LogFactory;
35 |
36 |
37 | /**
38 | * Implementation of the {@code org.apache.catalina.Manager} interface which uses
39 | * Google Datastore to share sessions across nodes.
40 | *
41 | *
This manager should be used in conjunction with {@link DatastoreValve} and can be used
42 | * with {@link DatastoreStore}.
43 | * Example configuration:
The sessions is never stored locally and is always fetched from the Datastore.
55 | */
56 | public class DatastoreManager extends ManagerBase implements StoreManager {
57 |
58 | private static final Log log = LogFactory.getLog(DatastoreManager.class);
59 |
60 | /**
61 | * The store will be in charge of all the interactions with the Datastore.
62 | */
63 | protected Store store = null;
64 |
65 | /**
66 | * {@inheritDoc}
67 | *
68 | *
Ensure that a store is present and initialized.
69 | *
70 | * @throws LifecycleException If an error occurs during the store initialization
71 | */
72 | @Override
73 | protected synchronized void startInternal() throws LifecycleException {
74 | super.startInternal();
75 |
76 | if (store == null) {
77 | throw new LifecycleException("No Store configured, persistence disabled");
78 | } else if (store instanceof Lifecycle) {
79 | ((Lifecycle) store).start();
80 | }
81 |
82 | setState(LifecycleState.STARTING);
83 | }
84 |
85 | /**
86 | * Search in the store for an existing session with the specified id.
87 | *
88 | * @param id The session id for the session to be returned
89 | * @return The request session or null if a session with the requested ID could not be found
90 | * @throws IOException If an input/output error occurs while processing this request
91 | */
92 | @Override
93 | public Session findSession(String id) throws IOException {
94 | log.debug("Datastore manager is loading session: " + id);
95 | Session session = null;
96 |
97 | try {
98 | session = this.getStore().load(id);
99 | } catch (ClassNotFoundException ex) {
100 | log.warn("An error occurred during session deserialization", ex);
101 | }
102 |
103 | return session;
104 | }
105 |
106 | /**
107 | * {@inheritDoc}
108 | *
109 | *
Note: Sessions are loaded at each request therefore no session is loaded at
110 | * initialization.
Note: Sessions are persisted after each requests but never saved into the local manager,
122 | * therefore no operation is needed during unload.
123 | *
124 | * @throws IOException Cannot occurs
125 | */
126 | @Override
127 | public void unload() throws IOException {}
128 |
129 | /**
130 | * Remove the Session from the manager but not from the Datastore.
131 | *
132 | * @param session The session to remove.
133 | */
134 | @Override
135 | public void removeSuper(Session session) {
136 | super.remove(session);
137 | }
138 |
139 | /**
140 | * Remove this Session from the active Sessions and the Datastore.
141 | *
142 | * @param session The session to remove.
143 | */
144 | @Override
145 | public void remove(Session session) {
146 | this.removeSuper(session);
147 |
148 | try {
149 | store.remove(session.getId());
150 | } catch (IOException e) {
151 | log.error("An error occurred while removing session with id: " + session.getId(), e);
152 | }
153 | }
154 |
155 | /**
156 | * Returns the number of sessions present in the Store.
157 | *
158 | *
Note: Aggregation can be slow on the Datastore, cache the result if possible
159 | *
160 | * @return the session count.
161 | */
162 | @Override
163 | public int getActiveSessionsFull() {
164 | int sessionCount = 0;
165 | try {
166 | sessionCount = store.getSize();
167 | } catch (IOException e) {
168 | log.error("An error occurred while counting sessions: ", e);
169 | }
170 |
171 | return sessionCount;
172 | }
173 |
174 | /**
175 | * Returns a set of all sessions IDs or null if an error occurs.
176 | *
177 | *
Note: Listing all the keys can be slow on the Datastore.
178 | *
179 | * @return The complete set of sessions IDs across the cluster.
180 | */
181 | @Override
182 | public Set getSessionIdsFull() {
183 | Set sessionsId = null;
184 | try {
185 | String[] keys = this.store.keys();
186 | sessionsId = new HashSet<>(Arrays.asList(keys));
187 | } catch (IOException e) {
188 | log.error("An error occurred while listing active sessions: ", e);
189 | }
190 |
191 | return sessionsId;
192 | }
193 |
194 | @Override
195 | protected void stopInternal() throws LifecycleException {
196 | super.stopInternal();
197 |
198 | if (store instanceof Lifecycle) {
199 | ((Lifecycle) store).stop();
200 | }
201 |
202 | setState(LifecycleState.STOPPING);
203 | }
204 |
205 | @Override
206 | public void processExpires() {
207 | log.debug("Processing expired sessions");
208 | if (store instanceof StoreBase) {
209 | ((StoreBase) store).processExpires();
210 | }
211 | }
212 |
213 | @Override
214 | protected StandardSession getNewSession() {
215 | return new DatastoreSession(this);
216 | }
217 |
218 | public Store getStore() {
219 | return this.store;
220 | }
221 |
222 | /**
223 | * The store will be injected by Tomcat on startup.
224 | *
225 | *
See distributed-sessions.xml for the configuration.
226 | */
227 | public void setStore(Store store) {
228 | this.store = store;
229 | store.setManager(this);
230 | }
231 | }
232 |
--------------------------------------------------------------------------------
/tomcat-gcp-lib/src/main/java/com/google/cloud/runtimes/tomcat/session/DatastoreSession.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.session;
18 |
19 | import com.google.cloud.datastore.Blob;
20 | import com.google.cloud.datastore.BlobValue;
21 | import com.google.cloud.datastore.Entity;
22 | import com.google.cloud.datastore.Key;
23 | import com.google.cloud.datastore.KeyFactory;
24 | import com.google.common.annotations.VisibleForTesting;
25 | import java.io.ByteArrayOutputStream;
26 | import java.io.IOException;
27 | import java.io.InputStream;
28 | import java.io.ObjectInputStream;
29 | import java.io.ObjectOutputStream;
30 | import java.io.UncheckedIOException;
31 | import java.util.Collections;
32 | import java.util.HashSet;
33 | import java.util.LinkedList;
34 | import java.util.List;
35 | import java.util.Set;
36 | import java.util.stream.Collectors;
37 | import java.util.stream.Stream;
38 | import org.apache.catalina.Manager;
39 | import org.apache.catalina.session.StandardSession;
40 |
41 | /**
42 | * A DatastoreSession have the same behavior as a standard session but provide utilities to interact
43 | * with the Datastore, such as helper for attributes and metadata serialization.
44 | */
45 | public class DatastoreSession extends StandardSession {
46 |
47 | protected Set accessedAttributes;
48 | protected Set initialAttributes;
49 |
50 | @VisibleForTesting
51 | class SessionMetadata {
52 | public static final String CREATION_TIME = "creationTime";
53 | public static final String LAST_ACCESSED_TIME = "lastAccessedTime";
54 | public static final String MAX_INACTIVE_INTERVAL = "maxInactiveInterval";
55 | public static final String IS_NEW = "isNew";
56 | public static final String IS_VALID = "isValid";
57 | public static final String THIS_ACCESSED_TIME = "thisAccessedTime";
58 | public static final String EXPIRATION_TIME = "expirationTime";
59 | public static final String ATTRIBUTE_VALUE_NAME = "value";
60 | }
61 |
62 | /**
63 | * Create a new session which can be stored in the Datastore.
64 | * @param manager The session manager which manage this session.
65 | */
66 | public DatastoreSession(Manager manager) {
67 | super(manager);
68 | this.accessedAttributes = new HashSet<>();
69 | this.initialAttributes = new HashSet<>();
70 | }
71 |
72 | /**
73 | * Restore the attributes and metadata of the session from Datastore Entities.
74 | *
75 | * @param entities An iterator of entity, containing the metadata and attributes of the session.
76 | * @throws ClassNotFoundException The class in attempt to be deserialized is not present in the
77 | * application.
78 | * @throws IOException Error during the deserialization of the object.
79 | */
80 | public void restoreFromEntities(Key sessionKey, Iterable entities) throws
81 | ClassNotFoundException, IOException {
82 | Entity metadataEntity = null;
83 | List attributeEntities = new LinkedList<>();
84 | for (Entity entity : entities) {
85 | if (entity.getKey().equals(sessionKey)) {
86 | metadataEntity = entity;
87 | } else {
88 | attributeEntities.add(entity);
89 | }
90 | }
91 |
92 | if (metadataEntity == null) {
93 | throw new IOException("The serialized session is missing the metadata entity");
94 | }
95 |
96 | restoreMetadataFromEntity(metadataEntity);
97 | restoreAttributesFromEntity(attributeEntities);
98 | setId(sessionKey.getName());
99 | initialAttributes.addAll(Collections.list(getAttributeNames()));
100 | }
101 |
102 | /**
103 | * Restore the metadata of a session with the values contains in the entity.
104 | * @param metadata An entity containing the metadata to restore
105 | */
106 | private void restoreMetadataFromEntity(Entity metadata) {
107 | creationTime = metadata.getLong(SessionMetadata.CREATION_TIME);
108 | lastAccessedTime = metadata.getLong(SessionMetadata.LAST_ACCESSED_TIME);
109 | maxInactiveInterval = (int) metadata.getLong(SessionMetadata.MAX_INACTIVE_INTERVAL);
110 | isNew = metadata.getBoolean(SessionMetadata.IS_NEW);
111 | isValid = metadata.getBoolean(SessionMetadata.IS_VALID);
112 | thisAccessedTime = metadata.getLong(SessionMetadata.THIS_ACCESSED_TIME);
113 | }
114 |
115 | /**
116 | * Deserialize the content of each entity and add them as attribute of the session.
117 | * @param entities The entities containing the serialized attributes.
118 | * @throws IOException If an error occur during the deserialization
119 | * @throws ClassNotFoundException If the class being deserialized is not present in this program.
120 | */
121 | private void restoreAttributesFromEntity(Iterable entities) throws IOException,
122 | ClassNotFoundException {
123 | for (Entity entity : entities) {
124 | String name = entity.getKey().getName();
125 | Blob value = entity.getBlob(SessionMetadata.ATTRIBUTE_VALUE_NAME);
126 | try (InputStream fis = value.asInputStream();
127 | ObjectInputStream ois = new ObjectInputStream(fis)) {
128 | Object attribute = ois.readObject();
129 | setAttribute(name, attribute, false);
130 | }
131 | }
132 | }
133 |
134 | /**
135 | * Serialize the session metadata and attributes into entities storable in the datastore.
136 | * @param sessionKey The key of the serialized session
137 | * @param attributeKeyFactory A key factory containing sessionKey in its ancestors, used to
138 | * generate the key for the attributes.
139 | * @return A list of entities containing the metadata and each attribute.
140 | * @throws IOException If an error occur during the serialization.
141 | */
142 | public List saveToEntities(Key sessionKey, KeyFactory attributeKeyFactory) throws
143 | IOException {
144 | List entities = saveAttributesToEntity(attributeKeyFactory);
145 | entities.add(saveMetadataToEntity(sessionKey));
146 | return entities;
147 | }
148 |
149 | /**
150 | * Store the metadata of the session in an entity.
151 | * @param sessionKey Identifier of the session on the Datastore
152 | * @return An entity containing the metadata.
153 | */
154 | @VisibleForTesting
155 | Entity saveMetadataToEntity(Key sessionKey) {
156 | Entity.Builder sessionEntity = Entity.newBuilder(sessionKey)
157 | .set(SessionMetadata.CREATION_TIME, getCreationTime())
158 | .set(SessionMetadata.LAST_ACCESSED_TIME, getLastAccessedTime())
159 | .set(SessionMetadata.MAX_INACTIVE_INTERVAL, getMaxInactiveInterval())
160 | .set(SessionMetadata.IS_NEW, isNew())
161 | .set(SessionMetadata.IS_VALID, isValid())
162 | .set(SessionMetadata.THIS_ACCESSED_TIME, getThisAccessedTime());
163 |
164 | // A negative time indicates that the session should never time out
165 | if (getMaxInactiveInterval() >= 0) {
166 | sessionEntity.set(SessionMetadata.EXPIRATION_TIME,
167 | getLastAccessedTime() + getMaxInactiveInterval() * 1000);
168 | }
169 |
170 | return sessionEntity.build();
171 | }
172 |
173 | /**
174 | * Serialize the session attributes into entities.
175 | * @param attributeKeyFactory The key builder for the entities.
176 | * @return A list of entities where the key correspond to the name of the attribute
177 | and the property `value` to the serialized attribute.
178 | * @throws IOException If an error occur during the serialization.
179 | */
180 | @VisibleForTesting
181 | List saveAttributesToEntity(KeyFactory attributeKeyFactory) throws
182 | IOException {
183 | Stream entities = Collections.list(getAttributeNames()).stream()
184 | .filter(name -> accessedAttributes.contains(name))
185 | .filter(name -> isAttributeDistributable(name, getAttribute(name)))
186 | .map(name -> serializeAttribute(attributeKeyFactory, name));
187 |
188 | try {
189 | return entities.collect(Collectors.toList());
190 | } catch (UncheckedIOException e) {
191 | throw e.getCause();
192 | }
193 | }
194 |
195 | /**
196 | * Serialize an attribute an embed it into an entity whose key is generated by the provided
197 | * KeyFactory.
198 | * @param attributeKeyFactory The KeyFactory to use to create the key for the entity.
199 | * @param name The name of the attribute to serialize.
200 | * @return An Entity containing the serialized attribute.
201 | */
202 | private Entity serializeAttribute(KeyFactory attributeKeyFactory, String name) {
203 | ByteArrayOutputStream bos = new ByteArrayOutputStream();
204 | try (ObjectOutputStream oos = new ObjectOutputStream(bos)) {
205 | oos.writeObject(getAttribute(name));
206 | } catch (IOException e) {
207 | throw new UncheckedIOException(e);
208 | }
209 |
210 | return Entity.newBuilder(attributeKeyFactory.newKey(name))
211 | .set(SessionMetadata.ATTRIBUTE_VALUE_NAME,
212 | BlobValue.newBuilder(Blob.copyFrom(bos.toByteArray()))
213 | .setExcludeFromIndexes(true)
214 | .build())
215 | .build();
216 | }
217 |
218 | /**
219 | * List the attributes that were present at the beginning of the request and suppressed during
220 | * its execution. This is used to reflect the suppression of attributes in the Datastore (The
221 | * suppressed attributes would be left unchanged in the Datastore otherwise).
222 | * @return A set of the suppressed attributes.
223 | */
224 | public Set getSuppressedAttributes() {
225 | Set suppressedAttribute = new HashSet<>(initialAttributes);
226 | suppressedAttribute.removeAll(Collections.list(getAttributeNames()));
227 | return suppressedAttribute;
228 | }
229 |
230 | @Override
231 | public Object getAttribute(String name) {
232 | accessedAttributes.add(name);
233 | return super.getAttribute(name);
234 | }
235 |
236 | @Override
237 | public void setAttribute(String name, Object value, boolean notify) {
238 | super.setAttribute(name, value, notify);
239 | if (notify) {
240 | accessedAttributes.add(name);
241 | }
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/tomcat-gcp-lib/src/main/java/com/google/cloud/runtimes/tomcat/session/DatastoreStore.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.session;
18 |
19 | import com.google.cloud.datastore.Datastore;
20 | import com.google.cloud.datastore.DatastoreOptions;
21 | import com.google.cloud.datastore.Entity;
22 | import com.google.cloud.datastore.FullEntity;
23 | import com.google.cloud.datastore.Key;
24 | import com.google.cloud.datastore.KeyFactory;
25 | import com.google.cloud.datastore.PathElement;
26 | import com.google.cloud.datastore.Query;
27 | import com.google.cloud.datastore.QueryResults;
28 | import com.google.cloud.datastore.StructuredQuery.PropertyFilter;
29 | import com.google.cloud.runtimes.tomcat.session.DatastoreSession.SessionMetadata;
30 | import com.google.cloud.trace.Trace;
31 | import com.google.cloud.trace.Tracer;
32 | import com.google.cloud.trace.core.TraceContext;
33 | import com.google.common.annotations.VisibleForTesting;
34 | import com.google.common.collect.Lists;
35 | import com.google.common.collect.Streams;
36 |
37 | import java.io.IOException;
38 | import java.time.Clock;
39 | import java.util.Arrays;
40 |
41 | import java.util.Iterator;
42 | import java.util.List;
43 | import java.util.stream.Stream;
44 | import org.apache.catalina.LifecycleException;
45 | import org.apache.catalina.Session;
46 | import org.apache.catalina.session.StoreBase;
47 | import org.apache.juli.logging.Log;
48 | import org.apache.juli.logging.LogFactory;
49 |
50 |
51 | /**
52 | * This store interacts with the Datastore service to persist sessions.
53 | *
54 | *
It does not make any assumptions about the manager, so it could be used
55 | * by all manager implementations.
56 | *
57 | *
However, aggregations can be slow on the Datastore. So, if performance is a concern, prefer
58 | * using a manager implementation which is not using aggregations such as
59 | * {@link DatastoreManager}
60 | */
61 | public class DatastoreStore extends StoreBase {
62 |
63 | private static final Log log = LogFactory.getLog(DatastoreStore.class);
64 |
65 | private Datastore datastore = null;
66 |
67 | /**
68 | * Name of the kind used in The Datastore for the session.
69 | */
70 | private String sessionKind;
71 |
72 | /**
73 | * Namespace to use in the Datastore.
74 | */
75 | private String namespace;
76 |
77 | /**
78 | * Whether or not to send traces to Stackdriver for the operations related to session persistence.
79 | */
80 | private boolean traceRequest = false;
81 |
82 | private Clock clock;
83 |
84 | /**
85 | * {@inheritDoc}
86 | *
87 | *
The Datastore does not support counting elements in a collection.
108 | * So, all keys are fetched and the counted locally.
109 | *
110 | *
This method may be slow if a large number of sessions are persisted,
111 | * prefer operations on individual entities rather than aggregations.
112 | *
113 | * @return The number of sessions stored into the Datastore
114 | */
115 | @Override
116 | public int getSize() throws IOException {
117 | log.debug("Accessing sessions count, be cautious this operation can cause performance issues");
118 | Query query = Query.newKeyQueryBuilder().build();
119 | QueryResults results = datastore.run(query);
120 | long count = Streams.stream(results).count();
121 | return Math.toIntExact(count);
122 | }
123 |
124 | /**
125 | * Returns an array containing the session identifiers of all Sessions currently saved in this
126 | * Store. If there are no such Sessions, a zero-length array is returned.
127 | *
128 | *
This operation may be slow if a large number of sessions is persisted.
129 | * Note that the number of keys returned may be bounded by the Datastore configuration.
130 | *
131 | * @return The ids of all persisted sessions
132 | */
133 | @Override
134 | public String[] keys() throws IOException {
135 | String[] keys;
136 |
137 | Query query = Query.newKeyQueryBuilder().build();
138 | QueryResults results = datastore.run(query);
139 | keys = Streams.stream(results)
140 | .map(key -> key.getNameOrId().toString())
141 | .toArray(String[]::new);
142 |
143 | if (keys == null) {
144 | keys = new String[0];
145 | }
146 |
147 | return keys;
148 | }
149 |
150 | /**
151 | * Load and return the Session associated with the specified session identifier from this Store,
152 | * without removing it. If there is no such stored Session, return null.
153 | *
154 | *
Look in the Datastore for a serialized session and attempt to deserialize it.
155 | *
156 | *
If the session is successfully deserialized, it is added to the current manager and is
157 | * returned by this method. Otherwise null is returned.
158 | *
159 | * @param id Session identifier of the session to load
160 | * @return The loaded session instance
161 | * @throws ClassNotFoundException If a deserialization error occurs
162 | */
163 | @Override
164 | public Session load(String id) throws ClassNotFoundException, IOException {
165 | log.debug("Session " + id + " requested");
166 | TraceContext context = startSpan("Loading session");
167 | Key sessionKey = newKey(id);
168 |
169 | DatastoreSession session = deserializeSession(sessionKey);
170 |
171 | endSpan(context);
172 | log.debug("Session " + id + " loaded");
173 | return session;
174 | }
175 |
176 | /**
177 | * Create a new session usable by Tomcat, from a serialized session in a Datastore Entity.
178 | * @param sessionKey The key associated with the session metadata and attributes.
179 | * @return A new session containing the metadata and attributes stored in the entity.
180 | * @throws ClassNotFoundException Thrown if a class serialized in the entity is not available in
181 | * this context.
182 | * @throws IOException Thrown when an error occur during the deserialization.
183 | */
184 | private DatastoreSession deserializeSession(Key sessionKey)
185 | throws ClassNotFoundException, IOException {
186 | TraceContext loadingSessionContext = startSpan("Fetching the session from Datastore");
187 | Iterator entities = datastore.run(Query.newEntityQueryBuilder()
188 | .setKind(sessionKind)
189 | .setFilter(PropertyFilter.hasAncestor(sessionKey))
190 | .build());
191 | endSpan(loadingSessionContext);
192 |
193 | DatastoreSession session = null;
194 | if (entities.hasNext()) {
195 | session = (DatastoreSession) manager.createEmptySession();
196 | TraceContext deserializationContext = startSpan("Deserialization of the session");
197 | session.restoreFromEntities(sessionKey, Lists.newArrayList(entities));
198 | endSpan(deserializationContext);
199 | }
200 | return session;
201 | }
202 |
203 | /**
204 | * Remove the Session with the specified session identifier from this Store.
205 | * If no such Session is present, this method takes no action.
206 | *
207 | * @param id Session identifier of the session to remove
208 | */
209 | @Override
210 | public void remove(String id) {
211 | log.debug("Removing session: " + id);
212 | datastore.delete(newKey(id));
213 | }
214 |
215 | /**
216 | * Remove all Sessions from this Store.
217 | */
218 | @Override
219 | public void clear() throws IOException {
220 | log.debug("Deleting all sessions");
221 | datastore.delete(Arrays.stream(keys())
222 | .map(this::newKey)
223 | .toArray(Key[]::new));
224 | }
225 |
226 | /**
227 | * Save the specified Session into this Store. Any previously saved information for
228 | * the associated session identifier is replaced.
229 | *
230 | *
Attempt to serialize the session and send it to the datastore.
231 | *
232 | * @throws IOException If an error occurs during the serialization of the session.
233 | *
234 | * @param session Session to be saved
235 | */
236 | @Override
237 | public void save(Session session) throws IOException {
238 | log.debug("Persisting session: " + session.getId());
239 |
240 | if (!(session instanceof DatastoreSession)) {
241 | throw new IOException(
242 | "The session must be an instance of DatastoreSession to be serialized");
243 | }
244 | DatastoreSession datastoreSession = (DatastoreSession) session;
245 | Key sessionKey = newKey(session.getId());
246 | KeyFactory attributeKeyFactory = datastore.newKeyFactory()
247 | .setKind(sessionKind)
248 | .addAncestor(PathElement.of(sessionKind, sessionKey.getName()));
249 |
250 | List entities = serializeSession(datastoreSession, sessionKey, attributeKeyFactory);
251 |
252 | TraceContext datastoreSaveContext = startSpan("Storing the session in the Datastore");
253 | datastore.put(entities.toArray(new FullEntity[0]));
254 | datastore.delete(datastoreSession.getSuppressedAttributes().stream()
255 | .map(attributeKeyFactory::newKey)
256 | .toArray(Key[]::new));
257 | endSpan(datastoreSaveContext);
258 | }
259 |
260 | /**
261 | * Serialize a session to a list of Entities that can be stored to the Datastore.
262 | * @param session The session to serialize.
263 | * @return A list of one or more entities containing the session and its attributes.
264 | * @throws IOException If the session cannot be serialized.
265 | */
266 | @VisibleForTesting
267 | List serializeSession(DatastoreSession session, Key sessionKey,
268 | KeyFactory attributeKeyFactory) throws IOException {
269 | TraceContext serializationContext = startSpan("Serialization of the session");
270 | List entities = session.saveToEntities(sessionKey, attributeKeyFactory);
271 | endSpan(serializationContext);
272 | return entities;
273 | }
274 |
275 | /**
276 | * Remove expired sessions from the datastore.
277 | */
278 | @Override
279 | public void processExpires() {
280 | log.debug("Processing expired sessions");
281 |
282 | Query query = Query.newKeyQueryBuilder().setKind(sessionKind)
283 | .setFilter(PropertyFilter.le(SessionMetadata.EXPIRATION_TIME,
284 | clock.millis()))
285 | .build();
286 |
287 | QueryResults keys = datastore.run(query);
288 |
289 | Stream toDelete = Streams.stream(keys)
290 | .parallel()
291 | .flatMap(key -> Streams.stream(datastore.run(Query.newKeyQueryBuilder()
292 | .setKind(sessionKind)
293 | .setFilter(PropertyFilter.hasAncestor(newKey(key.getName())))
294 | .build())));
295 | datastore.delete(toDelete.toArray(Key[]::new));
296 | }
297 |
298 | @VisibleForTesting
299 | TraceContext startSpan(String spanName) {
300 | TraceContext context = null;
301 | if (traceRequest) {
302 | context = Trace.getTracer().startSpan(spanName);
303 | }
304 | return context;
305 | }
306 |
307 | @VisibleForTesting
308 | private void endSpan(TraceContext context) {
309 | if (context != null) {
310 | Tracer tracer = Trace.getTracer();
311 | tracer.endSpan(context);
312 | }
313 | }
314 |
315 | /**
316 | * This property will be injected by Tomcat on startup.
317 | *
318 | *
See context.xml and catalina.properties for the default values
319 | */
320 | public void setNamespace(String namespace) {
321 | this.namespace = namespace;
322 | }
323 |
324 | /**
325 | * This property will be injected by Tomcat on startup.
326 | */
327 | public void setSessionKind(String sessionKind) {
328 | this.sessionKind = sessionKind;
329 | }
330 |
331 | /**
332 | * This property will be injected by Tomcat on startup.
333 | */
334 | public void setTraceRequest(boolean traceRequest) {
335 | this.traceRequest = traceRequest;
336 | }
337 |
338 | @VisibleForTesting
339 | void setDatastore(Datastore datastore) {
340 | this.datastore = datastore;
341 | }
342 |
343 | @VisibleForTesting
344 | void setClock(Clock clock) {
345 | this.clock = clock;
346 | }
347 |
348 | }
349 |
--------------------------------------------------------------------------------
/tomcat-gcp-lib/src/main/java/com/google/cloud/runtimes/tomcat/session/DatastoreValve.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.session;
18 |
19 | import java.io.IOException;
20 | import java.util.regex.Pattern;
21 | import javax.servlet.ServletException;
22 |
23 | import org.apache.catalina.Context;
24 | import org.apache.catalina.Manager;
25 | import org.apache.catalina.Session;
26 | import org.apache.catalina.StoreManager;
27 | import org.apache.catalina.connector.Request;
28 | import org.apache.catalina.connector.Response;
29 | import org.apache.catalina.valves.ValveBase;
30 | import org.apache.juli.logging.Log;
31 | import org.apache.juli.logging.LogFactory;
32 |
33 |
34 | /**
35 | * This valve uses the Store Manager to persist the session after each request.
36 | */
37 | public class DatastoreValve extends ValveBase {
38 |
39 | private static final Log log = LogFactory.getLog(DatastoreValve.class);
40 |
41 | private String uriExcludePattern;
42 |
43 | /**
44 | * {@inheritDoc}
45 | *
46 | *
If the manager contain a store, use it to persist the session at the end of the request.
47 | */
48 | @Override
49 | public void invoke(Request request, Response response) throws IOException, ServletException {
50 |
51 | log.debug("Processing request with session:" + request.getRequestedSessionId());
52 |
53 | getNext().invoke(request, response);
54 |
55 | Context context = request.getContext();
56 | Manager manager = context.getManager();
57 |
58 | Session session = request.getSessionInternal(false);
59 | if (session != null && !isUriExcluded(request.getRequestURI())) {
60 | log.debug("Persisting session with id: " + session.getId());
61 | session.access();
62 | session.endAccess();
63 |
64 | if (manager instanceof StoreManager) {
65 | StoreManager storeManager = (StoreManager) manager;
66 | storeManager.getStore().save(session);
67 | storeManager.removeSuper(session);
68 | } else {
69 | log.error("In order to persist the session the manager must implement StoreManager");
70 | }
71 | } else {
72 | log.debug("Session not persisted (Non existent or the URI is ignored)");
73 | }
74 |
75 | }
76 |
77 | /**
78 | * Verify if the specified URI should be ignored for session persistence.
79 | * @param uri The URI of the request
80 | * @return Whether the URI should be ignored or not
81 | */
82 | private boolean isUriExcluded(String uri) {
83 | return uriExcludePattern != null && Pattern.matches(uriExcludePattern, uri);
84 | }
85 |
86 | /**
87 | * This property will be injected by Tomcat on startup.
88 | */
89 | public void setUriExcludePattern(String uriExcludePattern) {
90 | this.uriExcludePattern = uriExcludePattern;
91 | }
92 | }
--------------------------------------------------------------------------------
/tomcat-gcp-lib/src/main/java/com/google/cloud/runtimes/tomcat/trace/HttpLabels.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.trace;
18 |
19 | public enum HttpLabels {
20 | HTTP_REQUEST_SIZE("/http/request/size"),
21 | HTTP_RESPONSE_SIZE("/http/response/size"),
22 | HTTP_METHOD("/http/method"),
23 | HTTP_STATUS_CODE("/http/status_code"),
24 | HTTP_URL("/http/url"),
25 | HTTP_USER_AGENT("/http/user_agent"),
26 | HTTP_CLIENT_PROTOCOL("/http/client_protocol");
27 |
28 | private final String value;
29 |
30 | HttpLabels(String value) {
31 | this.value = value;
32 | }
33 |
34 | public String getValue() {
35 | return this.value;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tomcat-gcp-lib/src/main/java/com/google/cloud/runtimes/tomcat/trace/TraceValve.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.google.cloud.runtimes.tomcat.trace;
18 |
19 | import com.google.cloud.ServiceOptions;
20 | import com.google.cloud.trace.Trace;
21 | import com.google.cloud.trace.Tracer;
22 | import com.google.cloud.trace.core.Labels;
23 | import com.google.cloud.trace.core.SpanContext;
24 | import com.google.cloud.trace.core.SpanContextFactory;
25 | import com.google.cloud.trace.core.SpanContextHandle;
26 | import com.google.cloud.trace.core.TraceContext;
27 | import com.google.cloud.trace.service.TraceGrpcApiService;
28 | import com.google.cloud.trace.service.TraceService;
29 | import com.google.common.annotations.VisibleForTesting;
30 | import com.google.common.net.HttpHeaders;
31 |
32 | import java.io.IOException;
33 | import javax.servlet.ServletException;
34 |
35 | import org.apache.catalina.LifecycleException;
36 | import org.apache.catalina.connector.Request;
37 | import org.apache.catalina.connector.Response;
38 | import org.apache.catalina.valves.ValveBase;
39 | import org.apache.juli.logging.Log;
40 | import org.apache.juli.logging.LogFactory;
41 |
42 |
43 | /**
44 | * This valve sends information about the requests to the Stackdriver Trace service.
45 | */
46 | public class TraceValve extends ValveBase {
47 |
48 | /**
49 | * Header used by GCP to stores the trace id and the span id.
50 | */
51 | private static final String X_CLOUD_TRACE_HEADER = SpanContextFactory.headerKey();
52 |
53 | private static final Log log = LogFactory.getLog(TraceValve.class);
54 |
55 | private TraceService traceService;
56 |
57 | /**
58 | * Delay in second before the trace scheduler send the traces (allow buffering of traces).
59 | */
60 | private Integer traceScheduledDelay;
61 |
62 | /**
63 | * {@inheritDoc}
64 | *
65 | *