You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@deltaspike.apache.org by rm...@apache.org on 2016/11/09 11:52:33 UTC

[4/5] deltaspike git commit: config proxy basic implementation

config proxy basic implementation


Project: http://git-wip-us.apache.org/repos/asf/deltaspike/repo
Commit: http://git-wip-us.apache.org/repos/asf/deltaspike/commit/b81d4987
Tree: http://git-wip-us.apache.org/repos/asf/deltaspike/tree/b81d4987
Diff: http://git-wip-us.apache.org/repos/asf/deltaspike/diff/b81d4987

Branch: refs/heads/master
Commit: b81d4987f218ee2fc37835787c80bd8db7f5c468
Parents: 3f8252e
Author: rmannibucau <rm...@apache.org>
Authored: Mon Nov 7 12:33:36 2016 +0100
Committer: rmannibucau <rm...@apache.org>
Committed: Mon Nov 7 12:33:36 2016 +0100

----------------------------------------------------------------------
 .../core/api/config/Configuration.java          |  50 ++++++
 .../spi/config/BaseConfigPropertyProducer.java  |  13 +-
 .../impl/config/ConfigurationExtension.java     |  38 ++++-
 .../config/ProxyConfigurationLifecycle.java     | 157 +++++++++++++++++++
 .../core/api/config/injectable/ConfigBean.java  |  83 ++++++++++
 .../InjectableConfigPropertyTest.java           |  67 +++++---
 6 files changed, 383 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/deltaspike/blob/b81d4987/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/Configuration.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/Configuration.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/Configuration.java
new file mode 100644
index 0000000..5c3e33d
--- /dev/null
+++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/api/config/Configuration.java
@@ -0,0 +1,50 @@
+/*
+ * 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.deltaspike.core.api.config;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.concurrent.TimeUnit;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+/**
+ * Marker to let DeltaSpike pick this interface and create a proxy getting values
+ * from the configuration.
+ *
+ * The underlying Bean should be normal-scoped.
+ */
+@Target(TYPE)
+@Retention(RUNTIME)
+@Documented
+public @interface Configuration
+{
+    /**
+     * @return the duration while the value is not reloaded.
+     */
+    long cacheFor() default -1;
+
+    /**
+     * @return the duration unit for {@see cacheFor()}.
+     */
+    TimeUnit cacheUnit() default SECONDS;
+}

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/b81d4987/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/spi/config/BaseConfigPropertyProducer.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/spi/config/BaseConfigPropertyProducer.java b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/spi/config/BaseConfigPropertyProducer.java
index 7cae68d..48517dc 100644
--- a/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/spi/config/BaseConfigPropertyProducer.java
+++ b/deltaspike/core/api/src/main/java/org/apache/deltaspike/core/spi/config/BaseConfigPropertyProducer.java
@@ -165,6 +165,17 @@ public abstract class BaseConfigPropertyProducer
                            final Class<? extends ConfigResolver.Converter> converterType,
                            final String parameterizedBy, final boolean projectStageAware, final boolean evaluate)
     {
+        final ConfigResolver.TypedResolver<T> resolver = asResolver(
+                key, stringDefault, ipCls, converterType, parameterizedBy, projectStageAware, evaluate);
+        return resolver.getValue();
+    }
+
+    public <T> ConfigResolver.TypedResolver<T> asResolver(final String key, final String stringDefault,
+                                                          final Type ipCls,
+                                                          final Class<? extends ConfigResolver.Converter> converterType,
+                                                          final String parameterizedBy,
+                                                          final boolean projectStageAware, final boolean evaluate)
+    {
         final ConfigResolver.UntypedResolver<String> untypedResolver = ConfigResolver.resolve(key);
         final ConfigResolver.TypedResolver<T> resolver =
                 (ConfigResolver.Converter.class == converterType ?
@@ -179,6 +190,6 @@ public abstract class BaseConfigPropertyProducer
         {
             resolver.parameterizedBy(parameterizedBy);
         }
-        return resolver.evaluateVariables(evaluate).getValue();
+        return resolver.evaluateVariables(evaluate);
     }
 }

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/b81d4987/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigurationExtension.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigurationExtension.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigurationExtension.java
index 65e3f2a..e1c43d2 100644
--- a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigurationExtension.java
+++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ConfigurationExtension.java
@@ -45,10 +45,13 @@ import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.deltaspike.core.api.config.ConfigProperty;
 import org.apache.deltaspike.core.api.config.ConfigResolver;
+import org.apache.deltaspike.core.api.config.Configuration;
 import org.apache.deltaspike.core.api.config.Filter;
 import org.apache.deltaspike.core.api.config.PropertyFileConfig;
 import org.apache.deltaspike.core.api.config.Source;
 import org.apache.deltaspike.core.api.exclude.Exclude;
+import org.apache.deltaspike.core.api.literal.AnyLiteral;
+import org.apache.deltaspike.core.api.literal.DefaultLiteral;
 import org.apache.deltaspike.core.api.provider.BeanProvider;
 import org.apache.deltaspike.core.spi.activation.Deactivatable;
 import org.apache.deltaspike.core.spi.config.BaseConfigPropertyProducer;
@@ -58,6 +61,7 @@ import org.apache.deltaspike.core.spi.config.ConfigValidator;
 import org.apache.deltaspike.core.util.ClassDeactivationUtils;
 import org.apache.deltaspike.core.util.ClassUtils;
 import org.apache.deltaspike.core.util.ServiceUtils;
+import org.apache.deltaspike.core.util.bean.BeanBuilder;
 
 /**
  * This extension handles {@link org.apache.deltaspike.core.api.config.PropertyFileConfig}s
@@ -91,6 +95,7 @@ public class ConfigurationExtension implements Extension, Deactivatable
 
     private final List<Bean<? extends ConfigSource>> cdiSources = new ArrayList<Bean<? extends ConfigSource>>();
     private final List<Bean<? extends ConfigFilter>> cdiFilters = new ArrayList<Bean<? extends ConfigFilter>>();
+    private final List<Class<?>> dynamicConfigurationBeanClasses = new ArrayList<Class<?>>();
 
     @SuppressWarnings("UnusedDeclaration")
     protected void init(@Observes BeforeBeanDiscovery beforeBeanDiscovery)
@@ -127,9 +132,24 @@ public class ConfigurationExtension implements Extension, Deactivatable
         propertyFileConfigClasses.add(pcsClass);
     }
 
+    public void findDynamicConfigurationBeans(@Observes ProcessAnnotatedType<?> pat)
+    {
+        if (!pat.getAnnotatedType().isAnnotationPresent(Configuration.class))
+        {
+            return;
+        }
+        final Class<?> javaClass = pat.getAnnotatedType().getJavaClass();
+        if (!javaClass.isInterface())
+        {
+            return;
+        }
+        dynamicConfigurationBeanClasses.add(javaClass);
+    }
+
     public void findSources(@Observes ProcessBean<? extends ConfigSource> source)
     {
-        if (!source.getAnnotated().isAnnotationPresent(Source.class)) {
+        if (!source.getAnnotated().isAnnotationPresent(Source.class))
+        {
             return;
         }
         cdiSources.add(source.getBean());
@@ -137,7 +157,8 @@ public class ConfigurationExtension implements Extension, Deactivatable
 
     public void findFilters(@Observes ProcessBean<? extends ConfigFilter> filter)
     {
-        if (!filter.getAnnotated().isAnnotationPresent(Filter.class)) {
+        if (!filter.getAnnotated().isAnnotationPresent(Filter.class))
+        {
             return;
         }
         cdiFilters.add(filter.getBean());
@@ -162,12 +183,23 @@ public class ConfigurationExtension implements Extension, Deactivatable
         }
     }
 
-    public void addDynamicBean(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager bm)
+    public void addDynamicBeans(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager bm)
     {
         if (dynamicProducer != null && !dynamicConfigTypes.isEmpty())
         {
             afterBeanDiscovery.addBean(new DynamicBean(dynamicProducer, dynamicConfigTypes));
         }
+        for (final Class<?> proxyType : dynamicConfigurationBeanClasses)
+        {
+            afterBeanDiscovery.addBean(new BeanBuilder(null)
+                    .types(proxyType, Object.class)
+                    .qualifiers(new DefaultLiteral(), new AnyLiteral())
+                    .beanLifecycle(new ProxyConfigurationLifecycle(proxyType))
+                    .scope(ApplicationScoped.class)
+                    .passivationCapable(true)
+                    .beanClass(proxyType)
+                    .create());
+        }
     }
 
     @SuppressWarnings("UnusedDeclaration")

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/b81d4987/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ProxyConfigurationLifecycle.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ProxyConfigurationLifecycle.java b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ProxyConfigurationLifecycle.java
new file mode 100644
index 0000000..645e227
--- /dev/null
+++ b/deltaspike/core/impl/src/main/java/org/apache/deltaspike/core/impl/config/ProxyConfigurationLifecycle.java
@@ -0,0 +1,157 @@
+/*
+ * 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.deltaspike.core.impl.config;
+
+import org.apache.deltaspike.core.api.config.ConfigProperty;
+import org.apache.deltaspike.core.api.config.ConfigResolver;
+import org.apache.deltaspike.core.api.config.Configuration;
+import org.apache.deltaspike.core.spi.config.BaseConfigPropertyProducer;
+import org.apache.deltaspike.core.util.metadata.builder.ContextualLifecycle;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+class ProxyConfigurationLifecycle implements ContextualLifecycle
+{
+    private Class<?>[] api;
+
+    ProxyConfigurationLifecycle(final Class<?> proxyType)
+    {
+        this.api = new Class<?>[]{proxyType};
+    }
+
+    @Override
+    public Object create(final Bean bean, final CreationalContext creationalContext)
+    {
+        // TODO: support partialbean binding? can make sense for virtual properties + would integrate with jcache
+        // we'd need to add @PartialBeanBinding on a bean created from ConfigurationHandler
+        // detection can just be a loadClass of this API
+        // for now: waiting for user request for it
+
+        final Configuration configuration = api[0].getAnnotation(Configuration.class);
+        final long cacheFor = configuration.cacheFor();
+        return Proxy.newProxyInstance(
+                Thread.currentThread().getContextClassLoader(), api,
+                new ConfigurationHandler(cacheFor <= 0 ? -1 : configuration.cacheUnit().toMillis(cacheFor)));
+    }
+
+    @Override
+    public void destroy(final Bean bean, final Object instance, final CreationalContext creationalContext)
+    {
+        // no-op
+    }
+
+    private static final class ConfigurationHandler implements InvocationHandler
+    {
+        private final BaseConfigPropertyProducer delegate = new BaseConfigPropertyProducer()
+        {
+        };
+
+        private final ConcurrentMap<Method, ConfigResolver.TypedResolver<?>> resolvers =
+                new ConcurrentHashMap<Method, ConfigResolver.TypedResolver<?>>();
+        private final long cacheMs;
+
+        private ConfigurationHandler(final long cacheMs)
+        {
+            this.cacheMs = cacheMs;
+        }
+
+        @Override
+        public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable
+        {
+            if (Object.class == method.getDeclaringClass())
+            {
+                try
+                {
+                    return method.invoke(this, args);
+                }
+                catch (final InvocationTargetException ite)
+                {
+                    throw ite.getCause();
+                }
+            }
+
+            ConfigResolver.TypedResolver<?> typedResolver = resolvers.get(method);
+            if (typedResolver == null)
+            {
+                final ConfigProperty annotation = method.getAnnotation(ConfigProperty.class);
+                if (annotation == null)
+                {
+                    throw new UnsupportedOperationException(
+                            method + " doesn't have @ConfigProperty and therefore is illegal");
+                }
+
+                // handle primitive bridge there (cdi doesnt support primitives but no reason our proxies don't)
+                Class<?> returnType = method.getReturnType();
+                if (int.class == returnType)
+                {
+                    returnType = Integer.class;
+                }
+                else if (long.class == returnType)
+                {
+                    returnType = Long.class;
+                }
+                else if (boolean.class == returnType)
+                {
+                    returnType = Boolean.class;
+                }
+                else if (short.class == returnType)
+                {
+                    returnType = Short.class;
+                }
+                else if (byte.class == returnType)
+                {
+                    returnType = Byte.class;
+                }
+                else if (float.class == returnType)
+                {
+                    returnType = Float.class;
+                }
+                else if (double.class == returnType)
+                {
+                    returnType = Double.class;
+                }
+
+                typedResolver = delegate.asResolver(
+                        annotation.name(), annotation.defaultValue(), returnType,
+                        annotation.converter(), annotation.parameterizedBy(),
+                        annotation.projectStageAware(), annotation.evaluateVariables());
+                if (cacheMs > 0)
+                {
+                    typedResolver.cacheFor(MILLISECONDS, cacheMs);
+                }
+
+                final ConfigResolver.TypedResolver<?> existing = resolvers.putIfAbsent(method, typedResolver);
+                if (existing != null)
+                {
+                    typedResolver = existing;
+                }
+            }
+            return typedResolver.getValue();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/b81d4987/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/ConfigBean.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/ConfigBean.java b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/ConfigBean.java
new file mode 100644
index 0000000..40bcb45
--- /dev/null
+++ b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/ConfigBean.java
@@ -0,0 +1,83 @@
+/*
+ * 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.deltaspike.test.core.api.config.injectable;
+
+import org.apache.deltaspike.core.api.config.ConfigProperty;
+import org.apache.deltaspike.core.api.config.Configuration;
+
+import java.net.URL;
+import java.util.List;
+
+@Configuration
+public interface ConfigBean
+{
+    @ConfigProperty(name = "configProperty1")
+    int intProperty1();
+
+    @ConfigProperty(name = "configProperty1", defaultValue = "myDefaultValue")
+    String stringProperty3Filled();
+
+    @ConfigProperty(name = "nonexistingProperty", defaultValue = "myDefaultValue")
+    String stringProperty3Defaulted();
+
+    @ConfigProperty(name = "nonexistingProperty", defaultValue = "42")
+    Integer intProperty4Defaulted();
+
+    @ConfigProperty(name = "configProperty1")
+    Boolean booleanPropertyNull();
+
+    @ConfigProperty(name = "configProperty1", defaultValue = "false")
+    boolean booleanPropertyFalse();
+
+    @ConfigProperty(name = "configPropertyTrue1")
+    Boolean booleanPropertyTrue1();
+
+    @ConfigProperty(name = "configPropertyTrue2")
+    Boolean booleanPropertyTrue2();
+
+    @ConfigProperty(name = "configPropertyTrue3")
+    Boolean booleanPropertyTrue3();
+
+    @ConfigProperty(name = "configPropertyTrue4")
+    Boolean booleanPropertyTrue4();
+
+    @ConfigProperty(name = "configPropertyTrue5")
+    Boolean booleanPropertyTrue5();
+
+    @ConfigProperty(name = "configPropertyTrue6")
+    Boolean booleanPropertyTrue6();
+
+    @ConfigProperty(name = "configPropertyTrue7")
+    Boolean booleanPropertyTrue7();
+
+    @ConfigProperty(name = "configPropertyTrue8")
+    Boolean booleanPropertyTrue8();
+
+    @ConfigProperty(name = "testDbConfig")
+    String dbConfig();
+
+    @ConfigProperty(name = "urlList", converter = SettingsBean.UrlList.class, defaultValue = "http://localhost,http://127.0.0.1")
+    List<URL> urlList();
+
+    @ConfigProperty(name = "urlListFromProperties", converter = SettingsBean.UrlList.class)
+    List<URL> urlListFromProperties();
+
+    @ConfigProperty(name = "custom-source.test")
+    String customSourceValue();
+}

http://git-wip-us.apache.org/repos/asf/deltaspike/blob/b81d4987/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/InjectableConfigPropertyTest.java
----------------------------------------------------------------------
diff --git a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/InjectableConfigPropertyTest.java b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/InjectableConfigPropertyTest.java
index aea89ed..bb1e573 100644
--- a/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/InjectableConfigPropertyTest.java
+++ b/deltaspike/core/impl/src/test/java/org/apache/deltaspike/test/core/api/config/injectable/InjectableConfigPropertyTest.java
@@ -31,7 +31,6 @@ import org.jboss.shrinkwrap.api.ShrinkWrap;
 import org.jboss.shrinkwrap.api.asset.EmptyAsset;
 import org.jboss.shrinkwrap.api.spec.JavaArchive;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
-import org.junit.Assert;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -41,6 +40,7 @@ import org.apache.deltaspike.test.core.api.config.injectable.numberconfig.Number
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 
 @RunWith(Arquillian.class)
@@ -73,41 +73,41 @@ public class InjectableConfigPropertyTest
     {
         SettingsBean settingsBean = BeanProvider.getContextualReference(SettingsBean.class, false);
 
-        Assert.assertEquals(14, settingsBean.getProperty1());
-        Assert.assertEquals(7L, settingsBean.getProperty2());
-        Assert.assertEquals(-7L, settingsBean.getInverseProperty2());
+        assertEquals(14, settingsBean.getProperty1());
+        assertEquals(7L, settingsBean.getProperty2());
+        assertEquals(-7L, settingsBean.getInverseProperty2());
 
         // also check the ones with defaultValue
-        Assert.assertEquals("14", settingsBean.getProperty3Filled());
-        Assert.assertEquals("myDefaultValue", settingsBean.getProperty3Defaulted());
-        Assert.assertEquals(42, settingsBean.getProperty4Defaulted());
+        assertEquals("14", settingsBean.getProperty3Filled());
+        assertEquals("myDefaultValue", settingsBean.getProperty3Defaulted());
+        assertEquals(42, settingsBean.getProperty4Defaulted());
 
-        Assert.assertEquals("some setting for prodDB", settingsBean.getDbConfig());
+        assertEquals("some setting for prodDB", settingsBean.getDbConfig());
     }
 
     @Test
     public void testBooleanPropertyInjection()
     {
         SettingsBean settingsBean = BeanProvider.getContextualReference(SettingsBean.class, false);
-        Assert.assertEquals(Boolean.FALSE, settingsBean.getBooleanPropertyFalse());
-
-        Assert.assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue1());
-        Assert.assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue2());
-        Assert.assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue3());
-        Assert.assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue4());
-        Assert.assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue5());
-        Assert.assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue6());
-        Assert.assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue7());
-        Assert.assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue8());
+        assertEquals(Boolean.FALSE, settingsBean.getBooleanPropertyFalse());
+
+        assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue1());
+        assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue2());
+        assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue3());
+        assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue4());
+        assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue5());
+        assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue6());
+        assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue7());
+        assertEquals(Boolean.TRUE, settingsBean.getBooleanPropertyTrue8());
     }
 
     @Test
     public void testNmberConfigInjection()
     {
         NumberConfiguredBean numberBean = BeanProvider.getContextualReference(NumberConfiguredBean.class, false);
-        Assert.assertNull(numberBean.getPropertyNonexisting());
-        Assert.assertEquals(Float.valueOf(123.45f), numberBean.getPropertyFromConfig());
-        Assert.assertEquals(Float.valueOf(42.42f), numberBean.getPropertyNonexistingDefaulted());
+        assertNull(numberBean.getPropertyNonexisting());
+        assertEquals(Float.valueOf(123.45f), numberBean.getPropertyFromConfig());
+        assertEquals(Float.valueOf(42.42f), numberBean.getPropertyNonexistingDefaulted());
     }
 
     @Test
@@ -124,4 +124,29 @@ public class InjectableConfigPropertyTest
         SettingsBean settingsBean = BeanProvider.getContextualReference(SettingsBean.class, false);
         assertEquals("value", settingsBean.getCustomSourceValue());
     }
+
+    @Test
+    public void proxy() throws MalformedURLException
+    {
+        ConfigBean settingsBean = BeanProvider.getContextualReference(ConfigBean.class);
+
+        assertEquals(14, settingsBean.intProperty1());
+        assertEquals("14", settingsBean.stringProperty3Filled());
+        assertEquals("myDefaultValue", settingsBean.stringProperty3Defaulted());
+        assertEquals(42, settingsBean.intProperty4Defaulted().intValue());
+
+        assertEquals("some setting for prodDB", settingsBean.dbConfig());
+        assertEquals(Boolean.FALSE, settingsBean.booleanPropertyFalse());
+        assertEquals(Boolean.TRUE, settingsBean.booleanPropertyTrue1());
+        assertEquals(Boolean.TRUE, settingsBean.booleanPropertyTrue2());
+        assertEquals(Boolean.TRUE, settingsBean.booleanPropertyTrue3());
+        assertEquals(Boolean.TRUE, settingsBean.booleanPropertyTrue4());
+        assertEquals(Boolean.TRUE, settingsBean.booleanPropertyTrue5());
+        assertEquals(Boolean.TRUE, settingsBean.booleanPropertyTrue6());
+        assertEquals(Boolean.TRUE, settingsBean.booleanPropertyTrue7());
+        assertEquals(Boolean.TRUE, settingsBean.booleanPropertyTrue8());
+        assertEquals(asList(new URL("http://localhost"), new URL("http://127.0.0.1")), settingsBean.urlList());
+        assertEquals(singletonList(new URL("http://127.0.0.2")), settingsBean.urlListFromProperties());
+        assertEquals("value", settingsBean.customSourceValue());
+    }
 }