2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.common.endpoints.http.server.internal;
23 import com.fasterxml.jackson.annotation.JsonIgnore;
25 import org.eclipse.jetty.security.ConstraintMapping;
26 import org.eclipse.jetty.security.ConstraintSecurityHandler;
27 import org.eclipse.jetty.security.HashLoginService;
28 import org.eclipse.jetty.security.authentication.BasicAuthenticator;
29 import org.eclipse.jetty.server.Server;
30 import org.eclipse.jetty.server.ServerConnector;
31 import org.eclipse.jetty.server.Slf4jRequestLog;
32 import org.eclipse.jetty.servlet.ServletContextHandler;
33 import org.eclipse.jetty.util.security.Constraint;
34 import org.eclipse.jetty.util.security.Credential;
35 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * Http Server implementation using Embedded Jetty
42 public abstract class JettyServletServer implements HttpServletServer, Runnable {
47 private static Logger logger = LoggerFactory.getLogger(JettyServletServer.class);
52 protected final String name;
57 protected final String host;
62 protected final int port;
65 * server auth user name
67 protected String user;
70 * server auth password name
72 protected String password;
75 * server base context path
77 protected final String contextPath;
80 * embedded jetty server
82 protected final Server jettyServer;
87 protected final ServletContextHandler context;
92 protected final ServerConnector connector;
97 protected volatile Thread jettyThread;
102 protected Object startCondition = new Object();
107 * @param name server name
108 * @param host server host
109 * @param port server port
110 * @param contextPath context path
112 * @throws IllegalArgumentException if invalid parameters are passed in
114 public JettyServletServer(String name, String host, int port, String contextPath) {
115 String srvName = name;
116 String srvHost = host;
117 String ctxtPath = contextPath;
119 if (srvName == null || srvName.isEmpty()) {
120 srvName = "http-" + port;
123 if (port <= 0 && port >= 65535) {
124 throw new IllegalArgumentException("Invalid Port provided: " + port);
127 if (srvHost == null || srvHost.isEmpty()) {
128 srvHost = "localhost";
131 if (ctxtPath == null || ctxtPath.isEmpty()) {
140 this.contextPath = ctxtPath;
142 this.context = new ServletContextHandler(ServletContextHandler.SESSIONS);
143 this.context.setContextPath(ctxtPath);
145 this.jettyServer = new Server();
146 this.jettyServer.setRequestLog(new Slf4jRequestLog());
148 this.connector = new ServerConnector(this.jettyServer);
149 this.connector.setName(srvName);
150 this.connector.setReuseAddress(true);
151 this.connector.setPort(port);
152 this.connector.setHost(srvHost);
154 this.jettyServer.addConnector(this.connector);
155 this.jettyServer.setHandler(context);
159 public void setBasicAuthentication(String user, String password, String servletPath) {
160 String srvltPath = servletPath;
162 if (user == null || user.isEmpty() || password == null || password.isEmpty()) {
163 throw new IllegalArgumentException("Missing user and/or password");
166 if (srvltPath == null || srvltPath.isEmpty()) {
170 HashLoginService hashLoginService = new HashLoginService();
171 hashLoginService.putUser(user, Credential.getCredential(password), new String[] {"user"});
172 hashLoginService.setName(this.connector.getName() + "-login-service");
174 Constraint constraint = new Constraint();
175 constraint.setName(Constraint.__BASIC_AUTH);
176 constraint.setRoles(new String[] {"user"});
177 constraint.setAuthenticate(true);
179 ConstraintMapping constraintMapping = new ConstraintMapping();
180 constraintMapping.setConstraint(constraint);
181 constraintMapping.setPathSpec(srvltPath);
183 ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
184 securityHandler.setAuthenticator(new BasicAuthenticator());
185 securityHandler.setRealmName(this.connector.getName() + "-realm");
186 securityHandler.addConstraintMapping(constraintMapping);
187 securityHandler.setLoginService(hashLoginService);
189 this.context.setSecurityHandler(securityHandler);
192 this.password = password;
196 * jetty server execution
201 logger.info("{}: STARTING", this);
203 this.jettyServer.start();
205 if (logger.isInfoEnabled()) {
206 logger.info("{}: STARTED: {}", this, this.jettyServer.dump());
209 synchronized (this.startCondition) {
210 this.startCondition.notifyAll();
213 this.jettyServer.join();
214 } catch (Exception e) {
215 logger.error("{}: error found while bringing up server", this, e);
220 public boolean waitedStart(long maxWaitTime) throws InterruptedException {
221 logger.info("{}: WAITED-START", this);
223 if (maxWaitTime < 0) {
224 throw new IllegalArgumentException("max-wait-time cannot be negative");
227 long pendingWaitTime = maxWaitTime;
233 synchronized (this.startCondition) {
235 while (!this.jettyServer.isRunning()) {
237 long startTs = System.currentTimeMillis();
239 this.startCondition.wait(pendingWaitTime);
241 if (maxWaitTime == 0) {
242 /* spurious notification */
246 long endTs = System.currentTimeMillis();
247 pendingWaitTime = pendingWaitTime - (endTs - startTs);
249 logger.info("{}: pending time is {} ms.", this, pendingWaitTime);
251 if (pendingWaitTime <= 0) {
255 } catch (InterruptedException e) {
256 logger.warn("{}: waited-start has been interrupted", this);
261 return this.jettyServer.isRunning();
266 public boolean start() {
267 logger.info("{}: STARTING", this);
269 synchronized (this) {
270 if (jettyThread == null || !this.jettyThread.isAlive()) {
272 this.jettyThread = new Thread(this);
273 this.jettyThread.setName(this.name + "-" + this.port);
274 this.jettyThread.start();
282 public boolean stop() {
283 logger.info("{}: STOPPING", this);
285 synchronized (this) {
286 if (jettyThread == null) {
290 if (!jettyThread.isAlive()) {
291 this.jettyThread = null;
295 this.connector.stop();
296 } catch (Exception e) {
297 logger.error("{}: error while stopping management server", this, e);
301 this.jettyServer.stop();
302 } catch (Exception e) {
303 logger.error("{}: error while stopping management server", this, e);
314 public void shutdown() {
315 logger.info("{}: SHUTTING DOWN", this);
319 if (this.jettyThread == null) {
323 Thread jettyThreadCopy = this.jettyThread;
325 if (jettyThreadCopy.isAlive()) {
327 jettyThreadCopy.join(2000L);
328 } catch (InterruptedException e) {
329 logger.warn("{}: error while shutting down management server", this);
330 Thread.currentThread().interrupt();
332 if (!jettyThreadCopy.isInterrupted()) {
334 jettyThreadCopy.interrupt();
335 } catch (Exception e) {
337 logger.warn("{}: exception while shutting down (OK)", this, e);
342 this.jettyServer.destroy();
346 public boolean isAlive() {
347 if (this.jettyThread != null) {
348 return this.jettyThread.isAlive();
355 public int getPort() {
362 public String getName() {
369 public String getHost() {
376 public String getUser() {
381 * @return the password
384 public String getPassword() {
389 public String toString() {
390 StringBuilder builder = new StringBuilder();
391 builder.append("JettyServer [name=").append(name).append(", host=").append(host).append(", port=").append(port)
392 .append(", user=").append(user).append(", password=").append(password != null).append(", contextPath=")
393 .append(contextPath).append(", jettyServer=").append(jettyServer).append(", context=")
394 .append(this.context).append(", connector=").append(connector).append(", jettyThread=")
395 .append(jettyThread).append("]");
396 return builder.toString();