diff --git a/java/google/registry/BUILD b/java/google/registry/BUILD
index 697c04745..a76e2bee2 100644
--- a/java/google/registry/BUILD
+++ b/java/google/registry/BUILD
@@ -40,6 +40,7 @@ registry_ear_file(
},
wars = {
"registry_default.war": "default",
+ "registry_pubapi.war": "pubapi",
"registry_backend.war": "backend",
"registry_tools.war": "tools",
},
@@ -57,6 +58,7 @@ registry_ear_file(
},
wars = {
"registry_default_nocron.war": "default",
+ "registry_pubapi.war": "pubapi",
"registry_backend.war": "backend",
"registry_tools.war": "tools",
},
@@ -93,6 +95,27 @@ zip_file(
deps = [":registry_default_war"],
)
+zip_file(
+ name = "registry_pubapi_war",
+ srcs = [
+ "env/common/pubapi/WEB-INF/dos.xml",
+ "env/common/pubapi/WEB-INF/logging.properties",
+ "env/common/pubapi/WEB-INF/web.xml",
+ "env/production/pubapi/WEB-INF/appengine-web.xml",
+ "//java/google/registry/module/pubapi:pubapi_jar_deploy.jar",
+ ],
+ out = "registry_pubapi.war",
+ mappings = {
+ "domain_registry/java/google/registry/env/common/pubapi": "",
+ "domain_registry/java/google/registry/env/production/pubapi": "",
+ "domain_registry/java/google/registry/module/pubapi": "WEB-INF/lib",
+ },
+ deps = [
+ ":common_war",
+ "//java/google/registry/ui:war_debug",
+ ],
+)
+
zip_file(
name = "registry_backend_war",
srcs = [
@@ -148,6 +171,7 @@ registry_ear_file(
},
wars = {
"registry_default_sandbox.war": "default",
+ "registry_pubapi_sandbox.war": "pubapi",
"registry_backend_sandbox.war": "backend",
"registry_tools_sandbox.war": "tools",
},
@@ -166,6 +190,18 @@ zip_file(
deps = [":registry_default_war"],
)
+zip_file(
+ name = "registry_pubapi_sandbox_war",
+ srcs = [
+ "env/sandbox/pubapi/WEB-INF/appengine-web.xml",
+ ],
+ out = "registry_pubapi_sandbox.war",
+ mappings = {
+ "domain_registry/java/google/registry/env/sandbox/pubapi": "",
+ },
+ deps = [":registry_pubapi_war"],
+)
+
zip_file(
name = "registry_backend_sandbox_war",
srcs = [
@@ -204,6 +240,7 @@ registry_ear_file(
},
wars = {
"registry_default_alpha.war": "default",
+ "registry_pubapi_alpha.war": "pubapi",
"registry_backend_alpha.war": "backend",
"registry_tools_alpha.war": "tools",
},
@@ -224,6 +261,7 @@ registry_ear_file(
},
wars = {
"registry_default_alpha_nocron.war": "default",
+ "registry_pubapi_alpha.war": "pubapi",
"registry_backend_alpha.war": "backend",
"registry_tools_alpha.war": "tools",
},
@@ -249,6 +287,18 @@ zip_file(
deps = [":registry_default_alpha_war"],
)
+zip_file(
+ name = "registry_pubapi_alpha_war",
+ srcs = [
+ "env/alpha/pubapi/WEB-INF/appengine-web.xml",
+ ],
+ out = "registry_pubapi_alpha.war",
+ mappings = {
+ "domain_registry/java/google/registry/env/alpha/pubapi": "",
+ },
+ deps = [":registry_pubapi_war"],
+)
+
zip_file(
name = "registry_backend_alpha_war",
srcs = [
@@ -287,6 +337,7 @@ registry_ear_file(
},
wars = {
"registry_default_crash.war": "default",
+ "registry_pubapi_crash.war": "pubapi",
"registry_backend_crash.war": "backend",
"registry_tools_crash.war": "tools",
},
@@ -307,6 +358,7 @@ registry_ear_file(
},
wars = {
"registry_default_crash_nocron.war": "default",
+ "registry_pubapi_crash.war": "pubapi",
"registry_backend_crash.war": "backend",
"registry_tools_crash.war": "tools",
},
@@ -332,6 +384,18 @@ zip_file(
deps = [":registry_default_crash_war"],
)
+zip_file(
+ name = "registry_pubapi_crash_war",
+ srcs = [
+ "env/crash/pubapi/WEB-INF/appengine-web.xml",
+ ],
+ out = "registry_pubapi_crash.war",
+ mappings = {
+ "domain_registry/java/google/registry/env/crash/pubapi": "",
+ },
+ deps = [":registry_pubapi_war"],
+)
+
zip_file(
name = "registry_backend_crash_war",
srcs = [
@@ -370,6 +434,7 @@ registry_ear_file(
},
wars = {
"registry_default_local.war": "default",
+ "registry_pubapi_local.war": "pubapi",
"registry_backend_local.war": "backend",
"registry_tools_local.war": "tools",
},
@@ -387,6 +452,18 @@ zip_file(
deps = [":registry_default_war"],
)
+zip_file(
+ name = "registry_pubapi_local_war",
+ srcs = [
+ "env/local/pubapi/WEB-INF/appengine-web.xml",
+ ],
+ out = "registry_pubapi_local.war",
+ mappings = {
+ "domain_registry/java/google/registry/env/local/pubapi": "",
+ },
+ deps = [":registry_pubapi_war"],
+)
+
zip_file(
name = "registry_backend_local_war",
srcs = [
diff --git a/java/google/registry/env/alpha/pubapi/WEB-INF/appengine-web.xml b/java/google/registry/env/alpha/pubapi/WEB-INF/appengine-web.xml
new file mode 100644
index 000000000..ca8587338
--- /dev/null
+++ b/java/google/registry/env/alpha/pubapi/WEB-INF/appengine-web.xml
@@ -0,0 +1,29 @@
+
+
+
+ domain-registry
+ 1
+ java8
+ pubapi
+ true
+ true
+ B4
+
+ 8
+ 10m
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java/google/registry/env/common/META-INF/application.xml b/java/google/registry/env/common/META-INF/application.xml
index 485557063..80d8baf00 100644
--- a/java/google/registry/env/common/META-INF/application.xml
+++ b/java/google/registry/env/common/META-INF/application.xml
@@ -19,6 +19,12 @@ encoding="UTF-8"?>
default
+
+
+ pubapi
+ pubapi
+
+
backend
diff --git a/java/google/registry/env/common/pubapi/WEB-INF/dos.xml b/java/google/registry/env/common/pubapi/WEB-INF/dos.xml
new file mode 100644
index 000000000..14af3ba15
--- /dev/null
+++ b/java/google/registry/env/common/pubapi/WEB-INF/dos.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
diff --git a/java/google/registry/env/common/pubapi/WEB-INF/logging.properties b/java/google/registry/env/common/pubapi/WEB-INF/logging.properties
new file mode 100644
index 000000000..064aa4905
--- /dev/null
+++ b/java/google/registry/env/common/pubapi/WEB-INF/logging.properties
@@ -0,0 +1,13 @@
+# A default java.util.logging configuration.
+# (All App Engine logging is through java.util.logging by default).
+#
+# To use this configuration, copy it into your application's WEB-INF
+# folder and add the following to your appengine-web.xml:
+#
+#
+#
+#
+#
+
+# Set the default logging level for all loggers to INFO.
+.level = INFO
diff --git a/java/google/registry/env/common/pubapi/WEB-INF/web.xml b/java/google/registry/env/common/pubapi/WEB-INF/web.xml
new file mode 100644
index 000000000..ca266e06e
--- /dev/null
+++ b/java/google/registry/env/common/pubapi/WEB-INF/web.xml
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+ PubApiServlet
+ pubapi-servlet
+ google.registry.module.pubapi.PubApiServlet
+ 1
+
+
+
+
+ pubapi-servlet
+ /whois/*
+
+
+
+
+ pubapi-servlet
+ /_dr/whois
+
+
+
+
+ pubapi-servlet
+ /rdap/*
+
+
+
+
+ pubapi-servlet
+ /check
+
+
+
+
+
+ Internal
+
+ Admin-only internal section. Requests for paths covered by the URL patterns below will be
+ checked for a logged-in user account that's allowed to access the AppEngine admin console
+ (NOTE: this includes Editor/Viewer permissions in addition to Owner and the new IAM
+ App Engine Admin role. See https://cloud.google.com/appengine/docs/java/access-control
+ specifically the "Access handlers that have a login:admin restriction" line.)
+
+ TODO(b/28219927): lift some of these restrictions so that we can allow OAuth authentication
+ for endpoints that need to be accessed by open-source automated processes.
+
+
+
+ /_ah/*
+
+
+ /assets/sources/*
+
+
+ /assets/js/registrar_bin.js.map
+ /assets/js/registrar_dbg.js
+ /assets/js/brain_bin.js.map
+ /assets/css/registrar_dbg.css
+
+
+
+ admin
+
+
+
+
+ CONFIDENTIAL
+
+
+
+
+
+ Registrar console
+
+ Registrar console requires user login. This is in addition to the
+ code-level "requireLogin" configuration on individual @Actions.
+
+ /registrar*
+
+
+ *
+
+
+
+ CONFIDENTIAL
+
+
+
+
+
+
+ Secure
+
+ Require encryption for all paths. http URLs will be redirected to https.
+
+ /*
+
+
+ CONFIDENTIAL
+
+
+
+
+
+ ObjectifyFilter
+ com.googlecode.objectify.ObjectifyFilter
+
+
+ ObjectifyFilter
+ /*
+
+
+
+
+ OfyFilter
+ google.registry.model.ofy.OfyFilter
+
+
+ OfyFilter
+ /*
+
+
diff --git a/java/google/registry/env/crash/pubapi/WEB-INF/appengine-web.xml b/java/google/registry/env/crash/pubapi/WEB-INF/appengine-web.xml
new file mode 100644
index 000000000..47a7f6edf
--- /dev/null
+++ b/java/google/registry/env/crash/pubapi/WEB-INF/appengine-web.xml
@@ -0,0 +1,29 @@
+
+
+
+ domain-registry
+ 1
+ java8
+ pubapi
+ true
+ true
+ B4
+
+ 8
+ 10m
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java/google/registry/env/local/pubapi/WEB-INF/appengine-web.xml b/java/google/registry/env/local/pubapi/WEB-INF/appengine-web.xml
new file mode 100644
index 000000000..a26210a8a
--- /dev/null
+++ b/java/google/registry/env/local/pubapi/WEB-INF/appengine-web.xml
@@ -0,0 +1,39 @@
+
+
+
+ domain-registry
+ 1
+ java8
+ pubapi
+ true
+ true
+ B4
+
+ 8
+ 10m
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java/google/registry/env/production/pubapi/WEB-INF/appengine-web.xml b/java/google/registry/env/production/pubapi/WEB-INF/appengine-web.xml
new file mode 100644
index 000000000..5a6ea5c8e
--- /dev/null
+++ b/java/google/registry/env/production/pubapi/WEB-INF/appengine-web.xml
@@ -0,0 +1,33 @@
+
+
+
+ domain-registry
+ 1
+ java8
+ pubapi
+ true
+ true
+ B4_1G
+
+ 20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java/google/registry/env/sandbox/pubapi/WEB-INF/appengine-web.xml b/java/google/registry/env/sandbox/pubapi/WEB-INF/appengine-web.xml
new file mode 100644
index 000000000..cde9e57a8
--- /dev/null
+++ b/java/google/registry/env/sandbox/pubapi/WEB-INF/appengine-web.xml
@@ -0,0 +1,33 @@
+
+
+
+ domain-registry
+ 1
+ java8
+ pubapi
+ true
+ true
+ B4_1G
+
+ 20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/java/google/registry/module/pubapi/BUILD b/java/google/registry/module/pubapi/BUILD
new file mode 100644
index 000000000..9e52e21e5
--- /dev/null
+++ b/java/google/registry/module/pubapi/BUILD
@@ -0,0 +1,43 @@
+package(
+ default_visibility = ["//java/google/registry:registry_project"],
+)
+
+licenses(["notice"]) # Apache 2.0
+
+java_library(
+ name = "pubapi",
+ srcs = glob(["*.java"]),
+ deps = [
+ "//java/google/registry/config",
+ "//java/google/registry/dns",
+ "//java/google/registry/flows",
+ "//java/google/registry/keyring/api",
+ "//java/google/registry/keyring/kms",
+ "//java/google/registry/monitoring/whitebox",
+ "//java/google/registry/rdap",
+ "//java/google/registry/request",
+ "//java/google/registry/request:modules",
+ "//java/google/registry/request/auth",
+ "//java/google/registry/util",
+ "//java/google/registry/whois",
+ "@com_google_appengine_api_1_0_sdk",
+ "@com_google_code_findbugs_jsr305",
+ "@com_google_dagger",
+ "@com_google_guava",
+ "@com_google_monitoring_client_metrics",
+ "@javax_servlet_api",
+ "@org_bouncycastle_bcpkix_jdk15on",
+ ],
+)
+
+# This rule is used so bazel can generate "frontend_jar_deploy.jar" (which
+# contains transitive dependencies) for deployment to App Engine. It MUST
+# explicitly depend upon upon anything loaded at runtime, e.g. old servlets
+# referenced by the module's web.xml file, that isn't statically linked above.
+java_binary(
+ name = "pubapi_jar",
+ create_executable = 0,
+ runtime_deps = [
+ ":pubapi",
+ ],
+)
diff --git a/java/google/registry/module/pubapi/PubApiComponent.java b/java/google/registry/module/pubapi/PubApiComponent.java
new file mode 100644
index 000000000..d1edf4172
--- /dev/null
+++ b/java/google/registry/module/pubapi/PubApiComponent.java
@@ -0,0 +1,71 @@
+// Copyright 2018 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.module.pubapi;
+
+import com.google.monitoring.metrics.MetricReporter;
+import dagger.Component;
+import dagger.Lazy;
+import google.registry.config.RegistryConfig.ConfigModule;
+import google.registry.flows.ServerTridProviderModule;
+import google.registry.flows.custom.CustomLogicFactoryModule;
+import google.registry.keyring.api.KeyModule;
+import google.registry.keyring.kms.KmsModule;
+import google.registry.module.pubapi.PubApiRequestComponent.PubApiRequestComponentModule;
+import google.registry.monitoring.whitebox.StackdriverModule;
+import google.registry.request.Modules.AppIdentityCredentialModule;
+import google.registry.request.Modules.GoogleCredentialModule;
+import google.registry.request.Modules.Jackson2Module;
+import google.registry.request.Modules.ModulesServiceModule;
+import google.registry.request.Modules.NetHttpTransportModule;
+import google.registry.request.Modules.UrlFetchTransportModule;
+import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
+import google.registry.request.Modules.UserServiceModule;
+import google.registry.request.auth.AuthModule;
+import google.registry.util.SystemClock.SystemClockModule;
+import google.registry.util.SystemSleeper.SystemSleeperModule;
+import javax.inject.Singleton;
+
+/** Dagger component with instance lifetime for "pubapi" App Engine module. */
+@Singleton
+@Component(
+ modules = {
+ // TODO(b/79692981): Remove flow-related includes once check API is rewritten to not wrap flow.
+ AppIdentityCredentialModule.class,
+ AuthModule.class,
+ ConfigModule.class,
+ CustomLogicFactoryModule.class,
+ google.registry.keyring.api.DummyKeyringModule.class,
+ PubApiMetricsModule.class,
+ PubApiRequestComponentModule.class,
+ GoogleCredentialModule.class,
+ Jackson2Module.class,
+ KeyModule.class,
+ KmsModule.class,
+ ModulesServiceModule.class,
+ NetHttpTransportModule.class,
+ ServerTridProviderModule.class,
+ StackdriverModule.class,
+ SystemClockModule.class,
+ SystemSleeperModule.class,
+ UrlFetchTransportModule.class,
+ UseAppIdentityCredentialForGoogleApisModule.class,
+ UserServiceModule.class,
+ }
+)
+interface PubApiComponent {
+ PubApiRequestHandler requestHandler();
+
+ Lazy metricReporter();
+}
diff --git a/java/google/registry/module/pubapi/PubApiMetricsModule.java b/java/google/registry/module/pubapi/PubApiMetricsModule.java
new file mode 100644
index 000000000..d2e5b9cc2
--- /dev/null
+++ b/java/google/registry/module/pubapi/PubApiMetricsModule.java
@@ -0,0 +1,25 @@
+// Copyright 2018 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.module.pubapi;
+
+import dagger.Module;
+import javax.inject.Singleton;
+
+/**
+ * Dagger module for injecting metrics. All metrics should have {@link Singleton} scope to avoid
+ * being recreated per-request, as the metrics generally track cumulative values.
+ */
+@Module
+public class PubApiMetricsModule {}
diff --git a/java/google/registry/module/pubapi/PubApiRequestComponent.java b/java/google/registry/module/pubapi/PubApiRequestComponent.java
new file mode 100644
index 000000000..2e3612517
--- /dev/null
+++ b/java/google/registry/module/pubapi/PubApiRequestComponent.java
@@ -0,0 +1,78 @@
+// Copyright 2018 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.module.pubapi;
+
+import dagger.Module;
+import dagger.Subcomponent;
+import google.registry.dns.DnsModule;
+import google.registry.flows.CheckApiAction;
+import google.registry.flows.CheckApiAction.CheckApiModule;
+import google.registry.flows.FlowComponent;
+import google.registry.flows.TlsCredentials.EppTlsModule;
+import google.registry.monitoring.whitebox.WhiteboxModule;
+import google.registry.rdap.RdapAutnumAction;
+import google.registry.rdap.RdapDomainAction;
+import google.registry.rdap.RdapDomainSearchAction;
+import google.registry.rdap.RdapEntityAction;
+import google.registry.rdap.RdapEntitySearchAction;
+import google.registry.rdap.RdapHelpAction;
+import google.registry.rdap.RdapIpAction;
+import google.registry.rdap.RdapModule;
+import google.registry.rdap.RdapNameserverAction;
+import google.registry.rdap.RdapNameserverSearchAction;
+import google.registry.request.RequestComponentBuilder;
+import google.registry.request.RequestModule;
+import google.registry.request.RequestScope;
+import google.registry.whois.WhoisAction;
+import google.registry.whois.WhoisHttpAction;
+import google.registry.whois.WhoisModule;
+
+/** Dagger component with per-request lifetime for "pubapi" App Engine module. */
+@RequestScope
+@Subcomponent(
+ modules = {
+ CheckApiModule.class,
+ DnsModule.class,
+ EppTlsModule.class,
+ RdapModule.class,
+ RequestModule.class,
+ WhiteboxModule.class,
+ WhoisModule.class,
+ })
+interface PubApiRequestComponent {
+ CheckApiAction checkApiAction();
+ // TODO(b/79692981): Remove flow-related includes once check API is rewritten to not wrap flow.
+ FlowComponent.Builder flowComponentBuilder();
+ RdapAutnumAction rdapAutnumAction();
+ RdapDomainAction rdapDomainAction();
+ RdapDomainSearchAction rdapDomainSearchAction();
+ RdapEntityAction rdapEntityAction();
+ RdapEntitySearchAction rdapEntitySearchAction();
+ RdapHelpAction rdapHelpAction();
+ RdapIpAction rdapDefaultAction();
+ RdapNameserverAction rdapNameserverAction();
+ RdapNameserverSearchAction rdapNameserverSearchAction();
+ WhoisHttpAction whoisHttpAction();
+ WhoisAction whoisAction();
+
+ @Subcomponent.Builder
+ abstract class Builder implements RequestComponentBuilder {
+ @Override public abstract Builder requestModule(RequestModule requestModule);
+ @Override public abstract PubApiRequestComponent build();
+ }
+
+ @Module(subcomponents = PubApiRequestComponent.class)
+ class PubApiRequestComponentModule {}
+}
diff --git a/java/google/registry/module/pubapi/PubApiRequestHandler.java b/java/google/registry/module/pubapi/PubApiRequestHandler.java
new file mode 100644
index 000000000..71a263c89
--- /dev/null
+++ b/java/google/registry/module/pubapi/PubApiRequestHandler.java
@@ -0,0 +1,31 @@
+// Copyright 2018 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.module.pubapi;
+
+import google.registry.request.RequestHandler;
+import google.registry.request.auth.RequestAuthenticator;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+/** Request handler for the frontend module. */
+public class PubApiRequestHandler extends RequestHandler {
+
+ @Inject
+ PubApiRequestHandler(
+ Provider componentBuilderProvider,
+ RequestAuthenticator requestAuthenticator) {
+ super(componentBuilderProvider, requestAuthenticator);
+ }
+}
diff --git a/java/google/registry/module/pubapi/PubApiServlet.java b/java/google/registry/module/pubapi/PubApiServlet.java
new file mode 100644
index 000000000..aab42779b
--- /dev/null
+++ b/java/google/registry/module/pubapi/PubApiServlet.java
@@ -0,0 +1,68 @@
+// Copyright 2018 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.module.pubapi;
+
+import com.google.appengine.api.LifecycleManager;
+import com.google.monitoring.metrics.MetricReporter;
+import dagger.Lazy;
+import google.registry.util.FormattingLogger;
+import java.io.IOException;
+import java.security.Security;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/** Servlet that should handle all requests to our "default" App Engine module. */
+public final class PubApiServlet extends HttpServlet {
+
+ private static final PubApiComponent component = DaggerPubApiComponent.create();
+ private static final PubApiRequestHandler requestHandler = component.requestHandler();
+ private static final Lazy metricReporter = component.metricReporter();
+ private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();
+
+ @Override
+ public void init() {
+ Security.addProvider(new BouncyCastleProvider());
+
+ // If metric reporter failed to instantiate for any reason (bad keyring, bad json credential,
+ // etc), we log the error but keep the main thread running. Also the shutdown hook will only be
+ // registered if metric reporter starts up correctly.
+ try {
+ metricReporter.get().startAsync().awaitRunning(10, TimeUnit.SECONDS);
+ logger.info("Started up MetricReporter");
+ LifecycleManager.getInstance()
+ .setShutdownHook(
+ () -> {
+ try {
+ metricReporter.get().stopAsync().awaitTerminated(10, TimeUnit.SECONDS);
+ logger.info("Shut down MetricReporter");
+ } catch (TimeoutException e) {
+ logger.severe(e, "Failed to stop MetricReporter.");
+ }
+ });
+ } catch (Exception e) {
+ logger.severe(e, "Failed to initialize MetricReporter.");
+ }
+ }
+
+ @Override
+ public void service(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
+ logger.info("Received frontend request");
+ requestHandler.handleRequest(req, rsp);
+ }
+}
diff --git a/java/google/registry/module/pubapi/package-info.java b/java/google/registry/module/pubapi/package-info.java
new file mode 100644
index 000000000..55adfa585
--- /dev/null
+++ b/java/google/registry/module/pubapi/package-info.java
@@ -0,0 +1,16 @@
+// Copyright 2018 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+@javax.annotation.ParametersAreNonnullByDefault
+package google.registry.module.pubapi;
diff --git a/java/google/registry/tools/BUILD b/java/google/registry/tools/BUILD
index e042c8026..67c521c45 100644
--- a/java/google/registry/tools/BUILD
+++ b/java/google/registry/tools/BUILD
@@ -51,6 +51,7 @@ java_library(
"//java/google/registry/model",
"//java/google/registry/module/backend",
"//java/google/registry/module/frontend",
+ "//java/google/registry/module/pubapi",
"//java/google/registry/module/tools",
"//java/google/registry/pricing",
"//java/google/registry/rde",
diff --git a/javatests/google/registry/module/pubapi/BUILD b/javatests/google/registry/module/pubapi/BUILD
new file mode 100644
index 000000000..6af438dd1
--- /dev/null
+++ b/javatests/google/registry/module/pubapi/BUILD
@@ -0,0 +1,35 @@
+package(
+ default_testonly = 1,
+ default_visibility = ["//java/google/registry:registry_project"],
+)
+
+licenses(["notice"]) # Apache 2.0
+
+java_library(
+ name = "pubapi",
+ srcs = glob(["*.java"]),
+ resources = glob(["testdata/*"]),
+ runtime_deps = [
+ # TODO(b/19332643): Remove this dependency when Modules is lazy.
+ "@com_google_appengine_api_1_0_sdk//:testonly",
+ ],
+ deps = [
+ "//java/google/registry/module/pubapi",
+ "//java/google/registry/request",
+ "//javatests/google/registry/testing",
+ "@com_google_guava",
+ "@com_google_truth",
+ "@com_google_truth_extensions_truth_java8_extension",
+ "@javax_servlet_api",
+ "@junit",
+ "@org_mockito_all",
+ ],
+)
+
+load("//java/com/google/testing/builddefs:GenTestRules.bzl", "GenTestRules")
+
+GenTestRules(
+ name = "GeneratedTestRules",
+ test_files = glob(["*Test.java"]),
+ deps = [":pubapi"],
+)
diff --git a/javatests/google/registry/module/pubapi/PubApiRequestComponentTest.java b/javatests/google/registry/module/pubapi/PubApiRequestComponentTest.java
new file mode 100644
index 000000000..f85549a72
--- /dev/null
+++ b/javatests/google/registry/module/pubapi/PubApiRequestComponentTest.java
@@ -0,0 +1,33 @@
+// Copyright 2018 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.module.pubapi;
+
+
+import google.registry.testing.GoldenFileTestHelper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link PubApiRequestComponent}. */
+@RunWith(JUnit4.class)
+public class PubApiRequestComponentTest {
+
+ @Test
+ public void testRoutingMap() throws Exception {
+ GoldenFileTestHelper.assertThatRoutesFromComponent(PubApiRequestComponent.class)
+ .describedAs("pubapi routing map")
+ .isEqualToGolden(PubApiRequestComponentTest.class, "pubapi_routing.txt");
+ }
+}
diff --git a/javatests/google/registry/module/pubapi/PubApiServletTest.java b/javatests/google/registry/module/pubapi/PubApiServletTest.java
new file mode 100644
index 000000000..58439782e
--- /dev/null
+++ b/javatests/google/registry/module/pubapi/PubApiServletTest.java
@@ -0,0 +1,47 @@
+// Copyright 2018 The Nomulus Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package google.registry.module.pubapi;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import google.registry.testing.AppEngineRule;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link PubApiServlet}. */
+@RunWith(JUnit4.class)
+public class PubApiServletTest {
+
+ @Rule
+ public final AppEngineRule appEngine =
+ AppEngineRule.builder().withDatastore().withLocalModules().build();
+
+ private final HttpServletRequest req = mock(HttpServletRequest.class);
+ private final HttpServletResponse rsp = mock(HttpServletResponse.class);
+
+ @Test
+ public void testService_unknownPath_returnNotFound() throws Exception {
+ when(req.getMethod()).thenReturn("GET");
+ when(req.getRequestURI()).thenReturn("/lol");
+ new PubApiServlet().service(req, rsp);
+ verify(rsp).sendError(404);
+ }
+}
diff --git a/javatests/google/registry/module/pubapi/testdata/pubapi_routing.txt b/javatests/google/registry/module/pubapi/testdata/pubapi_routing.txt
new file mode 100644
index 000000000..089d67c16
--- /dev/null
+++ b/javatests/google/registry/module/pubapi/testdata/pubapi_routing.txt
@@ -0,0 +1,13 @@
+PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY
+/_dr/whois WhoisAction POST n INTERNAL,API APP PUBLIC
+/check CheckApiAction GET n INTERNAL NONE PUBLIC
+/rdap/autnum/(*) RdapAutnumAction GET,HEAD n INTERNAL NONE PUBLIC
+/rdap/domain/(*) RdapDomainAction GET,HEAD n INTERNAL,API,LEGACY NONE PUBLIC
+/rdap/domains RdapDomainSearchAction GET,HEAD n INTERNAL,API,LEGACY NONE PUBLIC
+/rdap/entities RdapEntitySearchAction GET,HEAD n INTERNAL,API,LEGACY NONE PUBLIC
+/rdap/entity/(*) RdapEntityAction GET,HEAD n INTERNAL,API,LEGACY NONE PUBLIC
+/rdap/help(*) RdapHelpAction GET,HEAD n INTERNAL NONE PUBLIC
+/rdap/ip/(*) RdapIpAction GET,HEAD n INTERNAL NONE PUBLIC
+/rdap/nameserver/(*) RdapNameserverAction GET,HEAD n INTERNAL NONE PUBLIC
+/rdap/nameservers RdapNameserverSearchAction GET,HEAD n INTERNAL NONE PUBLIC
+/whois/(*) WhoisHttpAction GET n INTERNAL NONE PUBLIC