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