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 <code>jetty.xml</code> 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    *       <code>Jetty/config</code> setting in the config file.
082    *       <code>Jetty-Config</code> setting in the manifest file.
083    * </ul>
084    *
085    * <p>
086    * By default, we look for the <code>jetty.xml</code> file in the following locations:
087    * <ul class='spaced-list'>
088    *    <li><code>jetty.xml</code> in home directory.
089    *    <li><code>files/jetty.xml</code> in home directory.
090    *    <li><code>/jetty.xml</code> in classpath.
091    *    <li><code>/files/jetty.xml</code> 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 <code>UTF-8</code> 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
108    */
109   public JettyMicroserviceBuilder jettyXml(Object jettyXml, boolean resolveVars) throws IOException {
110      if (jettyXml instanceof String)
111         this.jettyXml = IOUtils.read(new File(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    * <code>jetty.xml</code> 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    *       <code>Jetty/port</code> setting in the config file.
137    *    <li>
138    *       <code>Jetty-Port</code> setting in the manifest file.
139    *    <li>
140    *       <code>8000</code>
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 RestResource#path()} annotation.
158    *
159    * @param c The servlet to add to the servlet container.
160    * @return This object (for method chaining).
161    * @throws InstantiationException
162    * @throws IllegalAccessException
163    */
164   public JettyMicroserviceBuilder servlet(Class<? extends RestServlet> c) throws InstantiationException, IllegalAccessException {
165      RestServlet rs = c.newInstance();
166      return servlet(rs, '/' + rs.getPath());
167   }
168
169   /**
170    * Adds a servlet to the servlet container.
171    *
172    * <p>
173    * This method can only be used with servlets with no-arg constructors.
174    *
175    * @param c The servlet to add to the servlet container.
176    * @param path The servlet path spec.
177    * @return This object (for method chaining).
178    * @throws InstantiationException
179    * @throws IllegalAccessException
180    */
181   public JettyMicroserviceBuilder servlet(Class<? extends Servlet> c, String path) throws InstantiationException, IllegalAccessException {
182      return servlet(c.newInstance(), path);
183   }
184
185   /**
186    * Adds a servlet instance to the servlet container.
187    *
188    * @param servlet The servlet to add to the servlet container.
189    * @param path The servlet path spec.
190    * @return This object (for method chaining).
191    */
192   public JettyMicroserviceBuilder servlet(Servlet servlet, String path) {
193      servlets.put(path, servlet);
194      return this;
195   }
196
197   /**
198    * Adds a set of servlets to the servlet container.
199    *
200    * @param servlets
201    *    A map of servlets to add to the servlet container.
202    *    <br>Keys are path specs for the servlet.
203    * @return This object (for method chaining).
204    */
205   public JettyMicroserviceBuilder servlets(Map<String,Servlet> servlets) {
206      if (servlets != null)
207         this.servlets.putAll(servlets);
208      return this;
209   }
210
211   /**
212    * Adds a servlet attribute to the servlet container.
213    *
214    * @param name The attribute name.
215    * @param value The attribute value.
216    * @return This object (for method chaining).
217    */
218   public JettyMicroserviceBuilder servletAttribute(String name, Object value) {
219      this.servletAttributes.put(name, value);
220      return this;
221   }
222
223   /**
224    * Adds a set of servlet attributes to the servlet container.
225    *
226    * @param values The map of attributes.
227    * @return This object (for method chaining).
228    */
229   public JettyMicroserviceBuilder servletAttribute(Map<String,Object> values) {
230      if (values != null)
231         this.servletAttributes.putAll(values);
232      return this;
233   }
234
235   /**
236    * Specifies the factory to use for creating the Jetty {@link Server} instance.
237    *
238    * <p>
239    * If not specified, uses {@link BasicJettyServerFactory}.
240    *
241    * @param value
242    * @return This object (for method chaining).
243    */
244   public JettyMicroserviceBuilder jettyServerFactory(JettyServerFactory value) {
245      this.factory = value;
246      return this;
247   }
248
249   //-----------------------------------------------------------------------------------------------------------------
250   // Inherited from MicroserviceBuilder
251   //-----------------------------------------------------------------------------------------------------------------
252
253   @Override /* MicroserviceBuilder */
254   public JettyMicroservice build() throws Exception {
255      return new JettyMicroservice(this);
256   }
257
258   @Override /* MicroserviceBuilder */
259   public JettyMicroserviceBuilder args(Args args) {
260      super.args(args);
261      return this;
262   }
263
264   @Override /* MicroserviceBuilder */
265   public JettyMicroserviceBuilder args(String...args) {
266      super.args(args);
267      return this;
268   }
269
270   @Override /* MicroserviceBuilder */
271   public JettyMicroserviceBuilder manifest(Object manifest) throws IOException {
272      super.manifest(manifest);
273      return this;
274   }
275
276   @Override /* MicroserviceBuilder */
277   public JettyMicroserviceBuilder logger(Logger logger) {
278      super.logger(logger);
279      return this;
280   }
281
282
283   @Override /* MicroserviceBuilder */
284   public JettyMicroserviceBuilder logConfig(LogConfig logConfig) {
285      return this;
286   }
287
288   @Override /* MicroserviceBuilder */
289   public JettyMicroserviceBuilder config(Config config) {
290      super.config(config);
291      return this;
292   }
293
294   @Override /* MicroserviceBuilder */
295   public JettyMicroserviceBuilder configName(String configName) {
296      super.configName(configName);
297      return this;
298   }
299
300   @Override /* MicroserviceBuilder */
301   public JettyMicroserviceBuilder configStore(ConfigStore configStore) {
302      super.configStore(configStore);
303      return this;
304   }
305
306   @Override /* MicroserviceBuilder */
307   public JettyMicroserviceBuilder consoleEnabled(boolean consoleEnabled) {
308      super.consoleEnabled(consoleEnabled);
309      return this;
310   }
311
312   @Override /* MicroserviceBuilder */
313   public JettyMicroserviceBuilder consoleCommands(ConsoleCommand...consoleCommands) {
314      super.consoleCommands(consoleCommands);
315      return this;
316   }
317
318   @Override /* MicroserviceBuilder */
319   public JettyMicroserviceBuilder console(Scanner consoleReader, PrintWriter consoleWriter) {
320      super.console(consoleReader, consoleWriter);
321      return this;
322   }
323
324   @Override /* MicroserviceBuilder */
325   @SuppressWarnings("unchecked")
326   public JettyMicroserviceBuilder vars(Class<? extends Var>...vars) {
327      super.vars(vars);
328      return this;
329   }
330
331   @Override /* MicroserviceBuilder */
332   public JettyMicroserviceBuilder varContext(String name, Object object) {
333      super.varContext(name, object);
334      return this;
335   }
336
337   /**
338    * Registers an event listener for this microservice.
339    *
340    * @param listener An event listener for this microservice.
341    * @return This object (for method chaining).
342    */
343   public JettyMicroserviceBuilder listener(JettyMicroserviceListener listener) {
344      super.listener(listener);
345      this.listener = listener;
346      return this;
347   }
348}