You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@brooklyn.apache.org by neykov <gi...@git.apache.org> on 2017/01/03 17:11:26 UTC

[GitHub] brooklyn-server pull request #492: Allow a user to add or override deseriali...

Github user neykov commented on a diff in the pull request:

    https://github.com/apache/brooklyn-server/pull/492#discussion_r94443161
  
    --- Diff: core/src/main/java/org/apache/brooklyn/core/mgmt/persist/DeserializingClassRenamesProvider.java ---
    @@ -20,65 +20,217 @@
     
     import java.io.IOException;
     import java.io.InputStream;
    +import java.util.Dictionary;
     import java.util.Enumeration;
    +import java.util.List;
     import java.util.Map;
     import java.util.Properties;
     
    +import org.apache.brooklyn.util.collections.MutableMap;
     import org.apache.brooklyn.util.core.ResourceUtils;
     import org.apache.brooklyn.util.exceptions.Exceptions;
     import org.apache.brooklyn.util.javalang.Reflections;
     import org.apache.brooklyn.util.stream.Streams;
    +import org.osgi.framework.Constants;
    +import org.osgi.framework.InvalidSyntaxException;
    +import org.osgi.service.cm.Configuration;
    +import org.osgi.service.cm.ConfigurationAdmin;
    +import org.slf4j.Logger;
    +import org.slf4j.LoggerFactory;
     
     import com.google.common.annotations.Beta;
    +import com.google.common.collect.ImmutableList;
     import com.google.common.collect.ImmutableMap;
    +import com.google.common.collect.Lists;
     import com.google.common.collect.Maps;
     
     @Beta
     public class DeserializingClassRenamesProvider {
     
    +    /*
    +     * This provider keeps a cache of the class-renames, which is lazily populated (see {@link #cache}. 
    +     * Calling {@link #reset()} will set this cache to null, causing it to be reloaded next time 
    +     * it is requested.
    +     * 
    +     * Loading the cache involves iterating over the {@link #loaders}, returning the union of 
    +     * the results from {@link Loader#load()}.
    +     * 
    +     * Initially, the only loader is the basic {@link ClasspathConfigLoader}.
    +     * 
    +     * However, when running in karaf the {@link OsgiConfigLoader} will be instantiated and added.
    +     * See karaf/init/src/main/resources/OSGI-INF/blueprint/blueprint.xml
    +     */
    +    
    +    private static final Logger LOG = LoggerFactory.getLogger(DeserializingClassRenamesProvider.class);
    +    private static final List<String> EXCLUDED_KEYS = ImmutableList.of("service.pid", "felix.fileinstall.filename");
    +
         public static final String DESERIALIZING_CLASS_RENAMES_PROPERTIES_PATH = "classpath://org/apache/brooklyn/core/mgmt/persist/deserializingClassRenames.properties";
    +    public static final String KARAF_DESERIALIZING_CLASS_RENAMES_PROPERTIES = "org.apache.brooklyn.classrename";
    +
    +    private static final List<Loader> loaders = Lists.newCopyOnWriteArrayList();
    +    static {
    +        loaders.add(new ClasspathConfigLoader());
    +    }
         
         private static volatile Map<String, String> cache;
    -    
    +
         @Beta
         public static Map<String, String> loadDeserializingClassRenames() {
    -        // Double-checked locking - got to use volatile or some such!
    -        if (cache == null) {
    -            synchronized (DeserializingClassRenamesProvider.class) {
    -                if (cache == null) {
    -                    cache = loadDeserializingClassRenamesCache();
    +        synchronized (DeserializingClassRenamesProvider.class) {
    +            if (cache == null) {
    +                MutableMap.Builder<String, String> builder = MutableMap.<String, String>builder();
    +                for (Loader loader : loaders) {
    +                    builder.putAll(loader.load());
                     }
    +                cache = builder.build();
    +                LOG.info("Class-renames cache loaded, size {}", cache.size());
                 }
    +            return cache;
             }
    -        return cache;
    +    }
    +
    +    /**
    +     * Handles inner classes, where the outer class has been renamed. For example:
    +     * 
    +     * {@code findMappedName("com.example.MyFoo$MySub")} will return {@code com.example.renamed.MyFoo$MySub}, if
    +     * the renamed contains {@code com.example.MyFoo: com.example.renamed.MyFoo}.
    +     */
    +    @Beta
    +    public static String findMappedName(String name) {
    +        return Reflections.findMappedNameAndLog(DeserializingClassRenamesProvider.loadDeserializingClassRenames(), name);
    +    }
    +
    +    public static void reset() {
    +        synchronized (DeserializingClassRenamesProvider.class) {
    +            cache = null;
    +        }
    +    }
    +
    +    public interface Loader {
    +        public Map<String, String> load();
         }
         
    -    private static Map<String, String> loadDeserializingClassRenamesCache() {
    -        InputStream resource = new ResourceUtils(DeserializingClassRenamesProvider.class).getResourceFromUrl(DESERIALIZING_CLASS_RENAMES_PROPERTIES_PATH);
    -        if (resource != null) {
    +    /**
    +     * Loads the class-renames from the OSGi configuration file: {@code org.apache.brooklyn.classrename.cfg}.
    +     * 
    +     * Only public for OSGi instantiation - treat as an internal class, which may change in 
    +     * future releases.
    +     * 
    +     * See http://stackoverflow.com/questions/18844987/creating-a-blueprint-bean-from-an-inner-class:
    +     * we unfortunately need to include {@code !org.apache.brooklyn.core.mgmt.persist.DeserializingClassRenamesProvider}
    +     * in the Import-Package, as the mvn plugin gets confused due to the use of this inner class
    +     * within the blueprint.xml.
    +     * 
    +     * @see {@link DeserializingClassRenamesProvider#KARAF_DESERIALIZING_CLASS_RENAMES_PROPERTIES}
    +     */
    +    public static class OsgiConfigLoader implements Loader {
    +        private ConfigurationAdmin configAdmin;
    +
    +        public OsgiConfigLoader() {
    +            LOG.trace("OsgiConfigLoader instance created");
    +        }
    +
    +        // For injection as OSGi bean
    +        public void setConfigAdmin(ConfigurationAdmin configAdmin) {
    +            this.configAdmin = configAdmin;
    +        }
    +
    +        // Called by OSGi
    +        public void init() {
    +            LOG.trace("DeserializingClassRenamesProvider.OsgiConfigLoader.init: registering loader");
    +            DeserializingClassRenamesProvider.loaders.add(this);
    +            DeserializingClassRenamesProvider.reset();
    +        }
    +
    +        // Called by OSGi
    +        public void destroy() {
    +            LOG.trace("DeserializingClassRenamesProvider.OsgiConfigLoader.destroy: unregistering loader");
    +            boolean removed = DeserializingClassRenamesProvider.loaders.remove(this);
    +            if (removed) {
    +                DeserializingClassRenamesProvider.reset();
    +            }
    +        }
    +
    +        // Called by OSGi when configuration changes
    +        public void updateProperties(Map properties) {
    +            LOG.debug("DeserializingClassRenamesProvider.OsgiConfigLoader.updateProperties: clearing cache, so class-renames will be reloaded");
    +            DeserializingClassRenamesProvider.reset();
    +        }
    +
    +        @Override
    +        public Map<String, String> load() {
    +            if (configAdmin == null) {
    +                LOG.warn("No OSGi configuration-admin available - cannot load {}.cfg", KARAF_DESERIALIZING_CLASS_RENAMES_PROPERTIES);
    +                return ImmutableMap.of();
    +            }
    +            
    +            String filter = '(' + Constants.SERVICE_PID + '=' + KARAF_DESERIALIZING_CLASS_RENAMES_PROPERTIES + ')';
    --- End diff --
    
    @geomacy I believe `updateCallback` is not called on the initial creation of the bean, only after configuration changes subsequently. This will cause the properties to be loaded only after someone goes and edits the file post-boot.


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastructure@apache.org or file a JIRA ticket
with INFRA.
---