Package com.ericsson.ssa.config.annotations

Source Code of com.ericsson.ssa.config.annotations.ConfigurationAnnotationIntrospector$DynamicConfigInjector

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.config.annotations;

import com.ericsson.ssa.config.ConfigFactory;
import com.ericsson.ssa.config.ConfigRuntimeException;
import com.ericsson.ssa.config.event.ConfigAddEvent;
import com.ericsson.ssa.config.event.ConfigChangeListener;
import com.ericsson.ssa.config.event.ConfigRemoveEvent;
import com.ericsson.ssa.config.event.ConfigUpdateEvent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.util.logging.Logger;

/**
*
* @author elnelbo
*/
//TODO Optimization to reduce the number of listeners for a specific node.
//Now for each annotation possibly a listeners is registered.
//This can be done by seperating the Injector from the listeners and have the
// listerner call all associated injectors to do injection.
public class ConfigurationAnnotationIntrospector {
    private static final ConfigurationAnnotationIntrospector INSTANCE =
            new ConfigurationAnnotationIntrospector();
    private Map<Object, List<ConfigInjection>> configurables =
            new HashMap<Object, List<ConfigInjection>>();
    private Logger log = LogUtil.SIP_LOGGER.getLogger();
   
    private ConfigurationAnnotationIntrospector() {       
    }
   
    public static ConfigurationAnnotationIntrospector instance() {
        return INSTANCE;
    }
   
    public void activateConfiguration(Object configurable) {
        if (configurable!=null && !configurables.containsKey(configurable)) {
            if (log.isLoggable(Level.FINE)) {
                log.log(Level.FINE, "Activating Configuration for Object "+
                        configurable.getClass().getName()+":"+
                        configurable.hashCode());
            }
            List<ConfigInjection> configInjectors =
                    new ArrayList<ConfigInjection>();
            configurables.put(configurable, configInjectors);
           
            for (Method method : configurable.getClass().getMethods()) {
                Configuration annotation =
                        method.getAnnotation(Configuration.class);
                if (annotation!=null) {                   
                    if (log.isLoggable(Level.FINEST)) {
                        log.log(Level.FINEST, "Annotated Method found:"+method);
                    }
                    String key = getCompatibleKey(annotation, method);
                    Class propertyType = getCompatibleArgument(method);
                    if (log.isLoggable(Level.FINEST)) {
                        log.log(Level.FINEST, "Method is compatable");
                    }
                   
                    ConfigInjection configInjector = null;               
                    switch (annotation.update()) {
                    case STARTUP:
                        configInjector = new StartupConfigInjector(key, configurable,
                                method, propertyType, annotation);
                        break;
                    case DYNAMIC:
                        configInjector = new DynamicConfigInjector(key, configurable,
                                method, propertyType, annotation);
                        break;
                    }

                    configInjectors.add(configInjector);
                    configInjector.setup();
                    if (log.isLoggable(Level.FINE)) {
                        log.log(Level.FINE, "Injector setup: "+configInjector);
                    }
                }
            }
        }
    }
    public void deactivateConfiguration(Object configurable) {
        List<ConfigInjection> configInjectors =
                configurables.remove(configurable);
        if (configInjectors!=null) {
            for (ConfigInjection configInjector : configInjectors) {
                configInjector.cleanup();
            }
        }
    }

    private String getCompatibleKey(Configuration annotation, Method method) {
            String key = annotation.key();
             
            //Key is not specified, take the property name from the setter
            //Note couldn't find a propery BeanUtil that does the trick easily.
            if (key.length()==0) {
                key = method.getName();
                if (key.startsWith("set") && key.length()>"set".length()) {
                    key = Character.toUpperCase(key.charAt("set".length())) +
                            key.substring("set".length()+1);
                } else {
                    if (log.isLoggable(Level.CONFIG)) {
                        log.log(Level.CONFIG, "sip.common.config_annotations_no_javabean_property_setter",
                                method.getName());
                    }
                    throw new ConfigRuntimeException("sip.common.config_annotations_activation_failed_exception");
                }
            }
           
            return key;      
    }
   
    private Class getCompatibleArgument(Method method) {
        Class propertyType = null;
        Class[] paramTypes = method.getParameterTypes();
        if (paramTypes.length == 1) {
            try {
                if (!paramTypes[0].isPrimitive()) {
                    paramTypes[0].getConstructor(String.class);
                }
                propertyType = paramTypes[0];
            } catch (NoSuchMethodException ex) {
                if (log.isLoggable(Level.CONFIG)) {
                    log.log(Level.CONFIG, "sip.common.config_annotations_in_compatable_method_signature",
                            new Object[] { method.getName(),
                            "arguments type must have have constructure with string argument" });
                }
                throw new ConfigRuntimeException("sip.common.config_annotations_activation_failed_exception");
            }
        } else {
            if (log.isLoggable(Level.CONFIG)) {
                log.log(Level.CONFIG, "sip.common.config_annotations_in_compatable_method_signature",
                        new Object[] { method.getName(),
                        "nr of arguments must be 1" });
            }
            throw new ConfigRuntimeException("sip.common.config_annotations_activation_failed_exception");
        }
        return propertyType;
    }
   
    private interface ConfigInjection {
        void setup();
        void cleanup();
    }
   
    private class StartupConfigInjector implements ConfigInjection {
        protected Object configurable;
        protected Method method;
        protected Class propertyType;
        protected Configuration annotation;
        protected String key;
        private String lastValue;

        public StartupConfigInjector(String aKey, Object aConfigurable,
                Method aMethod,
                Class aPropertyType,
                Configuration anAnnotation) {
            key = aKey;
            configurable = aConfigurable;
            method = aMethod;
            propertyType = aPropertyType;
            annotation = anAnnotation;
            lastValue = null;
        }

        public synchronized void setup() {
            String value = getCurrentConfigValue();
            inject(value, annotation.usageAtStartup());
            lastValue = value;           
        }

        public synchronized void injectChange() {
            String value = getCurrentConfigValue();
            if (isValueChanged(value)) {
                inject(value, annotation.usage());
                lastValue = value;
            }           
        }
       
        public synchronized void inject(String value, UsagePolicy usage) {           
            String resolvedValue = value;
            Object arg = null;
               
            if (value==null) {
                switch(usage) {
                case FAIL:
                    if (log.isLoggable(Level.SEVERE)) {
                        log.log(Level.SEVERE, "sip.common.config_annotations_FAIL_usage_on_missing_or_incompatible_value",
                            new Object[] { method.getName(), key, annotation.node() });
                    }
                    throw new ConfigRuntimeException("sip.common.config_annotations_FAIL_usage_exception");
                case WARN:
                    if (log.isLoggable(Level.WARNING)) {
                        log.log(Level.WARNING, "sip.common.config_annotations_WARN_usage_on_missing_or_incompatible_value",
                            new Object[] { method.getName(), key, annotation.node() });
                    }
                    break;
                case IGNORE:
                    if (log.isLoggable(Level.FINEST)) {
                        log.log(Level.FINEST, "sip.common.config_annotations_IGNORE_usage_on_missing_or_incompatible_value",
                            new Object[] { method.getName(), key, annotation.node() });
                    }
                    break;
                case DEFAULT:
                    if (log.isLoggable(Level.CONFIG)) {
                        log.log(Level.CONFIG, "sip.common.config_annotations_DEFAULT_usage_on_missing_or_incompatible_value",
                            new Object[] { method.getName(), key, annotation.node(), annotation.value() });
                    }                   
                    resolvedValue = annotation.value();
                    break;
                }
            }              

            if (resolvedValue != null)
                arg = resolveArg(resolvedValue, usage);
           
            if (arg!=null) {
                try {
                    if (log.isLoggable(Level.FINEST)) {
                        log.log(Level.FINEST, "Injecting arg "+arg+" in Method "+method);
                    }
                    method.invoke(configurable, arg);
                } catch (IllegalAccessException ex) {
                    if (log.isLoggable(Level.WARNING)) {
                        log.log(Level.WARNING, "sip.common.config_annotations_injection_failed_exception_on_invoke",
                            new Object[] { method.getName(), key, annotation.node() });
            log.log(Level.WARNING, ex.getMessage(), ex);
                    }                   
                } catch (IllegalArgumentException ex) {
                    if (log.isLoggable(Level.WARNING)) {
                        log.log(Level.WARNING, "sip.common.config_annotations_injection_failed_exception_on_invoke",
                            new Object[] { method.getName(), key, annotation.node() });
            log.log(Level.WARNING, ex.getMessage(), ex);
                    }                   
                } catch (InvocationTargetException ex) {
                    if (log.isLoggable(Level.WARNING)) {
                        log.log(Level.WARNING, "sip.common.config_annotations_injection_failed_exception_on_invoke",
                            new Object[] { method.getName(), key, annotation.node() });
            log.log(Level.WARNING, ex.getMessage(), ex);
                    }                   
                }
            }
        }

        protected String getCurrentConfigValue() {
            String value = null;
            String node = annotation.node();
            node = stripTrailingSlash(node);
           
            if (node!=null && annotation.node().length() > 0) {
                value = ConfigFactory.getConfig().get(annotation.node(), key);
            } else {
                value = ConfigFactory.getConfig().get(key);
            }

            return value;
        }
       
        protected String stripTrailingSlash(String str) {
            String stripped = str!=null && str.endsWith("/") ?
                str.substring(str.length()-1): str;
            return stripped;
        }
       
        protected boolean isValueChanged(String aValue) {
            /* Irrespective of lastValue, we shouldn't allow setting of null values
             * So if value changes from 'x' -> null, do not inject
             */
            return aValue!=null ? !aValue.equals(lastValue) : false;           
        }

        protected Object resolveArg(String value, UsagePolicy usage) throws IllegalArgumentException, SecurityException {
            Object arg = null;
            boolean done = false;

            while (!done) {
                try {
                    Class argType = !propertyType.isPrimitive() ?
                        propertyType :
                        getWrapperForPrimitiveArg();
                    if (argType!=Character.class) {
                        arg = argType.getConstructor(
                            String.class).newInstance(value);
                    } else {
                        //Special case for Character, who doesn't have a
                        //constructor taking a String as argument.
                        //We take the first Character of the value.
                        arg = value.charAt(0);
                    }
                    done = true;
                } catch (Exception ex) {
                    switch(usage) {
                    case FAIL:
                        done=true;
                        if (log.isLoggable(Level.SEVERE)) {
                            log.log(Level.SEVERE, "sip.common.config_annotations_FAIL_usage_on_missing_or_incompatible_value",
                                new Object[] { method.getName(), key, annotation.node(), value });
              log.log(Level.SEVERE, ex.getMessage(), ex);
                        }
                        throw new ConfigRuntimeException("sip.common.config_annotations_FAIL_usage_exception");
                    case WARN:
                        done=true;
                        if (log.isLoggable(Level.WARNING)) {
                            log.log(Level.WARNING, "sip.common.config_annotations_WARN_usage_on_missing_or_incompatible_value",
                                new Object[] { method.getName(), key, annotation.node(), value });
              log.log(Level.WARNING, ex.getMessage(), ex);
                        }
                        break;
                    case IGNORE:
                        done=true;
                        if (log.isLoggable(Level.FINEST)) {
                            log.log(Level.FINEST, "sip.common.config_annotations_IGNORE_usage_on_missing_or_incompatible_value",
                                new Object[] { method.getName(), key, annotation.node(), value });
                            log.log(Level.FINEST, ex.getMessage(), ex);
                        }
                        break;
                    case DEFAULT:
                        if (value!=annotation.value()) /* intentional ref compare */ {
                            if (log.isLoggable(Level.CONFIG)) {
                                log.log(Level.CONFIG, "sip.common.config_annotations_DEFAULT_usage_on_missing_or_incompatible_value",
                                    new Object[] { method.getName(), key, annotation.node(), annotation.value(), value });
                log.log(Level.CONFIG, ex.getMessage(), ex);
                            }                   
                            value = annotation.value();
                        } else {
                            done = true;
                        }
                        break;
                    }
                }
            }
           
            return arg;
        }

        private Class getWrapperForPrimitiveArg() {
            Class wrapper = null;
           
            if (propertyType==Boolean.TYPE) {
                wrapper = Boolean.class;
            } else if (propertyType==Byte.TYPE) {
                wrapper = Byte.class;
            } else if (propertyType==Short.TYPE) {
                wrapper = Short.class;
            } else if (propertyType==Integer.TYPE) {
                wrapper = Integer.class;
            } else if (propertyType==Long.TYPE) {
                wrapper = Long.class;
            } else if (propertyType==Float.TYPE) {
                wrapper = Float.class;
            } else if (propertyType==Double.TYPE) {
                wrapper = Double.class;
            } else if (propertyType==Character.TYPE) {
                wrapper = Character.class;
            }
           
            return wrapper;
        }
       
        public synchronized void cleanup() {
            configurable = null;
            method = null;
            propertyType = null;
            annotation = null;
        }
       
        @Override
        public String toString() {
            StringBuffer buf = new StringBuffer("ConfigInjection: ");
            buf.append(getClass().getName());
            buf.append(" {key=");
            buf.append(key);
            buf.append(", node=");
            buf.append(annotation.node());
            buf.append(", method=");
            buf.append(method.getName());
            buf.append(", property type=");
            buf.append(propertyType.getName());
            buf.append("}");
           
            return buf.toString();
        }
    }
   
    private class DynamicConfigInjector extends StartupConfigInjector
            implements ConfigChangeListener {
       
        private DynamicConfigInjector(String aKey, Object aConfigurable,
                Method aMethod,
                Class aPropertyType,
                Configuration anAnnotation) {
            super(aKey, aConfigurable, aMethod, aPropertyType, anAnnotation);
        }

        @Override
        public synchronized void setup() {
            ConfigFactory.instance().registerConfigChangeListener(
                    stripTrailingSlash(annotation.node()), this);
            super.setup();
        }
       
        @Override
        public synchronized void cleanup() {
            ConfigFactory.instance().deregisterConfigChangeListener(this);
            super.cleanup();
        }       

        public void handleConfigEvent(ConfigAddEvent event) {
            injectChange();
        }

        public void handleConfigEvent(ConfigUpdateEvent event) {
            injectChange();
        }

        public void handleConfigEvent(ConfigRemoveEvent event) {
            injectChange();
        }       
    }   
}
TOP

Related Classes of com.ericsson.ssa.config.annotations.ConfigurationAnnotationIntrospector$DynamicConfigInjector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.