009ad3039e7f638b99477bdfc82be67be7b13bfc
[policy/drools-pdp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * policy-endpoints
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20 package org.openecomp.policy.drools.http.server.internal;
21
22 import org.eclipse.jetty.security.ConstraintMapping;
23 import org.eclipse.jetty.security.ConstraintSecurityHandler;
24 import org.eclipse.jetty.security.HashLoginService;
25 import org.eclipse.jetty.security.authentication.BasicAuthenticator;
26 import org.eclipse.jetty.server.Server;
27 import org.eclipse.jetty.server.ServerConnector;
28 import org.eclipse.jetty.servlet.ServletContextHandler;
29 import org.eclipse.jetty.util.security.Constraint;
30 import org.eclipse.jetty.util.security.Credential;
31 import org.openecomp.policy.drools.http.server.HttpServletServer;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 import com.fasterxml.jackson.annotation.JsonIgnore;
36
37 /**
38  * Http Server implementation using Embedded Jetty
39  */
40 public abstract class JettyServletServer implements HttpServletServer, Runnable {
41         
42         private static Logger logger = LoggerFactory.getLogger(JettyServletServer.class);
43
44         protected final String name;
45
46         protected final String host;
47         protected final int port;
48         
49         protected String user;
50         protected String password;
51         
52         protected final String contextPath;
53         
54         protected final Server jettyServer;
55         protected final ServletContextHandler context;
56         protected final ServerConnector connector;
57         
58         protected volatile Thread jettyThread;
59         
60         protected Object startCondition = new Object();
61         
62         public JettyServletServer(String name, String host, int port, String contextPath) 
63                    throws IllegalArgumentException {
64                         
65                 if (name == null || name.isEmpty())
66                         name = "http-" + port;
67                 
68                 if (port <= 0 && port >= 65535)
69                         throw new IllegalArgumentException("Invalid Port provided: " + port);
70                 
71                 if (host == null || host.isEmpty())
72                         host = "localhost";
73                 
74                 if (contextPath == null || contextPath.isEmpty())
75                         contextPath = "/";
76                 
77                 this.name = name;
78                 
79                 this.host = host;
80                 this.port = port;
81
82                 this.contextPath = contextPath;
83                 
84         this.context = new ServletContextHandler(ServletContextHandler.SESSIONS);
85         this.context.setContextPath(contextPath);
86         
87         this.jettyServer = new Server();
88         
89         this.connector = new ServerConnector(this.jettyServer);
90         this.connector.setName(name);
91         this.connector.setReuseAddress(true);
92         this.connector.setPort(port);
93         this.connector.setHost(host);    
94         
95         this.jettyServer.addConnector(this.connector);       
96         this.jettyServer.setHandler(context);
97         }
98         
99         /**
100          * {@inheritDoc}
101          */
102         @Override
103         public void setBasicAuthentication(String user, String password, String servletPath) {
104         if (user == null || user.isEmpty() || password == null || password.isEmpty()) 
105                 throw new IllegalArgumentException("Missing user and/or password");
106         
107         if (servletPath == null || servletPath.isEmpty())
108                 servletPath = "/*";
109                         
110         HashLoginService hashLoginService = new HashLoginService();
111         hashLoginService.putUser(user, 
112                                         Credential.getCredential(password), 
113                                         new String[] {"user"});
114         hashLoginService.setName(this.connector.getName() + "-login-service");
115         
116         Constraint constraint = new Constraint();
117         constraint.setName(Constraint.__BASIC_AUTH);
118         constraint.setRoles(new String[]{"user"});
119         constraint.setAuthenticate(true);
120          
121         ConstraintMapping constraintMapping = new ConstraintMapping();
122         constraintMapping.setConstraint(constraint);
123         constraintMapping.setPathSpec(servletPath);
124         
125         ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
126         securityHandler.setAuthenticator(new BasicAuthenticator());
127         securityHandler.setRealmName(this.connector.getName() + "-realm");
128         securityHandler.addConstraintMapping(constraintMapping);
129         securityHandler.setLoginService(hashLoginService);              
130         
131         this.context.setSecurityHandler(securityHandler);
132         
133                 this.user = user;
134                 this.password = password;
135         }
136         
137         /**
138          * Jetty Server Execution
139          */
140         @Override
141         public void run() {
142         try {
143                 logger.info("{}: STARTING", this);
144                 
145             this.jettyServer.start();
146             
147             if (logger.isInfoEnabled()) 
148                 logger.info("{}: STARTED: {}", this, this.jettyServer.dump());
149             
150                 synchronized(this.startCondition) {
151                         this.startCondition.notifyAll();
152                 }
153                 
154             this.jettyServer.join();
155         } catch (Exception e) {
156                         logger.error("{}: error found while bringing up server", this, e);
157                 } 
158         }
159         
160         @Override
161         public boolean waitedStart(long maxWaitTime) throws IllegalArgumentException {
162                 logger.info("{}: WAITED-START", this);
163                 
164                 if (maxWaitTime < 0)
165                         throw new IllegalArgumentException("max-wait-time cannot be negative");
166                 
167                 long pendingWaitTime = maxWaitTime;
168                 
169                 if (!this.start())
170                         return false;
171                 
172                 synchronized (this.startCondition) {
173                         
174                         while (!this.jettyServer.isRunning()) {
175                                 try {
176                                         long startTs = System.currentTimeMillis();
177                                         
178                                         this.startCondition.wait(pendingWaitTime);
179                                         
180                                         if (maxWaitTime == 0)
181                                                 /* spurious notification */
182                                                 continue;
183                                         
184                                         long endTs = System.currentTimeMillis();
185                                         pendingWaitTime = pendingWaitTime - (endTs - startTs);
186                                         
187                                         logger.info("{}: pending time is {} ms.", this, pendingWaitTime);
188                                         
189                                         if (pendingWaitTime <= 0)
190                                                 return false;
191                                         
192                                 } catch (InterruptedException e) {
193                                         logger.warn("{}: waited-start has been interrupted", this);
194                                         return false;                   
195                                 }
196                         }
197                         
198                         return (this.jettyServer.isRunning());
199                 }
200         }
201
202         /**
203          * {@inheritDoc}
204          */
205         @Override
206         public boolean start() throws IllegalStateException {
207                 logger.info("{}: STARTING", this);
208                 
209                 synchronized(this) {                    
210                         if (jettyThread == null || 
211                                 !this.jettyThread.isAlive()) {
212                                 
213                                 this.jettyThread = new Thread(this);
214                                 this.jettyThread.setName(this.name + "-" + this.port);
215                                 this.jettyThread.start();
216                         }
217                 }
218                 
219                 return true;
220         }
221
222         /**
223          * {@inheritDoc}
224          */
225         @Override
226         public boolean stop() throws IllegalStateException {
227                 logger.info("{}: STOPPING", this);
228                 
229                 synchronized(this) {
230                         if (jettyThread == null) {
231                                 return true;
232                         } 
233                         
234                         if (!jettyThread.isAlive()) {
235                                 this.jettyThread = null;
236                         } 
237                         
238                         try {
239                                 this.connector.stop();
240                         } catch (Exception e) {
241                                 logger.error("{}: error while stopping management server", this, e);
242                         }
243                         
244                         try {
245                                 this.jettyServer.stop();
246                         } catch (Exception e) {
247                                 logger.error("{}: error while stopping management server", this, e);
248                                 return false;
249                         }
250                         
251                         Thread.yield();
252                 }
253
254                 return true;
255         }
256
257         /**
258          * {@inheritDoc}
259          */
260         @Override
261         public void shutdown() throws IllegalStateException {
262                 logger.info("{}: SHUTTING DOWN", this);
263                 
264                 this.stop();
265                 
266                 if (this.jettyThread == null)
267                         return;
268                 
269                 Thread jettyThreadCopy = this.jettyThread;
270
271                 if (jettyThreadCopy.isAlive()) {
272                         try {
273                                 jettyThreadCopy.join(1000L);
274                         } catch (InterruptedException e) {
275                                 logger.warn("{}: error while shutting down management server", this);
276                         }
277                         if (!jettyThreadCopy.isInterrupted()) {
278                                 try {
279                                         jettyThreadCopy.interrupt();
280                                 } catch(Exception e) {
281                                         // do nothing
282                                         logger.warn("{}: exception while shutting down (OK)", this);
283                                 }
284                         }
285                 }
286                 
287                 this.jettyServer.destroy();
288         }
289
290         /**
291          * {@inheritDoc}
292          */
293         @Override
294         public boolean isAlive() {
295                 if (this.jettyThread != null)
296                         return this.jettyThread.isAlive();
297                 
298                 return false;
299         }
300
301         @Override
302         public int getPort() {
303                 return this.port;
304         }
305
306         /**
307          * @return the name
308          */
309         public String getName() {
310                 return name;
311         }
312
313         /**
314          * @return the host
315          */
316         public String getHost() {
317                 return host;
318         }
319
320         /**
321          * @return the user
322          */
323         public String getUser() {
324                 return user;
325         }
326
327         /**
328          * @return the password
329          */
330         @JsonIgnore
331         public String getPassword() {
332                 return password;
333         }
334
335         @Override
336         public String toString() {
337                 StringBuilder builder = new StringBuilder();
338                 builder.append("JettyServer [name=").append(name).append(", host=").append(host).append(", port=").append(port)
339                                 .append(", user=").append(user).append(", password=").append((password != null)).append(", contextPath=")
340                                 .append(contextPath).append(", jettyServer=").append(jettyServer).append(", context=").append(this.context)
341                                 .append(", connector=").append(connector).append(", jettyThread=").append(jettyThread)
342                                 .append("]");
343                 return builder.toString();
344         }
345
346 }