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                    log.error(e, "Could not load granite-config.xml");
121                    throw new ServletException("Could not load custom granite-config.xml", e);
122                }
123                finally {
124                    if (is != null) try {
125                            is.close();
126                    } catch (IOException e) {
127                            // Ignore...
128                    }
129                }
130            }
131    
132            return servletGraniteConfig.config;
133        }
134    
135        public static synchronized ServletGraniteConfig loadConfig(ServletContext context, GraniteConfig graniteConfig) {
136            ServletGraniteConfig servletGraniteConfig = new ServletGraniteConfig(context, graniteConfig);
137            
138            context.setAttribute(GRANITE_CONFIG_KEY, servletGraniteConfig);
139            
140            return servletGraniteConfig;
141        }
142    
143        private static String getCustomConfigPath(ServletContext context) {
144            String path = null;
145            
146            Configuration configuration = (Configuration)context.getAttribute(GRANITE_CONFIG_CONFIGURATION_KEY);
147            if (configuration != null)
148                    path = configuration.getGraniteConfig();
149            
150            if (path == null)
151                    path = ServletParams.get(context, "graniteConfigPath", String.class, GRANITE_CONFIG_DEFAULT);
152            
153            return path;
154            }
155        
156        public String getCustomConfigPath() {
157            return getCustomConfigPath(context);
158        }
159        
160    
161        ///////////////////////////////////////////////////////////////////////////
162        // GraniteConfigMBean implementation: attributes.
163        
164            public boolean isRegisterMBeans() {
165                    return registerMBeans;
166            }
167            public void setRegisterMBeans(boolean registerMBeans) {
168                    this.registerMBeans = registerMBeans;
169            }
170    
171            
172            public synchronized void reload() {
173    
174                    if (context == null)
175                    throw new IllegalStateException("GraniteConfig was not registered in ServletContext");
176    
177            ServletGraniteConfig oldConfig = (ServletGraniteConfig)context.getAttribute(GRANITE_CONFIG_KEY);
178                    
179                    try {
180                    context.removeAttribute(GRANITE_CONFIG_KEY);
181                    GraniteConfig config = loadConfig(context);
182                    for (GraniteConfigReloadListener listener : reloadListeners) {
183                            try {
184                                    listener.onReload(context, config);
185                            }
186                            catch (Exception e) {
187                                    log.error(e, "Error while calling reload listener: %s", listener);
188                            }
189                    }
190                    }
191                    catch (Exception e) {
192                            throw new RuntimeException(e.getMessage(), e);
193                    }
194                    finally {
195                            if (context.getAttribute(GRANITE_CONFIG_KEY) == null)
196                                    context.setAttribute(GRANITE_CONFIG_KEY, oldConfig);
197                    }
198            }
199    
200    
201        ///////////////////////////////////////////////////////////////////////////
202        // GraniteConfigMBean implementation
203            
204            public void addReloadListener(GraniteConfigReloadListener listener) {
205                    synchronized (reloadListeners) {
206                            if (!reloadListeners.contains(listener))
207                                    reloadListeners.add(listener);
208                    }
209            }
210            
211            public boolean removeReloadListener(GraniteConfigReloadListener listener) {
212                    synchronized (reloadListeners) {
213                            return reloadListeners.remove(listener);
214                    }
215            }
216        
217        public boolean getScan() {
218            return config.getScan();
219        }
220    
221            public String getAmf3DeserializerClass() {
222                    return MBeanUtil.format(
223                            config.getAmf3DeserializerConstructor() != null ?
224                            config.getAmf3DeserializerConstructor().getDeclaringClass().getName():
225                            null
226                    );
227            }
228    
229            public String getAmf3SerializerClass() {
230                    return MBeanUtil.format(
231                            config.getAmf3SerializerConstructor() != null ?
232                            config.getAmf3SerializerConstructor().getDeclaringClass().getName():
233                            null
234                    );
235            }
236    
237            public String getAmf3MessageInterceptorClass() {
238                    return MBeanUtil.format(
239                            config.getAmf3MessageInterceptor() != null ?
240                            config.getAmf3MessageInterceptor().getClass().getName() :
241                            null
242                    );
243            }
244    
245            public String getClassGetterClass() {
246                    return MBeanUtil.format(
247                            config.getClassGetter() != null ?
248                            config.getClassGetter().getClass().getName() :
249                            null
250                    );
251            }
252    
253            public String getMessageSelectorClass() {
254                    return MBeanUtil.format(
255                            config.getMessageSelectorConstructor() != null ?
256                            config.getMessageSelectorConstructor().getDeclaringClass().getName() :
257                            null
258                    );
259            }
260    
261            public String getMethodMatcherClass() {
262                    return MBeanUtil.format(
263                            config.getMethodMatcher() != null ?
264                            config.getMethodMatcher().getClass().getName() :
265                            null
266                    );
267            }
268    
269            public String getSecurityServiceClass() {
270                    return MBeanUtil.format(
271                            config.getSecurityService() != null ?
272                            config.getSecurityService().getClass().getName() :
273                            null
274                    );
275            }
276    
277            public String getServiceInvocationListenerClass() {
278                    return MBeanUtil.format(
279                            config.getInvocationListener() != null ?
280                            config.getInvocationListener().getClass().getName() :
281                            null
282                    );
283            }
284    
285            public String showStandardConfig() throws IOException {
286                    String s = StreamUtil.getResourceAsString("org/granite/config/granite-config.xml", getClass().getClassLoader());
287                    return MBeanUtil.format(s);
288            }
289            
290        public String showCustomConfig() throws IOException {
291            String path = getCustomConfigPath();
292    
293            InputStream is = context.getResourceAsStream(path);
294            try {
295                    String s = StreamUtil.getStreamAsString(is);
296                    return MBeanUtil.format(s);
297            }
298            finally {
299                    if (is != null)
300                            is.close();
301            }
302            }
303    
304            public String showConverters() {
305                    Converter[] cs = config.getConverters().getConverters();
306                    String[] names = new String[cs.length];
307                    for (int i = 0; i < cs.length; i++)
308                            names[i] = cs[i].getClass().getName();
309                    return MBeanUtil.format(names);
310            }
311    
312            public String showExceptionConverters() {
313                    String[] names = new String[config.getExceptionConverters().size()];
314                    for (int i = 0; i < config.getExceptionConverters().size(); i++)
315                            names[i] = config.getExceptionConverters().get(i).getClass().getName();
316                    return MBeanUtil.format(names);
317            }
318    
319            public String showInstantiators() {
320                    String[] names = new String[config.getInstantiators().size()];
321                    int i = 0;
322                    for (Map.Entry<String, String> e : config.getInstantiators().entrySet())
323                            names[i++] = e.getKey() + "=" + e.getValue();
324                    return MBeanUtil.format(names, true);
325            }
326    
327            public String showAs3DescriptorsByInstanceOf() {
328                    String[] names = new String[config.getAs3DescriptorsByInstanceOf().size()];
329                    int i = 0;
330                    for (Map.Entry<String, String> e : config.getAs3DescriptorsByInstanceOf().entrySet())
331                            names[i++] = e.getKey() + "=" + e.getValue();
332                    return MBeanUtil.format(names, true);
333            }
334    
335            public String showAs3DescriptorsByType() {
336                    String[] names = new String[config.getAs3DescriptorsByType().size()];
337                    int i = 0;
338                    for (Map.Entry<String, Class<? extends ActionScriptClassDescriptor>> e : config.getAs3DescriptorsByType().entrySet())
339                            names[i++] = e.getKey() + "=" + e.getValue().getName();
340                    return MBeanUtil.format(names, true);
341            }
342    
343            public String showDisabledTideComponentsByName() {
344                    String[] names = new String[config.getDisabledTideComponentsByName().size()];
345                    int i = 0;
346                    for (Map.Entry<String, Object[]> e : config.getDisabledTideComponentsByName().entrySet())
347                            names[i++] = e.getKey() + "=" + e.getValue()[0];
348                    return MBeanUtil.format(names, true);
349            }
350    
351            public String showEnabledTideComponentsByName() {
352                    String[] names = new String[config.getEnabledTideComponentsByName().size()];
353                    int i = 0;
354                    for (Map.Entry<String, Object[]> e : config.getEnabledTideComponentsByName().entrySet())
355                            names[i++] = e.getKey() + "=" + e.getValue()[0];
356                    return MBeanUtil.format(names, true);
357            }
358    
359            public String showExternalizersByAnnotatedWith() {
360                    String[] names = new String[config.getExternalizersByAnnotatedWith().size()];
361                    int i = 0;
362                    for (Map.Entry<String, String> e : config.getExternalizersByAnnotatedWith().entrySet())
363                            names[i++] = e.getKey() + "=" + e.getValue();
364                    return MBeanUtil.format(names, true);
365            }
366    
367            public String showExternalizersByInstanceOf() {
368                    String[] names = new String[config.getExternalizersByInstanceOf().size()];
369                    int i = 0;
370                    for (Map.Entry<String, String> e : config.getExternalizersByInstanceOf().entrySet())
371                            names[i++] = e.getKey() + "=" + e.getValue();
372                    return MBeanUtil.format(names, true);
373            }
374    
375            public String showExternalizersByType() {
376                    List<String> names = new ArrayList<String>(config.getExternalizersByType().size());
377                    for (Map.Entry<String, Externalizer> e : config.getExternalizersByType().entrySet()) {
378                            if (config.EXTERNALIZER_FACTORY.getNullInstance() != e.getValue())
379                                    names.add(e.getKey() + "=" + e.getValue().getClass().getName());
380                    }
381                    return MBeanUtil.format(names.toArray(new String[names.size()]), true);
382            }
383    
384            public String showJavaDescriptorsByInstanceOf() {
385                    String[] names = new String[config.getJavaDescriptorsByInstanceOf().size()];
386                    int i = 0;
387                    for (Map.Entry<String, String> e : config.getJavaDescriptorsByInstanceOf().entrySet())
388                            names[i++] = e.getKey() + "=" + e.getValue();
389                    return MBeanUtil.format(names, true);
390            }
391    
392            public String showJavaDescriptorsByType() {
393                    String[] names = new String[config.getJavaDescriptorsByType().size()];
394                    int i = 0;
395                    for (Map.Entry<String, Class<? extends JavaClassDescriptor>> e : config.getJavaDescriptorsByType().entrySet())
396                            names[i++] = e.getKey() + "=" + e.getValue().getName();
397                    return MBeanUtil.format(names, true);
398            }
399    
400            public String showScannedExternalizers() {
401                    String[] names = new String[config.getScannedExternalizers().size()];
402                    for (int i = 0; i < config.getScannedExternalizers().size(); i++)
403                            names[i] = config.getScannedExternalizers().get(i).getClass().getName();
404                    return MBeanUtil.format(names);
405            }
406    
407            public String showTideComponentMatchers() {
408                    String[] names = new String[config.getTideComponentMatchers().size()];
409                    for (int i = 0; i < config.getTideComponentMatchers().size(); i++)
410                            names[i] = config.getTideComponentMatchers().get(i).toString();
411                    return MBeanUtil.format(names);
412            }
413    }