View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.config.builders;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.config.ConfigurationException;
11  import org.mule.config.ConfigResource;
12  import org.mule.config.spring.MuleApplicationContext;
13  import org.mule.config.spring.SpringXmlConfigurationBuilder;
14  
15  import java.io.FileNotFoundException;
16  import java.io.IOException;
17  import java.io.InputStream;
18  
19  import javax.servlet.ServletContext;
20  
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.springframework.beans.BeansException;
24  import org.springframework.beans.factory.access.BeanFactoryLocator;
25  import org.springframework.context.ApplicationContext;
26  import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
27  import org.springframework.core.io.AbstractResource;
28  import org.springframework.core.io.ClassPathResource;
29  import org.springframework.core.io.Resource;
30  import org.springframework.util.Assert;
31  import org.springframework.util.ClassUtils;
32  import org.springframework.util.StringUtils;
33  import org.springframework.web.context.ContextLoader;
34  import org.springframework.web.context.support.ServletContextResource;
35  
36  /**
37   * <code>WebappMuleXmlConfigurationBuilder</code> will first try and load config
38   * resources using the ServletContext and if this fails then it will attempt to load
39   * config resource from the classpath.
40   * <li> ServletContext resources should be relative to the webapp root directory and
41   * start with '/'.
42   * <li> Classpath resources should be in the webapp classpath and should not start
43   * with '/'.
44   * 
45   * @see org.mule.config.spring.SpringXmlConfigurationBuilder
46   */
47  public class WebappMuleXmlConfigurationBuilder extends SpringXmlConfigurationBuilder
48  {    
49      /**
50       * Logger used by this class
51       */
52      protected transient final Log logger = LogFactory.getLog(getClass());
53  
54      private ServletContext context;
55  
56      public WebappMuleXmlConfigurationBuilder(ServletContext servletContext, String configResources)
57          throws ConfigurationException
58      {
59          super(configResources);
60          context = servletContext;
61      }
62  
63      public WebappMuleXmlConfigurationBuilder(ServletContext servletContext, String[] configResources)
64          throws ConfigurationException
65      {
66          super(configResources);
67          context = servletContext;
68      }
69  
70      @Override
71      protected void doConfigure(MuleContext muleContext) throws Exception
72      {
73          if (getParentContext() == null)
74          {
75              setParentContext(loadParentContext(context));
76          }
77  
78          super.doConfigure(muleContext);
79      }
80  
81      protected ConfigResource[] loadConfigResources(String[] configs) throws ConfigurationException
82      {
83          try
84          {
85              configResources = new ConfigResource[configs.length];
86              for (int i = 0; i < configs.length; i++)
87              {
88                  configResources[i] = new ServletContextOrClassPathConfigResource(configs[i]);
89              }
90              return configResources;
91          }
92          catch (IOException e)
93          {
94              throw new ConfigurationException(e);
95          }
96      }
97  
98      protected ApplicationContext createApplicationContext(MuleContext muleContext, ConfigResource[] configResources)
99      {
100         Resource[] servletContextResources = new Resource[configResources.length];
101         for (int i = 0; i < configResources.length; i++)
102         {
103             servletContextResources[i] = new ServletContextOrClassPathResource(context, configResources[i].getResourceName());
104         }
105 
106         return new MuleApplicationContext(muleContext, servletContextResources);
107     }
108 
109     /**
110      * Used to lookup parent spring ApplicationContext. This allows a parent spring
111      * ApplicatonContet to be provided in the same way you would configure a parent
112      * ApplicationContext for a spring WebAppplicationContext
113      * 
114      * @param servletContext
115      * @throws BeansException
116      */
117     protected ApplicationContext loadParentContext(ServletContext servletContext) throws BeansException
118     {
119         ApplicationContext parentContext = null;
120         String locatorFactorySelector = servletContext.getInitParameter(ContextLoader.LOCATOR_FACTORY_SELECTOR_PARAM);
121         String parentContextKey = servletContext.getInitParameter(ContextLoader.LOCATOR_FACTORY_KEY_PARAM);
122 
123         if (parentContextKey != null)
124         {
125             // locatorFactorySelector may be null, indicating the default
126             // "classpath*:beanRefContext.xml"
127             BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);
128             if (logger.isDebugEnabled())
129             {
130                 logger.debug("Getting parent context definition: using parent context key of '" + parentContextKey
131                              + "' with BeanFactoryLocator");
132             }
133             parentContext = (ApplicationContext) locator.useBeanFactory(parentContextKey).getFactory();
134         }
135 
136         return parentContext;
137     }
138 
139     class ServletContextOrClassPathConfigResource extends ConfigResource
140     {
141         public ServletContextOrClassPathConfigResource(String resourceName) throws IOException
142         {
143             super(resourceName, null);
144         }
145 
146     }
147 
148     /**
149      * Combines {@link ServletContextResource} and {@link ClassPathResource} to
150      * create a {@link Resource} implementation that first tries to load a resource
151      * using the {@link ServletContext} and then fails back to use try to load the
152      * resource from the classpath.
153      */
154     class ServletContextOrClassPathResource extends AbstractResource
155     {
156 
157         private final ServletContext servletContext;
158 
159         private final String path;
160 
161         public ServletContextOrClassPathResource(ServletContext servletContext, String path)
162         {
163             Assert.notNull(servletContext, "Cannot resolve ServletContextResource without ServletContext");
164             this.servletContext = servletContext;
165             // check path
166             Assert.notNull(path, "path is required");
167             this.path = StringUtils.cleanPath(path);
168         }
169 
170         public InputStream getInputStream() throws IOException
171         {
172             InputStream is = getServletContextInputStream();
173             if (is == null)
174             {
175                 is = getClasspathInputStream();
176             }
177             if (is == null)
178             {
179                 throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
180             }
181             return is;
182         }
183 
184         protected InputStream getServletContextInputStream()
185         {
186             String servletContextPath = path;
187             if (!servletContextPath.startsWith("/"))
188             {
189                 servletContextPath = "/" + servletContextPath;
190             }
191             return servletContext.getResourceAsStream(servletContextPath);
192         }
193 
194         protected InputStream getClasspathInputStream()
195         {
196             String classpathPath = path;
197             if (classpathPath.startsWith("/"))
198             {
199                 classpathPath = classpathPath.substring(1);
200             }
201             return ClassUtils.getDefaultClassLoader().getResourceAsStream(classpathPath);
202         }
203 
204         public String getDescription()
205         {
206             return path;
207         }
208     }
209 }