Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 6E3FC200D58 for ; Sun, 17 Dec 2017 09:00:52 +0100 (CET) Received: by cust-asf.ponee.io (Postfix) id 6C9F5160C17; Sun, 17 Dec 2017 08:00:52 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id DFD73160BFF for ; Sun, 17 Dec 2017 09:00:49 +0100 (CET) Received: (qmail 21634 invoked by uid 500); 17 Dec 2017 08:00:49 -0000 Mailing-List: contact commits-help@tamaya.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@tamaya.incubator.apache.org Delivered-To: mailing list commits@tamaya.incubator.apache.org Received: (qmail 21623 invoked by uid 99); 17 Dec 2017 08:00:48 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd3-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 17 Dec 2017 08:00:48 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd3-us-west.apache.org (ASF Mail Server at spamd3-us-west.apache.org) with ESMTP id 63A9D18070C for ; Sun, 17 Dec 2017 08:00:48 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd3-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -4.222 X-Spam-Level: X-Spam-Status: No, score=-4.222 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-0.001, SPF_PASS=-0.001] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd3-us-west.apache.org [10.40.0.10]) (amavisd-new, port 10024) with ESMTP id mnTRrnjrVB5C for ; Sun, 17 Dec 2017 08:00:36 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id 60F215FB72 for ; Sun, 17 Dec 2017 08:00:33 +0000 (UTC) Received: (qmail 21408 invoked by uid 99); 17 Dec 2017 08:00:32 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 17 Dec 2017 08:00:32 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 8DE92DFF73; Sun, 17 Dec 2017 08:00:29 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: anatole@apache.org To: commits@tamaya.incubator.apache.org Date: Sun, 17 Dec 2017 08:00:32 -0000 Message-Id: <155a88943d0c49e593a0f5ddbc4f95dc@git.apache.org> In-Reply-To: <6a315c5912194970a1e733b5832bce63@git.apache.org> References: <6a315c5912194970a1e733b5832bce63@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [4/6] incubator-tamaya git commit: Added full JSR implementation. archived-at: Sun, 17 Dec 2017 08:00:52 -0000 http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/filter/FilterManager.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/filter/FilterManager.java b/code/base/src/main/java/org/apache/tamaya/base/filter/FilterManager.java new file mode 100644 index 0000000..32c8f3f --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/filter/FilterManager.java @@ -0,0 +1,273 @@ +/* + * 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.base.filter; + +import org.apache.tamaya.base.FormatUtils; +import org.apache.tamaya.spi.Filter; +import org.apache.tamaya.spi.ConfigValue; +import org.apache.tamaya.spi.ServiceContext; +import org.apache.tamaya.spi.ServiceContextManager; + +import javax.config.Config; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Implementation of the Configuration API. This class uses the current {@link javax.config.Config} to evaluate the + * chain of {@link javax.config.spi.ConfigSource} and {@link Filter} + * instance to evaluate the current Configuration. + */ +public class FilterManager { + /** + * The logger. + */ + private static final Logger LOG = Logger.getLogger(FilterManager.class.getName()); + /** + * The maximal number of filter cycles performed before aborting. + */ + private static final int MAX_FILTER_LOOPS = 10; + + private List filters = new ArrayList<>(); + + private ClassLoader classloader = ServiceContext.defaultClassLoader(); + + /** + * Create a new filter manager. + */ + public FilterManager(){ + } + + /** + * Create a new filter manager. + * @param filters the filters to be used, not null. + */ + public FilterManager(List filters){ + this.filters.addAll(filters); + LOG.info("Registered " + filters.size() + " config filter: " + filters); + } + + /** + * Get the classloader used for instance creation. + * @return the classloader, never null. + */ + public ClassLoader getClassloader(){ + return classloader; + } + + /** + * Sets the classloader to use for loading of instances. + * @param ClassLoader the classloader, not null. + * @return this instance for chaining. + */ + public FilterManager setClassloader(ClassLoader ClassLoader){ + this.classloader = Objects.requireNonNull(classloader); + return this; + } + + /** + * Get the current list of filters. + * @return the list of filters. + */ + public List getFilters(){ + return Collections.unmodifiableList(filters); + } + + /** + * Adds the given Filter instances, hereby the instances are added + * to the end of the list with highest priority. The ordering of existing + * property filters remains unchanged. To sort the + * filters call {@link #sortFilter}. + * + * @param filters the filters to add + * @return this instance, for chaining, never null. + */ + public FilterManager addFilter(Filter... filters) { + return addFilter(Arrays.asList(filters)); + } + + /** + * Adds the given Filter instances, hereby the instances are added + * to the end of the list with highest priority. The ordering of existing + * property filters remains unchanged. To sort the + * filters call {@link #sortFilter}. + * + * @param filters the filters to add + * @return this instance, for chaining, never null. + */ + public FilterManager addFilter(Collection filters) { + Objects.requireNonNull(filters); + for(Filter filter:filters) { + if (!this.filters.contains(filter)) { + this.filters.add(filter); + } + } + return this; + } + + /** + * Removes the given PropertyFilter instances, if existing. The order of the remaining + * filters is preserved. + * + * @param filters the filter to remove + * @return this builder, for chaining, never null. + */ + public FilterManager removeFilters(Filter... filters){ + return removeFilters(Arrays.asList(filters)); + } + + /** + * Removes the given PropertyFilter instances, if existing. The order of the remaining + * filters is preserved. + * + * @param filters the filter to remove + * @return this builder, for chaining, never null. + */ + public FilterManager removeFilters(Collection filters){ + Objects.requireNonNull(filters); + this.filters.removeAll(filters); + return this; + } + + /** + * Add all registered (default) property filters to the context built. + * @return this builder, for chaining, never null. + */ + public FilterManager addDefaultFilters() { + for(Filter pf: ServiceContextManager.getServiceContext().getServices(Filter.class, classloader)){ + addFilter(pf); + } + return this; + } + + public FilterManager sortFilter(Comparator comparator) { + Collections.sort(filters, comparator); + return this; + } + + /** + * Removes all contained items. + * @return this instance for chaining. + */ + public FilterManager clear() { + this.filters.clear(); + return this; + } + + /** + * Filters a single value. + * @param value the raw value, not {@code null}. + * @return the filtered value, including {@code null}. + */ + public ConfigValue filterValue(ConfigValue value) { + FilterContext filterContext = new FilterContext(value, null); + return filterValue(filterContext); + } + + /** + * Filters a single value. + * @param value the raw value, not {@code null}. + * @param config the config + * @return the filtered value, including {@code null}. + */ + public ConfigValue filterValue(ConfigValue value, Config config) { + FilterContext filterContext = new FilterContext(value, config); + return filterValue(filterContext); + } + + /** + * Filters all properties. + * @param rawProperties the unfiltered properties, not {@code null}. + * @param config the config + * @return the filtered value, inclusing null. + */ + public Map applyFilters(Map rawProperties, Config config) { + Map result = new HashMap<>(); + // Apply filters to values, prevent values filtered to null! + for (Map.Entry entry : rawProperties.entrySet()) { + FilterContext filterContext = new FilterContext(ConfigValue.of(entry.getKey(), rawProperties), config); + ConfigValue filtered = filterValue(filterContext); + if(filtered!=null){ + result.putAll(filtered.asMap()); + } + } + return result; + } + + /** + * Basic filter logic. + * @param context the filter context, not {@code null}. + * @return the filtered value. + */ + private ConfigValue filterValue(FilterContext context) { + ConfigValue inputValue = context.getProperty(); + ConfigValue filteredValue = inputValue; + for (int i = 0; i < MAX_FILTER_LOOPS; i++) { + int changes = 0; + for (Filter filter :filters) { + filteredValue = filter.filterProperty(inputValue); + if (filteredValue != null && !filteredValue.equals(inputValue)) { + changes++; + LOG.finest("Filter - " + inputValue + " -> " + filteredValue + " by " + filter); + } + if(filteredValue==null){ + LOG.finest("Filter removed entry - " + inputValue + ": " + filter); + break; + }else{ + inputValue = filteredValue; + } + } + if (changes == 0) { + LOG.finest("Finishing filter loop, no changes detected."); + break; + } else if (filteredValue == null) { + break; + } else { + if (i == (MAX_FILTER_LOOPS - 1)) { + if (LOG.isLoggable(Level.WARNING)) { + LOG.warning("Maximal filter loop count reached, aborting filter evaluation after cycles: " + i); + } + } else { + LOG.finest("Repeating filter loop, changes detected: " + changes); + } + } + } + return filteredValue; + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(); + b.append("Filters\n"); + b.append("-------\n"); + if(filters.isEmpty()){ + b.append(" No filters loaded.\n\n"); + }else { + b.append(" CLASS INFO\n\n"); + for (Filter filter : filters) { + b.append(" "); + FormatUtils.appendFormatted(b, filter.getClass().getSimpleName(), 30); + b.append(FormatUtils.removeNewLines(filter.toString())); + b.append('\n'); + } + b.append("\n"); + } + return b.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/filter/package-info.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/filter/package-info.java b/code/base/src/main/java/org/apache/tamaya/base/filter/package-info.java new file mode 100644 index 0000000..7880500 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/filter/package-info.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +/** + * Default implementations for filter management and evaluation. + */ +package org.apache.tamaya.base.filter; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/base/package-info.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/base/package-info.java b/code/base/src/main/java/org/apache/tamaya/base/package-info.java new file mode 100644 index 0000000..820476e --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/base/package-info.java @@ -0,0 +1,23 @@ +/* + * 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. + */ + +/** + * Default implementations for config and related artifacts. + */ +package org.apache.tamaya.base; \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/ConfigContext.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/spi/ConfigContext.java b/code/base/src/main/java/org/apache/tamaya/spi/ConfigContext.java new file mode 100644 index 0000000..7c77e12 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/spi/ConfigContext.java @@ -0,0 +1,154 @@ +/* + * 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 javax.config.spi.ConfigSource; +import javax.config.spi.Converter; +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * Central SPI for programmatically dealing with the setup of the configuration system. + * This includes adding and enlisting {@link javax.config.spi.ConfigSource}s, + * managing {@link javax.config.spi.Converter}s, ConfigFilters, etc. + */ +public interface ConfigContext { + + /** + * This method returns the current list of registered PropertySources ordered via their ordinal. + * PropertySources with a lower ordinal come last. The PropertySource with the + * highest ordinal comes first. + * If two PropertySources have the same ordinal number they will get sorted + * using their class name just to ensure the user at least gets the same ordering + * after a JVM restart, hereby names before are added last. + * PropertySources are loaded when this method is called the first time, which basically is + * when the first time configuration is accessed. + * + * @return a sorted list of registered PropertySources. The returned list need not be modifiable + */ + List getSources(); + +// /** +// * Access a {@link ConfigSource} using its (unique) name. +// * @param name the propoerty source's name, not {@code null}. +// * @return the propoerty source found, or {@code null}. +// */ +// default ConfigSource getSource(String name) { +// for(ConfigSource ps: getSources()){ +// if(name.equals(ps.getName())){ +// return ps; +// } +// } +// return null; +// } + + /** + * Access the current PropertyFilter instances. + * @return the list of registered PropertyFilters, never null. + */ + List getFilters(); + + /** + *

+ * This method returns the Map of registered PropertyConverters + * per type. + * The List for each type is ordered via their {@link javax.annotation.Priority} and + * cladd name. + *

+ * + *

A simplified scenario could be like:

+ *
+     *  {
+     *      Date.class -> {StandardDateConverter, TimezoneDateConverter, MyCustomDateConverter }
+     *      Boolean.class -> {StandardBooleanConverter, FrenchBooleanConverter}
+     *      Integer.class -> {DynamicDefaultConverter}
+     *  }
+     * 
+ * + * @return map with sorted list of registered PropertySources per type. + */ + Map> getConverters(); + + /** + *

+ * This method returns the registered PropertyConverters for a given type. + * The List for each type is ordered via their {@link javax.annotation.Priority}. + *

+ * + *

+ * PropertyConverters with a higher Priority come first. The PropertyConverter with the + * lowest Priority comes last. + * If two PropertyConverter have the same ordinal number they will get sorted + * using their class name just to ensure the user at least gets the same ordering + * after a JVM restart. + *

+ * + *

+ * Additionally if a PropertyProvider is accessed, which is not registered the implementation + * should try to figure out, if there could be a default implementation as follows:

+ *
    + *
  1. Look for static factory methods: {@code of(String), valueOf(String), getInstance(String), + * instanceOf(String), fomr(String)}
  2. + *
  3. Look for a matching constructor: {@code T(String)}.
  4. + *
+ * + *

+ * If a correspoding factory method or constructor could be found, a corresponding + * PropertyConverter should be created and registered automatically for the given + * type. + *

+ * + *

The scenario could be like:

+ * + *
+     *  {
+     *      Date.class -> {MyCustomDateConverter,StandardDateConverter, TimezoneDateConverter}
+     *      Boolean.class -> {StandardBooleanConverter, FrenchBooleanConverter}
+     *      Integer.class -> {DynamicDefaultConverter}
+     *  }
+     * 
+ * + *

+ * The converters returned for a type should be used as a chain, whereas the result of the + * first converters that is able to convert the configured value, is taken as the chain's result. + * No more converters are called after a converters has successfully converted the input into + * the required target type. + *

+ * + * @param type type of the desired converters + * @return a sorted list of registered PropertySources per type, or null. + */ + default List getConverters(Type type){ + return Optional.ofNullable(getConverters().get(type)).orElse(Collections.emptyList()); + } + + /** + * Access the {@link ConfigValueCombinationPolicy} used to evaluate the final + * property values. + * @return the {@link ConfigValueCombinationPolicy} used, never null. + */ + default ConfigValueCombinationPolicy getConfigValueCombinationPolicy(){ + return ConfigValueCombinationPolicy.DEFAULT_OVERRIDING_POLICY; + } + +} + http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/ConfigValue.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/spi/ConfigValue.java b/code/base/src/main/java/org/apache/tamaya/spi/ConfigValue.java new file mode 100644 index 0000000..fedcef5 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/spi/ConfigValue.java @@ -0,0 +1,209 @@ +/* + * 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.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Class modelling the result of a request for a property value. A property value is basically identified by its key. + * There might be reasons, where one want to further analyze, which PropertySources provided a value and which not, so + * it is possible to create a PropertyValue with a null value. Nevertheless in all cases the provider source (typically + * the name of the PropertySource) must be set. + */ +public final class ConfigValue implements Serializable{ + private static final long serialVersionUID = 1L; + /** The requested key. */ + private String key; + /** The value. */ + private String value; + /** Additional metadata provided by the provider. */ + private String metaEntry; + + ConfigValue(ConfigValueBuilder builder){ + this.key = Objects.requireNonNull(builder.key); + this.value = Objects.requireNonNull(builder.value); + this.metaEntry = builder.metaEntry; + } + + /** + * Creates a new instance + * @param key the key, not {@code null}. + * @param value the value, not {@code null}. + * @param metaEntry the source, typically the name of the {@link javax.config.spi.ConfigSource} providing + * the value, not {@code null}. + */ + private ConfigValue(String key, String value, String metaEntry){ + this.key = Objects.requireNonNull(key, "Key is required."); + this.value = Objects.requireNonNull(value, "Value is required."); + this.metaEntry = metaEntry; + } + + /** + * The requested key. + * @return the, key never {@code null}. + */ + public String getKey() { + return key; + } + + /** + * The value. + * @return the value, in case a value is null it is valid to return {#code null} as result for + * {@link javax.config.spi.ConfigSource#getValue(String)}}. + */ + public String getValue() { + return this.value; + } + + /** + * Creates a full configuration map for this key, value pair and all its meta context data. This map + * is also used for subsequent processing, like value filtering. + * @return the property value entry map. + */ + public String getMetaEntry() { + return metaEntry; + } + + /** + * Creates a new builder instance. + * @param key the key, not {@code null}. + * @return a new builder instance. + */ + public static ConfigValueBuilder builder(String key){ + Objects.requireNonNull(key, "Key must be given."); + return new ConfigValueBuilder(key); + } + + /** + * Creates a new builder instance. + * @param key the key, not {@code null}. + * @param value the property value, not {@code null}. + * @return a new builder instance. + */ + public static ConfigValueBuilder builder(String key, String value) { + Objects.requireNonNull(key, "Key must be given."); + Objects.requireNonNull(value, "Value must be given"); + return new ConfigValueBuilder(key, value); + } + + + /** + * Creates a new PropertyValue without any metadata.. + * @param key the key, not {@code null}. + * @param value the value. + * @param metaEntry the metaEntry, typically the name of the {@link javax.config.spi.ConfigSource} + * providing the value, not {@code null}. + * @return a new property value instance, or {@code null}, + * if the value passed is {@code null}.. + */ + public static ConfigValue of(String key, String value, String metaEntry) { + if (value==null) { + return null; + } + return new ConfigValue(key, value, metaEntry); + } + + /** + * Creates a new builder instance based on this item. + * @return a new builder, never null. + */ + public ConfigValueBuilder toBuilder() { + return new ConfigValueBuilder(this.getKey()) + .setValue(this.getValue()) + .setMetaEntry(this.metaEntry); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ConfigValue)) return false; + ConfigValue that = (ConfigValue) o; + return Objects.equals(getKey(), that.getKey()) && + Objects.equals(getValue(), that.getValue()) && + Objects.equals(getMetaEntry(), that.getMetaEntry()); + } + + @Override + public int hashCode() { + return Objects.hash(getKey(), getValue(), + getMetaEntry()); + } + + @Override + public String toString() { + return "PropertyValue{" + + "key='" + key + '\'' + + ", value='" + value + '\'' + + ", metaEntry='" + metaEntry + '\'' + + '}'; + } + + /** + * Maps a map of {@code Map} to a {@code Map}. + * @param config the String based map, not {@code null}. + * @return the corresponding value based map. + */ + public static Map map(Map config) { + Map result = new HashMap<>(config.size()); + for(Map.Entry en:config.entrySet()){ + result.put(en.getKey(), ConfigValue.of(en.getKey(), en.getValue(), config.get(en.getKey()+"[meta]"))); + } + return result; + } + + /** + * Maps a map of {@code Map} to a {@code Map}. + * + * @param config the String based map, not {@code null}. + * @param source the source name, not {@code null}. + * @param metaData additional metadata, not {@code null}. + * @return the corresponding value based map. + */ + public static Map map(Map config, String source, + Map metaData) { + Objects.requireNonNull(config, "Config must be given."); + Objects.requireNonNull(source, "Source must be given."); + Objects.requireNonNull(metaData, "Meta data must be given."); + + Map result = new HashMap<>(config.size()); + + for(Map.Entry en:config.entrySet()){ + ConfigValue value = new ConfigValueBuilder(en.getKey(), source).setValue(en.getValue()) + .addMetaEntries(metaData).build(); + result.put(en.getKey(), value); + } + return result; + } + + public Map asMap() { + Map map = new HashMap<>(); + map.put(key, value); + map.put(key+"[meta]", this.metaEntry); + return map; + } + + public static ConfigValue of(String key, Map rawProperties) { + String value = rawProperties.get(key); + String meta = rawProperties.get(key+"[meta]"); + return new ConfigValue(key, value, meta); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/ConfigValueBuilder.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/spi/ConfigValueBuilder.java b/code/base/src/main/java/org/apache/tamaya/spi/ConfigValueBuilder.java new file mode 100644 index 0000000..d1c2d60 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/spi/ConfigValueBuilder.java @@ -0,0 +1,179 @@ +/* + * 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.io.StringWriter; +import java.util.*; + +/** + * Builder to create a {@link ConfigValue} instance. + */ +public class ConfigValueBuilder { + /** The key accessed. */ + String key; + /** The property value. */ + String value; + /** additional metadata entries (optional). */ + String metaEntry; + + /** + * Create a new builder instance, for a given set of parameters. + * Before calling build at least a {@link #value} and its {@link #metaEntry} + * must be set. + */ + ConfigValueBuilder(String key){ + this.key = Objects.requireNonNull(key); + } + + /** + * Create a new builder instance, for a given set of parameters. + * @param key to access a property value, not {@code null}. + * @param value property value. + */ + ConfigValueBuilder(String key, String value) { + this.key = Objects.requireNonNull(key); + this.value = Objects.requireNonNull(value); + } + + /** + * Create a new builder instance, for a given set of parameters. + * + * @param key to access a property value. + * @param value the value, not {@code null}. If a value is {@code null} + * {@link javax.config.spi.ConfigSource#getValue(String)} should return {@code null}. + * @param metaEntry property metaEntry. + */ + ConfigValueBuilder(String key, String value, String metaEntry) { + this.key = Objects.requireNonNull(key); + this.value = value; + this.metaEntry = Objects.requireNonNull(metaEntry); + } + + /** + * Replaces/sets the context data. + * @param metaEntry the context data to be applied. + * @return the builder for chaining. + */ + public ConfigValueBuilder setMetaEntry(String metaEntry) { + this.metaEntry = metaEntry; + return this; + } + + /** + * Add an additional context data information. + * @param key the context data key, not {@code null}. + * @param value the context value, not {@code null} (will be converted to String). + * @return the builder for chaining. + */ + public ConfigValueBuilder addMetaEntry(String key, String value) { + Objects.requireNonNull(key, "Meta key must be given."); + Objects.requireNonNull(value, "Meta value must be given."); + if(metaEntry==null){ + metaEntry = key+"="+value; + }else{ + metaEntry = "\n" + key+"="+value; + } + return this; + } + + /** + * Adds the context data given. + * @param metaEntries the context data to be applied, not {@code null}. + * @return the builder for chaining. + */ + public ConfigValueBuilder addMetaEntries(Map metaEntries) { + Properties props = new Properties(); + props.putAll(metaEntries); + StringWriter stringWriter = new StringWriter(); + try { + props.store(stringWriter, null); + stringWriter.flush(); + if(metaEntry==null){ + metaEntry = stringWriter.toString(); + }else{ + metaEntry += '\n' + stringWriter.toString(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + + /** + * Adds the context data given as JSON object. + * @param meta the context data in JSON format, not {@code null}. + * @return the builder for chaining. + */ + public ConfigValueBuilder addMetaEntry(String meta) { + if(metaEntry==null){ + metaEntry = meta; + }else{ + metaEntry += '\n' + meta; + } + return this; + } + + /** + * Get the value's context data. + * @return the context data, not {@code null}. + */ + public String getMetaEntry() { + return metaEntry; + } + + /** + * Sets a new key. + * @param key the new key, not {@code null}. + * @return the builder for chaining. + */ + public ConfigValueBuilder setKey(String key) { + this.key = Objects.requireNonNull(key); + return this; + } + + /** + * Sets a new value. + * @param value the new value, not {@code null}. + * @return the builder for chaining. + */ + public ConfigValueBuilder setValue(String value) { + this.value = Objects.requireNonNull(value, "Value must be given."); + + return this; + } + + /** + * Creates a new immutable {@link ConfigValue}. + * @return a new immutable {@link ConfigValue}, never {@code null}. + */ + public ConfigValue build(){ + return new ConfigValue(this); + } + + @Override + public String toString() { + return "PropertyValueBuilder{" + + "key='" + key + '\'' + + "value='" + value + '\'' + + ", metaEntry=" + metaEntry + + '}'; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/Experimental.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/spi/Experimental.java b/code/base/src/main/java/org/apache/tamaya/spi/Experimental.java new file mode 100644 index 0000000..9831820 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/spi/Experimental.java @@ -0,0 +1,32 @@ +/* + * 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.lang.annotation.*; + +/** + * This is a simple annotation for flaging out functionality or features the Tamaya team is not sure if it is already + * stabilized, so use it with some caution. + */ +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, + ElementType.TYPE}) +public @interface Experimental { +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/Filter.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/spi/Filter.java b/code/base/src/main/java/org/apache/tamaya/spi/Filter.java new file mode 100644 index 0000000..9fdad86 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/spi/Filter.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.tamaya.spi; + + +/** + *

Interface for filtering the current map of properties during the evaluation of the chain of PropertySources. + * Filters can be registered using the {@link org.apache.tamaya.spi.ServiceContext}. The ordinal + * hereby is defined by the corresponding {@code @Priority} annotation.

+ *

Filters

+ */ +@FunctionalInterface +public interface Filter { + + /** + *

Maps the current {@code valueToBeFiltered} value to a new value. The resulting value will be used as the result + * passed to the user.

+ *

If a filter is currently not available, it should just pass the input map to the method's + * output.

+ *

Returning {@code null} will remove the entry.

+ *

Implementation specification

+ * Implementations of this class must be + *
    + *
  • reentrant
  • + *
  • thread-safe
  • + *
+ * @param value the value to be filtered, which also can be {@code null} if removed by another filter. + * @return the filtered value, or {@code null} if the value should be removed alltogether. + * @see ConfigValue + * @see ConfigValueBuilder + */ + ConfigValue filterProperty(ConfigValue value); + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/ServiceContext.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/spi/ServiceContext.java b/code/base/src/main/java/org/apache/tamaya/spi/ServiceContext.java new file mode 100644 index 0000000..ff0bb9f --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/spi/ServiceContext.java @@ -0,0 +1,147 @@ +/* + * 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.Enumeration; +import java.util.List; + + +/** + * This class models the component that is managing the lifecycle current the + * services used by the Configuration API. + */ +public interface ServiceContext { + + /** + * @return ordinal of the ServiceContext. The one with the highest ordinal will be taken. + */ + int ordinal(); + + static ClassLoader defaultClassLoader(){ + ClassLoader classloader = Thread.currentThread().getContextClassLoader(); + if(classloader==null){ + classloader = ServiceContextManager.class.getClassLoader(); + } + return classloader; + } + + /** + * Access a service singleton via its type. + * If multiple implementations for the very serviceType exist then + * the one with the highest {@link javax.annotation.Priority} will be used. + * + * @param the type of the service type. + * @param serviceType the service type. + * @return The instance to be used, or {@code null} + * @throws IllegalArgumentException if there are multiple service implementations with the maximum priority. + */ + default T getService(Class serviceType){ + return getService(serviceType, defaultClassLoader()); + } + + /** + * Access a service singleton via its type. + * If multiple implementations for the very serviceType exist then + * the one with the highest {@link javax.annotation.Priority} will be used. + * + * @param the type of the service type. + * @param serviceType the service type. + * @param classLoader the class loader to be considered. + * @return The instance to be used, or {@code null} + * @throws IllegalArgumentException if there are multiple service implementations with the maximum priority. + */ + T getService(Class serviceType, ClassLoader classLoader); + + /** + * Factory method to create a type, hereby a new instance is created on each access. + * If multiple implementations for the very serviceType exist then + * the one with the highest {@link javax.annotation.Priority} will be used as the base + * for creating subsequent instances. + * + * @param the type of the service type. + * @param serviceType the service type. + * @return The new instance to be used, or {@code null} + * @throws IllegalArgumentException if there are multiple service implementations with the maximum priority. + */ + default T create(Class serviceType){ + return create(serviceType, defaultClassLoader()); + } + + /** + * Factory method to create a type, hereby a new instance is created on each access. + * If multiple implementations for the very serviceType exist then + * the one with the highest {@link javax.annotation.Priority} will be used as the base + * for creating subsequent instances. + * + * @param the type of the service type. + * @param serviceType the service type. + * @param classLoader the class loader to be considered. + * @return The new instance to be used, or {@code null} + * @throws IllegalArgumentException if there are multiple service implementations with the maximum priority. + */ + T create(Class serviceType, ClassLoader classLoader); + + + /** + * Access a list current services, given its type. The bootstrap mechanism should + * order the instance for precedence, hereby the most significant should be + * first in order. + * + * @param serviceType + * the service type. + * @param the type of the list element returned by this method + * @return The instance to be used, never {@code null} + */ + default List getServices(Class serviceType){ + return getServices(serviceType, defaultClassLoader()); + } + + /** + * Access a list current services, given its type. The bootstrap mechanism should + * order the instance for precedence, hereby the most significant should be + * first in order. + * + * @param serviceType + * the service type. + * @param the type of the list element returned by this method + * @return The instance to be used, never {@code null} + */ + List getServices(Class serviceType, ClassLoader classLoader); + + /** + * Loads resources from the current runtime context. This method allows to use runtime + * specific code to load resources, e.g. within OSGI environments. + * @param resource the resource, not {@code null}. + * @param cl the desired classloader context, if null, the current thread context classloader is used. + * @return the resources found + * @throws IOException if load fails. + */ + Enumeration getResources(String resource, ClassLoader cl) throws IOException; + + /** + * Loads a resource from the current runtime context. This method allows to use runtime + * specific code to load a resource, e.g. within OSGI environments. + * @param resource the resource, not {@code null}. + * @param cl the desired classloader context, if null, the current thread context classloader is used. + * @return the resource found, or {@code null}. + */ + URL getResource(String resource, ClassLoader cl); +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java b/code/base/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java new file mode 100644 index 0000000..ddfd425 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/spi/ServiceContextManager.java @@ -0,0 +1,118 @@ +/* + * 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.util.Objects; +import java.util.ServiceLoader; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * This singleton provides access to the services available in the current {@link ServiceContext}. The + * behaviour can be adapted, by calling {@link ServiceContextManager#set(ServiceContext)} before accessing any + * services. + */ +public final class ServiceContextManager { + + /** + * The logger used. + */ + private static final Logger LOG = Logger.getLogger(ServiceContextManager.class.getName()); + + /** + * The ServiceProvider used. + */ + private static volatile ServiceContext serviceContextProviderDelegate; + + /** + * Private singletons constructor. + */ + private ServiceContextManager() { + } + + /** + * Load the {@link ServiceContext} to be used. + * + * @return {@link ServiceContext} to be used for loading the services. + */ + private static ServiceContext loadDefaultServiceProvider() { + ServiceContext highestServiceContext = null; + try { + int highestOrdinal = 0; + for (ServiceContext serviceContext : ServiceLoader.load(ServiceContext.class)) { + if (highestServiceContext == null) { + highestServiceContext = serviceContext; + } else if (serviceContext.ordinal() > highestOrdinal) { + highestServiceContext = serviceContext; + highestOrdinal = serviceContext.ordinal(); + } + } + } catch (Exception e) { + throw new IllegalStateException("ServiceContext not loadable", e); + } + if (highestServiceContext == null) { + throw new IllegalStateException("No ServiceContext found"); + } + LOG.info("Using Service Context of type: " + highestServiceContext.getClass().getName()); + return highestServiceContext; + } + + /** + * Replace the current {@link ServiceContext} in use. + * + * @param serviceContextProvider the new {@link ServiceContext}, not {@code null}. + * @return the currently used context after setting it. + */ + public static ServiceContext set(ServiceContext serviceContextProvider) { + Objects.requireNonNull(serviceContextProvider); + ServiceContext currentContext = ServiceContextManager.serviceContextProviderDelegate; + + synchronized (ServiceContextManager.class) { + if (ServiceContextManager.serviceContextProviderDelegate == null) { + ServiceContextManager.serviceContextProviderDelegate = serviceContextProvider; + LOG.log(Level.INFO, "Using ServiceProvider: " + serviceContextProvider.getClass().getName()); + } else { + LOG.log(Level.WARNING, "Replacing ServiceProvider " + + ServiceContextManager.serviceContextProviderDelegate.getClass().getName() + + " with: " + serviceContextProvider.getClass().getName()); + ServiceContextManager.serviceContextProviderDelegate = serviceContextProvider; + } + } + + return currentContext; + } + + /** + * Ge {@link ServiceContext}. If necessary the {@link ServiceContext} will be laziliy loaded. + * + * @return the {@link ServiceContext} used. + */ + public static ServiceContext getServiceContext(){ + if (serviceContextProviderDelegate == null) { + synchronized (ServiceContextManager.class) { + if (serviceContextProviderDelegate == null) { + serviceContextProviderDelegate = loadDefaultServiceProvider(); + } + } + } + return serviceContextProviderDelegate; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/StandaloneConfigContextBuilder.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/spi/StandaloneConfigContextBuilder.java b/code/base/src/main/java/org/apache/tamaya/spi/StandaloneConfigContextBuilder.java new file mode 100644 index 0000000..f8cd841 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/spi/StandaloneConfigContextBuilder.java @@ -0,0 +1,407 @@ +///* +// * 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.base.configsource.ConfigSourceComparator; +//import org.apache.tamaya.base.configsource.CLIConfigSource; +//import org.apache.tamaya.base.configsource.EnvironmentConfigSource; +//import org.apache.tamaya.base.configsource.JavaConfigurationConfigSource; +//import org.apache.tamaya.base.configsource.SystemConfigSource; +// +//import javax.config.spi.ConfigSource; +//import javax.config.spi.ConfigSourceProvider; +//import javax.config.spi.Converter; +//import java.lang.reflect.ParameterizedType; +//import java.lang.reflect.Type; +//import java.util.*; +//import java.util.logging.Logger; +// +///** +// * Default implementation of {@link StandaloneConfigContextBuilder}. +// */ +//public final class StandaloneConfigContextBuilder { +// +// private static final Logger LOG = Logger.getLogger(StandaloneConfigContextBuilder.class.getName()); +// +// protected List filters = new ArrayList<>(); +// protected List propertySources = new ArrayList<>(); +// protected ConfigValueCombinationPolicy combinationPolicy = ConfigValueCombinationPolicy.DEFAULT_OVERRIDING_POLICY; +// protected Map> propertyConverters = new HashMap<>(); +// +// /** +// * Flag if the config has already been built. +// * Configuration can be built only once +// */ +// private boolean built; +// private ClassLoader classLoader; +// +// /** +// * Creates a new builder instance. +// */ +// public StandaloneConfigContextBuilder() { +// } +// +// /** +// * Creates a new builder instance initializing it with the given context. +// * @param context the context to be used, not null. +// */ +// public StandaloneConfigContextBuilder(ConfigContext context) { +// this.propertyConverters.putAll(context.getConverters()); +// this.filters.addAll(context.getFilters()); +// for(ConfigSource ps:context.getSources()) { +// withSources(ps); +// } +// this.combinationPolicy = context.getConfigValueCombinationPolicy(); +// } +// +// /** +// * Allows to reset configuration context during unit tests. +// */ +// public final StandaloneConfigContextBuilder reset() { +// checkBuilderState(); +// this.filters.clear(); +// this.propertySources.clear(); +// this.propertyConverters.clear(); +// this.combinationPolicy = ConfigValueCombinationPolicy.DEFAULT_OVERRIDING_POLICY; +// return this; +// } +// +// +// public StandaloneConfigContextBuilder withContext(ConfigContext context) { +// checkBuilderState(); +// this.propertyConverters.putAll(context.getConverters()); +// for(ConfigSource ps:context.getSources()){ +// this.propertySources.add(ps); +// } +// this.filters.addAll(context.getFilters()); +// this.combinationPolicy = context.getConfigValueCombinationPolicy(); +// return this; +// } +// +// public final StandaloneConfigContextBuilder withSources(ConfigSource... sources){ +// return withSources(Arrays.asList(sources)); +// } +// +// public StandaloneConfigContextBuilder withSources(Collection sources){ +// checkBuilderState(); +// for(ConfigSource source:sources) { +// if (!this.propertySources.contains(source)) { +// this.propertySources.add(source); +// } +// } +// return this; +// } +// +// +// public StandaloneConfigContextBuilder addDiscoveredSources() { +// checkBuilderState(); +// List propertySources = new ArrayList<>(); +//// addDiscoveredSources(propertySources); +// for(ConfigSource ps: ServiceContextManager.getServiceContext().getServices(ConfigSource.class, classLoader)) { +// if(!propertySources.contains(ps)){ +// propertySources.add(ps); +// } +// } +// +// for(ConfigSourceProvider provider: +// ServiceContextManager.getServiceContext().getServices(ConfigSourceProvider.class, classLoader)){ +// for(ConfigSource src: provider.getConfigSources(classLoader)){ +// propertySources.add(src); +// } +// } +// Collections.sort(propertySources, ConfigSourceComparator.getInstance()); +// return withSources(propertySources); +// } +// +// private StandaloneConfigContextBuilder addDiscoveredSources(List propertySources) { +// for(ConfigSource ps: new ConfigSource[]{ +// new EnvironmentConfigSource(), +// new JavaConfigurationConfigSource(), +// new CLIConfigSource(), +// new SystemConfigSource() +// }){ +// if(!propertySources.contains(ps)){ +// propertySources.add(ps); +// } +// } +// return this; +// } +// +// public StandaloneConfigContextBuilder addDiscoveredPropertyFilters() { +// checkBuilderState(); +// for(Filter pf:ServiceContextManager.getServiceContext().getServices(Filter.class, classLoader)){ +// withFilters(pf); +// } +// return this; +// } +// +// public StandaloneConfigContextBuilder addDiscoveredConverters() { +// checkBuilderState(); +// addDiscoveredConverters(); +// for(Map.Entry> en: getDefaultConverters().entrySet()){ +// for(Converter pc: en.getValue()) { +// withConverters(en.getKey(), pc); +// } +// } +// return this; +// } +// +// @SuppressWarnings("unchecked") +// public void addDiscoveredConverters() { +// // should be overridden by subclasses. +// } +// +// public final StandaloneConfigContextBuilder removeSources(ConfigSource... propertySources) { +// return removeSources(Arrays.asList(propertySources)); +// } +// +// public StandaloneConfigContextBuilder removeSources(Collection propertySources) { +// checkBuilderState(); +// this.propertySources.removeAll(propertySources); +// return this; +// } +// +// protected ConfigSource getSource(String name) { +// for(ConfigSource ps:propertySources){ +// if(ps.getName().equals(name)){ +// return ps; +// } +// } +// throw new IllegalArgumentException("No such PropertySource: "+name); +// } +// +// public List getSources() { +// return Collections.unmodifiableList(this.propertySources); +// } +// +// public StandaloneConfigContextBuilder increasePriority(ConfigSource propertySource) { +// checkBuilderState(); +// int index = propertySources.indexOf(propertySource); +// if(index<0){ +// throw new IllegalArgumentException("No such PropertySource: " + propertySource); +// } +// if(index<(propertySources.size()-1)){ +// propertySources.remove(propertySource); +// propertySources.add(index+1, propertySource); +// } +// return this; +// } +// +// public StandaloneConfigContextBuilder decreasePriority(ConfigSource propertySource) { +// checkBuilderState(); +// int index = propertySources.indexOf(propertySource); +// if(index<0){ +// throw new IllegalArgumentException("No such PropertySource: " + propertySource); +// } +// if(index>0){ +// propertySources.remove(propertySource); +// propertySources.add(index-1, propertySource); +// } +// return this; +// } +// +// public StandaloneConfigContextBuilder highestPriority(ConfigSource propertySource) { +// checkBuilderState(); +// int index = propertySources.indexOf(propertySource); +// if(index<0){ +// throw new IllegalArgumentException("No such PropertySource: " + propertySource); +// } +// if(index<(propertySources.size()-1)){ +// propertySources.remove(propertySource); +// propertySources.add(propertySource); +// } +// return this; +// } +// +// public StandaloneConfigContextBuilder lowestPriority(ConfigSource propertySource) { +// checkBuilderState(); +// int index = propertySources.indexOf(propertySource); +// if(index<0){ +// throw new IllegalArgumentException("No such PropertySource: " + propertySource); +// } +// if(index>0){ +// propertySources.remove(propertySource); +// propertySources.add(0, propertySource); +// } +// return this; +// } +// +// public final StandaloneConfigContextBuilder withFilters(Filter... filters){ +// return withFilters(Arrays.asList(filters)); +// } +// +// public final StandaloneConfigContextBuilder withFilters(Collection filters){ +// checkBuilderState(); +// for(Filter f:filters) { +// if (!this.filters.contains(f)) { +// this.filters.add(f); +// } +// } +// return this; +// } +// +// public final StandaloneConfigContextBuilder removeFilters(Filter... filters) { +// return removeFilters(Arrays.asList(filters)); +// } +// +// public final StandaloneConfigContextBuilder removeFilters(Collection filters) { +// checkBuilderState(); +// this.filters.removeAll(filters); +// return this; +// } +// +// +// public final StandaloneConfigContextBuilder removeConverters(Type typeToConvert, +// @SuppressWarnings("unchecked") Converter... converters) { +// return removeConverters(typeToConvert, Arrays.asList(converters)); +// } +// +// public final StandaloneConfigContextBuilder removeConverters(Type typeToConvert, +// Collection> converters) { +// Collection subConverters = this.propertyConverters.get(typeToConvert); +// if(subConverters!=null) { +// subConverters.removeAll(converters); +// } +// return this; +// } +// +// public final StandaloneConfigContextBuilder removeConverters(TypeLiteral typeToConvert) { +// this.propertyConverters.remove(typeToConvert); +// return this; +// } +// +// +// public final StandaloneConfigContextBuilder withPropertyValueCombinationPolicy(ConfigValueCombinationPolicy combinationPolicy){ +// checkBuilderState(); +// this.combinationPolicy = Objects.requireNonNull(combinationPolicy); +// return this; +// } +// +// +// public StandaloneConfigContextBuilder withConverters(Type type, Converter... propertyConverters){ +// checkBuilderState(); +// Objects.requireNonNull(type); +// Objects.requireNonNull(propertyConverters); +// Collection converters = this.propertyConverters.get(type); +// if(converters==null){ +// converters = new ArrayList<>(); +// this.propertyConverters.put(type, converters); +// } +// for(Converter propertyConverter:propertyConverters) { +// if (!converters.contains(propertyConverter)) { +// converters.add(propertyConverter); +// } else { +// LOG.warning("Converter ignored, already registered: " + propertyConverter); +// } +// } +// return this; +// } +// +// public StandaloneConfigContextBuilder withConverters(Type type, Collection> propertyConverters){ +// checkBuilderState(); +// Objects.requireNonNull(type); +// Objects.requireNonNull(propertyConverters); +// Collection converters = this.propertyConverters.get(type); +// if(converters==null){ +// converters = new ArrayList<>(); +// this.propertyConverters.put(type, converters); +// } +// for(Converter propertyConverter:propertyConverters) { +// if (!converters.contains(propertyConverter)) { +// converters.add(propertyConverter); +// } else { +// LOG.warning("Converter ignored, already registered: " + propertyConverter); +// } +// } +// return this; +// } +// +// protected StandaloneConfigContextBuilder loadDefaults() { +// checkBuilderState(); +// this.combinationPolicy = ConfigValueCombinationPolicy.DEFAULT_OVERRIDING_COLLECTOR; +// addDiscoveredSources(); +// addDiscoveredPropertyFilters(); +// addDiscoveredConverters(); +// return this; +// } +// +// +// protected Map> getDefaultConverters() { +// Map> result = new HashMap<>(); +// for (Converter conv : ServiceContextManager.getServiceContext().getServices( +// Converter.class, classLoader)) { +// for(Type type:conv.getClass().getGenericInterfaces()){ +// if(type instanceof ParameterizedType){ +// ParameterizedType pt = (ParameterizedType)type; +// if(Converter.class.equals(((ParameterizedType) type).getRawType())){ +// Type target = pt.getActualTypeArguments()[0]; +// Collection convList = result.get(target); +// if (convList == null) { +// convList = new ArrayList<>(); +// result.put(target, convList); +// } +// convList.add(conv); +// } +// } +// } +// } +// return result; +// } +// +// +// /** +// * Builds a new configuration based on the configuration of this builder instance. +// * +// * @return a new {@link javax.config.Config configuration instance}, +// * never {@code null}. +// */ +// public ConfigContext build() { +// checkBuilderState(); +// built = true; +// return new StandaloneConfigContext(this); +// } +// +// public StandaloneConfigContextBuilder sortFilter(Comparator comparator) { +// Collections.sort(filters, comparator); +// return this; +// } +// +// public StandaloneConfigContextBuilder sortSources(Comparator comparator) { +// Collections.sort(propertySources, comparator); +// return this; +// } +// +// private void checkBuilderState() { +// if (built) { +// throw new IllegalStateException("Configuration has already been build."); +// } +// } +// +// public List getFilters() { +// return filters; +// } +// +// public Map> getConverter() { +// return Collections.unmodifiableMap(this.propertyConverters); +// } +// +// public void setClassLoader(ClassLoader classLoader) { +// this.classLoader = classLoader; +// } +//} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/TamayaConfigBuilder.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/spi/TamayaConfigBuilder.java b/code/base/src/main/java/org/apache/tamaya/spi/TamayaConfigBuilder.java new file mode 100644 index 0000000..e48a6bd --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/spi/TamayaConfigBuilder.java @@ -0,0 +1,333 @@ +/* + * 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.base.DefaultConfigBuilder; + +import javax.config.spi.ConfigBuilder; +import javax.config.spi.ConfigSource; +import javax.config.spi.Converter; +import java.lang.reflect.Type; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * A builder for creating new or adapting instances of {@link javax.config.Config}. + * Builders can be obtained in exactly two ways: + *
    + *
  1. By accessing an empty builder instance from + * {@link javax.config.spi.ConfigProviderResolver#getBuilder()}.
  2. + *
+ */ +public interface TamayaConfigBuilder extends ConfigBuilder, ConfigContextSupplier{ + + /** + * Create a new empty configuration builder. + * @return + */ + static TamayaConfigBuilder create() { + return new DefaultConfigBuilder(); + } + + static TamayaConfigBuilder create(ConfigContextSupplier contextSupplier){ + return new DefaultConfigBuilder(contextSupplier.getConfigContext()); + } + + static TamayaConfigBuilder from(ConfigBuilder configBuilder){ + if(configBuilder instanceof TamayaConfigBuilder) { + return (TamayaConfigBuilder) configBuilder; + }else if(configBuilder instanceof ConfigContextSupplier){ + return create((ConfigContextSupplier)configBuilder); + } + throw new IllegalArgumentException("Builder must implement ConfigContextSupplier."); + } + + /** + * This method can be used for programmatically adding {@link ConfigSource}s. + * Hereby the property source is added to the tail of property sources with + * lowest priority regardless of its current ordinal value. To sort the property + * sources based on their ordinals call {@link #sortSources}. + * + * @param propertySources the PropertySources to add + * @return this builder, for chaining, never null. + * @throws IllegalArgumentException If a property source with a given name already + * exists. + */ + TamayaConfigBuilder withSources(Collection propertySources); + + /** + * Removes the given property sources, if existing. The existing order of property + * sources is preserved. + * + * @param propertySources the property sources to remove, not {@code null}. + * @return the builder for chaining. + */ + TamayaConfigBuilder removeSources(ConfigSource... propertySources); + + /** + * Removes the given property sources, if existing. The existing order of property + * sources is preserved. + * + * @param propertySources the property sources to remove, not {@code null}. + * @return the builder for chaining. + */ + TamayaConfigBuilder removeSources(Collection propertySources); + + /** + * Increases the priority of the given property source, by moving it towards the end + * of the chain of property sources. If the property source given is already at the end + * this method has no effect. This operation does not change any ordinal values. + * + * @param propertySource the property source to be incresed regarding its significance. + * @return the builder for chaining. + * @throws IllegalArgumentException If no such property source exists in the current + * chain. + */ + TamayaConfigBuilder increasePriority(ConfigSource propertySource); + + /** + * Decreases the priority of the given property source, by moving it towards the start + * of the chain of property sources. If the property source given is already the first + * this method has no effect. This operation does not change any ordinal values. + * + * @param propertySource the property source to be decresed regarding its significance. + * @return the builder for chaining. + * @throws IllegalArgumentException If no such property source exists in the current + * chain. + */ + TamayaConfigBuilder decreasePriority(ConfigSource propertySource); + + /** + * Increases the priority of the given property source to be maximal, by moving it to + * the tail of the of property source chain. If the property source given is + * already the last item this method has no effect. This operation does not change + * any ordinal values. + * + * @param propertySource the property source to be maximized regarding its significance. + * @return the builder for chaining. + * @throws IllegalArgumentException If no such property source exists in the current + * chain. + */ + TamayaConfigBuilder highestPriority(ConfigSource propertySource); + + /** + * Decreases the priority of the given property source to be minimal, by moving it to + * the start of the chain of property source chain. If the property source given is + * already the first item this method has no effect. This operation does not change + * any ordinal values. + * + * @param propertySource the property source to be minimized regarding its significance. + * @return the builder for chaining. + * @throws IllegalArgumentException If no such property source exists in the current + * chain. + */ + TamayaConfigBuilder lowestPriority(ConfigSource propertySource); + + /** + * Access the current chain of property sources. Items at the end of the list have + * precedence/more significance. + * + * @return the property source chain, never {@code null}. + */ + List getSources(); + + /** + * Sorts the current registered property sources using the given comparator. + * + * NOTE: property sources at the beginning have minimal significance. + * + * @param comparator the comparator to be used, not {@code null}. + * @return this instance for chaining. + */ + TamayaConfigBuilder sortSources(Comparator comparator); + + /** + * Adds the given PropertyFilter instances, hereby the instances are added + * to the end of the list with highest priority. The ordering of existing + * property filters remains unchanged. To sort the property + * filters call {@link #sortFilter}. + * + * @param filters the filters to add + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder withFilters(Filter... filters); + + /** + * Adds the given PropertyFilter instances, hereby the instances are added + * to the end of the list with highest priority. The ordering of existing + * property filters remains unchanged. To sort the property + * filters call {@link #sortFilter}. + * + * @param filters the filters to add + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder withFilters(Collection filters); + + /** + * Add all registered (default) property filters to the context built. + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder addDiscoveredFilters(); + + /** + * Removes the given PropertyFilter instances, if existing. The order of the remaining + * filters is preserved. + * + * @param filters the filter to remove + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder removeFilters(Filter... filters); + + /** + * Removes the given PropertyFilter instances, if existing. The order of the remaining + * filters is preserved. + * + * @param filters the filter to remove + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder removeFilters(Collection filters); + + /** + * Access the current chain of property filters. Items at the end of the list have + * precedence/more significance. + * + * @return the property source chain, never {@code null}. + */ + List getFilters(); + + /** + * This method can be used for adding {@link Converter}s. + * Converters are added at the end after any existing converters. + * For converters already registered for the current target type the + * method has no effect. + * + * @param converters the converters to add for this type + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder withConverters(Collection> converters); + + /** + * This method can be used for adding {@link Converter}s. + * Converters are added at the end after any existing converters. + * For converters already registered for the current target type the + * method has no effect. + * + * @param typeToConvert the type for which the converters is for + * @param converters the converters to add for this type + * @param the target type. + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder withConverters(Class typeToConvert, Converter... converters); + + /** + * This method can be used for adding {@link Converter}s. + * Converters are added at the end after any existing converters. + * For converters already registered for the current target type the + * method has no effect. + * + * @param typeToConvert the type for which the converters is for + * @param converters the converters to add for this type + * @param the target type. + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder withConverters(Class typeToConvert, Collection> converters); + + + /** + * Removes the given PropertyConverter instances for the given type, + * if existing. + * + * @param typeToConvert the type which the converters is for + * @param converters the converters to remove + * @param the target type. + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder removeConverters(Class typeToConvert, Converter... converters); + + /** + * Removes the given PropertyConverter instances for the given type, + * if existing. + * + * @param typeToConvert the type which the converters is for + * @param converters the converters to remove + * @param the target type. + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder removeConverters(Class typeToConvert, Collection> converters); + + /** + * Removes all converters for the given type, which actually renders a given type + * unsupported for type conversion. + * + * @param typeToConvert the type which the converters is for + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder removeConverters(Class typeToConvert); + + /** + * Access the current registered property converters. + * + * @return the current registered property converters. + */ + Map> getConverter(); + + /** + * Sets the {@link ConfigValueCombinationPolicy} used to evaluate the final + * property values. + * + * @param policy the {@link ConfigValueCombinationPolicy} used, not {@code null}. + * @return this builder, for chaining, never null. + */ + TamayaConfigBuilder withPropertyValueCombinationPolicy(ConfigValueCombinationPolicy policy); + + /** + * Sorts the current registered property filters using the given comparator. + * + * NOTE: property filters at the beginning have minimal significance. + * + * @param comparator the comparator to be used, not {@code null}. + * @return this instance for chaining. + */ + TamayaConfigBuilder sortFilter(Comparator comparator); + + @Override + TamayaConfigBuilder addDefaultSources(); + + @Override + TamayaConfigBuilder addDiscoveredSources(); + + @Override + TamayaConfigBuilder addDiscoveredConverters(); + + @Override + TamayaConfigBuilder forClassLoader(ClassLoader loader); + + @Override + TamayaConfigBuilder withSources(ConfigSource... sources); + + @Override + TamayaConfigBuilder withConverters(Converter... converters); + + @Override + TamayaConfigBuilder withConverter(Class type, Converter converter); + +} + http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/063f8ada/code/base/src/main/java/org/apache/tamaya/spi/TypeLiteral.java ---------------------------------------------------------------------- diff --git a/code/base/src/main/java/org/apache/tamaya/spi/TypeLiteral.java b/code/base/src/main/java/org/apache/tamaya/spi/TypeLiteral.java new file mode 100644 index 0000000..0139e85 --- /dev/null +++ b/code/base/src/main/java/org/apache/tamaya/spi/TypeLiteral.java @@ -0,0 +1,225 @@ +/* + * 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.Serializable; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Objects; + + +/** + *

Class for instantiation of objects that represent parameterized types + * with current parameters.

+ * + *

An object that represents a parameterized type may be obtained by + * subclassing TypeLiteral.

+ * + *
+ * TypeLiteral<List<Integer>> stringListType = new TypeLiteral<List<Integer>>() {};
+ * 
+ * + * @param the type, including all type parameters + */ +public class TypeLiteral implements Serializable { + + private static final long serialVersionUID = 1L; + private static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; + /** The current defined type. */ + private final Type definedType; + + /** + * Constructor. + * @param definedType the defined type. + */ + public TypeLiteral(Type definedType) { + Objects.requireNonNull(definedType, "Type must be given"); + + this.definedType = definedType; + } + + /** + * Constructor only for directly implemeting a TypeLiteral hereby dynamically implementing a generic interface. + */ + public TypeLiteral() { + this.definedType = getDefinedType(this.getClass()); + } + + /** + * Creates a new TypeLiteral based on a given type. + * + * @param type the type , not {@code null}. + * @param the literal generic type. + * @return the corresponding TypeLiteral, never {@code null}. + */ + public static TypeLiteral of(Type type) { + Objects.requireNonNull(type, "Type must be given."); + + return new TypeLiteral(type); + } + + /** + * Checks the current implemented generic interfaces and evaluates the given single type parameter. + * + * @param clazz the class to check, not {@code null}. + * @param interfaceType the interface type to be checked, not {@code null}. + * @return the generic type parameters, or an empty array, if it cannot be evaluated. + */ + public static Type[] getGenericInterfaceTypeParameters(Class clazz, Class interfaceType) { + Objects.requireNonNull(clazz, "Class parameter must be given."); + Objects.requireNonNull(interfaceType, "Interface parameter must be given."); + + for (Type type : clazz.getGenericInterfaces()) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + if(parameterizedType.getRawType().equals(interfaceType)){ + return parameterizedType.getActualTypeArguments(); + } + } + } + return EMPTY_TYPE_ARRAY; + } + + /** + * Method that checks the class's type for a generic interface implementation type. + * + * @param type the type, not {@code null}. + * @return the generic type parameter of the given single type generic interfaceType, or an empty array. + */ + public static Type[] getTypeParameters(Type type) { + Objects.requireNonNull(type, "Type must be given."); + + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + return parameterizedType.getActualTypeArguments(); + } + return EMPTY_TYPE_ARRAY; + } + + public final Type getType() { + return definedType; + } + + /** + * Returns basic raw Java type. + * + * @return the actual type represented by this object + */ + @SuppressWarnings("unchecked") + public final Class getRawType() { + Class rawType; + + if (this.definedType instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) this.definedType; + rawType = (Class) pt.getRawType(); + } else if (this.definedType instanceof GenericArrayType) { + rawType = (Class) Object[].class; + } else if (this.definedType instanceof Class) { + rawType = (Class) this.definedType; + } else { + throw new RuntimeException("Illegal type for the Type Literal Class"); + } + + return rawType; + } + + /** + * Returns actual type arguments, if present. + * + * @return the actual type represented by defined class, or an empty array. + */ + public final Type[] getActualTypeArguments() { + if (this.definedType instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) this.definedType; + return pt.getActualTypeArguments(); + } + return new Type[0]; + } + + + protected Type getDefinedType(Class clazz) { + Type type; + + if (clazz == null) { + throw new RuntimeException("Class parameter clazz can not be null"); + } + + Type superClazz = clazz.getGenericSuperclass(); + + if (superClazz instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) superClazz; + Type[] actualArgs = pt.getActualTypeArguments(); + + if (actualArgs.length == 1) { + type = actualArgs[0]; + + } else { + throw new RuntimeException("More than one parametric type"); + } + + } else if (superClazz.equals(Object.class)) { + throw new RuntimeException("Super class must be parametrized type"); + } else { + type = getDefinedType((Class) superClazz); + } + + return type; + } + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((definedType == null) ? 0 : definedType.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + TypeLiteral other = (TypeLiteral) obj; + if (definedType == null) { + if (other.definedType != null) { + return false; + } + } else if (!definedType.equals(other.definedType)) { + return false; + } + return true; + } + + + @Override + public String toString() { + return "TypeLiteral{" + + "type=" + definedType + + '}'; + } + +}