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 preferrable 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       */
67      public static JohnsonConfig getConfig()
68      {
69          return config;
70      }
71  
72      /**
73       * Attempts to retrieve the {@link JohnsonConfig} from the provided {@code ServletContext} under the key
74       * {@link #CONFIG_ATTRIBUTE} before falling back on {@link #getConfig()}.
75       * <p/>
76       * Note: If Johnson has not been {@link #initialize(ServletContext) initialised}, this method <i>will not</i>
77       * initialise it. Before attempting to use Johnson, it is left to the application developer to ensure it has
78       * been correctly initialised.
79       *
80       * @param context the servlet context
81       * @return the working configuration
82       */
83      public static JohnsonConfig getConfig(ServletContext context)
84      {
85          Object attribute = context.getAttribute(CONFIG_ATTRIBUTE);
86          if (attribute != null)
87          {
88              return (JohnsonConfig) attribute;
89          }
90          return config;
91      }
92  
93      /**
94       * Attempts to retrieve the {@link JohnsonEventContainer} from the provided {@code ServletContext} under the key
95       * {@link #EVENT_CONTAINER_ATTRIBUTE} before falling back on the statically-bound instance.
96       * <p/>
97       * Note: If Johnson has not been {@link #initialize(ServletContext) initialised}, this method <i>will not</i>
98       * initialise it. Before attempting to use Johnson, it is left to the application developer to ensure it has
99       * been correctly initialised.
100      *
101      * @param context the servlet context
102      * @return the working event container
103      */
104     public static JohnsonEventContainer getEventContainer(ServletContext context)
105     {
106         Object attribute = context.getAttribute(EVENT_CONTAINER_ATTRIBUTE);
107         if (attribute != null)
108         {
109             return (JohnsonEventContainer) attribute;
110         }
111         return eventContainer;
112     }
113 
114     /**
115      * Initialises Johnson, additionally binding the {@link JohnsonConfig} and {@link JohnsonEventContainer} to the
116      * provided {@code ServletContext} and performing any {@link ApplicationEventCheck}s which have been configured.
117      * <p/>
118      * If the {@link #CONFIG_LOCATION_PARAM} {@code init-param} is set, it is used to determine the location of the
119      * Johnson configuration file. Otherwise, {@link XmlJohnsonConfig#DEFAULT_CONFIGURATION_FILE} is assumed.
120      * <p/>
121      * Note: This method is <i>not synchronised</i> and <i>not thread-safe</i>. It is left to the <i>calling code</i>
122      * to ensure proper synchronisation. The easiest way to do this is to initialise Johnson by adding the
123      * {@link com.atlassian.johnson.context.JohnsonContextListener} to {@code web.xml}.
124      * 
125      * @param context the servlet context
126      */
127     public static void initialize(ServletContext context)
128     {
129         String location = StringUtils.defaultIfEmpty(context.getInitParameter(CONFIG_LOCATION_PARAM),
130                 XmlJohnsonConfig.DEFAULT_CONFIGURATION_FILE);
131         
132         initialize(location);
133 
134         context.setAttribute(CONFIG_ATTRIBUTE, config);
135         context.setAttribute(EVENT_CONTAINER_ATTRIBUTE, eventContainer);
136 
137         List<ApplicationEventCheck> checks = config.getApplicationEventChecks();
138         for (ApplicationEventCheck check : checks)
139         {
140             check.check(eventContainer, context);
141         }
142     }
143 
144     /**
145      * Initialises Johnson, loading its configuration from the provided location, and sets the statically-bound
146      * instances of {@link JohnsonConfig} and {@link JohnsonEventContainer}.
147      * <p/>
148      * If the location provided is {@code null} or empty, {@link XmlJohnsonConfig#DEFAULT_CONFIGURATION_FILE} is
149      * assumed.
150      * <p/>
151      * Note: If the configuration fails to load, {@link DefaultJohnsonConfig} is used to provide defaults. For more
152      * information about what those defaults are, see the documentation for that class.
153      *
154      * @param location the location of the Johnson configuration file
155      */
156     public static void initialize(String location)
157     {
158         location = StringUtils.defaultIfEmpty(location, XmlJohnsonConfig.DEFAULT_CONFIGURATION_FILE);
159 
160         LOG.debug("Initialising Johnson with configuration from [{}]", location);
161         try
162         {
163             config = XmlJohnsonConfig.fromFile(location);
164         }
165         catch (ConfigurationJohnsonException e)
166         {
167             LOG.warn("Failed to load configuration from [" + location + "]", e);
168             config = DefaultJohnsonConfig.getInstance();
169         }
170 
171         ContainerFactory containerFactory = config.getContainerFactory();
172         eventContainer = containerFactory.create();
173     }
174 
175     /**
176      * Terminates Johnson, clearing the statically-bound {@link JohnsonConfig} and {@link JohnsonEventContainer}.
177      */
178     public static void terminate()
179     {
180         config = null;
181         eventContainer = null;
182     }
183 
184     /**
185      * Terminates Johnson, removing Johnson-related attributes from the provided {@code ServletContext} and clearing
186      * the statically-bound {@link JohnsonConfig} and {@link JohnsonEventContainer}.
187      *
188      * @param context the servlet context
189      */
190     public static void terminate(ServletContext context)
191     {
192         terminate();
193 
194         context.removeAttribute(CONFIG_ATTRIBUTE);
195         context.removeAttribute(EVENT_CONTAINER_ATTRIBUTE);
196     }
197 }