001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2017 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 * <h2>Thread Safety</h2>
037 *
038 * <p>This class is not safe for concurrent use by multiple
039 * threads.</p>
040 *
041 * @author <a href="https://about.me/lairdnelson"
042 * target="_parent">Laird Nelson</a>
043 *
044 * @see #main(String[])
045 *
046 * @see SeContainerInitializer#initialize()
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   * <p>This field may be {@code null}.</p>
063   *
064   * @see #getCommandLineArguments()
065   *
066   * @see #main(SeContainerInitializer, Consumer, String[])
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="http://docs.jboss.org/cdi/spec/2.0-PFD2/cdi-spec.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   * <p>This method never returns {@code null}.</p>
097   *
098   * @return a {@link String} array of command line arguments; never
099   * {@code null}
100   *
101   * @see #commandLineArguments
102   *
103   * @see #main(SeContainerInitializer, Consumer, String[])
104   */
105  @Produces
106  @Named("commandLineArguments")
107  @Singleton
108  private static final String[] getCommandLineArguments() {
109    return commandLineArguments;
110  }
111
112  /**
113   * {@linkplain SeContainerInitializer#initialize() Initializes} a
114   * new {@link SeContainer} and then {@linkplain SeContainer#close()
115   * closes} it.
116   *
117   * <p>This method calls the {@link #main(SeContainerInitializer,
118   * Consumer, String[])} method with the return value of the {@link
119   * SeContainerInitializer#newInstance()} method, {@code null}, and
120   * the supplied {@code args} parameter value.</p>
121   *
122   * @param args command-line arguments; may be {@code null}
123   *
124   * @see #main(SeContainerInitializer, Consumer, String[])
125   */
126  public static final void main(final String[] args) {
127    main(SeContainerInitializer.newInstance(), null, args);
128  }
129  
130  /**
131   * {@linkplain SeContainerInitializer#initialize() Initializes} a
132   * new {@link SeContainer} and then {@linkplain SeContainer#close()
133   * closes} it.
134   *
135   * <p>This method calls the {@link #main(SeContainerInitializer,
136   * Consumer, String[])} method with the supplied {@code
137   * containerInitializer} parameter value, {@code null}, and the
138   * supplied {@code args} parameter value.</p>
139   *
140   * @param containerInitializer the {@link SeContainerInitializer} to
141   * use to initialize the {@link SeContainer}; may be {@code null} in
142   * which case the return value of {@link
143   * SeContainerInitializer#newInstance()} will be used instead
144   *
145   * @param args command-line arguments; may be {@code null}
146   *
147   * @see #main(SeContainerInitializer, Consumer, String[])
148   */
149  public static final void main(SeContainerInitializer containerInitializer, final String[] args) {
150    main(containerInitializer, null, args);
151  }
152
153  /**
154   * {@linkplain SeContainerInitializer#initialize() Initializes} a
155   * new {@link SeContainer}, {@linkplain Consumer#accept(Object)
156   * calls the supplied <code>consumer</code> parameter value with it}
157   * (if the supplied {@code consumer} parameter value is non-{@code
158   * null}), and then {@linkplain SeContainer#close() closes} it.
159   *
160   * <p>This method has a deliberate side effect of making the {@code
161   * args} parameter value available in the CDI container in {@link
162   * Singleton} scope with a qualifier of {@link
163   * Named @Named("commandLineArguments")}.  It also causes an
164   * instance of this class to be created by the CDI container in
165   * {@link Singleton} scope.</p>
166   *
167   * @param containerInitializer the {@link SeContainerInitializer} to
168   * use to initialize the {@link SeContainer}; may be {@code null} in
169   * which case the return value of {@link
170   * SeContainerInitializer#newInstance()} will be used instead.  This
171   * {@link SeContainerInitializer} instance will have its {@link
172   * SeContainerInitializer#addBeanClasses(Class...)} method called
173   * with {@link Main Main.class} as its only argument.
174   *
175   * @param consumer a {@link Consumer} whose {@link
176   * Consumer#accept(Object)} method will be called with an {@link
177   * SeContainer}; may be {@code null}; rarely needed
178   *
179   * @param args command-line arguments; may be {@code null}
180   *
181   * @see SeContainerInitializer#newInstance()
182   *
183   * @see SeContainerInitializer#initialize()
184   *
185   * @see SeContainer#close()
186   */
187  public static final void main(SeContainerInitializer containerInitializer, final Consumer<? super SeContainer> consumer, final String[] args) {
188    commandLineArguments = args == null ? new String[0] : args;
189    if (containerInitializer == null) {
190      containerInitializer = SeContainerInitializer.newInstance();
191    }
192    assert containerInitializer != null;
193    containerInitializer.addBeanClasses(Main.class);
194    final SeContainer container = containerInitializer.initialize();
195    assert container != null;
196    try {
197      assert container.select(Main.class).get() != null;
198      if (consumer != null) {
199        consumer.accept(container);
200      }
201    } finally {
202      if (container.isRunning()) {
203        container.close();
204      }
205    }
206  }
207  
208}