sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sseif...@apache.org
Subject svn commit: r1757657 - in /sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl: ConfigurationBuilderImpl.java ConfigurationProxy.java
Date Thu, 25 Aug 2016 11:03:44 GMT
Author: sseifert
Date: Thu Aug 25 11:03:44 2016
New Revision: 1757657

URL: http://svn.apache.org/viewvc?rev=1757657&view=rev
Log:
SLING-5982 refactor dynamic proxy handling to separate class and add a caching invocation
handler

Added:
    sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationProxy.java
  (with props)
Modified:
    sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationBuilderImpl.java

Modified: sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationBuilderImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationBuilderImpl.java?rev=1757657&r1=1757656&r2=1757657&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationBuilderImpl.java
(original)
+++ sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationBuilderImpl.java
Thu Aug 25 11:03:44 2016
@@ -18,21 +18,14 @@
  */
 package org.apache.sling.contextaware.config.impl;
 
-import java.lang.reflect.Array;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.stream.Collectors;
 
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.contextaware.config.ConfigurationBuilder;
-import org.apache.sling.contextaware.config.ConfigurationResolveException;
 import org.apache.sling.contextaware.config.resource.ConfigurationResourceResolver;
 
 class ConfigurationBuilderImpl implements ConfigurationBuilder {
@@ -94,7 +87,7 @@ class ConfigurationBuilderImpl implement
         if (clazz == ValueMap.class) {
             return (T)ResourceUtil.getValueMap(configResource);
         }
-        return convertPropsToClass(configResource, clazz);
+        return ConfigurationProxy.get(configResource, clazz);
     }
 
     @SuppressWarnings("unchecked")
@@ -109,77 +102,10 @@ class ConfigurationBuilderImpl implement
                         .collect(Collectors.toList());
             }
             return configResources.stream()
-                .map(resource -> convertPropsToClass(resource, clazz))
+                .map(resource -> ConfigurationProxy.get(resource, clazz))
                 .collect(Collectors.toList());
         }
         return Collections.emptyList();
     }
     
-    @SuppressWarnings("unchecked")
-    private <T> T convertPropsToClass(Resource resource, Class<T> clazz) {
-        
-        // only annotation interface classes are supported
-        if (!(clazz.isInterface() && clazz.isAnnotation())) {
-            throw new ConfigurationResolveException("Annotation interface class expected:
" + clazz.getName());
-        }
-
-        // create dynamic proxy for annotation class accessing underlying resource properties
-        return (T)Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz },
-            new InvocationHandler() {
-                @Override
-                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
-                    String propName = getInterfacePropertyName(method);
-                    if (propName == null) {
-                        return null;
-                    }
-
-                    // check for nested configuration classes
-                    Class<?> targetType = method.getReturnType();
-                    Class<?> componentType = method.getReturnType();
-                    boolean isArray = targetType.isArray();
-                    if (isArray) {
-                        componentType = targetType.getComponentType();
-                    }
-                    if (componentType.isInterface() && componentType.isAnnotation())
{
-                        Resource childResource = resource != null ? resource.getChild(propName)
: null;
-                        if (isArray) {
-                            Iterable<Resource> listItemResources = childResource !=
null ? childResource.getChildren() : new ArrayList<>();
-                            List<Object> listItems = new ArrayList<Object>();
-                            for (Resource listItemResource : listItemResources) {
-                                listItems.add(convertPropsToClass(listItemResource, componentType));
-                            }
-                            return listItems.toArray((Object[])Array.newInstance(componentType,
listItems.size()));
-                        }
-                        else {
-                            return convertPropsToClass(childResource, componentType);
-                        }
-                    }
-                    
-                    // detect default value
-                    Object defaultValue = method.getDefaultValue();
-                    if (defaultValue == null && targetType.isPrimitive() &&
!targetType.isArray()) {
-                        // get default value for primitive data type (use hack via array)
-                        defaultValue = Array.get(Array.newInstance(targetType, 1), 0);
-                    }
-                    
-                    // get value from valuemap with given type/default value
-                    ValueMap props = ResourceUtil.getValueMap(resource);
-                    Object value;
-                    if (defaultValue != null) {
-                        value = props.get(propName, defaultValue);
-                    }
-                    else {
-                        value = props.get(propName, targetType);
-                    }
-                    return value;
-                    
-                }
-            });
-    }
-
-    private static String getInterfacePropertyName(Method md) {
-        // TODO: support all the escaping mechanisms.
-        return md.getName().replace('_', '.');
-    }
-    
 }

Added: sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationProxy.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationProxy.java?rev=1757657&view=auto
==============================================================================
--- sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationProxy.java
(added)
+++ sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationProxy.java
Thu Aug 25 11:03:44 2016
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.contextaware.config.impl;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.contextaware.config.ConfigurationResolveException;
+
+
+/**
+ * Maps the property of a resource to a dynamic proxy object implementing
+ * the annotation class defining the configuration parameters.
+ * Nested configurations with annotation classes referencing other annotation classes are
also supported.
+ */
+final class ConfigurationProxy {
+    
+    private ConfigurationProxy() {
+        // static methods only
+    }
+    
+    /**
+     * Get dynamic proxy for given resources's properties mappend to given annotation class.
+     * @param resource Resource
+     * @param clazz Annotation class
+     * @return Dynamic proxy object
+     */
+    @SuppressWarnings("unchecked")
+    public @Nonnull static <T> T get(@Nullable Resource resource, @Nonnull Class<T>
clazz) {
+        
+        // only annotation interface classes are supported
+        if (!(clazz.isInterface() && clazz.isAnnotation())) {
+            throw new ConfigurationResolveException("Annotation interface class expected:
" + clazz.getName());
+        }
+
+        // create dynamic proxy for annotation class accessing underlying resource properties
+        // wrap in caching invocation handler so client code can call all methods multiple
times
+        // without having to worry about performance
+        return (T)Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz },
+                new CachingInvocationHandler(new DynamicProxyInvocationHandler(resource)));
+    }
+
+    /**
+     * Maps resource properties to annotation class proxy, and support nested configurations.
+     */
+    private static class DynamicProxyInvocationHandler implements InvocationHandler {
+        
+        private final Resource resource;
+        
+        private DynamicProxyInvocationHandler(Resource resource) {
+            this.resource = resource;
+        }
+        
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) {
+            String propName = getInterfacePropertyName(method);
+
+            // check for nested configuration classes
+            Class<?> targetType = method.getReturnType();
+            Class<?> componentType = method.getReturnType();
+            boolean isArray = targetType.isArray();
+            if (isArray) {
+                componentType = targetType.getComponentType();
+            }
+            if (componentType.isInterface() && componentType.isAnnotation()) {
+                Resource childResource = resource != null ? resource.getChild(propName) :
null;
+                if (isArray) {
+                    Iterable<Resource> listItemResources = childResource != null ?
childResource.getChildren() : Collections.emptyList();
+                    List<Object> listItems = new ArrayList<Object>();
+                    for (Resource listItemResource : listItemResources) {
+                        listItems.add(get(listItemResource, componentType));
+                    }
+                    return listItems.toArray((Object[])Array.newInstance(componentType, listItems.size()));
+                }
+                else {
+                    return get(childResource, componentType);
+                }
+            }
+            
+            // detect default value
+            Object defaultValue = method.getDefaultValue();
+            if (defaultValue == null && targetType.isPrimitive() && !targetType.isArray())
{
+                // get default value for primitive data type (use hack via array)
+                defaultValue = Array.get(Array.newInstance(targetType, 1), 0);
+            }
+            
+            // get value from valuemap with given type/default value
+            ValueMap props = ResourceUtil.getValueMap(resource);
+            Object value;
+            if (defaultValue != null) {
+                value = props.get(propName, defaultValue);
+            }
+            else {
+                value = props.get(propName, targetType);
+            }
+            return value;
+            
+        }
+        
+    }
+    
+    private static String getInterfacePropertyName(Method md) {
+        // TODO: support all the escaping mechanisms.
+        return md.getName().replace('_', '.');
+    }
+        
+    /**
+     * Invocation handler that caches all results for each method name, and returns
+     * the result from cache on next invocation.
+     */
+    private static class CachingInvocationHandler implements InvocationHandler {
+        
+        private final InvocationHandler delegate;
+        private final Map<String, Object> results = new HashMap<>();
+        private static final Object NULL_OBJECT = new Object();
+        
+        public CachingInvocationHandler(InvocationHandler delegate) {
+            this.delegate = delegate;
+        }
+
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
+            String key = method.getName();
+            Object result = results.get(key);
+            if (result == null) {
+                result = delegate.invoke(proxy, method, args);
+                if (result == null) {
+                    result = NULL_OBJECT;
+                }
+                results.put(key,  result);
+            }
+            if (result == NULL_OBJECT) {
+                return null;
+            }
+            else {
+                return result;
+            }
+        }
+        
+    }
+    
+}

Propchange: sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationProxy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationProxy.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Thu Aug 25 11:03:44 2016
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author

Propchange: sling/trunk/contrib/extensions/contextaware-config/impl/src/main/java/org/apache/sling/contextaware/config/impl/ConfigurationProxy.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message