001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.microservice.jetty;
014
015import java.io.*;
016import java.util.*;
017import java.util.logging.*;
018
019import javax.servlet.*;
020
021import org.apache.juneau.*;
022import org.apache.juneau.config.*;
023import org.apache.juneau.config.store.*;
024import org.apache.juneau.internal.*;
025import org.apache.juneau.microservice.*;
026import org.apache.juneau.microservice.console.*;
027import org.apache.juneau.rest.*;
028import org.apache.juneau.rest.annotation.*;
029import org.apache.juneau.svl.*;
030import org.apache.juneau.utils.*;
031import org.eclipse.jetty.server.*;
032
033/**
034 * Builder for {@link JettyMicroservice} class.
035 *
036 * <p>
037 * Instances of this class are created using {@link JettyMicroservice#create()}.
038 */
039public class JettyMicroserviceBuilder extends MicroserviceBuilder {
040
041   String jettyXml;
042   int[] ports;
043   Boolean jettyXmlResolveVars;
044   Map<String,Servlet> servlets = new LinkedHashMap<>();
045   Map<String,Object> servletAttributes = new LinkedHashMap<>();
046   JettyMicroserviceListener listener;
047   JettyServerFactory factory;
048
049   /**
050    * Constructor.
051    */
052   protected JettyMicroserviceBuilder() {}
053
054   /**
055    * Copy constructor.
056    *
057    * @param copyFrom The builder to copy settings from.
058    */
059   protected JettyMicroserviceBuilder(JettyMicroserviceBuilder copyFrom) {
060      super(copyFrom);
061      this.jettyXml = copyFrom.jettyXml;
062      this.ports = copyFrom.ports;
063      this.jettyXmlResolveVars = copyFrom.jettyXmlResolveVars;
064      this.servlets = new LinkedHashMap<>(copyFrom.servlets);
065      this.servletAttributes = new LinkedHashMap<>(copyFrom.servletAttributes);
066      this.listener = copyFrom.listener;
067   }
068
069   @Override /* MicroserviceBuilder */
070   public JettyMicroserviceBuilder copy() {
071      return new JettyMicroserviceBuilder(this);
072   }
073
074   /**
075    * Specifies the contents or location of the <c>jetty.xml</c> file used by the Jetty server.
076    *
077    * <p>
078    * If you do not specify this value, it is pulled from the following in the specified order:
079    * <ul class='spaced-list'>
080    *    <li>
081    *       <c>Jetty/config</c> setting in the config file.
082    *       <c>Jetty-Config</c> setting in the manifest file.
083    * </ul>
084    *
085    * <p>
086    * By default, we look for the <c>jetty.xml</c> file in the following locations:
087    * <ul class='spaced-list'>
088    *    <li><c>jetty.xml</c> in home directory.
089    *    <li><c>files/jetty.xml</c> in home directory.
090    *    <li><c>/jetty.xml</c> in classpath.
091    *    <li><c>/files/jetty.xml</c> in classpath.
092    * </ul>
093    *
094    * @param jettyXml
095    *    The contents or location of the file.
096    *    <br>Can be any of the following:
097    *    <ul>
098    *       <li>{@link String} - Relative path to file on file system or classpath.
099    *       <li>{@link File} - File on file system.
100    *       <li>{@link InputStream} - Raw contents as <c>UTF-8</c> encoded stream.
101    *       <li>{@link Reader} - Raw contents.
102    *    </ul>
103    *
104    * @param resolveVars
105    *    If <jk>true</jk>, SVL variables in the file will automatically be resolved.
106    * @return This object (for method chaining).
107    * @throws IOException Thrown by underlying stream.
108    */
109   public JettyMicroserviceBuilder jettyXml(Object jettyXml, boolean resolveVars) throws IOException {
110      if (jettyXml instanceof String)
111         this.jettyXml = IOUtils.read(resolveFile(jettyXml.toString()));
112      else if (jettyXml instanceof File)
113         this.jettyXml = IOUtils.read((File)jettyXml);
114      else if (jettyXml instanceof InputStream)
115         this.jettyXml = IOUtils.read((InputStream)jettyXml);
116      else if (jettyXml instanceof Reader)
117         this.jettyXml = IOUtils.read((Reader)jettyXml);
118      else
119         throw new FormattedRuntimeException("Invalid object type passed to jettyXml(Object)", jettyXml == null ? null : jettyXml.getClass().getName());
120      this.jettyXmlResolveVars = resolveVars;
121      return this;
122   }
123
124   /**
125    * Specifies the ports to use for the web server.
126    *
127    * <p>
128    * You can specify multiple ports.  The first available will be used.  <js>'0'</js> indicates to try a random port.
129    * The resulting available port gets set as the system property <js>"availablePort"</js> which can be referenced in the
130    * <c>jetty.xml</c> file as <js>"$S{availablePort}"</js> (assuming resolveVars is enabled).
131    *
132    * <p>
133    * If you do not specify this value, it is pulled from the following in the specified order:
134    * <ul class='spaced-list'>
135    *    <li>
136    *       <c>Jetty/port</c> setting in the config file.
137    *    <li>
138    *       <c>Jetty-Port</c> setting in the manifest file.
139    *    <li>
140    *       <c>8000</c>
141    * </ul>
142    *
143    * Jetty/port", mf.getWithDefault("Jetty-Port", new int[]{8000}
144    * @param ports The ports to use for the web server.
145    * @return This object (for method chaining).
146    */
147   public JettyMicroserviceBuilder ports(int...ports) {
148      this.ports = ports;
149      return this;
150   }
151
152   /**
153    * Adds a servlet to the servlet container.
154    *
155    * <p>
156    * This method can only be used with servlets with no-arg constructors.
157    * <br>The path is pulled from the {@link Rest#path()} annotation.
158    *
159    * @param c The servlet to add to the servlet container.
160    * @return This object (for method chaining).
161    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
162    */
163   public JettyMicroserviceBuilder servlet(Class<? extends RestServlet> c) throws ExecutableException {
164      RestServlet rs;
165      try {
166         rs = c.newInstance();
167      } catch (InstantiationException | IllegalAccessException e) {
168         throw new ExecutableException(e);
169      }
170      return servlet(rs, '/' + rs.getPath());
171   }
172
173   /**
174    * Adds a servlet to the servlet container.
175    *
176    * <p>
177    * This method can only be used with servlets with no-arg constructors.
178    *
179    * @param c The servlet to add to the servlet container.
180    * @param path The servlet path spec.
181    * @return This object (for method chaining).
182    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
183    */
184   public JettyMicroserviceBuilder servlet(Class<? extends Servlet> c, String path) throws ExecutableException {
185      try {
186         return servlet(c.newInstance(), path);
187      } catch (InstantiationException | IllegalAccessException e) {
188         throw new ExecutableException(e);
189      }
190   }
191
192   /**
193    * Adds a servlet instance to the servlet container.
194    *
195    * @param servlet The servlet to add to the servlet container.
196    * @param path The servlet path spec.
197    * @return This object (for method chaining).
198    */
199   public JettyMicroserviceBuilder servlet(Servlet servlet, String path) {
200      servlets.put(path, servlet);
201      return this;
202   }
203
204   /**
205    * Adds a set of servlets to the servlet container.
206    *
207    * @param servlets
208    *    A map of servlets to add to the servlet container.
209    *    <br>Keys are path specs for the servlet.
210    * @return This object (for method chaining).
211    */
212   public JettyMicroserviceBuilder servlets(Map<String,Servlet> servlets) {
213      if (servlets != null)
214         this.servlets.putAll(servlets);
215      return this;
216   }
217
218   /**
219    * Adds a servlet attribute to the servlet container.
220    *
221    * @param name The attribute name.
222    * @param value The attribute value.
223    * @return This object (for method chaining).
224    */
225   public JettyMicroserviceBuilder servletAttribute(String name, Object value) {
226      this.servletAttributes.put(name, value);
227      return this;
228   }
229
230   /**
231    * Adds a set of servlet attributes to the servlet container.
232    *
233    * @param values The map of attributes.
234    * @return This object (for method chaining).
235    */
236   public JettyMicroserviceBuilder servletAttribute(Map<String,Object> values) {
237      if (values != null)
238         this.servletAttributes.putAll(values);
239      return this;
240   }
241
242   /**
243    * Specifies the factory to use for creating the Jetty {@link Server} instance.
244    *
245    * <p>
246    * If not specified, uses {@link BasicJettyServerFactory}.
247    *
248    * @param value The new value for this property.
249    * @return This object (for method chaining).
250    */
251   public JettyMicroserviceBuilder jettyServerFactory(JettyServerFactory value) {
252      this.factory = value;
253      return this;
254   }
255
256   //-----------------------------------------------------------------------------------------------------------------
257   // Inherited from MicroserviceBuilder
258   //-----------------------------------------------------------------------------------------------------------------
259
260   @Override /* MicroserviceBuilder */
261   public JettyMicroservice build() throws Exception {
262      return new JettyMicroservice(this);
263   }
264
265   @Override /* MicroserviceBuilder */
266   public JettyMicroserviceBuilder args(Args args) {
267      super.args(args);
268      return this;
269   }
270
271   @Override /* MicroserviceBuilder */
272   public JettyMicroserviceBuilder args(String...args) {
273      super.args(args);
274      return this;
275   }
276
277   @Override /* MicroserviceBuilder */
278   public JettyMicroserviceBuilder manifest(Object manifest) throws IOException {
279      super.manifest(manifest);
280      return this;
281   }
282
283   @Override /* MicroserviceBuilder */
284   public JettyMicroserviceBuilder logger(Logger logger) {
285      super.logger(logger);
286      return this;
287   }
288
289   @Override /* MicroserviceBuilder */
290   public JettyMicroserviceBuilder logConfig(LogConfig logConfig) {
291      return this;
292   }
293
294   @Override /* MicroserviceBuilder */
295   public JettyMicroserviceBuilder config(Config config) {
296      super.config(config);
297      return this;
298   }
299
300   @Override /* MicroserviceBuilder */
301   public JettyMicroserviceBuilder configName(String configName) {
302      super.configName(configName);
303      return this;
304   }
305
306   @Override /* MicroserviceBuilder */
307   public JettyMicroserviceBuilder configStore(ConfigStore configStore) {
308      super.configStore(configStore);
309      return this;
310   }
311
312   @Override /* MicroserviceBuilder */
313   public JettyMicroserviceBuilder consoleEnabled(boolean consoleEnabled) {
314      super.consoleEnabled(consoleEnabled);
315      return this;
316   }
317
318   @Override /* MicroserviceBuilder */
319   public JettyMicroserviceBuilder consoleCommands(ConsoleCommand...consoleCommands) {
320      super.consoleCommands(consoleCommands);
321      return this;
322   }
323
324   @Override /* MicroserviceBuilder */
325   public JettyMicroserviceBuilder console(Scanner consoleReader, PrintWriter consoleWriter) {
326      super.console(consoleReader, consoleWriter);
327      return this;
328   }
329
330   @Override /* MicroserviceBuilder */
331   @SuppressWarnings("unchecked")
332   public JettyMicroserviceBuilder vars(Class<? extends Var>...vars) {
333      super.vars(vars);
334      return this;
335   }
336
337   @Override /* MicroserviceBuilder */
338   public JettyMicroserviceBuilder varContext(String name, Object object) {
339      super.varContext(name, object);
340      return this;
341   }
342
343   @Override /* MicroserviceBuilder */
344   public JettyMicroserviceBuilder workingDir(File path) {
345      super.workingDir(path);
346      return this;
347   }
348
349   @Override /* MicroserviceBuilder */
350   public JettyMicroserviceBuilder workingDir(String path) {
351      super.workingDir(path);
352      return this;
353   }
354
355   /**
356    * Registers an event listener for this microservice.
357    *
358    * @param listener An event listener for this microservice.
359    * @return This object (for method chaining).
360    */
361   public JettyMicroserviceBuilder listener(JettyMicroserviceListener listener) {
362      super.listener(listener);
363      this.listener = listener;
364      return this;
365   }
366}