View Javadoc

1   package com.atlassian.johnson;
2   
3   import com.atlassian.johnson.config.ConfigurationJohnsonException;
4   import com.atlassian.johnson.config.DefaultJohnsonConfig;
5   import com.atlassian.johnson.config.JohnsonConfig;
6   import com.atlassian.johnson.config.XmlJohnsonConfig;
7   import com.atlassian.johnson.event.ApplicationEventCheck;
8   import com.atlassian.johnson.setup.ContainerFactory;
9   import org.apache.commons.lang.StringUtils;
10  import org.slf4j.Logger;
11  import org.slf4j.LoggerFactory;
12  
13  import javax.servlet.ServletContext;
14  import java.util.List;
15  
16  /**
17   * Singleton class for controlling Johnson.
18   * <p/>
19   * Because Johnson is intended to be a maintenance and application consistency layer around an application, its
20   * functionality is exposed statically. This class provides both the mechanisms for initialising and terminating
21   * Johnson, as well as for retrieving its configuration and the container of events.
22   * <p/>
23   * Where possible to avoid synchronisation issues, it is preferable to bind Johnson to the lifecycle for the servlet
24   * container, using {@link com.atlassian.johnson.context.JohnsonContextListener JohnsonContextListener} to initialise
25   * and terminate Johnson.
26   *
27   * @since 2.0
28   */
29  public class Johnson
30  {
31      /**
32       * When used in a web environment, the {@link JohnsonConfig} will be exposed in the {@code ServletContext} under
33       * an attribute with this key.
34       */
35      public static final String CONFIG_ATTRIBUTE = Johnson.class.getName() + ":Config";
36      /**
37       * During {@link #initialize(javax.servlet.ServletContext) initialisation}, the {@code ServletContext} is examined
38       * for an {@code init-param} with this name. If one is found, it controls the location from which the configuration
39       * is loaded. Otherwise, {@link XmlJohnsonConfig#DEFAULT_CONFIGURATION_FILE} is used as the default.
40       */
41      public static final String CONFIG_LOCATION_PARAM = "johnsonConfigLocation";
42      /**
43       * When used in a web environment, the {@link JohnsonEventContainer} will be exposed in the {@code ServletContext}
44       * under an attribute with this key.
45       */
46      public static final String EVENT_CONTAINER_ATTRIBUTE = Johnson.class.getName() + ":EventContainer";
47  
48      private static final Logger LOG = LoggerFactory.getLogger(Johnson.class);
49  
50      private static JohnsonConfig config;
51      private static JohnsonEventContainer eventContainer;
52  
53      private Johnson()
54      {
55  
56      }
57  
58      /**
59       * Retrieves the statically-bound {@link JohnsonConfig}.
60       * <p/>
61       * Note: If Johnson has not been {@link #initialize(String) initialised}, this method <i>will not</i> initialise
62       * it. Before attempting to use Johnson, it is left to the application developer to ensure it has been correctly
63       * initialised.
64       *
65       * @return the working configuration
66       * @throws IllegalStateException if initialize has not been called
67       */
68      public static JohnsonConfig getConfig()
69      {
70          if (config == null)
71          {
72              throw new IllegalStateException("Johnson.getConfig() was called before initialisation");
73          }
74          return config;
75      }
76  
77      /**
78       * Attempts to retrieve the {@link JohnsonConfig} from the provided {@code ServletContext} under the key
79       * {@link #CONFIG_ATTRIBUTE} before falling back on {@link #getConfig()}.
80       * <p/>
81       * Note: If Johnson has not been {@link #initialize(ServletContext) initialised}, this method <i>will not</i>
82       * initialise it. Before attempting to use Johnson, it is left to the application developer to ensure it has
83       * been correctly initialised.
84       *
85       * @param context the servlet context
86       * @return the working configuration
87       */
88      public static JohnsonConfig getConfig(ServletContext context)
89      {
90          Object attribute = context.getAttribute(CONFIG_ATTRIBUTE);
91          if (attribute != null)
92          {
93              return (JohnsonConfig) attribute;
94          }
95          return config;
96      }
97  
98      /**
99       * Attempts to retrieve the {@link JohnsonEventContainer} from the provided {@code ServletContext} under the key
100      * {@link #EVENT_CONTAINER_ATTRIBUTE} before falling back on the statically-bound instance.
101      * <p/>
102      * Note: If Johnson has not been {@link #initialize(ServletContext) initialised}, this method <i>will not</i>
103      * initialise it. Before attempting to use Johnson, it is left to the application developer to ensure it has
104      * been correctly initialised.
105      *
106      * @param context the servlet context
107      * @return the working event container
108      */
109     public static JohnsonEventContainer getEventContainer(ServletContext context)
110     {
111         Object attribute = context.getAttribute(EVENT_CONTAINER_ATTRIBUTE);
112         if (attribute != null)
113         {
114             return (JohnsonEventContainer) attribute;
115         }
116         return eventContainer;
117     }
118 
119     /**
120      * Initialises Johnson, additionally binding the {@link JohnsonConfig} and {@link JohnsonEventContainer} to the
121      * provided {@code ServletContext} and performing any {@link ApplicationEventCheck}s which have been configured.
122      * <p/>
123      * If the {@link #CONFIG_LOCATION_PARAM} {@code init-param} is set, it is used to determine the location of the
124      * Johnson configuration file. Otherwise, {@link XmlJohnsonConfig#DEFAULT_CONFIGURATION_FILE} is assumed.
125      * <p/>
126      * Note: This method is <i>not synchronised</i> and <i>not thread-safe</i>. It is left to the <i>calling code</i>
127      * to ensure proper synchronisation. The easiest way to do this is to initialise Johnson by adding the
128      * {@link com.atlassian.johnson.context.JohnsonContextListener} to {@code web.xml}.
129      *
130      * @param context the servlet context
131      */
132     public static void initialize(ServletContext context)
133     {
134         String location = StringUtils.defaultIfEmpty(context.getInitParameter(CONFIG_LOCATION_PARAM),
135                 XmlJohnsonConfig.DEFAULT_CONFIGURATION_FILE);
136 
137         initialize(location);
138 
139         context.setAttribute(CONFIG_ATTRIBUTE, config);
140         context.setAttribute(EVENT_CONTAINER_ATTRIBUTE, eventContainer);
141 
142         List<ApplicationEventCheck> checks = config.getApplicationEventChecks();
143         for (ApplicationEventCheck check : checks)
144         {
145             check.check(eventContainer, context);
146         }
147     }
148 
149     /**
150      * Initialises Johnson, loading its configuration from the provided location, and sets the statically-bound
151      * instances of {@link JohnsonConfig} and {@link JohnsonEventContainer}.
152      * <p/>
153      * If the location provided is {@code null} or empty, {@link XmlJohnsonConfig#DEFAULT_CONFIGURATION_FILE} is
154      * assumed.
155      * <p/>
156      * Note: If the configuration fails to load, {@link DefaultJohnsonConfig} is used to provide defaults. For more
157      * information about what those defaults are, see the documentation for that class.
158      *
159      * @param location the location of the Johnson configuration file
160      */
161     public static void initialize(String location)
162     {
163         location = StringUtils.defaultIfEmpty(location, XmlJohnsonConfig.DEFAULT_CONFIGURATION_FILE);
164 
165         LOG.debug("Initialising Johnson with configuration from [{}]", location);
166         try
167         {
168             config = XmlJohnsonConfig.fromFile(location);
169         }
170         catch (ConfigurationJohnsonException e)
171         {
172             LOG.warn("Failed to load configuration from [" + location + "]", e);
173             config = DefaultJohnsonConfig.getInstance();
174         }
175 
176         ContainerFactory containerFactory = config.getContainerFactory();
177         eventContainer = containerFactory.create();
178     }
179 
180     /**
181      * Terminates Johnson, clearing the statically-bound {@link JohnsonConfig} and {@link JohnsonEventContainer}.
182      */
183     public static void terminate()
184     {
185         config = null;
186         eventContainer = null;
187     }
188 
189     /**
190      * Terminates Johnson, removing Johnson-related attributes from the provided {@code ServletContext} and clearing
191      * the statically-bound {@link JohnsonConfig} and {@link JohnsonEventContainer}.
192      *
193      * @param context the servlet context
194      */
195     public static void terminate(ServletContext context)
196     {
197         terminate();
198 
199         context.removeAttribute(CONFIG_ATTRIBUTE);
200         context.removeAttribute(EVENT_CONTAINER_ATTRIBUTE);
201     }
202 }