http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/test/java/org/apache/tamaya/spi/FilterContextTest.java ---------------------------------------------------------------------- diff --git a/code/compat/src/test/java/org/apache/tamaya/spi/FilterContextTest.java b/code/compat/src/test/java/org/apache/tamaya/spi/FilterContextTest.java new file mode 100644 index 0000000..e272b9a --- /dev/null +++ b/code/compat/src/test/java/org/apache/tamaya/spi/FilterContextTest.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.tamaya.spi; + +import org.apache.tamaya.spi.FilterContext; +import org.junit.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * Tests for {@link org.apache.tamaya.spi.FilterContext}. + */ +public class FilterContextTest { + + @Test(expected = NullPointerException.class) + public void constructorRequiresNonNullPropertyValueTwoParameterVariant() { + new org.apache.tamaya.spi.FilterContext(null, new TestConfigContext()); + } + + @Test(expected = NullPointerException.class) + public void constructorRequiresNonNullConfigurationContextTwoParameterVariant() { + new org.apache.tamaya.spi.FilterContext(PropertyValue.of("a", "b", "s"), null); + } + + @SuppressWarnings("unchecked") + @Test(expected = NullPointerException.class) + public void constructorRequiresNonNullPropertyValueThreeParameterVariant() { + new org.apache.tamaya.spi.FilterContext(null, Collections.EMPTY_MAP, new TestConfigContext()); + } + + @SuppressWarnings("unchecked") + @Test(expected = NullPointerException.class) + public void constructorRequiresNonNullConfigurationContextThreeParameterVariant() { + new org.apache.tamaya.spi.FilterContext(PropertyValue.of("a", "b", "s"), Collections.EMPTY_MAP, null); + } + + @Test(expected = NullPointerException.class) + public void constructorRequiresNonNullMapForConfigEntriesThreeParameterVariant() { + new org.apache.tamaya.spi.FilterContext(PropertyValue.of("a", "b", "s"), null, new TestConfigContext()); + } + + @Test + public void getKey() throws Exception { + PropertyValue val = PropertyValue.of("getKey", "v", ""); + org.apache.tamaya.spi.FilterContext ctx = new org.apache.tamaya.spi.FilterContext(val, + new HashMap<String,PropertyValue>(), new TestConfigContext()); + assertEquals(val, ctx.getProperty()); + } + + @Test + public void isSinglePropertyScoped() throws Exception { + PropertyValue val = PropertyValue.of("isSinglePropertyScoped", "v", ""); + org.apache.tamaya.spi.FilterContext ctx = new org.apache.tamaya.spi.FilterContext(val, new HashMap<String,PropertyValue>(), new TestConfigContext()); + assertEquals(false, ctx.isSinglePropertyScoped()); + ctx = new org.apache.tamaya.spi.FilterContext(val, new TestConfigContext()); + assertEquals(true, ctx.isSinglePropertyScoped()); + } + + @Test + public void getConfigEntries() throws Exception { + Map<String,PropertyValue> config = new HashMap<>(); + for(int i=0;i<10;i++) { + config.put("key-"+i, PropertyValue.of("key-"+i, "value-"+i, "test")); + } + PropertyValue val = PropertyValue.of("getConfigEntries", "v", ""); + org.apache.tamaya.spi.FilterContext ctx = new org.apache.tamaya.spi.FilterContext(val, config, new TestConfigContext()); + assertEquals(config, ctx.getConfigEntries()); + } + + @Test + public void testToString() throws Exception { + Map<String,PropertyValue> config = new HashMap<>(); + for(int i=0;i<2;i++) { + config.put("key-"+i, PropertyValue.of("key-"+i, "value-"+i, "test")); + } + PropertyValue val = PropertyValue.of("testToString", "val", "mySource"); + org.apache.tamaya.spi.FilterContext ctx = new FilterContext(val, config, new TestConfigContext()); + String toString = ctx.toString(); + + assertNotNull(toString); + assertTrue(toString.contains("FilterContext{value='PropertyValue{key='testToString', value='val', " + + "source='mySource'}', configEntries=[")); + assertTrue(toString.contains("key-0")); + assertTrue(toString.contains("key-1")); + assertTrue(toString.endsWith("}")); + } + + private static class TestConfigContext implements ConfigurationContext { + + @Override + public void addPropertySources(PropertySource... propertySources) { + + } + + @Override + public List<PropertySource> getPropertySources() { + return null; + } + + @Override + public PropertySource getPropertySource(String name) { + return null; + } + + @Override + public <T> void addPropertyConverter(TypeLiteral<T> type, PropertyConverter<T> propertyConverter) { + + } + + @Override + public Map<TypeLiteral<?>, List<PropertyConverter<?>>> getPropertyConverters() { + return null; + } + + @Override + public <T> List<PropertyConverter<T>> getPropertyConverters(TypeLiteral<T> type) { + return null; + } + + @Override + public List<PropertyFilter> getPropertyFilters() { + return null; + } + + @Override + public PropertyValueCombinationPolicy getPropertyValueCombinationPolicy() { + return null; + } + + @Override + public ConfigurationContextBuilder toBuilder() { + return null; + } + } + +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/test/java/org/apache/tamaya/spi/ServiceContextManagerTest.java ---------------------------------------------------------------------- diff --git a/code/compat/src/test/java/org/apache/tamaya/spi/ServiceContextManagerTest.java b/code/compat/src/test/java/org/apache/tamaya/spi/ServiceContextManagerTest.java new file mode 100644 index 0000000..31fb2f8 --- /dev/null +++ b/code/compat/src/test/java/org/apache/tamaya/spi/ServiceContextManagerTest.java @@ -0,0 +1,105 @@ +/* + * 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.tamaya.spi; + +import org.junit.Test; + +import java.io.IOException; +import java.net.URL; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * Additional tests for {@link ServiceContextManager}, created by atsticks on 20.08.16. + */ +public class ServiceContextManagerTest { + + @Test + public void setGetServiceContext() throws Exception { + ServiceContext prev = ServiceContextManager.getServiceContext(); + try { + MyServiceContext mine = new MyServiceContext(); + ServiceContextManager.set(mine); + assertTrue(ServiceContextManager.getServiceContext() == mine); + ServiceContextManager.set(mine); + assertTrue(ServiceContextManager.getServiceContext() == mine); + } finally { + ServiceContextManager.set(prev); + assertTrue(ServiceContextManager.getServiceContext() == prev); + } + + } + + @Test(expected = NullPointerException.class) + public void setRequiresNonNullParameter() { + ServiceContextManager.set(null); + } + + private static final class MyServiceContext implements ServiceContext{ + + @Override + public int ordinal() { + return 0; + } + + @Override + public <T> T getService(Class<T> serviceType) { + return null; + } + + @Override + public <T> T getService(Class<T> serviceType, ClassLoader classLoader) { + return getService(serviceType, ServiceContext.defaultClassLoader()); + } + + @Override + public <T> T create(Class<T> serviceType) { + return null; + } + + @Override + public <T> T create(Class<T> serviceType, ClassLoader classLoader) { + return create(serviceType, ServiceContext.defaultClassLoader()); + } + + @Override + public <T> List<T> getServices(Class<T> serviceType) { + return Collections.emptyList(); + } + + @Override + public <T> List<T> getServices(Class<T> serviceType, ClassLoader classLoader) { + return getServices(serviceType, ServiceContext.defaultClassLoader()); + } + + @Override + public Enumeration<URL> getResources(String resource, ClassLoader cl) throws IOException { + return null; + } + + @Override + public URL getResource(String resource, ClassLoader cl) { + return null; + } + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/test/java/org/apache/tamaya/spi/ServiceContextTest.java ---------------------------------------------------------------------- diff --git a/code/compat/src/test/java/org/apache/tamaya/spi/ServiceContextTest.java b/code/compat/src/test/java/org/apache/tamaya/spi/ServiceContextTest.java new file mode 100644 index 0000000..fe4ee05 --- /dev/null +++ b/code/compat/src/test/java/org/apache/tamaya/spi/ServiceContextTest.java @@ -0,0 +1,128 @@ +/* + * 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.tamaya.spi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.net.URL; +import java.util.*; + +import org.junit.Test; + +public class ServiceContextTest { + + private final ServiceContext serviceContext = new ServiceContext(){ + + @Override + public int ordinal() { + return 1; + } + + @Override + public <T> T getService(Class<T> serviceType) { + if(String.class.equals(serviceType)){ + return serviceType.cast("ServiceContextTest"); + } + return null; + } + + @Override + public <T> T getService(Class<T> serviceType, ClassLoader classLoader) { + return getService(serviceType, ServiceContext.defaultClassLoader()); + } + + @Override + public <T> T create(Class<T> serviceType) { + return getService(serviceType); + } + + @Override + public <T> T create(Class<T> serviceType, ClassLoader classLoader) { + return create(serviceType, ServiceContext.defaultClassLoader()); + } + + @SuppressWarnings("unchecked") + @Override + public <T> List<T> getServices(Class<T> serviceType) { + if(String.class.equals(serviceType)){ + List<String> list = new ArrayList<>(); + list.add("ServiceContextTest"); + return List.class.cast(list); + } + return Collections.emptyList(); + } + + @Override + public <T> List<T> getServices(Class<T> serviceType, ClassLoader classLoader) { + return getServices(serviceType, ServiceContext.defaultClassLoader()); + } + + @Override + public Enumeration<URL> getResources(String resource, ClassLoader cl) throws IOException { + return cl.getResources(resource); + } + + @Override + public URL getResource(String resource, ClassLoader cl) { + return cl.getResource(resource); + } + }; + + @Test + public void testOrdinal() throws Exception { + assertEquals(1, serviceContext.ordinal()); + } + + @Test + public void testgetService() throws Exception { + assertEquals("ServiceContextTest", serviceContext.getService(String.class)); + assertNull(serviceContext.getService(Integer.class)); + } + + @Test + public void testGetService() throws Exception { + String service = serviceContext.getService(String.class); + assertNotNull(service); + assertEquals("ServiceContextTest", service); + Integer intService = serviceContext.getService(Integer.class); + assertNull(intService); + } + + @Test + public void testGetServices() throws Exception { + Collection<String> services = serviceContext.getServices(String.class); + assertNotNull(services); + assertFalse(services.isEmpty()); + assertEquals("ServiceContextTest", services.iterator().next()); + List<Integer> intServices = serviceContext.getServices(Integer.class); + assertNotNull(intServices); + assertTrue(intServices.isEmpty()); + } + + @Test + public void testGetInstance() throws Exception { + assertNotNull(ServiceContextManager.getServiceContext()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/test/java/org/apache/tamaya/spi/TestServiceContext.java ---------------------------------------------------------------------- diff --git a/code/compat/src/test/java/org/apache/tamaya/spi/TestServiceContext.java b/code/compat/src/test/java/org/apache/tamaya/spi/TestServiceContext.java new file mode 100644 index 0000000..4b8edc1 --- /dev/null +++ b/code/compat/src/test/java/org/apache/tamaya/spi/TestServiceContext.java @@ -0,0 +1,120 @@ +/* + * 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.tamaya.spi; + +import java.io.IOException; +import java.net.URL; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class implements the (default) {@link org.apache.tamaya.spi.ServiceContext} interface and hereby uses the JDK + * {@link java.util.ServiceLoader} to load the services required. + */ +public final class TestServiceContext implements ServiceContext { + /** List current services loaded, per class. */ + private final ConcurrentHashMap<Class<?>, List<Object>> servicesLoaded = new ConcurrentHashMap<>(); + + private final Map<Class<?>, Object> singletons = new ConcurrentHashMap<>(); + + @Override + public int ordinal() { + return 1; + } + + @SuppressWarnings("rawtypes") + @Override + public <T> T getService(Class<T> serviceType) { + T cached = serviceType.cast(singletons.get(serviceType)); + if(cached==null) { + cached = create(serviceType); + singletons.put((Class)serviceType, cached); + } + if (cached == Object.class) { + cached = null; + } + return cached; + } + + @Override + public <T> T getService(Class<T> serviceType, ClassLoader classLoader) { + return getService(serviceType, ServiceContext.defaultClassLoader()); + } + + @SuppressWarnings("unchecked") + @Override + public <T> T create(Class<T> serviceType) { + Collection<T> services = getServices(serviceType); + if (services.isEmpty()) { + return (T) Object.class; // as marker for 'nothing here' + } + else{ + return services.iterator().next(); + } + } + + @Override + public <T> T create(Class<T> serviceType, ClassLoader classLoader) { + return create(serviceType, ServiceContext.defaultClassLoader()); + } + + /** + * Loads and registers services. + * + * @param <T> the concrete type. + * + * @param serviceType The service type. + * @return the items found, never {@code null}. + */ + @Override + public <T> List<T> getServices(Class<T> serviceType) { + try { + List<T> services = new ArrayList<>(); + for (T t : ServiceLoader.load(serviceType)) { + services.add(t); + } + services = Collections.unmodifiableList(services); + @SuppressWarnings("unchecked") + final List<T> previousServices = List.class.cast(servicesLoaded.putIfAbsent(serviceType, (List<Object>)services)); + return previousServices != null ? previousServices : services; + } catch (Exception e) { + Logger.getLogger(TestServiceContext.class.getName()).log(Level.WARNING, + "Error loading services current type " + serviceType, e); + return Collections.emptyList(); + } + } + + @Override + public <T> List<T> getServices(Class<T> serviceType, ClassLoader classLoader) { + return getServices(serviceType, ServiceContext.defaultClassLoader()); + } + + @Override + public Enumeration<URL> getResources(String resource, ClassLoader cl) throws IOException { + return cl.getResources(resource); + } + + @Override + public URL getResource(String resource, ClassLoader cl) { + return cl.getResource(resource); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/test/java/org/apache/tamaya/spisupport/RegexFilterTest.java ---------------------------------------------------------------------- diff --git a/code/compat/src/test/java/org/apache/tamaya/spisupport/RegexFilterTest.java b/code/compat/src/test/java/org/apache/tamaya/spisupport/RegexFilterTest.java new file mode 100644 index 0000000..5614c01 --- /dev/null +++ b/code/compat/src/test/java/org/apache/tamaya/spisupport/RegexFilterTest.java @@ -0,0 +1,75 @@ +/* + * 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.tamaya.spisupport; + +import org.apache.tamaya.Configuration; +import org.apache.tamaya.spi.FilterContext; +import org.apache.tamaya.spi.PropertyValue; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * Tests for {@link RegexPropertyFilter}. Created by anatole on 11.02.16. + */ +public class RegexFilterTest { + + private static PropertyValue prop1 = PropertyValue.of("test1", "test1", "test"); + private static PropertyValue prop2 = PropertyValue.of("test2", "test2", "test"); + private static PropertyValue prop3 = PropertyValue.of("test1.test3", "test.test3", "test"); + private static Configuration config = new DefaultConfigurationBuilder().build(); + + @org.junit.Test + public void testFilterProperty() throws Exception { + RegexPropertyFilter filter = new RegexPropertyFilter(); + filter.setIncludes("test1.*"); + Map<String,PropertyValue> map = new HashMap<>(); + map.put(prop1.getKey(), prop1); + map.put(prop2.getKey(), prop2); + map.put(prop3.getKey(), prop3); + FilterContext ctx = new FilterContext(prop1, config.getContext()); + assertEquals(filter.filterProperty(prop1, ctx), prop1); + ctx = new FilterContext(prop2, config.getContext()); + assertNull(filter.filterProperty(prop2, ctx)); + ctx = new FilterContext(prop3, map, config.getContext()); + assertEquals(filter.filterProperty( + prop3, ctx), prop3); + ctx = new FilterContext(prop3, map, config.getContext()); + assertEquals(filter.filterProperty( + prop3, ctx), prop3); + filter = new RegexPropertyFilter(); + filter.setIncludes("test1.*"); + ctx = new FilterContext(prop1, map, config.getContext()); + assertNotNull(filter.filterProperty(prop1, ctx)); + ctx = new FilterContext(prop2, map, config.getContext()); + assertNull(filter.filterProperty(prop2, ctx)); + ctx = new FilterContext(prop3, map, config.getContext()); + assertNotNull(filter.filterProperty(prop3, ctx)); + } + + @org.junit.Test + public void testToString() throws Exception { + RegexPropertyFilter filter = new RegexPropertyFilter(); + filter.setIncludes("test\\..*"); + assertTrue(filter.toString().contains("test\\..*")); + assertTrue(filter.toString().contains("RegexPropertyFilter")); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/test/resources/META-INF/services/org.apache.tamaya.spi.ConfigurationProviderSpi ---------------------------------------------------------------------- diff --git a/code/compat/src/test/resources/META-INF/services/org.apache.tamaya.spi.ConfigurationProviderSpi b/code/compat/src/test/resources/META-INF/services/org.apache.tamaya.spi.ConfigurationProviderSpi new file mode 100644 index 0000000..b9c5ba5 --- /dev/null +++ b/code/compat/src/test/resources/META-INF/services/org.apache.tamaya.spi.ConfigurationProviderSpi @@ -0,0 +1,19 @@ +# +# 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 current 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. +# +org.apache.tamaya.TestConfigurationProvider http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/compat/src/test/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext ---------------------------------------------------------------------- diff --git a/code/compat/src/test/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext b/code/compat/src/test/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext new file mode 100644 index 0000000..199956f --- /dev/null +++ b/code/compat/src/test/resources/META-INF/services/org.apache.tamaya.spi.ServiceContext @@ -0,0 +1,19 @@ +# +# 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 current 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. +# +org.apache.tamaya.spi.TestServiceContext \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/BannerManager.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/BannerManager.java b/code/core/src/main/java/org/apache/tamaya/core/BannerManager.java new file mode 100644 index 0000000..d387267 --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/BannerManager.java @@ -0,0 +1,163 @@ +/* + * 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.tamaya.core; + +import javax.config.ConfigProvider; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Locale; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * Controls the output of the banner of Tamaya. + * + * <p>This class controls if and how the banner of Tamaya is presented the user. + * The banner is provided by the Tamaya Core under the resource path + * {@value BANNER_RESOURCE_PATH}.</p> + * + * <p>The behavior of the banner manager can be controlled by + * specifying the configuration key {@code tamaya.banner} with one of + * the three folowing values: + * + * <dl> + * <dt>OFF</dt> + * <dd>Banner will not be shown</dd> + * <dt>CONSOLE</dt> + * <dd>The banner will be printed on STDOUT</dd> + * <dt>LOGGER</dt> + * <dd>The banner will be logged</dd> + * </dl> + * + * In case of any other value the banner will not be shown. + * </p> + * + * + * + * @see BannerTarget + */ +class BannerManager { + /** + * The resouce path to the file containing the banner of Tamaya. + */ + protected final static String BANNER_RESOURCE_PATH = "/tamaya-banner.txt"; + + enum BannerTarget { + OFF, CONSOLE, LOGGER + } + + private BannerTarget bannerTarget; + + public BannerManager(String value) { + value = Objects.requireNonNull(value).toUpperCase(Locale.getDefault()); + + try { + bannerTarget = BannerTarget.valueOf(value); + } catch (NullPointerException | IllegalArgumentException e) { + bannerTarget = BannerTarget.OFF; + } + } + + public void outputBanner() { + BannerPrinter bp = new SilentBannerPrinter(); + + switch (bannerTarget) { + case CONSOLE: + bp = new ConsoleBannerPrinter(); + break; + case LOGGER: + bp = new LoggingBannerPrinter(); + break; + case OFF: + default: + break; + } + + bp.outputBanner(); + } +} + +abstract class AbstractBannerPrinter implements BannerPrinter { + private static final Logger log = Logger.getLogger(AbstractBannerPrinter.class.getName()); + + @Override + public void outputBanner() { + try (InputStream in = ConfigProvider.class.getResourceAsStream(BannerManager.BANNER_RESOURCE_PATH)) { + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line; + + while ((line = reader.readLine()) != null) { + outputSingleLine(line); + } + } catch (Exception e) { + log.log(Level.WARNING, "Failed to output the banner of tamaya.", e); + } + } + + abstract void outputSingleLine(String line); +} + + +/** + * Outputs the Tamaya banner to an implementation specific output channel + * as STDOUT or the logging system. + */ +interface BannerPrinter { + /** + * Outputs the banner to the output channel + * used by the implementation. + */ + void outputBanner(); +} + +/** + * Silent implementation of a {@link BannerPrinter}. + */ +class SilentBannerPrinter implements BannerPrinter { + @Override + public void outputBanner() { + } +} + +/** + * Logs the banner via JUL at level {@link java.util.logging.Level#INFO}. + */ +class LoggingBannerPrinter extends AbstractBannerPrinter { + private static final Logger log = Logger.getLogger(LoggingBannerPrinter.class.getName()); + + @Override + void outputSingleLine(String line) { + log.log(Level.INFO, line); + } +} + +/** + * Prints the banner to the console. + */ +class ConsoleBannerPrinter extends AbstractBannerPrinter { + @Override + void outputSingleLine(String line) { + System.out.println(line); + } +} + + http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceComparator.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceComparator.java b/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceComparator.java new file mode 100644 index 0000000..009927d --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceComparator.java @@ -0,0 +1,70 @@ +/* + * 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.tamaya.core; + +import org.osgi.framework.ServiceReference; + +import javax.annotation.Priority; +import java.util.Comparator; + +/** + * Comparator implementation for ordering services loaded based on their increasing priority values. + */ +@SuppressWarnings("rawtypes") +class OSGIServiceComparator implements Comparator<ServiceReference> { + + @Override + public int compare(ServiceReference o1, ServiceReference o2) { + int prio = getPriority(o1) - getPriority(o2); + if (prio < 0) { + return 1; + } else if (prio > 0) { + return -1; + } else { + return 0; //o1.getClass().getSimpleName().compareTo(o2.getClass().getSimpleName()); + } + } + + /** + * Checks the given instance for a @Priority annotation. If present the annotation's value is evaluated. If no such + * annotation is present, a default priority {@code 1} is returned. + * + * @param o the instance, not {@code null}. + * @return a priority, by default 1. + */ + public static int getPriority(Object o) { + return getPriority(o.getClass()); + } + + /** + * Checks the given type optionally annotated with a @Priority. If present the annotation's value is evaluated. + * If no such annotation is present, a default priority {@code 1} is returned. + * + * @param type the type, not {@code null}. + * @return a priority, by default 1. + */ + public static int getPriority(Class<? extends Object> type) { + int prio = 1; + Priority priority = type.getAnnotation(Priority.class); + if (priority != null) { + prio = priority.value(); + } + return prio; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceContext.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceContext.java b/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceContext.java new file mode 100644 index 0000000..eb01733 --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceContext.java @@ -0,0 +1,187 @@ +/* + * 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.tamaya.core; + +import org.apache.tamaya.spi.ServiceContext; +import org.osgi.framework.Bundle; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.framework.ServiceReference; + +import javax.config.spi.ConfigProviderResolver; +import java.io.IOException; +import java.net.URL; +import java.util.*; +import java.util.logging.Logger; + +/** + * ServiceContext implementation based on OSGI Service mechanisms. + */ +public class OSGIServiceContext implements ServiceContext{ + + private static final Logger LOG = Logger.getLogger(OSGIServiceContext.class.getName()); + private static final OSGIServiceComparator REF_COMPARATOR = new OSGIServiceComparator(); + + private final OSGIServiceLoader osgiServiceLoader; + + public OSGIServiceContext(OSGIServiceLoader osgiServiceLoader){ + this.osgiServiceLoader = Objects.requireNonNull(osgiServiceLoader); + } + + public boolean isInitialized(){ + return osgiServiceLoader != null; + } + + + @Override + public int ordinal() { + return 10; + } + + @Override + public <T> T getService(Class<T> serviceType) { + LOG.finest("TAMAYA Loading service: " + serviceType.getName()); + ServiceReference<T> ref = this.osgiServiceLoader.getBundleContext().getServiceReference(serviceType); + if(ref!=null){ + return this.osgiServiceLoader.getBundleContext().getService(ref); + } + if(ConfigProviderResolver.class==serviceType){ + @SuppressWarnings("unchecked") + T service = (T)new TamayaConfigProviderResolver(); + this.osgiServiceLoader.getBundleContext().registerService( + serviceType.getName(), + service, + new Hashtable<String, Object>()); + return service; + } + return null; + } + + @Override + public <T> T getService(Class<T> serviceType, ClassLoader classLoader) { + return getService(serviceType); + } + + @SuppressWarnings("unchecked") + @Override + public <T> T create(Class<T> serviceType) { + LOG.finest("TAMAYA Creating service: " + serviceType.getName()); + ServiceReference<T> ref = this.osgiServiceLoader.getBundleContext().getServiceReference(serviceType); + if(ref!=null){ + try { + return (T)this.osgiServiceLoader.getBundleContext().getService(ref).getClass().newInstance(); + } catch (Exception e) { + return null; + } + } + return null; + } + + @Override + public <T> T create(Class<T> serviceType, ClassLoader classLoader) { + return create(serviceType); + } + + @Override + public <T> List<T> getServices(Class<T> serviceType) { + LOG.finest("TAMAYA Loading services: " + serviceType.getName()); + List<ServiceReference<T>> refs = new ArrayList<>(); + List<T> services = new ArrayList<>(refs.size()); + try { + refs.addAll(this.osgiServiceLoader.getBundleContext().getServiceReferences(serviceType, null)); + Collections.sort(refs, REF_COMPARATOR); + for(ServiceReference<T> ref:refs){ + T service = osgiServiceLoader.getBundleContext().getService(ref); + if(service!=null) { + services.add(service); + } + } + } catch (InvalidSyntaxException e) { + e.printStackTrace(); + } + try{ + for(T service:ServiceLoader.load(serviceType)){ + services.add(service); + } + return services; + } catch (Exception e) { + e.printStackTrace(); + } + return services; + } + + @Override + public <T> List<T> getServices(Class<T> serviceType, ClassLoader classLoader) { + return getServices(serviceType); + } + + @Override + public Enumeration<URL> getResources(String resource, ClassLoader cl) throws IOException{ + LOG.finest("TAMAYA Loading resources: " + resource); + List<URL> result = new ArrayList<>(); + URL url = osgiServiceLoader.getBundleContext().getBundle() + .getEntry(resource); + if(url != null) { + LOG.finest("TAMAYA Resource: " + resource + " found in unregistered bundle " + + osgiServiceLoader.getBundleContext().getBundle().getSymbolicName()); + result.add(url); + } + for(Bundle bundle: osgiServiceLoader.getResourceBundles()) { + url = bundle.getEntry(resource); + if (url != null && !result.contains(url)) { + LOG.finest("TAMAYA Resource: " + resource + " found in registered bundle " + bundle.getSymbolicName()); + result.add(url); + } + } + for(Bundle bundle: osgiServiceLoader.getBundleContext().getBundles()) { + url = bundle.getEntry(resource); + if (url != null && !result.contains(url)) { + LOG.finest("TAMAYA Resource: " + resource + " found in unregistered bundle " + bundle.getSymbolicName()); + result.add(url); + } + } + return Collections.enumeration(result); + } + + @Override + public URL getResource(String resource, ClassLoader cl){ + LOG.finest("TAMAYA Loading resource: " + resource); + URL url = osgiServiceLoader.getBundleContext().getBundle() + .getEntry(resource); + if(url!=null){ + LOG.finest("TAMAYA Resource: " + resource + " found in bundle " + + osgiServiceLoader.getBundleContext().getBundle().getSymbolicName()); + return url; + } + for(Bundle bundle: osgiServiceLoader.getResourceBundles()) { + url = bundle.getEntry(resource); + if(url != null){ + LOG.finest("TAMAYA Resource: " + resource + " found in registered bundle " + bundle.getSymbolicName()); + return url; + } + } + for(Bundle bundle: osgiServiceLoader.getBundleContext().getBundles()) { + url = bundle.getEntry(resource); + if(url != null){ + LOG.finest("TAMAYA Resource: " + resource + " found in unregistered bundle " + bundle.getSymbolicName()); + return url; + } + } + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceLoader.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceLoader.java b/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceLoader.java new file mode 100644 index 0000000..90e74bc --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/OSGIServiceLoader.java @@ -0,0 +1,254 @@ +/* + * 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.tamaya.core; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.tamaya.spisupport.PriorityServiceComparator; +import org.osgi.framework.*; + +/** + * A bundle listener that registers services defined in META-INF/services, when + * a bundle is starting. + * + * @author [email protected] + */ +@SuppressWarnings("rawtypes") +public class OSGIServiceLoader implements BundleListener { + // Provide logging + private static final Logger log = Logger.getLogger(OSGIServiceLoader.class.getName()); + private static final String META_INF_SERVICES = "META-INF/services/"; + + private BundleContext context; + + private Set<Bundle> resourceBundles = Collections.synchronizedSet(new HashSet<Bundle>()); + + public OSGIServiceLoader(BundleContext context) { + this.context = Objects.requireNonNull(context); + // Check for matching bundles already installed... + for (Bundle bundle : context.getBundles()) { + switch (bundle.getState()) { + case Bundle.ACTIVE: + checkAndLoadBundle(bundle); + } + } + } + + public BundleContext getBundleContext() { + return context; + } + + public Set<Bundle> getResourceBundles() { + synchronized (resourceBundles) { + return new HashSet<>(resourceBundles); + } + } + + @Override + public void bundleChanged(BundleEvent bundleEvent) { + // Parse and create metadata when installed + if (bundleEvent.getType() == BundleEvent.STARTED) { + Bundle bundle = bundleEvent.getBundle(); + checkAndLoadBundle(bundle); + } else if (bundleEvent.getType() == BundleEvent.STOPPED) { + Bundle bundle = bundleEvent.getBundle(); + checkAndUnloadBundle(bundle); + } + } + + private void checkAndUnloadBundle(Bundle bundle) { + if (bundle.getEntry(META_INF_SERVICES) == null) { + return; + } + synchronized (resourceBundles) { + resourceBundles.remove(bundle); + log.fine("Unregistered ServiceLoader bundle: " + bundle.getSymbolicName()); + } + Enumeration<String> entryPaths = bundle.getEntryPaths(META_INF_SERVICES); + while (entryPaths.hasMoreElements()) { + String entryPath = entryPaths.nextElement(); + if (!entryPath.endsWith("/")) { + removeEntryPath(bundle, entryPath); + } + } + } + + private void checkAndLoadBundle(Bundle bundle) { + if (bundle.getEntry(META_INF_SERVICES) == null) { + return; + } + synchronized (resourceBundles) { + resourceBundles.add(bundle); + log.info("Registered ServiceLoader bundle: " + bundle.getSymbolicName()); + } + Enumeration<String> entryPaths = bundle.getEntryPaths(META_INF_SERVICES); + while (entryPaths.hasMoreElements()) { + String entryPath = entryPaths.nextElement(); + if (!entryPath.endsWith("/")) { + processEntryPath(bundle, entryPath); + } + } + } + + private void processEntryPath(Bundle bundle, String entryPath) { + try { + String serviceName = entryPath.substring(META_INF_SERVICES.length()); + if (!serviceName.startsWith("org.apache.tamaya")) { + // Ignore non Tamaya entries... + return; + } + Class<?> serviceClass = bundle.loadClass(serviceName); + URL child = bundle.getEntry(entryPath); + InputStream inStream = child.openStream(); + log.info("Loading Services " + serviceClass.getName() + " from bundle...: " + bundle.getSymbolicName()); + try (BufferedReader br = new BufferedReader(new InputStreamReader(inStream, "UTF-8"))) { + String implClassName = br.readLine(); + while (implClassName != null) { + int hashIndex = implClassName.indexOf("#"); + if (hashIndex > 0) { + implClassName = implClassName.substring(0, hashIndex - 1); + } else if (hashIndex == 0) { + implClassName = ""; + } + implClassName = implClassName.trim(); + if (implClassName.length() > 0) { + try { + // Load the service class + log.fine("Loading Class " + implClassName + " from bundle...: " + bundle.getSymbolicName()); + Class<?> implClass = bundle.loadClass(implClassName); + if (!serviceClass.isAssignableFrom(implClass)) { + log.warning("Configured service: " + implClassName + " is not assignable to " + + serviceClass.getName()); + continue; + } + log.info("Loaded Service Factory (" + serviceName + "): " + implClassName); + // Provide service properties + Hashtable<String, String> props = new Hashtable<>(); + props.put(Constants.VERSION_ATTRIBUTE, bundle.getVersion().toString()); + String vendor = bundle.getHeaders().get(Constants.BUNDLE_VENDOR); + props.put(Constants.SERVICE_VENDOR, (vendor != null ? vendor : "anonymous")); + // Translate annotated @Priority into a service ranking + props.put(Constants.SERVICE_RANKING, + String.valueOf(PriorityServiceComparator.getPriority(implClass))); + + // Register the service factory on behalf of the intercepted bundle + JDKUtilServiceFactory factory = new JDKUtilServiceFactory(implClass); + BundleContext bundleContext = bundle.getBundleContext(); + bundleContext.registerService(serviceName, factory, props); + log.info("Registered Tamaya service class: " + implClassName + "(" + serviceName + ")"); + } catch (Exception e) { + log.log(Level.SEVERE, "Failed to load service: " + implClassName, e); + } catch (NoClassDefFoundError err) { + log.log(Level.SEVERE, "Failed to load service: " + implClassName, err); + } + } + implClassName = br.readLine(); + } + } + } catch (RuntimeException rte) { + throw rte; + } catch (Exception e) { + log.log(Level.SEVERE, "Failed to read services from: " + entryPath, e); + } + } + + private void removeEntryPath(Bundle bundle, String entryPath) { + try { + String serviceName = entryPath.substring(META_INF_SERVICES.length()); + if (!serviceName.startsWith("org.apache.tamaya")) { + // Ignore non Tamaya entries... + return; + } + Class<?> serviceClass = bundle.loadClass(serviceName); + + URL child = bundle.getEntry(entryPath); + InputStream inStream = child.openStream(); + + BufferedReader br = new BufferedReader(new InputStreamReader(inStream, "UTF-8")); + String implClassName = br.readLine(); + while (implClassName != null) { + int hashIndex = implClassName.indexOf("#"); + if (hashIndex > 0) { + implClassName = implClassName.substring(0, hashIndex - 1); + } else if (hashIndex == 0) { + implClassName = ""; + } + implClassName = implClassName.trim(); + if (implClassName.length() > 0) { + log.fine("Unloading Service (" + serviceName + "): " + implClassName); + try { + // Load the service class + Class<?> implClass = bundle.loadClass(implClassName); + if (!serviceClass.isAssignableFrom(implClass)) { + log.warning("Configured service: " + implClassName + " is not assignable to " + + serviceClass.getName()); + continue; + } + ServiceReference<?> ref = bundle.getBundleContext().getServiceReference(implClass); + if (ref != null) { + bundle.getBundleContext().ungetService(ref); + } + } catch (Exception e) { + log.log(Level.SEVERE, "Failed to unload service: " + implClassName, e); + } catch (NoClassDefFoundError err) { + log.log(Level.SEVERE, "Failed to unload service: " + implClassName, err); + } + } + implClassName = br.readLine(); + } + br.close(); + } catch (RuntimeException rte) { + throw rte; + } catch (Exception e) { + log.log(Level.SEVERE, "Failed to read services from: " + entryPath, e); + } + } + + /** + * Service factory simply instantiating the configured service. + */ + static class JDKUtilServiceFactory implements ServiceFactory { + private final Class<?> serviceClass; + + public JDKUtilServiceFactory(Class<?> serviceClass) { + this.serviceClass = serviceClass; + } + + @Override + public Object getService(Bundle bundle, ServiceRegistration registration) { + try { + log.fine("Creating Service...:" + serviceClass.getName()); + return serviceClass.newInstance(); + } catch (Exception ex) { + ex.printStackTrace(); + throw new IllegalStateException("Failed to create service: " + serviceClass.getName(), ex); + } + } + + @Override + public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) { + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/TamayaConfigProviderResolver.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/TamayaConfigProviderResolver.java b/code/core/src/main/java/org/apache/tamaya/core/TamayaConfigProviderResolver.java new file mode 100644 index 0000000..f4bc048 --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/TamayaConfigProviderResolver.java @@ -0,0 +1,92 @@ +/* + * 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.tamaya.core; + +import org.apache.tamaya.base.DefaultConfigBuilder; +import org.apache.tamaya.spi.ConfigContext; +import org.apache.tamaya.spi.Filter; +import org.apache.tamaya.spi.ServiceContext; +import org.osgi.service.component.annotations.Component; + +import javax.config.Config; +import javax.config.spi.ConfigBuilder; +import javax.config.spi.ConfigProviderResolver; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +/** + * Implementation of the Configuration API. This class uses the current {@link ConfigContext} to evaluate the + * chain of {@link javax.config.spi.ConfigSource} and {@link Filter} + * instance to evaluate the current Configuration. + */ +@Component(service = ConfigProviderResolver.class) +public class TamayaConfigProviderResolver extends ConfigProviderResolver { + + private Map<ClassLoader, Config> configs = new ConcurrentHashMap<>(); + + @Override + public Config getConfig() { + return getConfig(Thread.currentThread().getContextClassLoader()); + } + + @Override + public Config getConfig(ClassLoader loader) { + Config config = this.configs.get(loader); + if(config==null){ + config = new DefaultConfigBuilder() + .addDiscoveredFilters() + .addDiscoveredConverters() + .addDefaultSources() + .addDiscoveredSources() + .build(); + this.configs.put(loader, config); + } + return config; + } + + @Override + public ConfigBuilder getBuilder() { + return new DefaultConfigBuilder(); + } + + @Override + public void registerConfig(Config config, ClassLoader classLoader) { + if(classLoader==null){ + classLoader = ServiceContext.defaultClassLoader(); + } + if(configs.containsKey(classLoader)){ + Logger.getLogger(getClass().getName()) + .warning("Replacing existing config for classloader: " + classLoader); +// throw new IllegalArgumentException("Already a config registered with classloader: " + classLoader); + } + this.configs.put(classLoader, config); + } + + @Override + public void releaseConfig(Config config) { + for(Map.Entry<ClassLoader, Config> en: this.configs.entrySet()){ + if(en.getValue().equals(config)){ + this.configs.remove(en.getKey()); + return; + } + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/converters/BigDecimalConverter.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/converters/BigDecimalConverter.java b/code/core/src/main/java/org/apache/tamaya/core/converters/BigDecimalConverter.java new file mode 100644 index 0000000..e29cf85 --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/converters/BigDecimalConverter.java @@ -0,0 +1,78 @@ +/* + * 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.tamaya.core.converters; + +import org.apache.tamaya.base.convert.ConversionContext; +import org.osgi.service.component.annotations.Component; + +import javax.config.spi.Converter; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Objects; +import java.util.logging.Logger; + +/** + * Converter, converting from String to BigDecimal, the supported format is one of the following: + * <ul> + * <li>232573527352.76352753</li> + * <li>-23257352.735276352753</li> + * <li>-0xFFFFFF (integral numbers only)</li> + * <li>-0XFFFFAC (integral numbers only)</li> + * <li>0xFFFFFF (integral numbers only)</li> + * <li>0XFFFFAC (integral numbers only)</li> + * </ul> + */ +@Component(service = Converter.class) +public class BigDecimalConverter implements Converter<BigDecimal> { + + /** The logger. */ + private static final Logger LOG = Logger.getLogger(BigDecimalConverter.class.getName()); + /** Converter to be used if the format is not directly supported by BigDecimal, e.g. for integral hex values. */ + private final BigIntegerConverter integerConverter = new BigIntegerConverter(); + + @Override + public BigDecimal convert(String value) { + ConversionContext context = ConversionContext.getContext(); + if(context!=null) { + context.addSupportedFormats(getClass(), "<BigDecimal> -> new BigDecimal(String)"); + } + String trimmed = Objects.requireNonNull(value).trim(); + try{ + return new BigDecimal(trimmed); + } catch(Exception e){ + LOG.finest("Parsing BigDecimal failed, trying BigInteger for: " + value); + BigInteger bigInt = integerConverter.convert(value); + if(bigInt!=null){ + return new BigDecimal(bigInt); + } + LOG.finest("Failed to parse BigDecimal from: " + value); + return null; + } + } + + @Override + public boolean equals(Object o){ + return getClass().equals(o.getClass()); + } + + @Override + public int hashCode(){ + return getClass().hashCode(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/converters/BigIntegerConverter.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/converters/BigIntegerConverter.java b/code/core/src/main/java/org/apache/tamaya/core/converters/BigIntegerConverter.java new file mode 100644 index 0000000..1e73b19 --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/converters/BigIntegerConverter.java @@ -0,0 +1,107 @@ +/* + * 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.tamaya.core.converters; + +import org.apache.tamaya.base.convert.ConversionContext; +import org.osgi.service.component.annotations.Component; + +import javax.config.spi.Converter; +import java.math.BigInteger; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Converter, converting from String to BigInteger, the supported format is one of the following: + * <ul> + * <li>0xFFFFFF</li> + * <li>0XFFFFAC</li> + * <li>23257352735276352753</li> + * <li>-0xFFFFFF</li> + * <li>-0XFFFFAC</li> + * <li>-23257352735276352753</li> + * </ul> + */ +@Component(service = Converter.class) +public class BigIntegerConverter implements Converter<BigInteger> { + + /** The logger. */ + private static final Logger LOG = Logger.getLogger(BigIntegerConverter.class.getName()); + /** Converter used to decode hex, octal values. */ + private final ByteConverter byteConverter = new ByteConverter(); + + @Override + public BigInteger convert(String value) { + ConversionContext context = ConversionContext.getContext(); + context.addSupportedFormats(getClass(), "[-]0X.. (hex)", "[-]0x... (hex)", "<bigint> -> new BigInteger(bigint)"); + String trimmed = Objects.requireNonNull(value).trim(); + if(trimmed.startsWith("0x") || trimmed.startsWith("0X")){ + LOG.finest("Parsing Hex value to BigInteger: " + value); + trimmed = trimmed.substring(2); + StringBuilder decimal = new StringBuilder(); + for(int offset = 0;offset < trimmed.length();offset+=2){ + if(offset==trimmed.length()-1){ + LOG.finest("Invalid Hex-Byte-String: " + value); + return null; + } + byte val = byteConverter.convert("0x" + trimmed.substring(offset, offset + 2)); + if(val<10){ + decimal.append('0').append(val); + } else{ + decimal.append(val); + } + } + return new BigInteger(decimal.toString()); + } else if(trimmed.startsWith("-0x") || trimmed.startsWith("-0X")){ + LOG.finest("Parsing Hex value to BigInteger: " + value); + trimmed = trimmed.substring(3); + StringBuilder decimal = new StringBuilder(); + for(int offset = 0;offset < trimmed.length();offset+=2){ + if(offset==trimmed.length()-1){ + LOG.finest("Invalid Hex-Byte-String: " + trimmed); + return null; + } + byte val = byteConverter.convert("0x" + trimmed.substring(offset, offset + 2)); + if(val<10){ + decimal.append('0').append(val); + } else{ + decimal.append(val); + } + } + return new BigInteger('-' + decimal.toString()); + } + try{ + return new BigInteger(trimmed); + } catch(Exception e){ + LOG.log(Level.FINEST, "Failed to parse BigInteger from: " + value, e); + return null; + } + } + + @Override + public boolean equals(Object o){ + return getClass().equals(o.getClass()); + } + + @Override + public int hashCode(){ + return getClass().hashCode(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/converters/BooleanConverter.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/converters/BooleanConverter.java b/code/core/src/main/java/org/apache/tamaya/core/converters/BooleanConverter.java new file mode 100644 index 0000000..df322b1 --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/converters/BooleanConverter.java @@ -0,0 +1,74 @@ +/* + * 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.tamaya.core.converters; + +import org.apache.tamaya.base.convert.ConversionContext; +import org.osgi.service.component.annotations.Component; + +import javax.config.spi.Converter; +import java.util.Locale; +import java.util.Objects; +import java.util.logging.Logger; + +/** + * Converter, converting from String to Boolean. + */ +@Component(service = Converter.class) +public class BooleanConverter implements Converter<Boolean> { + + private final Logger LOG = Logger.getLogger(getClass().getName()); + + @Override + public Boolean convert(String value) { + ConversionContext context = ConversionContext.getContext(); + context.addSupportedFormats(getClass(), "yes (ignore case)", "y (ignore case)", "true (ignore case)", "t (ignore case)", "1", "no (ignore case)", "n (ignore case)", "false (ignore case)", "f (ignore case)", "0"); + String ignoreCaseValue = Objects.requireNonNull(value) + .trim() + .toLowerCase(Locale.ENGLISH); + switch(ignoreCaseValue) { + case "1": + case "yes": + case "y": + case "true": + case "t": + case "on": + return Boolean.TRUE; + case "no": + case "n": + case "false": + case "f": + case "0": + case "off": + return Boolean.FALSE; + default: + LOG.finest("Unknown boolean value encountered: " + value); + } + return null; + } + + @Override + public boolean equals(Object o){ + return getClass().equals(o.getClass()); + } + + @Override + public int hashCode(){ + return getClass().hashCode(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/converters/ByteConverter.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/converters/ByteConverter.java b/code/core/src/main/java/org/apache/tamaya/core/converters/ByteConverter.java new file mode 100644 index 0000000..7b72cc5 --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/converters/ByteConverter.java @@ -0,0 +1,84 @@ +/* + * 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.tamaya.core.converters; + +import org.apache.tamaya.base.convert.ConversionContext; +import org.osgi.service.component.annotations.Component; + +import javax.config.spi.Converter; +import java.util.Locale; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Converter, converting from String to Byte, the supported format is one of the following: + * <ul> + * <li>123 (byte value)</li> + * <li>0xFF (byte value)</li> + * <li>0XDF (byte value)</li> + * <li>0D1 (byte value)</li> + * <li>-123 (byte value)</li> + * <li>-0xFF (byte value)</li> + * <li>-0XDF (byte value)</li> + * <li>-0D1 (byte value)</li> + * <li>MIN_VALUE (ignoring case)</li> + * <li>MIN (ignoring case)</li> + * <li>MAX_VALUE (ignoring case)</li> + * <li>MAX (ignoring case)</li> + * </ul> + */ +@Component(service = Converter.class) +public class ByteConverter implements Converter<Byte> { + + private final Logger LOG = Logger.getLogger(getClass().getName()); + + @Override + public Byte convert(String value) { + ConversionContext context = ConversionContext.getContext(); + context.addSupportedFormats(getClass(),"<byte>", "MIN_VALUE", "MIN", "MAX_VALUE", "MAX"); + String trimmed = Objects.requireNonNull(value).trim(); + switch(trimmed.toUpperCase(Locale.ENGLISH)){ + case "MIN_VALUE": + case "MIN": + return Byte.MIN_VALUE; + case "MAX_VALUE": + case "MAX": + return Byte.MAX_VALUE; + default: + try{ + return Byte.decode(trimmed); + } + catch(Exception e){ + LOG.log(Level.FINEST, "Unparseable Byte: " + value); + return null; + } + } + } + + @Override + public boolean equals(Object o){ + return getClass().equals(o.getClass()); + } + + @Override + public int hashCode(){ + return getClass().hashCode(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/converters/CharConverter.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/converters/CharConverter.java b/code/core/src/main/java/org/apache/tamaya/core/converters/CharConverter.java new file mode 100644 index 0000000..9fa4e69 --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/converters/CharConverter.java @@ -0,0 +1,81 @@ +/* + * 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.tamaya.core.converters; + +import org.apache.tamaya.base.convert.ConversionContext; +import org.osgi.service.component.annotations.Component; + +import javax.config.spi.Converter; +import java.util.Objects; +import java.util.logging.Logger; + +/** + * Converter, converting from String to Character, the supported format is one of the following: + * <ul> + * <li>'a'</li> + * <li>123 (byte value)</li> + * <li>0xFF (byte value)</li> + * <li>0XDF (byte value)</li> + * <li>0D1 (byte value)</li> + * </ul> + */ +@Component(service = Converter.class) +public class CharConverter implements Converter<Character> { + + private static final Logger LOG = Logger.getLogger(CharConverter.class.getName()); + + @Override + public Character convert(String value) { + ConversionContext context = ConversionContext.getContext(); + context.addSupportedFormats(getClass(),"\\'<char>\\'", "<char>", "<charNum>"); + String trimmed = Objects.requireNonNull(value).trim(); + if(trimmed.isEmpty()){ + return null; + } + if(trimmed.startsWith("'")) { + try { + trimmed = trimmed.substring(1, trimmed.length() - 1); + if (trimmed.isEmpty()) { + return null; + } + return trimmed.charAt(0); + } catch (Exception e) { + LOG.finest("Invalid character format encountered: '" + value + "', valid formats are 'a', 101 and a."); + return null; + } + } + try { + Integer val = Integer.parseInt(trimmed); + return (char) val.shortValue(); + } catch (Exception e) { + LOG.finest("Character format is not numeric: '" + value + "', using first character."); + return trimmed.charAt(0); + } + } + + @Override + public boolean equals(Object o){ + return getClass().equals(o.getClass()); + } + + @Override + public int hashCode(){ + return getClass().hashCode(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/converters/ClassConverter.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/converters/ClassConverter.java b/code/core/src/main/java/org/apache/tamaya/core/converters/ClassConverter.java new file mode 100644 index 0000000..c998065 --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/converters/ClassConverter.java @@ -0,0 +1,79 @@ +/* + * 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.tamaya.core.converters; + +import org.apache.tamaya.base.convert.ConversionContext; +import org.osgi.service.component.annotations.Component; + +import javax.config.spi.Converter; +import java.util.Objects; +import java.util.logging.Logger; + +/** + * Converter, converting from String to Class, hereby using the following classloaders: + * <ul> + * <li>The current ThreadContext ClassLoader</li> + * <li>The Classloader of this class</li> + * <li>The system Classloader</li> + * </ul> + */ +@Component(service = Converter.class) +public class ClassConverter implements Converter<Class<?>> { + + private final Logger LOG = Logger.getLogger(getClass().getName()); + + @Override + public Class<?> convert(String value) { + ConversionContext context = ConversionContext.getContext(); + if(value==null){ + return null; + } + context.addSupportedFormats(getClass(),"<fullyQualifiedClassName>"); + String trimmed = Objects.requireNonNull(value).trim(); + try{ + return Class.forName(trimmed, false, Thread.currentThread().getContextClassLoader()); + } + catch(Exception e){ + LOG.finest("Class not found in context CL: " + trimmed); + } + try{ + return Class.forName(trimmed, false, ClassConverter.class.getClassLoader()); + } + catch(Exception e){ + LOG.finest("Class not found in ClassConverter's CL: " + trimmed); + } + try{ + return Class.forName(trimmed, false, ClassLoader.getSystemClassLoader()); + } + catch(Exception e){ + LOG.finest("Class not found in System CL (giving up): " + trimmed); + return null; + } + } + + @Override + public boolean equals(Object o){ + return getClass().equals(o.getClass()); + } + + @Override + public int hashCode(){ + return getClass().hashCode(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/converters/ConvertQuery.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/converters/ConvertQuery.java b/code/core/src/main/java/org/apache/tamaya/core/converters/ConvertQuery.java new file mode 100644 index 0000000..2b22d83 --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/converters/ConvertQuery.java @@ -0,0 +1,81 @@ +/* + * 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.tamaya.core.converters; + +import org.apache.tamaya.base.convert.ConversionContext; +import org.apache.tamaya.spi.ConfigContext; +import org.apache.tamaya.spi.ConfigContextSupplier; + +import javax.config.Config; +import javax.config.spi.Converter; +import java.lang.reflect.Type; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Query to convert a String value. + * @param <T> the target type. + */ +final class ConvertQuery<T> implements Function<Config, T> { + + private static final Logger LOG = Logger.getLogger(ConvertQuery.class.getName()); + + private String rawValue; + private Type type; + + public ConvertQuery(String rawValue, Type type) { + this.rawValue = Objects.requireNonNull(rawValue); + this.type = Objects.requireNonNull(type); + } + + @Override + public T apply(Config config) { + if(!(config instanceof ConfigContextSupplier)){ + throw new IllegalArgumentException("Config must implement ConfigContextSupplier"); + } + ConfigContext ctx = ((ConfigContextSupplier)config).getConfigContext(); + List<Converter> converters = ctx.getConverters(type); + ConversionContext context = new ConversionContext.Builder("<nokey>", type) + .setConfiguration(config) + .setKey(ConvertQuery.class.getName()) + .build(); + ConversionContext.setContext(context); + try { + for (Converter<?> conv : converters) { + try { + if (conv instanceof OptionalConverter) { + continue; + } + T result = (T) conv.convert(rawValue); + if (result != null) { + return result; + } + } catch (Exception e) { + LOG.log(Level.FINEST, e, () -> "Converter " + conv + " failed to convert to " + type); + } + } + }finally{ + ConversionContext.reset(); + } + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/d0e14ed7/code/core/src/main/java/org/apache/tamaya/core/converters/CurrencyConverter.java ---------------------------------------------------------------------- diff --git a/code/core/src/main/java/org/apache/tamaya/core/converters/CurrencyConverter.java b/code/core/src/main/java/org/apache/tamaya/core/converters/CurrencyConverter.java new file mode 100644 index 0000000..502e05c --- /dev/null +++ b/code/core/src/main/java/org/apache/tamaya/core/converters/CurrencyConverter.java @@ -0,0 +1,103 @@ +/* + * 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.tamaya.core.converters; + +import org.apache.tamaya.base.convert.ConversionContext; +import org.osgi.service.component.annotations.Component; + +import javax.config.spi.Converter; +import java.util.Currency; +import java.util.Locale; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Converter, converting from String to Currency, the supported format is one of the following: + * <ul> + * <li>CHF (currency code)</li> + * <li>123 (numeric currency value > + * = 0)</li> + * <li>DE (ISO 2-digit country)</li> + * <li>de_DE, de_DE_123 (Locale)</li> + * </ul> + */ +@Component(service = Converter.class) +public class CurrencyConverter implements Converter<Currency> { + + private static final Logger LOG = Logger.getLogger(CurrencyConverter.class.getName()); + + @Override + public Currency convert(String value) { + ConversionContext context = ConversionContext.getContext(); + context.addSupportedFormats(getClass(), "<currencyCode>, using Locale.ENGLISH", "<numericValue>", "<locale>"); + String trimmed = Objects.requireNonNull(value).trim(); + try { + return Currency.getInstance(trimmed.toUpperCase(Locale.ENGLISH)); + } catch (Exception e) { + LOG.log(Level.FINEST, "Not a valid textual currency code: " + trimmed + ", checking for numeric...", e); + } + try { + // Check for numeric code + Integer numCode = Integer.parseInt(trimmed); + for (Currency currency : Currency.getAvailableCurrencies()) { + if (currency.getNumericCode() == numCode) { + return currency; + } + } + } catch (Exception e) { + LOG.log(Level.FINEST, "Not a valid numeric currency code: " + trimmed + ", checking for locale...", e); + } + try { + // Check for numeric code + String[] parts = trimmed.split("\\_"); + Locale locale; + switch (parts.length) { + case 1: + locale = new Locale("", parts[0]); + break; + case 2: + locale = new Locale(parts[0], parts[1]); + break; + case 3: + locale = new Locale(parts[0], parts[1], parts[2]); + break; + default: + locale = null; + } + if (locale != null) { + return Currency.getInstance(locale); + } + LOG.finest("Not a valid currency: " + trimmed + ", giving up..."); + } catch (Exception e) { + LOG.log(Level.FINEST, "Not a valid country locale for currency: " + trimmed + ", giving up...", e); + } + return null; + } + + @Override + public boolean equals(Object o){ + return getClass().equals(o.getClass()); + } + + @Override + public int hashCode(){ + return getClass().hashCode(); + } +}
