aboutsummaryrefslogtreecommitdiffstats
path: root/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http
diff options
context:
space:
mode:
authormmis <michael.morris@ericsson.com>2018-07-20 15:28:25 +0100
committermmis <michael.morris@ericsson.com>2018-07-25 13:16:51 +0100
commitb40acf2d244058c162a8597968e59f2708e6abf4 (patch)
treed30890a123305c50e3280ebbe02742b72f226c90 /policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http
parent02bf7de575b246c63188988072aabb36ac3b697b (diff)
Copy policy-endpoints from drools-pdp to common
Issue-ID: POLICY-967 Change-Id: I374c155ee102c3e157c60d0a22d7191544abb76a Signed-off-by: mmis <michael.morris@ericsson.com>
Diffstat (limited to 'policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http')
-rw-r--r--policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClient.java54
-rw-r--r--policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactory.java68
-rw-r--r--policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/impl/IndexedHttpClientFactory.java185
-rw-r--r--policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java255
-rw-r--r--policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java77
-rw-r--r--policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java81
-rw-r--r--policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/impl/IndexedHttpServletServerFactory.java215
-rw-r--r--policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java254
-rw-r--r--policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java399
9 files changed, 1588 insertions, 0 deletions
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClient.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClient.java
new file mode 100644
index 00000000..b4aea22c
--- /dev/null
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClient.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-endpoints
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.endpoints.http.client;
+
+import javax.ws.rs.core.Response;
+
+import org.onap.policy.common.capabilities.Startable;
+
+public interface HttpClient extends Startable {
+
+ public Response get(String path);
+
+ public Response get();
+
+ public static <T> T getBody(Response response, Class<T> entityType) {
+ return response.readEntity(entityType);
+ }
+
+ public String getName();
+
+ public boolean isHttps();
+
+ public boolean isSelfSignedCerts();
+
+ public String getHostname();
+
+ public int getPort();
+
+ public String getBasePath();
+
+ public String getUserName();
+
+ public String getPassword();
+
+ public String getBaseUrl();
+}
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactory.java
new file mode 100644
index 00000000..5435ee9c
--- /dev/null
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/HttpClientFactory.java
@@ -0,0 +1,68 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-endpoints
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.endpoints.http.client;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Http Client Factory
+ */
+public interface HttpClientFactory {
+
+ /**
+ * build and http client with the following parameters
+ */
+ public HttpClient build(String name, boolean https, boolean selfSignedCerts, String hostname, int port,
+ String baseUrl, String userName, String password, boolean managed)
+ throws KeyManagementException, NoSuchAlgorithmException;
+
+ /**
+ * build http client from properties
+ */
+ public List<HttpClient> build(Properties properties) throws KeyManagementException, NoSuchAlgorithmException;
+
+ /**
+ * get http client
+ *
+ * @param name the name
+ * @return the http client
+ */
+ public HttpClient get(String name);
+
+ /**
+ * list of http clients
+ *
+ * @return http clients
+ */
+ public List<HttpClient> inventory();
+
+ /**
+ * destroy by name
+ *
+ * @param name name
+ */
+ public void destroy(String name);
+
+ public void destroy();
+}
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/impl/IndexedHttpClientFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/impl/IndexedHttpClientFactory.java
new file mode 100644
index 00000000..2e911c9a
--- /dev/null
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/impl/IndexedHttpClientFactory.java
@@ -0,0 +1,185 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-endpoints
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.endpoints.http.client.impl;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Properties;
+
+import org.onap.policy.common.endpoints.http.client.HttpClient;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactory;
+import org.onap.policy.common.endpoints.http.client.internal.JerseyClient;
+import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * http client factory implementation indexed by name
+ */
+public class IndexedHttpClientFactory implements HttpClientFactory {
+
+ private static final HttpClientFactory instance = new IndexedHttpClientFactory();
+
+ /**
+ * Logger
+ */
+ private static Logger logger = LoggerFactory.getLogger(IndexedHttpClientFactory.class);
+
+ protected HashMap<String, HttpClient> clients = new HashMap<>();
+
+ /**
+ * Get the singleton instance.
+ *
+ * @return the instance
+ */
+ public static HttpClientFactory getInstance() {
+ return instance;
+ }
+
+ private IndexedHttpClientFactory() {}
+
+ @Override
+ public synchronized HttpClient build(String name, boolean https, boolean selfSignedCerts, String hostname, int port,
+ String baseUrl, String userName, String password, boolean managed)
+ throws KeyManagementException, NoSuchAlgorithmException {
+ if (clients.containsKey(name)) {
+ return clients.get(name);
+ }
+
+ JerseyClient client =
+ new JerseyClient(name, https, selfSignedCerts, hostname, port, baseUrl, userName, password);
+
+ if (managed) {
+ clients.put(name, client);
+ }
+
+ return client;
+ }
+
+ @Override
+ public synchronized List<HttpClient> build(Properties properties)
+ throws KeyManagementException, NoSuchAlgorithmException {
+ ArrayList<HttpClient> clientList = new ArrayList<>();
+
+ String clientNames = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES);
+ if (clientNames == null || clientNames.isEmpty()) {
+ return clientList;
+ }
+
+ List<String> clientNameList = new ArrayList<>(Arrays.asList(clientNames.split("\\s*,\\s*")));
+
+ for (String clientName : clientNameList) {
+ String httpsString = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "."
+ + clientName + PolicyEndPointProperties.PROPERTY_HTTP_HTTPS_SUFFIX);
+ boolean https = false;
+ if (httpsString != null && !httpsString.isEmpty()) {
+ https = Boolean.parseBoolean(httpsString);
+ }
+
+ String hostName = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "."
+ + clientName + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX);
+
+ String servicePortString = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES
+ + "." + clientName + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX);
+ int port;
+ try {
+ if (servicePortString == null || servicePortString.isEmpty()) {
+ continue;
+ }
+ port = Integer.parseInt(servicePortString);
+ } catch (NumberFormatException nfe) {
+ logger.error("http-client-factory: cannot parse port {}", servicePortString, nfe);
+ continue;
+ }
+
+ String baseUrl = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "."
+ + clientName + PolicyEndPointProperties.PROPERTY_HTTP_URL_SUFFIX);
+
+ String userName = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "."
+ + clientName + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX);
+
+ String password = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "."
+ + clientName + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX);
+
+ String managedString = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_CLIENT_SERVICES + "."
+ + clientName + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX);
+ boolean managed = true;
+ if (managedString != null && !managedString.isEmpty()) {
+ managed = Boolean.parseBoolean(managedString);
+ }
+
+ try {
+ HttpClient client =
+ this.build(clientName, https, https, hostName, port, baseUrl, userName, password, managed);
+ clientList.add(client);
+ } catch (Exception e) {
+ logger.error("http-client-factory: cannot build client {}", clientName, e);
+ }
+ }
+
+ return clientList;
+ }
+
+ @Override
+ public synchronized HttpClient get(String name) {
+ if (clients.containsKey(name)) {
+ return clients.get(name);
+ }
+
+ throw new IllegalArgumentException("Http Client " + name + " not found");
+ }
+
+ @Override
+ public synchronized List<HttpClient> inventory() {
+ return new ArrayList<>(this.clients.values());
+ }
+
+ @Override
+ public synchronized void destroy(String name) {
+ if (!clients.containsKey(name)) {
+ return;
+ }
+
+ HttpClient client = clients.remove(name);
+ try {
+ client.shutdown();
+ } catch (IllegalStateException e) {
+ logger.error("http-client-factory: cannot shutdown client {}", client, e);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ List<HttpClient> clientsInventory = this.inventory();
+ for (HttpClient client : clientsInventory) {
+ client.shutdown();
+ }
+
+ synchronized (this) {
+ this.clients.clear();
+ }
+ }
+
+}
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java
new file mode 100644
index 00000000..d5e16117
--- /dev/null
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/client/internal/JerseyClient.java
@@ -0,0 +1,255 @@
+/*
+ * ============LICENSE_START=======================================================
+ * policy-endpoints
+ * ================================================================================
+ * Copyright (C) 2017-2018 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.endpoints.http.client.internal;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+
+import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
+import org.onap.policy.common.endpoints.http.client.HttpClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class JerseyClient implements HttpClient {
+
+ /**
+ * Logger
+ */
+ private static Logger logger = LoggerFactory.getLogger(JerseyClient.class);
+
+ protected final String name;
+ protected final boolean https;
+ protected final boolean selfSignedCerts;
+ protected final String hostname;
+ protected final int port;
+ protected final String basePath;
+ protected final String userName;
+ protected final String password;
+
+ protected final Client client;
+ protected final String baseUrl;
+
+ protected boolean alive = true;
+
+
+ public JerseyClient(String name, boolean https, boolean selfSignedCerts, String hostname, int port, String basePath,
+ String userName, String password) throws KeyManagementException, NoSuchAlgorithmException {
+
+ super();
+
+ if (name == null || name.isEmpty()) {
+ throw new IllegalArgumentException("Name must be provided");
+ }
+
+ if (hostname == null || hostname.isEmpty()) {
+ throw new IllegalArgumentException("Hostname must be provided");
+ }
+
+ if (port <= 0 && port >= 65535) {
+ throw new IllegalArgumentException("Invalid Port provided: " + port);
+ }
+
+ this.name = name;
+ this.https = https;
+ this.hostname = hostname;
+ this.port = port;
+ this.basePath = basePath;
+ this.userName = userName;
+ this.password = password;
+ this.selfSignedCerts = selfSignedCerts;
+
+ StringBuilder tmpBaseUrl = new StringBuilder();
+ if (this.https) {
+ tmpBaseUrl.append("https://");
+ ClientBuilder clientBuilder;
+ SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
+ if (this.selfSignedCerts) {
+ sslContext.init(null, new TrustManager[] {new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ // always trusted
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ // always trusted
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+
+ }}, new SecureRandom());
+ clientBuilder =
+ ClientBuilder.newBuilder().sslContext(sslContext).hostnameVerifier((host, session) -> true);
+ } else {
+ sslContext.init(null, null, null);
+ clientBuilder = ClientBuilder.newBuilder().sslContext(sslContext);
+ }
+ this.client = clientBuilder.build();
+ } else {
+ tmpBaseUrl.append("http://");
+ this.client = ClientBuilder.newClient();
+ }
+
+ if (this.userName != null && !this.userName.isEmpty() && this.password != null && !this.password.isEmpty()) {
+ HttpAuthenticationFeature authFeature = HttpAuthenticationFeature.basic(userName, password);
+ this.client.register(authFeature);
+ }
+
+ this.baseUrl = tmpBaseUrl.append(this.hostname).append(":").append(this.port).append("/")
+ .append((this.basePath == null) ? "" : this.basePath).toString();
+ }
+
+ @Override
+ public Response get(String path) {
+ if (path != null && !path.isEmpty()) {
+ return this.client.target(this.baseUrl).path(path).request().get();
+ } else {
+ return this.client.target(this.baseUrl).request().get();
+ }
+ }
+
+ @Override
+ public Response get() {
+ return this.client.target(this.baseUrl).request().get();
+ }
+
+
+ @Override
+ public boolean start() {
+ return alive;
+ }
+
+ @Override
+ public boolean stop() {
+ return !alive;
+ }
+
+ @Override
+ public void shutdown() {
+ synchronized (this) {
+ alive = false;
+ }
+
+ try {
+ this.client.close();
+ } catch (Exception e) {
+ logger.warn("{}: cannot close because of {}", this, e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public synchronized boolean isAlive() {
+ return this.alive;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public boolean isHttps() {
+ return https;
+ }
+
+ @Override
+ public boolean isSelfSignedCerts() {
+ return selfSignedCerts;
+ }
+
+ @Override
+ public String getHostname() {
+ return hostname;
+ }
+
+ @Override
+ public int getPort() {
+ return port;
+ }
+
+ @Override
+ public String getBasePath() {
+ return basePath;
+ }
+
+ @Override
+ public String getUserName() {
+ return userName;
+ }
+
+ @JsonIgnore
+ @Override
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String getBaseUrl() {
+ return baseUrl;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("JerseyClient [name=");
+ builder.append(name);
+ builder.append(", https=");
+ builder.append(https);
+ builder.append(", selfSignedCerts=");
+ builder.append(selfSignedCerts);
+ builder.append(", hostname=");
+ builder.append(hostname);
+ builder.append(", port=");
+ builder.append(port);
+ builder.append(", basePath=");
+ builder.append(basePath);
+ builder.append(", userName=");
+ builder.append(userName);
+ builder.append(", password=");
+ builder.append(password);
+ builder.append(", client=");
+ builder.append(client);
+ builder.append(", baseUrl=");
+ builder.append(baseUrl);
+ builder.append(", alive=");
+ builder.append(alive);
+ builder.append("]");
+ return builder.toString();
+ }
+
+}
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java
new file mode 100644
index 00000000..d62eb028
--- /dev/null
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServer.java
@@ -0,0 +1,77 @@
+/*
+ * ============LICENSE_START=======================================================
+ * policy-endpoints
+ * ================================================================================
+ * Copyright (C) 2017-2018 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.endpoints.http.server;
+
+import org.onap.policy.common.capabilities.Startable;
+
+/**
+ * A Jetty Server to server REST Requests
+ */
+public interface HttpServletServer extends Startable {
+
+ /**
+ *
+ * @return port
+ */
+ public int getPort();
+
+ /**
+ * enables basic authentication with user and password on the the relative path relativeUriPath
+ *
+ * @param user
+ * @param password
+ * @param relativeUriPath
+ */
+ public void setBasicAuthentication(String user, String password, String relativeUriPath);
+
+ /**
+ * adds a JAX-RS servlet class to serve REST requests
+ *
+ * @param servletPath servlet path
+ * @param restClass JAX-RS API Class
+ *
+ * @throws IllegalArgumentException unable to process because of invalid input
+ * @throws IllegalStateException unable to process because of invalid state
+ */
+ public void addServletClass(String servletPath, String restClass);
+
+ /**
+ * adds a package containing JAX-RS classes to serve REST requests
+ *
+ * @param servletPath servlet path
+ * @param restPackage JAX-RS package to scan
+ *
+ * @throws IllegalArgumentException unable to process because of invalid input
+ * @throws IllegalStateException unable to process because of invalid state
+ */
+ public void addServletPackage(String servletPath, String restPackage);
+
+ /**
+ * blocking start of the http server
+ *
+ * @param maxWaitTime max time to wait for the start to take place
+ * @return true if start was successful
+ *
+ * @throws IllegalArgumentException if arguments are invalid
+ * @throws InterruptedException if the blocking operation is interrupted
+ */
+ public boolean waitedStart(long maxWaitTime) throws InterruptedException;
+}
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java
new file mode 100644
index 00000000..d0909b58
--- /dev/null
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/HttpServletServerFactory.java
@@ -0,0 +1,81 @@
+/*
+ * ============LICENSE_START=======================================================
+ * policy-endpoints
+ * ================================================================================
+ * Copyright (C) 2017-2018 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.endpoints.http.server;
+
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Factory of HTTP Servlet-Enabled Servlets
+ */
+public interface HttpServletServerFactory {
+
+ /**
+ * builds an http server with support for servlets
+ *
+ * @param name name
+ * @param host binding host
+ * @param port port
+ * @param contextPath server base path
+ * @param swagger enable swagger documentation
+ * @param managed is it managed by infrastructure
+ * @return http server
+ * @throws IllegalArgumentException when invalid parameters are provided
+ */
+ public HttpServletServer build(String name, String host, int port, String contextPath, boolean swagger,
+ boolean managed);
+
+ /**
+ * list of http servers per properties
+ *
+ * @param properties properties based configuration
+ * @return list of http servers
+ * @throws IllegalArgumentException when invalid parameters are provided
+ */
+ public List<HttpServletServer> build(Properties properties);
+
+ /**
+ * gets a server based on the port
+ *
+ * @param port port
+ * @return http server
+ */
+ public HttpServletServer get(int port);
+
+ /**
+ * provides an inventory of servers
+ *
+ * @return inventory of servers
+ */
+ public List<HttpServletServer> inventory();
+
+ /**
+ * destroys server bound to a port
+ *
+ * @param port
+ */
+ public void destroy(int port);
+
+ /**
+ * destroys the factory and therefore all servers
+ */
+ public void destroy();
+}
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/impl/IndexedHttpServletServerFactory.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/impl/IndexedHttpServletServerFactory.java
new file mode 100644
index 00000000..9723d808
--- /dev/null
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/impl/IndexedHttpServletServerFactory.java
@@ -0,0 +1,215 @@
+/*
+ * ============LICENSE_START=======================================================
+ * policy-endpoints
+ * ================================================================================
+ * Copyright (C) 2017-2018 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.endpoints.http.server.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Properties;
+
+import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
+import org.onap.policy.common.endpoints.http.server.internal.JettyJerseyServer;
+import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Indexed factory implementation
+ */
+public class IndexedHttpServletServerFactory implements HttpServletServerFactory {
+
+ private static final HttpServletServerFactory instance = new IndexedHttpServletServerFactory();
+
+ /**
+ * Get the singleton instance.
+ *
+ * @return the instance
+ */
+ public static HttpServletServerFactory getInstance() {
+ return instance;
+ }
+
+ private IndexedHttpServletServerFactory() {}
+
+ private static final String SPACES_COMMA_SPACES = "\\s*,\\s*";
+
+ /**
+ * logger
+ */
+ protected static Logger logger = LoggerFactory.getLogger(IndexedHttpServletServerFactory.class);
+
+ /**
+ * servers index
+ */
+ protected HashMap<Integer, HttpServletServer> servers = new HashMap<>();
+
+ @Override
+ public synchronized HttpServletServer build(String name, String host, int port, String contextPath, boolean swagger,
+ boolean managed) {
+
+ if (servers.containsKey(port)) {
+ return servers.get(port);
+ }
+
+ JettyJerseyServer server = new JettyJerseyServer(name, host, port, contextPath, swagger);
+ if (managed) {
+ servers.put(port, server);
+ }
+
+ return server;
+ }
+
+ @Override
+ public synchronized List<HttpServletServer> build(Properties properties) {
+
+ ArrayList<HttpServletServer> serviceList = new ArrayList<>();
+
+ String serviceNames = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES);
+ if (serviceNames == null || serviceNames.isEmpty()) {
+ logger.warn("No topic for HTTP Service: {}", properties);
+ return serviceList;
+ }
+
+ List<String> serviceNameList = Arrays.asList(serviceNames.split(SPACES_COMMA_SPACES));
+
+ for (String serviceName : serviceNameList) {
+ String servicePortString = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
+ + serviceName + PolicyEndPointProperties.PROPERTY_HTTP_PORT_SUFFIX);
+
+ int servicePort;
+ try {
+ if (servicePortString == null || servicePortString.isEmpty()) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("No HTTP port for service in {}", serviceName);
+ }
+ continue;
+ }
+ servicePort = Integer.parseInt(servicePortString);
+ } catch (NumberFormatException nfe) {
+ if (logger.isWarnEnabled()) {
+ logger.warn("No HTTP port for service in {}", serviceName);
+ }
+ continue;
+ }
+
+ String hostName = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + serviceName
+ + PolicyEndPointProperties.PROPERTY_HTTP_HOST_SUFFIX);
+
+ String contextUriPath = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
+ + serviceName + PolicyEndPointProperties.PROPERTY_HTTP_CONTEXT_URIPATH_SUFFIX);
+
+ String userName = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + serviceName
+ + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_USERNAME_SUFFIX);
+
+ String password = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "." + serviceName
+ + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_PASSWORD_SUFFIX);
+
+ String authUriPath = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
+ + serviceName + PolicyEndPointProperties.PROPERTY_HTTP_AUTH_URIPATH_SUFFIX);
+
+ String restClasses = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
+ + serviceName + PolicyEndPointProperties.PROPERTY_HTTP_REST_CLASSES_SUFFIX);
+
+ String restPackages = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
+ + serviceName + PolicyEndPointProperties.PROPERTY_HTTP_REST_PACKAGES_SUFFIX);
+ String restUriPath = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
+ + serviceName + PolicyEndPointProperties.PROPERTY_HTTP_REST_URIPATH_SUFFIX);
+
+ String managedString = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
+ + serviceName + PolicyEndPointProperties.PROPERTY_MANAGED_SUFFIX);
+ boolean managed = true;
+ if (managedString != null && !managedString.isEmpty()) {
+ managed = Boolean.parseBoolean(managedString);
+ }
+
+ String swaggerString = properties.getProperty(PolicyEndPointProperties.PROPERTY_HTTP_SERVER_SERVICES + "."
+ + serviceName + PolicyEndPointProperties.PROPERTY_HTTP_SWAGGER_SUFFIX);
+ boolean swagger = false;
+ if (swaggerString != null && !swaggerString.isEmpty()) {
+ swagger = Boolean.parseBoolean(swaggerString);
+ }
+
+ HttpServletServer service = build(serviceName, hostName, servicePort, contextUriPath, swagger, managed);
+ if (userName != null && !userName.isEmpty() && password != null && !password.isEmpty()) {
+ service.setBasicAuthentication(userName, password, authUriPath);
+ }
+
+ if (restClasses != null && !restClasses.isEmpty()) {
+ List<String> restClassesList = Arrays.asList(restClasses.split(SPACES_COMMA_SPACES));
+ for (String restClass : restClassesList) {
+ service.addServletClass(restUriPath, restClass);
+ }
+ }
+
+ if (restPackages != null && !restPackages.isEmpty()) {
+ List<String> restPackageList = Arrays.asList(restPackages.split(SPACES_COMMA_SPACES));
+ for (String restPackage : restPackageList) {
+ service.addServletPackage(restUriPath, restPackage);
+ }
+ }
+
+ serviceList.add(service);
+ }
+
+ return serviceList;
+ }
+
+ @Override
+ public synchronized HttpServletServer get(int port) {
+
+ if (servers.containsKey(port)) {
+ return servers.get(port);
+ }
+
+ throw new IllegalArgumentException("Http Server for " + port + " not found");
+ }
+
+ @Override
+ public synchronized List<HttpServletServer> inventory() {
+ return new ArrayList<>(this.servers.values());
+ }
+
+ @Override
+ public synchronized void destroy(int port) {
+
+ if (!servers.containsKey(port)) {
+ return;
+ }
+
+ HttpServletServer server = servers.remove(port);
+ server.shutdown();
+ }
+
+ @Override
+ public synchronized void destroy() {
+ List<HttpServletServer> httpServletServers = this.inventory();
+ for (HttpServletServer server : httpServletServers) {
+ server.shutdown();
+ }
+
+ synchronized (this) {
+ this.servers.clear();
+ }
+ }
+
+}
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java
new file mode 100644
index 00000000..cd286927
--- /dev/null
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyJerseyServer.java
@@ -0,0 +1,254 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * policy-endpoints
+ * ================================================================================
+ * Copyright (C) 2017-2018 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.endpoints.http.server.internal;
+
+import java.util.HashMap;
+
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.onap.policy.common.utils.network.NetworkUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.swagger.jersey.config.JerseyJaxrsConfig;
+
+/**
+ * REST Jetty Server that uses Jersey Servlets to support JAX-RS Web Services
+ */
+public class JettyJerseyServer extends JettyServletServer {
+
+ /**
+ * Swagger API Base Path
+ */
+ protected static final String SWAGGER_API_BASEPATH = "swagger.api.basepath";
+
+ /**
+ * Swagger Context ID
+ */
+ protected static final String SWAGGER_CONTEXT_ID = "swagger.context.id";
+
+ /**
+ * Swagger Scanner ID
+ */
+ protected static final String SWAGGER_SCANNER_ID = "swagger.scanner.id";
+
+ /**
+ * Swagger Pretty Print
+ */
+ protected static final String SWAGGER_PRETTY_PRINT = "swagger.pretty.print";
+
+ /**
+ * Swagger Packages
+ */
+ protected static final String SWAGGER_INIT_PACKAGES_PARAM_VALUE = "io.swagger.jaxrs.listing";
+
+ /**
+ * Jersey Packages Init Param Name
+ */
+ protected static final String JERSEY_INIT_PACKAGES_PARAM_NAME = "jersey.config.server.provider.packages";
+
+ /**
+ * Jersey Packages Init Param Value
+ */
+ protected static final String JERSEY_INIT_PACKAGES_PARAM_VALUE = "com.fasterxml.jackson.jaxrs.json";
+
+ /**
+ * Jersey Classes Init Param Name
+ */
+ protected static final String JERSEY_INIT_CLASSNAMES_PARAM_NAME = "jersey.config.server.provider.classnames";
+
+ /**
+ * Jersey Jackson Classes Init Param Value
+ */
+ protected static final String JERSEY_JACKSON_INIT_CLASSNAMES_PARAM_VALUE =
+ "com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider";
+
+ /**
+ * Jersey Swagger Classes Init Param Value
+ */
+ protected static final String SWAGGER_INIT_CLASSNAMES_PARAM_VALUE =
+ "io.swagger.jaxrs.listing.ApiListingResource," + "io.swagger.jaxrs.listing.SwaggerSerializers";
+ /**
+ * Logger
+ */
+ protected static Logger logger = LoggerFactory.getLogger(JettyJerseyServer.class);
+
+ /**
+ * Container for servlets
+ */
+ protected HashMap<String, ServletHolder> servlets = new HashMap<>();
+
+ /**
+ * Swagger ID
+ */
+ protected String swaggerId = null;
+
+ /**
+ * Constructor
+ *
+ * @param name name
+ * @param host host server host
+ * @param port port server port
+ * @param swagger support swagger?
+ * @param contextPath context path
+ *
+ * @throws IllegalArgumentException in invalid arguments are provided
+ */
+ public JettyJerseyServer(String name, String host, int port, String contextPath, boolean swagger) {
+
+ super(name, host, port, contextPath);
+ if (swagger) {
+ this.swaggerId = "swagger-" + this.port;
+ attachSwaggerServlet();
+ }
+ }
+
+ /**
+ * attaches a swagger initialization servlet
+ */
+ protected void attachSwaggerServlet() {
+
+ ServletHolder swaggerServlet = context.addServlet(JerseyJaxrsConfig.class, "/");
+
+ String hostname = this.connector.getHost();
+ if (hostname == null || hostname.isEmpty() || hostname.equals(NetworkUtil.IPv4_WILDCARD_ADDRESS)) {
+ hostname = NetworkUtil.getHostname();
+ }
+
+ swaggerServlet.setInitParameter(SWAGGER_API_BASEPATH,
+ "http://" + hostname + ":" + this.connector.getPort() + "/");
+ swaggerServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId);
+ swaggerServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId);
+ swaggerServlet.setInitParameter(SWAGGER_PRETTY_PRINT, "true");
+ swaggerServlet.setInitOrder(2);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("{}: Swagger Servlet has been attached: {}", this, swaggerServlet.dump());
+ }
+ }
+
+ /**
+ * retrieves cached server based on servlet path
+ *
+ * @param servletPath servlet path
+ * @return the jetty servlet holder
+ *
+ * @throws IllegalArgumentException if invalid arguments are provided
+ */
+ protected synchronized ServletHolder getServlet(String servletPath) {
+
+ ServletHolder jerseyServlet = servlets.get(servletPath);
+ if (jerseyServlet == null) {
+ jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, servletPath);
+ jerseyServlet.setInitOrder(0);
+ servlets.put(servletPath, jerseyServlet);
+ }
+
+ return jerseyServlet;
+ }
+
+ @Override
+ public synchronized void addServletPackage(String servletPath, String restPackage) {
+ String servPath = servletPath;
+ if (restPackage == null || restPackage.isEmpty()) {
+ throw new IllegalArgumentException("No discoverable REST package provided");
+ }
+
+ if (servPath == null || servPath.isEmpty()) {
+ servPath = "/*";
+ }
+
+ ServletHolder jerseyServlet = this.getServlet(servPath);
+
+ String initClasses = jerseyServlet.getInitParameter(JERSEY_INIT_CLASSNAMES_PARAM_NAME);
+ if (initClasses != null && !initClasses.isEmpty()) {
+ logger.warn("Both packages and classes are used in Jetty+Jersey Configuration: {}", restPackage);
+ }
+
+ String initPackages = jerseyServlet.getInitParameter(JERSEY_INIT_PACKAGES_PARAM_NAME);
+ if (initPackages == null) {
+ if (this.swaggerId != null) {
+ initPackages =
+ JERSEY_INIT_PACKAGES_PARAM_VALUE + "," + SWAGGER_INIT_PACKAGES_PARAM_VALUE + "," + restPackage;
+
+ jerseyServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId);
+ jerseyServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId);
+ } else {
+ initPackages = JERSEY_INIT_PACKAGES_PARAM_VALUE + "," + restPackage;
+ }
+ } else {
+ initPackages = initPackages + "," + restPackage;
+ }
+
+ jerseyServlet.setInitParameter(JERSEY_INIT_PACKAGES_PARAM_NAME, initPackages);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("{}: added REST package: {}", this, jerseyServlet.dump());
+ }
+ }
+
+ @Override
+ public synchronized void addServletClass(String servletPath, String restClass) {
+
+ if (restClass == null || restClass.isEmpty()) {
+ throw new IllegalArgumentException("No discoverable REST class provided");
+ }
+
+ if (servletPath == null || servletPath.isEmpty()) {
+ servletPath = "/*";
+ }
+
+ ServletHolder jerseyServlet = this.getServlet(servletPath);
+
+ String initPackages = jerseyServlet.getInitParameter(JERSEY_INIT_PACKAGES_PARAM_NAME);
+ if (initPackages != null && !initPackages.isEmpty()) {
+ logger.warn("Both classes and packages are used in Jetty+Jersey Configuration: {}", restClass);
+ }
+
+ String initClasses = jerseyServlet.getInitParameter(JERSEY_INIT_CLASSNAMES_PARAM_NAME);
+ if (initClasses == null) {
+ if (this.swaggerId != null) {
+ initClasses = JERSEY_JACKSON_INIT_CLASSNAMES_PARAM_VALUE + "," + SWAGGER_INIT_CLASSNAMES_PARAM_VALUE
+ + "," + restClass;
+
+ jerseyServlet.setInitParameter(SWAGGER_CONTEXT_ID, swaggerId);
+ jerseyServlet.setInitParameter(SWAGGER_SCANNER_ID, swaggerId);
+ } else {
+ initClasses = JERSEY_JACKSON_INIT_CLASSNAMES_PARAM_VALUE + "," + restClass;
+ }
+ } else {
+ initClasses = initClasses + "," + restClass;
+ }
+
+ jerseyServlet.setInitParameter(JERSEY_INIT_CLASSNAMES_PARAM_NAME, initClasses);
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("{}: added REST class: {}", this, jerseyServlet.dump());
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("JettyJerseyServer [servlets=").append(servlets).append(", swaggerId=").append(swaggerId)
+ .append(", toString()=").append(super.toString()).append("]");
+ return builder.toString();
+ }
+}
diff --git a/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java
new file mode 100644
index 00000000..97166ec7
--- /dev/null
+++ b/policy-endpoints/src/main/java/org/onap/policy/common/endpoints/http/server/internal/JettyServletServer.java
@@ -0,0 +1,399 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.endpoints.http.server.internal;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+
+import org.eclipse.jetty.security.ConstraintMapping;
+import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.security.HashLoginService;
+import org.eclipse.jetty.security.authentication.BasicAuthenticator;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.Slf4jRequestLog;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.util.security.Credential;
+import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Http Server implementation using Embedded Jetty
+ */
+public abstract class JettyServletServer implements HttpServletServer, Runnable {
+
+ /**
+ * Logger
+ */
+ private static Logger logger = LoggerFactory.getLogger(JettyServletServer.class);
+
+ /**
+ * server name
+ */
+ protected final String name;
+
+ /**
+ * server host address
+ */
+ protected final String host;
+
+ /**
+ * server port to bind
+ */
+ protected final int port;
+
+ /**
+ * server auth user name
+ */
+ protected String user;
+
+ /**
+ * server auth password name
+ */
+ protected String password;
+
+ /**
+ * server base context path
+ */
+ protected final String contextPath;
+
+ /**
+ * embedded jetty server
+ */
+ protected final Server jettyServer;
+
+ /**
+ * servlet context
+ */
+ protected final ServletContextHandler context;
+
+ /**
+ * jetty connector
+ */
+ protected final ServerConnector connector;
+
+ /**
+ * jetty thread
+ */
+ protected volatile Thread jettyThread;
+
+ /**
+ * start condition
+ */
+ protected Object startCondition = new Object();
+
+ /**
+ * constructor
+ *
+ * @param name server name
+ * @param host server host
+ * @param port server port
+ * @param contextPath context path
+ *
+ * @throws IllegalArgumentException if invalid parameters are passed in
+ */
+ public JettyServletServer(String name, String host, int port, String contextPath) {
+ String srvName = name;
+ String srvHost = host;
+ String ctxtPath = contextPath;
+
+ if (srvName == null || srvName.isEmpty()) {
+ srvName = "http-" + port;
+ }
+
+ if (port <= 0 && port >= 65535) {
+ throw new IllegalArgumentException("Invalid Port provided: " + port);
+ }
+
+ if (srvHost == null || srvHost.isEmpty()) {
+ srvHost = "localhost";
+ }
+
+ if (ctxtPath == null || ctxtPath.isEmpty()) {
+ ctxtPath = "/";
+ }
+
+ this.name = srvName;
+
+ this.host = srvHost;
+ this.port = port;
+
+ this.contextPath = ctxtPath;
+
+ this.context = new ServletContextHandler(ServletContextHandler.SESSIONS);
+ this.context.setContextPath(ctxtPath);
+
+ this.jettyServer = new Server();
+ this.jettyServer.setRequestLog(new Slf4jRequestLog());
+
+ this.connector = new ServerConnector(this.jettyServer);
+ this.connector.setName(srvName);
+ this.connector.setReuseAddress(true);
+ this.connector.setPort(port);
+ this.connector.setHost(srvHost);
+
+ this.jettyServer.addConnector(this.connector);
+ this.jettyServer.setHandler(context);
+ }
+
+ @Override
+ public void setBasicAuthentication(String user, String password, String servletPath) {
+ String srvltPath = servletPath;
+
+ if (user == null || user.isEmpty() || password == null || password.isEmpty()) {
+ throw new IllegalArgumentException("Missing user and/or password");
+ }
+
+ if (srvltPath == null || srvltPath.isEmpty()) {
+ srvltPath = "/*";
+ }
+
+ HashLoginService hashLoginService = new HashLoginService();
+ hashLoginService.putUser(user, Credential.getCredential(password), new String[] {"user"});
+ hashLoginService.setName(this.connector.getName() + "-login-service");
+
+ Constraint constraint = new Constraint();
+ constraint.setName(Constraint.__BASIC_AUTH);
+ constraint.setRoles(new String[] {"user"});
+ constraint.setAuthenticate(true);
+
+ ConstraintMapping constraintMapping = new ConstraintMapping();
+ constraintMapping.setConstraint(constraint);
+ constraintMapping.setPathSpec(srvltPath);
+
+ ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
+ securityHandler.setAuthenticator(new BasicAuthenticator());
+ securityHandler.setRealmName(this.connector.getName() + "-realm");
+ securityHandler.addConstraintMapping(constraintMapping);
+ securityHandler.setLoginService(hashLoginService);
+
+ this.context.setSecurityHandler(securityHandler);
+
+ this.user = user;
+ this.password = password;
+ }
+
+ /**
+ * jetty server execution
+ */
+ @Override
+ public void run() {
+ try {
+ logger.info("{}: STARTING", this);
+
+ this.jettyServer.start();
+
+ if (logger.isInfoEnabled()) {
+ logger.info("{}: STARTED: {}", this, this.jettyServer.dump());
+ }
+
+ synchronized (this.startCondition) {
+ this.startCondition.notifyAll();
+ }
+
+ this.jettyServer.join();
+ } catch (Exception e) {
+ logger.error("{}: error found while bringing up server", this, e);
+ }
+ }
+
+ @Override
+ public boolean waitedStart(long maxWaitTime) throws InterruptedException {
+ logger.info("{}: WAITED-START", this);
+
+ if (maxWaitTime < 0) {
+ throw new IllegalArgumentException("max-wait-time cannot be negative");
+ }
+
+ long pendingWaitTime = maxWaitTime;
+
+ if (!this.start()) {
+ return false;
+ }
+
+ synchronized (this.startCondition) {
+
+ while (!this.jettyServer.isRunning()) {
+ try {
+ long startTs = System.currentTimeMillis();
+
+ this.startCondition.wait(pendingWaitTime);
+
+ if (maxWaitTime == 0) {
+ /* spurious notification */
+ continue;
+ }
+
+ long endTs = System.currentTimeMillis();
+ pendingWaitTime = pendingWaitTime - (endTs - startTs);
+
+ logger.info("{}: pending time is {} ms.", this, pendingWaitTime);
+
+ if (pendingWaitTime <= 0) {
+ return false;
+ }
+
+ } catch (InterruptedException e) {
+ logger.warn("{}: waited-start has been interrupted", this);
+ throw e;
+ }
+ }
+
+ return this.jettyServer.isRunning();
+ }
+ }
+
+ @Override
+ public boolean start() {
+ logger.info("{}: STARTING", this);
+
+ synchronized (this) {
+ if (jettyThread == null || !this.jettyThread.isAlive()) {
+
+ this.jettyThread = new Thread(this);
+ this.jettyThread.setName(this.name + "-" + this.port);
+ this.jettyThread.start();
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean stop() {
+ logger.info("{}: STOPPING", this);
+
+ synchronized (this) {
+ if (jettyThread == null) {
+ return true;
+ }
+
+ if (!jettyThread.isAlive()) {
+ this.jettyThread = null;
+ }
+
+ try {
+ this.connector.stop();
+ } catch (Exception e) {
+ logger.error("{}: error while stopping management server", this, e);
+ }
+
+ try {
+ this.jettyServer.stop();
+ } catch (Exception e) {
+ logger.error("{}: error while stopping management server", this, e);
+ return false;
+ }
+
+ Thread.yield();
+ }
+
+ return true;
+ }
+
+ @Override
+ public void shutdown() {
+ logger.info("{}: SHUTTING DOWN", this);
+
+ this.stop();
+
+ if (this.jettyThread == null) {
+ return;
+ }
+
+ Thread jettyThreadCopy = this.jettyThread;
+
+ if (jettyThreadCopy.isAlive()) {
+ try {
+ jettyThreadCopy.join(2000L);
+ } catch (InterruptedException e) {
+ logger.warn("{}: error while shutting down management server", this);
+ Thread.currentThread().interrupt();
+ }
+ if (!jettyThreadCopy.isInterrupted()) {
+ try {
+ jettyThreadCopy.interrupt();
+ } catch (Exception e) {
+ // do nothing
+ logger.warn("{}: exception while shutting down (OK)", this, e);
+ }
+ }
+ }
+
+ this.jettyServer.destroy();
+ }
+
+ @Override
+ public boolean isAlive() {
+ if (this.jettyThread != null) {
+ return this.jettyThread.isAlive();
+ }
+
+ return false;
+ }
+
+ @Override
+ public int getPort() {
+ return this.port;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the host
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * @return the user
+ */
+ public String getUser() {
+ return user;
+ }
+
+ /**
+ * @return the password
+ */
+ @JsonIgnore
+ public String getPassword() {
+ return password;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("JettyServer [name=").append(name).append(", host=").append(host).append(", port=").append(port)
+ .append(", user=").append(user).append(", password=").append(password != null).append(", contextPath=")
+ .append(contextPath).append(", jettyServer=").append(jettyServer).append(", context=")
+ .append(this.context).append(", connector=").append(connector).append(", jettyThread=")
+ .append(jettyThread).append("]");
+ return builder.toString();
+ }
+
+}