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
021package org.granite.config;
022
023import java.io.IOException;
024import java.io.InputStream;
025import java.util.ArrayList;
026import java.util.List;
027import java.util.Map;
028
029import javax.servlet.ServletContext;
030import javax.servlet.ServletException;
031
032import org.granite.config.api.Configuration;
033import org.granite.jmx.MBeanUtil;
034import org.granite.logging.Logger;
035import org.granite.messaging.amf.io.convert.Converter;
036import org.granite.messaging.amf.io.util.ActionScriptClassDescriptor;
037import org.granite.messaging.amf.io.util.JavaClassDescriptor;
038import org.granite.messaging.amf.io.util.externalizer.Externalizer;
039import org.granite.util.ServletParams;
040import org.granite.util.StreamUtil;
041
042/**
043 * @author Franck WOLFF
044 */
045public 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}