001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2017–2020 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.  See the License for the specific language governing
015 * permissions and limitations under the License.
016 */
017package org.microbean.main;
018
019import java.util.function.Consumer;
020
021import javax.enterprise.context.Dependent;
022
023import javax.enterprise.inject.Produces;
024
025import javax.enterprise.inject.se.SeContainer;
026import javax.enterprise.inject.se.SeContainerInitializer;
027
028import javax.inject.Named;
029import javax.inject.Singleton;
030
031/**
032 * A class whose {@linkplain #main(String[]) <code>main</code> method}
033 * {@linkplain SeContainerInitializer#initialize() initializes} a new
034 * {@link SeContainer}.
035 *
036 * @author <a href="https://about.me/lairdnelson"
037 * target="_parent">Laird Nelson</a>
038 *
039 * @see #main(String[])
040 *
041 * @see #main(SeContainerInitializer, Consumer, String[])
042 *
043 * @see SeContainerInitializer#initialize()
044 *
045 * @threadsafety Instances of this class are not safe for concurrent
046 * use by multiple threads.
047 */
048@Singleton
049public class Main {
050
051
052  /*
053   * Static fields.
054   */
055
056  
057  /**
058   * Any command line arguments installed by the last invocation of
059   * the {@link #main(SeContainerInitializer, Consumer, String[])}
060   * method.
061   *
062   * @see #getCommandLineArguments()
063   *
064   * @see #main(SeContainerInitializer, Consumer, String[])
065   *
066   * @nullability This field may be {@code null}.
067   */
068  private static String[] commandLineArguments;
069
070
071  /*
072   * Constructors.
073   */
074
075  
076  /**
077   * Creates a new {@link Main}.
078   */
079  protected Main() {
080    super();
081  }
082
083
084  /*
085   * Static methods.
086   */
087
088  
089  /**
090   * A <a
091   * href="https://jakarta.ee/specifications/cdi/2.0/cdi-spec-2.0.html#producer_method">producer
092   * method</a> that returns the command line arguments stored in the
093   * {@link #commandLineArguments} field by the {@link
094   * #main(String[])} method.
095   *
096   * @return a {@link String} array of command line arguments; never
097   * {@code null}
098   *
099   * @see #commandLineArguments
100   *
101   * @see #main(SeContainerInitializer, Consumer, String[])
102   *
103   * @nullability This method never returns {@code null}.
104   *
105   * @threadsafety This method is safe for concurrent use by multiple
106   * threads.
107   */
108  @Produces
109  @Named("commandLineArguments")
110  @Singleton
111  private static final String[] getCommandLineArguments() {
112    return commandLineArguments;
113  }
114
115  /**
116   * {@linkplain SeContainerInitializer#initialize() Initializes} a
117   * new {@link SeContainer} and then {@linkplain SeContainer#close()
118   * closes} it.
119   *
120   * <p>This method calls the {@link #main(SeContainerInitializer,
121   * Consumer, String[])} method with the return value of the {@link
122   * SeContainerInitializer#newInstance()} method, {@code null}, and
123   * the supplied {@code args} parameter value.</p>
124   *
125   * @param args command-line arguments; may be {@code null}
126   *
127   * @see #main(SeContainerInitializer, Consumer, String[])
128   *
129   * @threadsafety This method is not safe for concurrent use by
130   * multiple threads.
131   */
132  public static final void main(final String[] args) {
133    main(SeContainerInitializer.newInstance(), null, args);
134  }
135  
136  /**
137   * {@linkplain SeContainerInitializer#initialize() Initializes} a
138   * new {@link SeContainer} and then {@linkplain SeContainer#close()
139   * closes} it.
140   *
141   * <p>This method calls the {@link #main(SeContainerInitializer,
142   * Consumer, String[])} method with the supplied {@code
143   * containerInitializer} parameter value, {@code null}, and the
144   * supplied {@code args} parameter value.</p>
145   *
146   * @param containerInitializer the {@link SeContainerInitializer} to
147   * use to initialize the {@link SeContainer}; may be {@code null} in
148   * which case the return value of {@link
149   * SeContainerInitializer#newInstance()} will be used instead
150   *
151   * @param args command-line arguments; may be {@code null}
152   *
153   * @see #main(SeContainerInitializer, Consumer, String[])
154   *
155   * @threadsafety This method is not safe for concurrent use by
156   * multiple threads.
157   */
158  public static final void main(SeContainerInitializer containerInitializer, final String[] args) {
159    main(containerInitializer, null, args);
160  }
161
162  /**
163   * {@linkplain SeContainerInitializer#initialize() Initializes} a
164   * new {@link SeContainer}, {@linkplain Consumer#accept(Object)
165   * calls the supplied <code>consumer</code> parameter value with it}
166   * (if the supplied {@code consumer} parameter value is non-{@code
167   * null}), and then {@linkplain SeContainer#close() closes} it.
168   *
169   * <p>This method has a deliberate side effect of making the {@code
170   * args} parameter value available in the CDI container in {@link
171   * Singleton} scope with a qualifier of {@link
172   * Named @Named("commandLineArguments")}.  It also causes an
173   * instance of this class to be created by the CDI container in
174   * {@link Singleton} scope.</p>
175   *
176   * @param containerInitializer the {@link SeContainerInitializer} to
177   * use to initialize the {@link SeContainer}; may be {@code null} in
178   * which case the return value of {@link
179   * SeContainerInitializer#newInstance()} will be used instead.  This
180   * {@link SeContainerInitializer} instance will have its {@link
181   * SeContainerInitializer#addBeanClasses(Class...)} method called
182   * with {@link Main Main.class} as its only argument.
183   *
184   * @param consumer a {@link Consumer} whose {@link
185   * Consumer#accept(Object)} method will be called with an {@link
186   * SeContainer}; may be {@code null}; rarely needed
187   *
188   * @param args command-line arguments; may be {@code null}
189   *
190   * @see SeContainerInitializer#newInstance()
191   *
192   * @see SeContainerInitializer#initialize()
193   *
194   * @see SeContainer#close()
195   *
196   * @threadsafety This method is not safe for concurrent use by
197   * multiple threads.
198   */
199  public static final void main(SeContainerInitializer containerInitializer, final Consumer<? super SeContainer> consumer, final String[] args) {
200    commandLineArguments = args == null ? new String[0] : args;
201    if (containerInitializer == null) {
202      containerInitializer = SeContainerInitializer.newInstance();
203    }
204    assert containerInitializer != null;
205    containerInitializer.addBeanClasses(Main.class);
206    final SeContainer container = containerInitializer.initialize();
207    assert container != null;
208    try {
209      assert container.select(Main.class).get() != null;
210      if (consumer != null) {
211        consumer.accept(container);
212      }
213    } finally {
214      if (container.isRunning()) {
215        container.close();
216      }
217    }
218  }
219  
220}