001    /**
002     *   GRANITE DATA SERVICES
003     *   Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004     *
005     *   This file is part of the Granite Data Services Platform.
006     *
007     *   Granite Data Services is free software; you can redistribute it and/or
008     *   modify it under the terms of the GNU Lesser General Public
009     *   License as published by the Free Software Foundation; either
010     *   version 2.1 of the License, or (at your option) any later version.
011     *
012     *   Granite Data Services is distributed in the hope that it will be useful,
013     *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015     *   General Public License for more details.
016     *
017     *   You should have received a copy of the GNU Lesser General Public
018     *   License along with this library; if not, write to the Free Software
019     *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020     *   USA, or see <http://www.gnu.org/licenses/>.
021     */
022    package org.granite.config;
023    
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.util.ArrayList;
027    import java.util.List;
028    import java.util.Map;
029    
030    import javax.servlet.ServletContext;
031    import javax.servlet.ServletException;
032    
033    import org.granite.config.api.Configuration;
034    import org.granite.jmx.MBeanUtil;
035    import org.granite.logging.Logger;
036    import org.granite.messaging.amf.io.convert.Converter;
037    import org.granite.messaging.amf.io.util.ActionScriptClassDescriptor;
038    import org.granite.messaging.amf.io.util.JavaClassDescriptor;
039    import org.granite.messaging.amf.io.util.externalizer.Externalizer;
040    import org.granite.util.ServletParams;
041    import org.granite.util.StreamUtil;
042    
043    /**
044     * @author Franck WOLFF
045     */
046    public class ServletGraniteConfig implements ServletGraniteConfigMBean {
047    
048        ///////////////////////////////////////////////////////////////////////////
049        // Static fields.
050    
051        private static final Logger log = Logger.getLogger(ServletGraniteConfig.class);
052    
053        private static final String GRANITE_CONFIG_DEFAULT = "/WEB-INF/granite/granite-config.xml";
054        
055        public static final String GRANITE_CONFIG_KEY = GraniteConfig.class.getName() + "_CACHE";
056        public static final String GRANITE_CONFIG_DEFAULT_KEY = GraniteConfig.class.getName() + "_DEFAULT";
057        public static final String GRANITE_CONFIG_CONFIGURATION_KEY = GraniteConfig.class.getName() + "_CONFIG";
058    
059        ///////////////////////////////////////////////////////////////////////////
060        // Instance fields.
061    
062        private GraniteConfig config = null;
063        
064        // Context where this GraniteConfig instance is registered (required for reload operation).
065        private ServletContext context = null;
066        
067        // Should Granite MBeans be registered at startup.
068        private boolean registerMBeans = false;
069        
070        // Reload listeners.
071        private final List<GraniteConfigReloadListener> reloadListeners = new ArrayList<GraniteConfigReloadListener>();
072    
073        ///////////////////////////////////////////////////////////////////////////
074        // Constructor.
075    
076        private ServletGraniteConfig(ServletContext context, GraniteConfig config) {
077            this.context = context;
078            this.config = config;
079        }
080    
081        ///////////////////////////////////////////////////////////////////////////
082        // Static GraniteConfig loaders.
083    
084        public static synchronized GraniteConfig getConfig(ServletContext context) {
085            return ((ServletGraniteConfig)context.getAttribute(GRANITE_CONFIG_KEY)).config;
086        }
087    
088        public static synchronized ServletGraniteConfig getServletConfig(ServletContext context) {
089            return (ServletGraniteConfig)context.getAttribute(GRANITE_CONFIG_KEY);
090        }
091    
092        public static synchronized GraniteConfig loadConfig(ServletContext context) throws ServletException {
093            ServletGraniteConfig servletGraniteConfig = (ServletGraniteConfig)context.getAttribute(GRANITE_CONFIG_KEY);
094    
095            if (servletGraniteConfig == null) {
096                    String path = getCustomConfigPath(context);
097    
098                InputStream is = context.getResourceAsStream(path);
099                if (is == null) {
100                    log.warn("Could not load custom granite-config.xml: %s (file does not exists)", path);
101                    path = null;
102                }
103                    
104                    Configuration configuration = (Configuration)context.getAttribute(GRANITE_CONFIG_CONFIGURATION_KEY);
105                    
106                    String stdConfigPath = (String)context.getAttribute(GRANITE_CONFIG_DEFAULT_KEY);
107    
108                boolean registerMBeans = ServletParams.get(context, GraniteConfigListener.GRANITE_MBEANS_ATTRIBUTE, Boolean.TYPE, false);
109                
110                try {
111                    String mbeanContextName = null;
112                    // Use reflection for JDK 1.4 compatibility. 
113                    if (registerMBeans)
114                            mbeanContextName = (String)ServletContext.class.getMethod("getContextPath").invoke(context);
115    
116                    GraniteConfig graniteConfig = new GraniteConfig(stdConfigPath, is, configuration, mbeanContextName);
117                    
118                    servletGraniteConfig = loadConfig(context, graniteConfig);
119                } 
120                catch (Exception e) {
121                    log.error(e, "Could not load granite-config.xml");
122                    throw new ServletException("Could not load custom granite-config.xml", e);
123                }
124                finally {
125                    if (is != null) try {
126                            is.close();
127                    } catch (IOException e) {
128                            // Ignore...
129                    }
130                }
131            }
132    
133            return servletGraniteConfig.config;
134        }
135    
136        public static synchronized ServletGraniteConfig loadConfig(ServletContext context, GraniteConfig graniteConfig) {
137            ServletGraniteConfig servletGraniteConfig = new ServletGraniteConfig(context, graniteConfig);
138            
139            context.setAttribute(GRANITE_CONFIG_KEY, servletGraniteConfig);
140            
141            return servletGraniteConfig;
142        }
143    
144        private static String getCustomConfigPath(ServletContext context) {
145            String path = null;
146            
147            Configuration configuration = (Configuration)context.getAttribute(GRANITE_CONFIG_CONFIGURATION_KEY);
148            if (configuration != null)
149                    path = configuration.getGraniteConfig();
150            
151            if (path == null)
152                    path = ServletParams.get(context, "graniteConfigPath", String.class, GRANITE_CONFIG_DEFAULT);
153            
154            return path;
155            }
156        
157        public String getCustomConfigPath() {
158            return getCustomConfigPath(context);
159        }
160        
161    
162        ///////////////////////////////////////////////////////////////////////////
163        // GraniteConfigMBean implementation: attributes.
164        
165            public boolean isRegisterMBeans() {
166                    return registerMBeans;
167            }
168            public void setRegisterMBeans(boolean registerMBeans) {
169                    this.registerMBeans = registerMBeans;
170            }
171    
172            
173            public synchronized void reload() {
174    
175                    if (context == null)
176                    throw new IllegalStateException("GraniteConfig was not registered in ServletContext");
177    
178            ServletGraniteConfig oldConfig = (ServletGraniteConfig)context.getAttribute(GRANITE_CONFIG_KEY);
179                    
180                    try {
181                    context.removeAttribute(GRANITE_CONFIG_KEY);
182                    GraniteConfig config = loadConfig(context);
183                    for (GraniteConfigReloadListener listener : reloadListeners) {
184                            try {
185                                    listener.onReload(context, config);
186                            }
187                            catch (Exception e) {
188                                    log.error(e, "Error while calling reload listener: %s", listener);
189                            }
190                    }
191                    }
192                    catch (Exception e) {
193                            throw new RuntimeException(e.getMessage(), e);
194                    }
195                    finally {
196                            if (context.getAttribute(GRANITE_CONFIG_KEY) == null)
197                                    context.setAttribute(GRANITE_CONFIG_KEY, oldConfig);
198                    }
199            }
200    
201    
202        ///////////////////////////////////////////////////////////////////////////
203        // GraniteConfigMBean implementation
204            
205            public void addReloadListener(GraniteConfigReloadListener listener) {
206                    synchronized (reloadListeners) {
207                            if (!reloadListeners.contains(listener))
208                                    reloadListeners.add(listener);
209                    }
210            }
211            
212            public boolean removeReloadListener(GraniteConfigReloadListener listener) {
213                    synchronized (reloadListeners) {
214                            return reloadListeners.remove(listener);
215                    }
216            }
217        
218        public boolean getScan() {
219            return config.getScan();
220        }
221    
222            public String getAmf3DeserializerClass() {
223                    return MBeanUtil.format(
224                            config.getAmf3DeserializerConstructor() != null ?
225                            config.getAmf3DeserializerConstructor().getDeclaringClass().getName():
226                            null
227                    );
228            }
229    
230            public String getAmf3SerializerClass() {
231                    return MBeanUtil.format(
232                            config.getAmf3SerializerConstructor() != null ?
233                            config.getAmf3SerializerConstructor().getDeclaringClass().getName():
234                            null
235                    );
236            }
237    
238            public String getAmf3MessageInterceptorClass() {
239                    return MBeanUtil.format(
240                            config.getAmf3MessageInterceptor() != null ?
241                            config.getAmf3MessageInterceptor().getClass().getName() :
242                            null
243                    );
244            }
245    
246            public String getClassGetterClass() {
247                    return MBeanUtil.format(
248                            config.getClassGetter() != null ?
249                            config.getClassGetter().getClass().getName() :
250                            null
251                    );
252            }
253    
254            public String getMessageSelectorClass() {
255                    return MBeanUtil.format(
256                            config.getMessageSelectorConstructor() != null ?
257                            config.getMessageSelectorConstructor().getDeclaringClass().getName() :
258                            null
259                    );
260            }
261    
262            public String getMethodMatcherClass() {
263                    return MBeanUtil.format(
264                            config.getMethodMatcher() != null ?
265                            config.getMethodMatcher().getClass().getName() :
266                            null
267                    );
268            }
269    
270            public String getSecurityServiceClass() {
271                    return MBeanUtil.format(
272                            config.getSecurityService() != null ?
273                            config.getSecurityService().getClass().getName() :
274                            null
275                    );
276            }
277    
278            public String getServiceInvocationListenerClass() {
279                    return MBeanUtil.format(
280                            config.getInvocationListener() != null ?
281                            config.getInvocationListener().getClass().getName() :
282                            null
283                    );
284            }
285    
286            public String showStandardConfig() throws IOException {
287                    String s = StreamUtil.getResourceAsString("org/granite/config/granite-config.xml", getClass().getClassLoader());
288                    return MBeanUtil.format(s);
289            }
290            
291        public String showCustomConfig() throws IOException {
292            String path = getCustomConfigPath();
293    
294            InputStream is = context.getResourceAsStream(path);
295            try {
296                    String s = StreamUtil.getStreamAsString(is);
297                    return MBeanUtil.format(s);
298            }
299            finally {
300                    if (is != null)
301                            is.close();
302            }
303            }
304    
305            public String showConverters() {
306                    Converter[] cs = config.getConverters().getConverters();
307                    String[] names = new String[cs.length];
308                    for (int i = 0; i < cs.length; i++)
309                            names[i] = cs[i].getClass().getName();
310                    return MBeanUtil.format(names);
311            }
312    
313            public String showExceptionConverters() {
314                    String[] names = new String[config.getExceptionConverters().size()];
315                    for (int i = 0; i < config.getExceptionConverters().size(); i++)
316                            names[i] = config.getExceptionConverters().get(i).getClass().getName();
317                    return MBeanUtil.format(names);
318            }
319    
320            public String showInstantiators() {
321                    String[] names = new String[config.getInstantiators().size()];
322                    int i = 0;
323                    for (Map.Entry<String, String> e : config.getInstantiators().entrySet())
324                            names[i++] = e.getKey() + "=" + e.getValue();
325                    return MBeanUtil.format(names, true);
326            }
327    
328            public String showAs3DescriptorsByInstanceOf() {
329                    String[] names = new String[config.getAs3DescriptorsByInstanceOf().size()];
330                    int i = 0;
331                    for (Map.Entry<String, String> e : config.getAs3DescriptorsByInstanceOf().entrySet())
332                            names[i++] = e.getKey() + "=" + e.getValue();
333                    return MBeanUtil.format(names, true);
334            }
335    
336            public String showAs3DescriptorsByType() {
337                    String[] names = new String[config.getAs3DescriptorsByType().size()];
338                    int i = 0;
339                    for (Map.Entry<String, Class<? extends ActionScriptClassDescriptor>> e : config.getAs3DescriptorsByType().entrySet())
340                            names[i++] = e.getKey() + "=" + e.getValue().getName();
341                    return MBeanUtil.format(names, true);
342            }
343    
344            public String showDisabledTideComponentsByName() {
345                    String[] names = new String[config.getDisabledTideComponentsByName().size()];
346                    int i = 0;
347                    for (Map.Entry<String, Object[]> e : config.getDisabledTideComponentsByName().entrySet())
348                            names[i++] = e.getKey() + "=" + e.getValue()[0];
349                    return MBeanUtil.format(names, true);
350            }
351    
352            public String showEnabledTideComponentsByName() {
353                    String[] names = new String[config.getEnabledTideComponentsByName().size()];
354                    int i = 0;
355                    for (Map.Entry<String, Object[]> e : config.getEnabledTideComponentsByName().entrySet())
356                            names[i++] = e.getKey() + "=" + e.getValue()[0];
357                    return MBeanUtil.format(names, true);
358            }
359    
360            public String showExternalizersByAnnotatedWith() {
361                    String[] names = new String[config.getExternalizersByAnnotatedWith().size()];
362                    int i = 0;
363                    for (Map.Entry<String, String> e : config.getExternalizersByAnnotatedWith().entrySet())
364                            names[i++] = e.getKey() + "=" + e.getValue();
365                    return MBeanUtil.format(names, true);
366            }
367    
368            public String showExternalizersByInstanceOf() {
369                    String[] names = new String[config.getExternalizersByInstanceOf().size()];
370                    int i = 0;
371                    for (Map.Entry<String, String> e : config.getExternalizersByInstanceOf().entrySet())
372                            names[i++] = e.getKey() + "=" + e.getValue();
373                    return MBeanUtil.format(names, true);
374            }
375    
376            public String showExternalizersByType() {
377                    List<String> names = new ArrayList<String>(config.getExternalizersByType().size());
378                    for (Map.Entry<String, Externalizer> e : config.getExternalizersByType().entrySet()) {
379                            if (config.EXTERNALIZER_FACTORY.getNullInstance() != e.getValue())
380                                    names.add(e.getKey() + "=" + e.getValue().getClass().getName());
381                    }
382                    return MBeanUtil.format(names.toArray(new String[names.size()]), true);
383            }
384    
385            public String showJavaDescriptorsByInstanceOf() {
386                    String[] names = new String[config.getJavaDescriptorsByInstanceOf().size()];
387                    int i = 0;
388                    for (Map.Entry<String, String> e : config.getJavaDescriptorsByInstanceOf().entrySet())
389                            names[i++] = e.getKey() + "=" + e.getValue();
390                    return MBeanUtil.format(names, true);
391            }
392    
393            public String showJavaDescriptorsByType() {
394                    String[] names = new String[config.getJavaDescriptorsByType().size()];
395                    int i = 0;
396                    for (Map.Entry<String, Class<? extends JavaClassDescriptor>> e : config.getJavaDescriptorsByType().entrySet())
397                            names[i++] = e.getKey() + "=" + e.getValue().getName();
398                    return MBeanUtil.format(names, true);
399            }
400    
401            public String showScannedExternalizers() {
402                    String[] names = new String[config.getScannedExternalizers().size()];
403                    for (int i = 0; i < config.getScannedExternalizers().size(); i++)
404                            names[i] = config.getScannedExternalizers().get(i).getClass().getName();
405                    return MBeanUtil.format(names);
406            }
407    
408            public String showTideComponentMatchers() {
409                    String[] names = new String[config.getTideComponentMatchers().size()];
410                    for (int i = 0; i < config.getTideComponentMatchers().size(); i++)
411                            names[i] = config.getTideComponentMatchers().get(i).toString();
412                    return MBeanUtil.format(names);
413            }
414    }