Return-Path: X-Original-To: apmail-tamaya-commits-archive@minotaur.apache.org Delivered-To: apmail-tamaya-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 98D8310A20 for ; Wed, 26 Nov 2014 18:09:08 +0000 (UTC) Received: (qmail 40747 invoked by uid 500); 26 Nov 2014 18:09:08 -0000 Delivered-To: apmail-tamaya-commits-archive@tamaya.apache.org Received: (qmail 40700 invoked by uid 500); 26 Nov 2014 18:09:08 -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 40681 invoked by uid 99); 26 Nov 2014 18:09:08 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 26 Nov 2014 18:09:08 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED,T_RP_MATCHES_RCVD X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO mail.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with SMTP; Wed, 26 Nov 2014 18:08:26 +0000 Received: (qmail 39192 invoked by uid 99); 26 Nov 2014 18:07:08 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 26 Nov 2014 18:07:08 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id B6189829EB2; Wed, 26 Nov 2014 18:07:07 +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: Wed, 26 Nov 2014 18:07:15 -0000 Message-Id: <392aa4679f4849bd8c303ee10dc399ec@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [10/11] incubator-tamaya git commit: Initial import from GitHub. Added PGP public key. X-Virus-Checked: Checked by ClamAV on apache.org http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/asciidoc/tasks.adoc ---------------------------------------------------------------------- diff --git a/api/src/main/asciidoc/tasks.adoc b/api/src/main/asciidoc/tasks.adoc new file mode 100644 index 0000000..2bfb3ef --- /dev/null +++ b/api/src/main/asciidoc/tasks.adoc @@ -0,0 +1,399 @@ +Apache Tamaya - Possible Tasks +============================== +:name: Tamaya +:rootpackage: org.apache.tamaya +:title: Apache Tamaya +:revnumber: 0.1-SNAPSHOT +:revremark: Draft +:revdate: October 2014 +:longversion: {revnumber} ({revremark}) {revdate} +:authorinitials: ATR +:author: Anatole Tresch +:email: +:source-highlighter: coderay +:website: http://tamaya.apache.org/ +:iconsdir: {imagesdir}/icons +:toc: +:toc-placement: manual +:icons: +:encoding: UTF-8 +:numbered: + +''' + +<<< + +-> add image : : https://raw.githubusercontent.com/JavaConfig/config-api/master/src/main/asciidoc/images/javaconfig.jpg[] + +toc::[] + +<<< +:numbered!: +----------------------------------------------------------- +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. +----------------------------------------------------------- + +:numbered: + +<<< + +== Introduction + +== What is Tamaya + +{name} is the Apache standard for flexible and powerful configuration. Objective is to provide flavors for +Java SE, ME as well as to ship with powerful features for Java EE and Cloud Solutions. All functions provided +is build on top of a small but very powerful, flexible and extendible API. This API is implemented by a core implementation, +which then can be extended or adapted for use in different runtime scenarios, such as SE, ME, EE, Spring, OSGI +and more. Similarly additional modules may be provided that help also existing solution to be plugged into +{name}, so you can start right away using {name} without having to rebuild/change your existing application. + +== Main Features of {name} + +The main features of {name} currently are + +* *A simple key/value store*, named +PropertyProvider+ (simply called "provider"). +* Simple *built-in meta-data model*, that allows to easily attach metadata to a provider or single property keys. +* Support for *different configuration formats*. +* Support for *different configuration locations*, including remote locations. +* Powerful and flexible *options to combine providers to new composite providers* using different combination policies. +* The *Configuration model* adds additional features: +** *Type Adapters* allow to convert configuration into any required target type. +** *Built-in support* for all basic Java types. +* *Contextual, hierarchical and multi-layered environment model*. The model is rather simple, but nevertheless + powerful enough to map complex layered runtime environments such as Java EE or multi-tenant SaaS solutions. +* *Configurable System Properties* allow to tweak system properties to behave contextually. +* *Templates* provide a type safe configuration mechanism where an annotated interface is implemented by the + configuration system, providing data from an underlying configuration. +* *Configuration Injection* allows configured values to be directly injected into the bean's configured. +* *Loading Policies* allow to control how configuration changes are reflected on the configured beans. +* *Configuration Operators and Queries* can be used to easily implement advanced features such as *Views, + Security Constraints and Filters*. +* Provider and configuration changes can be observed by registering *PropertyChangeListeners*. +* Configurations are *versioned*, so remote pull scenarios can be implemented very efficiently. +* The system supports *multiple configurations* identified by name. +* The configuration system provides a powerful management console for reading and updating of configuration. +* The system also supports distributed configuration scenarios by leveraging existing solutions, such as Memcached, + Hazelcast or Zookeper. +* The system is built on "Java 8 features*. +* A *Java 7 Backport* is provided. + +=== Purpose of this Document + +The document should help to organize people and ideas around the Apache Tamaya Library. It list possible features, +ideas and tasks that need to be done. Everybody can have a look at and see, where hos contribution and capabilities +would fit best. + +== Basics + +=== Styles, Logo + +The project requires + +* a good Apache styled logo and +* CSS styles as needed, +* an initial web page, +* a twitter account +* ... + +=== Infrastructure + +We should setup all needed infrastructure +* code repos +* project modules (including module sites) +* coding and documentation guidelines +* automatic builds (CI), included automatic coverage and sonar quality checks. +* a docker image or appliance, with everything setup, so contributors can easily + start contributing... +* ... + +== Main Features + +=== Metadata Model + +Currently +MetaInfo+ models metadata as a separate constuct. It has been shown that this leads to more complex +handling when creating composites and makes the API overall more complex. The idea is to model metadata as simple +key/value pairs, that are part of the provider/configuration data as well, but handled separately. Metadata hereby +is identified by a starting '_' character in its key. For example refer to the following configuration properties: + +[source,listing] +.Basic Properties +---------------------------------------------------------------- +a.b.Foo=foo +a.b.Bar=bar +a.AnyOther=whatelse +Something=none +---------------------------------------------------------------- + +Now we can model meta-data as follows: + +[source,listing] +.Metadata Properties +---------------------------------------------------------------- +[a.b].info=An area info +[a.b.Foo].auth=role1,role2 +[a.b.Foo].encrypt=PGP +[a.b.Foo].sensitive=true +[].info=This is a test configuration example. +---------------------------------------------------------------- + +The above would model the following: + +* The area +a.b+ has the meta property +info+. +* The entry +a.b.Foo+ has three meta properties +auth,encrypt+ and +sensitive+. These could be interpreted by a security + view and used to encrypt the values returned by the configuration instance, if not the current user has one of the + specified roles. +* The last meta data defines an attribute +info+ for the whole provider/configuration (the root area). + +Given that the overall entries would be as follows: + +[source,listing] +.Full Properties with Meta Properties +---------------------------------------------------------------- +[a.b].info=An area info +a.b.Foo=foo +[a.b.Foo].auth=role1,role2 +[a.b.Foo].encrypt=PGP +[a.b.Foo].sensitive=true +a.b.Bar=bar +[].info=This is a test configuration example. +a.AnyOther=whatelse +Something=none +---------------------------------------------------------------- + +The current +MetaInfo+ class could be adapted, so it is reading data from the underlying configuration/provider, +instead of its own datastructure. This would make a later mapping of configuration and its metadata into DB table, JSON +etc, much more easier. +The providers on the other side may suppress any metadata from ordinary output, such +as +toString()+, Similarly accessing metadata using the official config API (+get, getOrDefault, getAreas+ etc) +should be disabled. The +MetaInfoBuilder+ must probably as well adapted or redesigned. + +=== Collection Support + +Add a key/value based model for mapping collections such as sets, maps, list. Implement according adapters. +In combination with the metadata model above this could be something like: + +[source,listing] +.Collection Support +---------------------------------------------------------------- +mySet=[a,b,c,d,e\,e,f] +[mySet].type=set +#optional define the implementation class +[mySet].class=java.util.TreeSet + +myList=[a,b,c,d,e\,e,f] +[myList].type=list +#optional define the implementation class +[myList].class=java.util.ArrayList + +myMap=[a:aa,b:bb,c:cc,d:dd,e:e\,e,f:ff] +[myMap].type=map +#optional define the implementation class +[myMap].class=java.util.TreeMap + +#Finally we could also add support for non String based types +myTypedSet=[1,2,3,4.5,6,7.10.123] +[myTypedSet].contentClass=java.lang.Double +myTypedList=[CHF 10.20, EUR 12.20, BTC 0.002] +[myTypedList].contentType=org.javamoney.moneta.FastMoney +myTypedMap=[CHF:CHF 10.20, EUR:EUR 12.20, BTC:BTC 0.002] +[myTypedMap].contentTypes=javax.money.CurrencyUnit,javax.money.MonetaryAmount +---------------------------------------------------------------- + + +=== Management Service + +A JMX/Restful API should be designed and built that exposes configuration information. Access should be secured, e.g. +using OAuth or other security mechasnisms. + +=== Management Client + +A nice web-based client to manage configuration data would be nice as well. This also includes a UI for creating new +configurations. + +=== Mapping Configuration to a Database + +A flexible mechanism should be implemented that allows the use of databases (SQL/JPA as well as non-SQL) for +storing/retreiving/managing configuration: + +* JPA, Hibernate +* MongoDB +* ... + +=== Integration with OSGI + +Examples are to be created and tested, where OSGI is used as the basic runtime platform, e.g. Apache Felix, but as well +others. + +=== Integration with Jigsaw + +Once Jigsaw is mature and in a usable (still early) stage, examples are to be created and tested, where OSGI is used as +the basic runtime platform, e.g. Apache Felix, but as well others. + +== Distributed/Remote Configuration Support + +=== Configuration Server + +A configuration server should be implemented that provides access to configurations and triggers updates to registered +clients (push). Similarly a poull model should be supported, where clients can asl for the current version id of a certain +configuration and reload it if necessary. + +=== Configuration Distribution Policies + +Different configuration distribution policies should be defined any implemented, e.g. distributed cache, restful services, +web services, EJB/RMI calls, asynchronous queues, publish/subsribe models, ... + +=== Dynamic Service Lookup + +Configuration Servers and Clients should bea ble to locate each other in different ways: + +* with fixed configured IPs, or IP ranges +* using a dynamic service location protocol like +** SLP +** Distributed Maps/Datagrids +** Apache Zookeeper + +=== Configuration Client + +A subset of the API would be created that exposes only a well defined subset, of exactly one configuration targeted +to a certain instance, VM or whatever. The client should be connectable to a server in different ways (see configuration +distributiont policies). + +=== Preferences Support + +Write a +PreferencesFactory+ for +java.util.preferences+. + +== Third Party Integration + +=== Integration with Deltaspike Config + +Integration with Deltaspike Config should be implemented and discussed with Deltaspike guys. + +=== Integration with Spring + +A {name} module should be created that allows Spring to be used either as client or configuration provider. + +=== Integration with Jetty + +A {name} module should be created that allows a Jetty instance to be deployed and started that is (completely) +configured based on configuration server. + +=== Integration with Tomcat + +A {name} module should be created that allows a Tomcat instance to be deployed and started that is (completely) +configured based on configuration server. + +=== Configuration of Java EE + +In the Java EE area there would be several options: + +=== Configuration of Application Servers (administrative resources) + +It should be possible to start a application server instance remotely and configure all administrative resources and the +deployments based on the configuration service, server to be considered maybe + +* Wildfly +* IBM +* Weblogic +* Glassfish +* Apache Geronimo + +==== Configuration of CDI + +Implement a CDI extension that controls CDI based on configuration: +* Add beans +* Remove (veto) beans +* Add/remove interceptors +* Add/remove decorators +* Activate alternatives +* ... + +==== Configuration of Bean Validation + +* Add configurable validators. +* Configure bean validation based on configuration +* ... + +=== JNDI Support + +Write a +JCA+ adapter to provide configuration data through JNDI. + +==== Configure JSF + +Use the JSF +XML Document+ event to completely configure JSF. + +==== Configure Web Services + +Provide a WebServiceProviderFactory that may be configured. + +==== Configure JPA + +Provide an implementation that allows configuration of persistence units. Talk with JPA EG people to see if we can +get an SPI to hook in a stadardized way. + +==== Configure EJBs + +Provide an implementation that allows configuration of EJBs and MDBs: + +* Register beans +* Unregister/disable beans +* Intercept beans +* Support Configuration Injection (in the worst case using a standard Interceptor, provide supporting artifacts to + help developers to achive this easily). +* Talk with EE8 Umbrella EG (Bill Shanon, Linda DeMichels) on a feasible SPI for EE8, if possible join the EG. + +==== Configure ... + +Just think of any Java EE aspects that might be worth to be configured. If it can be done, e.g. by managing CDI managed +resources, it might be easy. For others it is a good idea to discuss things with our matter of experts... + +== Special Goodies + +=== Maintenance Mode Servlet Filter + +Provide a servlet filter that is capable of switching to maintenance mode, based on configuration. Similarly also a forwarding +servlet could be useful, wehere only request based on configuration are forwarded, other might be rejected or dropped +as configured. + +=== Dynamic Camel Routes + +Provides dynamic (configurable) Camel routes, e.g. usable within ServiceMix or standalone. + +=== Dynamic CXF + +Provides dynamic (configurable) CXF adapters, e.g. usable within ServiceMix or standalone. + +=== Configurable Apache MQ + +Provides an implementation for configuring Apache MQ. + +=== Dynamic Logging + +Provide a dynamic solution, where logging systems are configured (handlers, log levels etc), e.g. for + +* slf4j +* log4j +* JUL + +Also interesting is a feature that allows to dynamically log on a more finer level depending on a concrete +subject/user, session id or server or ... + +=== Dynamic ... + +Interested to see what other ideas are around. Let us know! + http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java b/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java new file mode 100644 index 0000000..25b503b --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/ConfigChangeSet.java @@ -0,0 +1,172 @@ +/* + * 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; + +import java.beans.PropertyChangeEvent; +import java.util.*; + +/** + * Event that contains a set of changes that were applied or could be applied. + * This class is immutable and thread-safe. To create instances use + * {@link ConfigChangeSetBuilder}. + * + * Created by Anatole on 22.10.2014. + */ +public final class ConfigChangeSet { + /** The base property provider/configuration. */ + private PropertyProvider propertyProvider; + /** The base version, usable for optimistic locking. */ + private String baseVersion; + /** The recorded changes. */ + private Map changes = new HashMap<>(); + + /** + * Constructor used by {@link ConfigChangeSetBuilder}. + * @param propertyProvider The base property provider/configuration, not null. + * @param baseVersion The base version, usable for optimistic locking. + * @param changes The recorded changes, not null. + */ + ConfigChangeSet(PropertyProvider propertyProvider, String baseVersion, Collection changes) { + this.propertyProvider = Objects.requireNonNull(propertyProvider); + this.baseVersion = baseVersion; + changes.forEach((c) -> this.changes.put(c.getPropertyName(), c)); + } + + /** + * Get the underlying property provider/configuration. + * @return the underlying property provider/configuration, never null. + */ + public PropertyProvider getPropertyProvider(){ + return this.propertyProvider; + } + + /** + * Get the base version, usable for optimistic locking. + * @return the base version. + */ + public String getBaseVersion(){ + return baseVersion; + } + + /** + * Get the changes recorded. + * @return the recorded changes, never null. + */ + public Collection getEvents(){ + return Collections.unmodifiableCollection(this.changes.values()); + } + + /** + * Access the number of removed entries. + * @return the number of removed entries. + */ + public int getRemovedSize() { + return (int) this.changes.values().stream().filter((e) -> e.getNewValue() == null).count(); + } + + /** + * Access the number of added entries. + * @return the number of added entries. + */ + public int getAddedSize() { + return (int) this.changes.values().stream().filter((e) -> e.getOldValue() == null).count(); + } + + /** + * Access the number of updated entries. + * @return the number of updated entries. + */ + public int getUpdatedSize() { + return (int) this.changes.values().stream().filter((e) -> e.getOldValue()!=null && e.getNewValue()!=null).count(); + } + + + /** + * Checks if the given key was removed. + * @param key the target key, not null. + * @return true, if the given key was removed. + */ + public boolean isRemoved(String key) { + PropertyChangeEvent change = this.changes.get(key); + return change != null && change.getNewValue() == null; + } + + /** + * Checks if the given key was added. + * @param key the target key, not null. + * @return true, if the given key was added. + */ + public boolean isAdded(String key) { + PropertyChangeEvent change = this.changes.get(key); + return change != null && change.getOldValue() == null; + } + + /** + * Checks if the given key was updated. + * @param key the target key, not null. + * @return true, if the given key was updated. + */ + public boolean isUpdated(String key) { + PropertyChangeEvent change = this.changes.get(key); + return change != null && change.getOldValue() != null && change.getNewValue() != null; + } + + /** + * Checks if the given key is added, or updated AND NOT removed. + * @param key the target key, not null. + * @return true, if the given key was added, or updated BUT NOT removed. + */ + public boolean containsKey(String key) { + PropertyChangeEvent change = this.changes.get(key); + return change != null && change.getNewValue() != null; + } + + /** + * CHecks if the current change set does not contain any changes. + * @return tru, if the change set is empty. + */ + public boolean isEmpty(){ + return this.changes.isEmpty(); + } + + /** + * Applies all changes to the given map instance. + * @param map the target map.never null. + */ + public void applyChangesTo(Map map) { + for(Map.Entry en: this.changes.entrySet()){ + if(en.getValue().getNewValue() == null){ + map.remove(en.getKey()); + } + else{ + map.put(en.getKey(), (String)en.getValue().getNewValue()); + } + } + } + + + @Override + public String toString() { + return "ConfigChangeSet{" + + "properties=" + propertyProvider + + ", baseVersion=" + baseVersion + + ", changes=" + changes + + '}'; + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java b/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java new file mode 100644 index 0000000..0d30764 --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/ConfigChangeSetBuilder.java @@ -0,0 +1,323 @@ +/* + * 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; + +import java.beans.PropertyChangeEvent; +import java.util.*; + +/** + * Models a set of changes to be applied to a configuration/property provider. Such a set can be applied + * to any {@link PropertyProvider} instance. If the provider is mutable it may check the + * version given and apply the changes to the provider/configuration, including triggering of regarding + * change events. + * + * Created by Anatole on 06.09.2014. + */ +public final class ConfigChangeSetBuilder { + /** The recorded changes. */ + final SortedMap delta = new TreeMap<>(); + /** The underlying configuration/provider. */ + PropertyProvider source; + /** The base version, if any. Used for optimistic version checking. */ + String baseVersion; + + /** + * Constructor. + * @param source the underlying configuration/provider, not null. + * @param baseVersion the base version, used for optimistic version checking. + */ + private ConfigChangeSetBuilder(PropertyProvider source, String baseVersion) { + Objects.requireNonNull(source); + this.source = source; + this.baseVersion= baseVersion; + } + + /** + * Creates a new instance of this builder. + * @param source the underlying property provider/configuration, not null. + * @param baseVersion the base version to be used. + * @return the builder for chaining. + */ + public static ConfigChangeSetBuilder of(PropertyProvider source, String baseVersion) { + return new ConfigChangeSetBuilder(source, baseVersion); + } + + /** + * Creates a new instance of this builder. + * @param configuration the base configuration, not null. + * @return the builder for chaining. + */ + public static ConfigChangeSetBuilder of(Configuration configuration) { + return new ConfigChangeSetBuilder(configuration, configuration.getVersion()); + } + + /** + * Add a change as a {@link java.beans.PropertyChangeEvent}. + * @param changeEvent the change event. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder addChange(PropertyChangeEvent changeEvent) { + Objects.requireNonNull(changeEvent); + this.delta.put(changeEvent.getPropertyName(), changeEvent); + return this; + } + + /** + * This method records all changes to be applied to the base property provider/configuration to + * achieve the given target state. + * @param newState the new target state, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder addChanges(PropertyProvider newState) { + compare(newState, this.source).forEach((c) -> this.delta.put(c.getPropertyName(), c)); + return this; + } + + /** + * Get the current values, also considering any changes recorded within this change set. + * @param key the key of the entry, not null. + * @return the value, or null. + */ + public String get(String key) { + PropertyChangeEvent change = this.delta.get(key); + if(change!=null && !(change.getNewValue()==null)){ + return (String)change.getNewValue(); + } + return null; + } + + /** + * Marks the given key(s) from the configuration/properties to be removed. + * @param key the key of the entry, not null. + * @param otherKeys additional keys to be removed (convenience), not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder remove(String key, String... otherKeys) { + String oldValue = this.source.get(key).orElse(null); + if(oldValue==null){ + this.delta.remove(key); + } + this.delta.put(key, new PropertyChangeEvent(this.source, key, oldValue, null)); + for(String addKey:otherKeys){ + oldValue = this.source.get(addKey).orElse(null); + if(oldValue==null){ + this.delta.remove(addKey); + } + this.delta.put(addKey, new PropertyChangeEvent(this.source, addKey, oldValue, null)); + } + return this; + } + + /** + * Applies the given value. + * @param key the key of the entry, not null. + * @param value the value to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder put(String key, String value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value))); + return this; + } + + /** + * Applies the given value. + * @param key the key of the entry, not null. + * @param value the value to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder put(String key, Boolean value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString())); + return this; + } + + /** + * Applies the given value. + * @param key the key of the entry, not null. + * @param value the value to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder put(String key, Byte value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString())); + return this; + } + + /** + * Applies the given value. + * @param key the key of the entry, not null. + * @param value the value to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder put(String key, Character value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString())); + return this; + } + + /** + * Applies the given value. + * @param key the key of the entry, not null. + * @param value the value to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder put(String key, Short value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString())); + return this; + } + + /** + * Applies the given value. + * @param key the key of the entry, not null. + * @param value the value to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder put(String key, Integer value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString())); + return this; + } + + /** + * Applies the given value. + * @param key the key of the entry, not null. + * @param value the value to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder put(String key, Long value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString())); + return this; + } + + /** + * Applies the given value. + * @param key the key of the entry, not null. + * @param value the value to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder put(String key, Float value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString())); + return this; + } + + /** + * Applies the given value. + * @param key the key of the entry, not null. + * @param value the value to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder put(String key, Double value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString())); + return this; + } + + /** + * Applies the given value. + * @param key the key of the entry, not null. + * @param value the value to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder put(String key, Object value) { + this.delta.put(key, new PropertyChangeEvent(this.source, key, this.source.get(key).orElse(null), Objects.requireNonNull(value).toString())); + return this; + } + + /** + * Apply all the given values to the base configuration/properties. + * @param changes the changes to be applied, not null. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder putAll(Map changes) { + changes.forEach((k,v) -> + this.delta.put(k, new PropertyChangeEvent(this.source, k, this.source.get(k).orElse(null), v))); + return this; + } + + /** + * This method will create a change set that clears all entries from the given base configuration/properties. + * @return the builder for chaining. + */ + public ConfigChangeSetBuilder clear() { + this.delta.clear(); + this.source.toMap().forEach((k,v) -> + this.delta.put(k, new PropertyChangeEvent(this.source, k, v, null))); + return this; + } + + /** + * Checks if the change set is empty, i.e. does not contain any changes. + * @return true, if the set is empty. + */ + public boolean isEmpty(){ + return this.delta.isEmpty(); + } + + /** + * Resets this change set instance. This will clear all changes done to this set, so the + * set will be empty. + */ + public void reset(){ + this.delta.clear(); + } + + /** + * Builds the corresponding change set. + * @return the new change set, never null. + */ + public ConfigChangeSet build() { + return new ConfigChangeSet(this.source, baseVersion, Collections.unmodifiableCollection(this.delta.values())); + } + + /** + * Compares the two property providers/configurations and creates a collection of all changes + * that must be appied to render {@code map1} into {@code map2}. + * @param map1 the source map, not null. + * @param map2 the target map, not null. + * @return a collection of change events, never null. + */ + public static Collection compare(PropertyProvider map1, PropertyProvider map2) { + List changes = new ArrayList<>(); + for (Map.Entry en : map1.toMap().entrySet()) { + Optional val = map2.get(en.getKey()); + if(!val.isPresent()) { + changes.add(new PropertyChangeEvent(map1, en.getKey(), null, en.getValue())); + } + else if(!val.get().equals(en.getValue())){ + changes.add(new PropertyChangeEvent(map1, en.getKey(), val.get(), en.getValue())); + } + } + for (Map.Entry en : map2.toMap().entrySet()) { + Optional val = map1.get(en.getKey()); + if(!val.isPresent()) { + changes.add(new PropertyChangeEvent(map1, en.getKey(), null, en.getValue())); + } + else if(!val.equals(en.getValue())){ + changes.add(new PropertyChangeEvent(map1, en.getKey(), val.get(), en.getValue())); + } + } + return changes; + } + + /* + * (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "PropertyChangeEventBuilder [source=" + source + ", " + + ", delta=" + delta + "]"; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigException.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/ConfigException.java b/api/src/main/java/org/apache/tamaya/ConfigException.java new file mode 100644 index 0000000..bac2ef4 --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/ConfigException.java @@ -0,0 +1,44 @@ +/* + * 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; + +/** + * Exception class (runtime exception) for configuration issues. + */ +public class ConfigException extends RuntimeException{ + + private static final long serialVersionUID = -5886094818057522680L; + + /** + * Creates a new configuration exception. + * @param message the exception message, not null. + */ + public ConfigException(String message){ + super(message); + } + + /** + * Creates a new configuration exception. + * @param message the exception message, not null. + * @param t the throwable. + */ + public ConfigException(String message, Throwable t){ + super(message, t); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigOperator.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/ConfigOperator.java b/api/src/main/java/org/apache/tamaya/ConfigOperator.java new file mode 100644 index 0000000..a59712b --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/ConfigOperator.java @@ -0,0 +1,37 @@ +/* + * 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; + + +/** + * Interface for an filter/operator that converts a configured String into another String. One typical + * use case would the the decryption of an encrypted configuration value. + */ +@FunctionalInterface +public interface ConfigOperator{ + + /** + * Method that creates a Configuration from another Configuration. This can be used for implementing + * views, security constraints or for overriding/inheriting of configuration. + * @param config The target configuration to be operated, never nnull. + * @return the operated configuration, never null. + */ + Configuration operate(Configuration config); + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigQuery.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/ConfigQuery.java b/api/src/main/java/org/apache/tamaya/ConfigQuery.java new file mode 100644 index 0000000..c296d12 --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/ConfigQuery.java @@ -0,0 +1,37 @@ +/* + * 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; + + +/** + * Interface for an query that converts a Configuration into another object. One typical + * use cases would creating a complex configuration parameter type from a Configuration instance or + * constraint views on configuration. + */ +@FunctionalInterface +public interface ConfigQuery{ + + /** + * Queries the given configuration. + * @param config the configuration to be wuiried, not null. + * @return the result T. + */ + T query(Configuration config); + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/Configuration.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/Configuration.java b/api/src/main/java/org/apache/tamaya/Configuration.java new file mode 100644 index 0000000..a23f44c --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/Configuration.java @@ -0,0 +1,376 @@ +/* + * 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; + +import com.sun.javafx.scene.control.behavior.OptionalBoolean; + +import java.beans.PropertyChangeListener; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * A configuration models a aggregated set of properties, identified by a unique key, but adds higher level access functions to + * a {@link PropertyProvider}. Hereby in most cases a configuration is a wrapper around a composite + * {@link PropertyProvider} instance, which may combine multiple child providers in well defined tree like structure, + * where nodes define logically the rules of priority, filtering, combination and overriding. + *
+ *

Implementation Requirements

+ * Implementations of this interface must be + *
    + *
  • Thread safe. + *
  • Immutable + *
+ * It is not recommended that implementations also are serializable, since the any configuration can be freezed + * by reading out its complete configuration map into a serializable and remotable structure. This helps significantly + * simplifying the development of this interface, e.g. for being backed up by systems and stores that are not part of + * this library at all. + */ +public interface Configuration extends PropertyProvider{ + + /** + * Get the property value as {@link Boolean}. + * + * @param key the property's absolute, or relative path, e.g. {@code + * a/b/c/d.myProperty}. + * @return the property's value. + */ + default OptionalBoolean getBoolean(String key){ + Optional val = get(key, Boolean.class); + if(val.isPresent()){ + if(val.get()){ + return OptionalBoolean.TRUE; + } + return OptionalBoolean.FALSE; + } + return OptionalBoolean.ANY; + } + + /** + * Get the property value as {@link Integer}. + * + * @param key the property's absolute, or relative path, e.g. @code + * a/b/c/d.myProperty}. + * @return the property's value. + */ + default OptionalInt getInteger(String key){ + Optional val = get(key, Integer.class); + if(val.isPresent()){ + return OptionalInt.of(val.get()); + } + return OptionalInt.empty(); + } + + + /** + * Get the property value as {@link Long}. + * + * @param key the property's absolute, or relative path, e.g. @code + * a/b/c/d.myProperty}. + * @return the property's value. + */ + default OptionalLong getLong(String key){ + Optional val = get(key, Long.class); + if(val.isPresent()){ + return OptionalLong.of(val.get()); + } + return OptionalLong.empty(); + } + + + /** + * Get the property value as {@link Double}. + * + * @param key the property's absolute, or relative path, e.g. @code + * a/b/c/d.myProperty}. + * @return the property's value. + * @throws IllegalArgumentException if no such property exists. + */ + default OptionalDouble getDouble(String key){ + + Optional val = get(key, Double.class); + if(val.isPresent()){ + return OptionalDouble.empty().of(val.get()); + } + return OptionalDouble.empty(); + } + + + /** + * Get the property value as type {@code Class}. + *

+ * If {@code Class} is not one of + * {@code Boolean, Short, Integer, Long, Float, Double, BigInteger, + * BigDecimal, String} , an according adapter must be + * available to perform the conversion from {@link String} to + * {@code Class}. + * + * @param key the property's absolute, or relative path, e.g. @code + * a/b/c/d.myProperty}. + * @param adapter the PropertyAdapter to perform the conversion from + * {@link String} to {@code Class}, not {@code null}. + * @return the property's value. + * @throws IllegalArgumentException if the value could not be converted to the required target + * type, or no such property exists. + */ + default Optional getAdapted(String key, PropertyAdapter adapter){ + Optional value = get(key); + if(value.isPresent()) { + return Optional.ofNullable(adapter.adapt(value.get())); + } + return Optional.empty(); + } + + + /** + * Get the property value as type T. This will implicitly require a corresponding {@link + * PropertyAdapter} to be available that is capable of providing type T + * from the given String value. + * + * @param key the property's absolute, or relative path, e.g. @code + * a/b/c/d.myProperty}. + * @param type The target type required, not null. + * @return the property's value. + * @throws IllegalArgumentException if the value could not be converted to the required target + * type. + */ + Optional get(String key, Class type); + + /** + * Return a set with all fully qualifies area names. + * + * @return s set with all areas, never {@code null}. + */ + default Set getAreas(){ + final Set areas = new HashSet<>(); + this.keySet().forEach(s -> { + int index = s.lastIndexOf('.'); + if(index > 0){ + areas.add(s.substring(0, index)); + } + else{ + areas.add(""); + } + }); + return areas; + } + + /** + * Return a set with all fully qualified area names, containing the transitive closure also including all + * subarea names, regardless if properties are accessible or not. + * + * @return s set with all transitive areas, never {@code null}. + */ + default Set getTransitiveAreas(){ + final Set transitiveAreas = new HashSet<>(); + getAreas().forEach(s -> { + int index = s.lastIndexOf('.'); + if (index < 0) { + transitiveAreas.add(""); + } else { + while (index > 0) { + s = s.substring(0, index); + transitiveAreas.add(s); + index = s.lastIndexOf('.'); + } + } + }); + return transitiveAreas; + } + + /** + * Return a set with all fully qualified area names, containing only the + * areas that match the predicate and have properties attached + * + * @param predicate A predicate to deternine, which areas should be returned, not {@code null}. + * @return s set with all areas, never {@code null}. + */ + default Set getAreas(final Predicate predicate){ + return getAreas().stream().filter(predicate).collect(Collectors.toCollection(TreeSet::new)); + } + + /** + * Return a set with all fully qualified area names, containing the transitive closure also including all + * subarea names, regardless if properties are accessible or not. + * + * @param predicate A predicate to deternine, which areas should be returned, not {@code null}. + * @return s set with all transitive areas, never {@code null}. + */ + default Set getTransitiveAreas(Predicate predicate){ + return getTransitiveAreas().stream().filter(predicate).collect(Collectors.toCollection(TreeSet::new)); + } + + /** + * Allows to evaluate if an area exists. + * + * @param key the configuration area (sub)path. + * @return {@code true}, if such a node exists. + */ + default boolean containsArea(String key){ + return getAreas().contains(key); + } + + /** + * Extension point for adjusting configuration. + * + * @param operator A configuration operator, e.g. a filter, or an adjuster + * combining configurations. + * @return the new adjusted configuration, never {@code null}. + */ + default Configuration with(ConfigOperator operator){ + return operator.operate(this); + } + + /** + * Query some value from a configuration. + * + * @param query the query, never {@code null}. + * @return the result + */ + default T query(ConfigQuery query){ + return query.query(this); + } + + /** + * Field that allows property providers to be versioned, meaning that each change on a provider requires this value + * to be incremented by one. This can be easily used to implement versioning (and optimistic locking) + * in distributed (remote) usage scenarios. + * @return the version of the current instance, or 'N/A'. + */ + default String getVersion(){return "N/A";} + + /** + * Add a ConfigChangeListener to this configuration instance. + * @param l the listener, not null. + */ + void addPropertyChangeListener(PropertyChangeListener l); + + /** + * Removes a ConfigChangeListener to this configuration instance. + * @param l the listener, not null. + */ + void removePropertyChangeListener(PropertyChangeListener l); + + /** + * Allows to check if a configuration with a given name is defined. + * + * @param name the configuration's name, not null, not empty. + * @return true, if such a configuration is defined. + */ + public static boolean isDefined(String name){ + return ConfigurationManager.isConfigurationDefined(name); + } + + /** + * Access a configuration by name. + * + * @param name the configuration's name, not null, not empty. + * @param template the annotated configuration's + * template interface, not null. + * @return the corresponding Configuration instance, never null. + * @throws ConfigException if no such configuration is defined. + */ + public static T of(String name, Class template){ + return ConfigurationManager.getConfiguration(name, template); + } + + + /** + * Access a configuration by name. + * + * @param name the configuration's name, not null, not empty. + * @return the corresponding Configuration instance, never null. + * @throws ConfigException if no such configuration is defined. + */ + public static Configuration of(String name){ + return ConfigurationManager.getConfiguration(name); + } + + /** + * Access a configuration. + * + * @return the corresponding Configuration instance, never null. + * @throws ConfigException if no such configuration is defined. + */ + public static Configuration of(){ + return ConfigurationManager.getConfiguration(); + } + + /** + * Access a typed configuration, based on the default configuration. + * + * @param type the annotated configuration type (could be an interface or + * a non abstract class), not null. + * @return the corresponding typed Configuration instance, never null. + * @throws ConfigException if the configuration could not be resolved. + */ + public static T of(Class type){ + return ConfigurationManager.getConfiguration(type); + } + + /** + * Configures an instance, by resolving and injecting the configuration + * entries. + * + * @param instance the instance with configuration annotations, not null. + * @return the corresponding typed Configuration instance, never null. + * @throws ConfigException if the configuration could not be resolved. + */ + public static void configure(Object instance){ + ConfigurationManager.configure(instance); + } + + /** + * Evaluate the current expression based on the current configuration valid. + * + * @param expression the expression, not null. + * @return the evaluated config expression. + */ + public static String evaluateValue(String expression){ + return ConfigurationManager.evaluateValue(expression); + } + + /** + * Evaluate the current expression based on the current configuration valid. + * + * @param config The configuration to be used for evluating, not null. + * @param expression the expression, not null. + * @return the evaluated config expression. + */ + public static String evaluateValue(Configuration config, String expression){ + return ConfigurationManager.evaluateValue(config, expression); + } + + /** + * Adds a (global) {@link java.beans.PropertyChangeListener} instance that listens to all kind of config changes. + * @param listener the {@link java.beans.PropertyChangeListener} instance to be added, not null. + */ + public static void addGlobalPropertyChangeListener(PropertyChangeListener listener){ + ConfigurationManager.addPropertyChangeListener(listener); + } + + /** + * Removes a (global) {@link java.beans.PropertyChangeListener} instance that listens to all kind of config changes, + * if one is currently registered. + * @param listener the {@link java.beans.PropertyChangeListener} instance to be removed, not null. + */ + public static void removeGlobalPropertyChangeListener(PropertyChangeListener listener){ + ConfigurationManager.removePropertyChangeListener(listener); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/ConfigurationManager.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/ConfigurationManager.java b/api/src/main/java/org/apache/tamaya/ConfigurationManager.java new file mode 100644 index 0000000..67d4ccb --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/ConfigurationManager.java @@ -0,0 +1,161 @@ +/* + * 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; + +import org.apache.tamaya.spi.Bootstrap; +import org.apache.tamaya.spi.ConfigurationManagerSingletonSpi; + +import java.beans.PropertyChangeListener; +import java.util.Optional; + +/** + * Singleton accessor for accessing {@link Configuration} instances and + * proxied configuration templates. + */ +final class ConfigurationManager{ + /** + * The backing SPI instance. + */ + private static final ConfigurationManagerSingletonSpi configManagerSingletonSpi = loadConfigServiceSingletonSpi(); + + /** + * Private singleton constructor. + */ + private ConfigurationManager(){ + } + + /** + * Method to initially load the singleton SPI from the {@link org.apache.tamaya.spi.Bootstrap} mechanism. + * The instance loaded will be used until the VM is shutdown. In case use cases require more flexibility + * it should be transparently implemented in the SPI implementation. This singleton will simply delegate calls + * and not cache any responses. + * + * @return the SPI, never null. + */ + private static ConfigurationManagerSingletonSpi loadConfigServiceSingletonSpi(){ + return Bootstrap.getService(ConfigurationManagerSingletonSpi.class); + } + + /** + * Allows to check if a configuration with a given name is defined. + * + * @param name the configuration's name, not null, not empty. + * @return true, if such a configuration is defined. + */ + public static boolean isConfigurationDefined(String name){ + return Optional.of(configManagerSingletonSpi).get().isConfigurationDefined(name); + } + + /** + * Access a configuration by name. + * + * @param name the configuration's name, not null, not empty. + * @param template the annotated configuration's + * template interface, not null. + * @return the corresponding Configuration instance, never null. + * @throws ConfigException if no such configuration is defined. + */ + public static T getConfiguration(String name, Class template){ + return Optional.of(configManagerSingletonSpi).get().getConfiguration(name, template); + } + + + /** + * Access a configuration by name. + * + * @param name the configuration's name, not null, not empty. + * @return the corresponding Configuration instance, never null. + * @throws ConfigException if no such configuration is defined. + */ + public static Configuration getConfiguration(String name){ + return Optional.of(configManagerSingletonSpi).get().getConfiguration(name); + } + + /** + * Access a configuration. + * + * @return the corresponding Configuration instance, never null. + * @throws ConfigException if no such configuration is defined. + */ + public static Configuration getConfiguration(){ + return Optional.of(configManagerSingletonSpi).get().getConfiguration(); + } + + /** + * Access a typed configuration, based on the default configuration. + * + * @param type the annotated configuration type (could be an interface or + * a non abstract class), not null. + * @return the corresponding typed Configuration instance, never null. + * @throws ConfigException if the configuration could not be resolved. + */ + public static T getConfiguration(Class type){ + return Optional.of(configManagerSingletonSpi).get().getConfiguration(type); + } + + /** + * Configures an instance, by resolving and injecting the configuration + * entries. + * + * @param instance the instance with configuration annotations, not null. + * @return the corresponding typed Configuration instance, never null. + * @throws ConfigException if the configuration could not be resolved. + */ + public static void configure(Object instance){ + Optional.of(configManagerSingletonSpi).get().configure(instance); + } + + /** + * Evaluate the current expression based on the current configuration valid. + * + * @param expression the expression, not null. + * @return the evaluated config expression. + */ + public static String evaluateValue(String expression){ + return evaluateValue(getConfiguration(), expression); + } + + /** + * Evaluate the current expression based on the current configuration valid. + * + * @param config The configuration to be used for evluating, not null. + * @param expression the expression, not null. + * @return the evaluated config expression. + */ + public static String evaluateValue(Configuration config, String expression){ + return Optional.of(configManagerSingletonSpi).get().evaluateValue(config, expression); + } + + /** + * Adds a (global) {@link java.beans.PropertyChangeListener} instance that listens to all kind of config changes. + * @param listener the {@link java.beans.PropertyChangeListener} instance to be added, not null. + */ + public static void addPropertyChangeListener(PropertyChangeListener listener){ + Optional.of(configManagerSingletonSpi).get().addPropertyChangeListener(listener); + } + + /** + * Removes a (global) {@link java.beans.PropertyChangeListener} instance that listens to all kind of config changes, + * if one is currently registered. + * @param listener the {@link java.beans.PropertyChangeListener} instance to be removed, not null. + */ + public static void removePropertyChangeListener(PropertyChangeListener listener){ + Optional.of(configManagerSingletonSpi).get().removePropertyChangeListener(listener); + } +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/Environment.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/Environment.java b/api/src/main/java/org/apache/tamaya/Environment.java new file mode 100644 index 0000000..3523083 --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/Environment.java @@ -0,0 +1,167 @@ +/* + * 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; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +/** + * Models a runtime environment. Instances of this class are used to + * evaluate the correct configuration artifacts.
+ *

Implementation Requirements

+ *

+ * Implementations of this interface must be + *

    + *
  • Thread safe. + *
  • Immutable + *
  • serializable + *
+ */ +public interface Environment extends StageSupplier, Iterable{ + + /** + * Get a unique type (within this VM) for this environment. + * Types represent the environment level within the hierarchy + * of possible environments, e.g. {@code system, ear, webapp, tenant}. + */ + String getEnvironmentType(); + + /** + * Get a unique name (in combination with the environment type within this VM) + * for this environment instance. + * Where a human readable name is available this would be preferable + * over a technical key/UUID. + * @return a unique id for this environment, when comined with the environment type. + */ + String getEnvironmentId(); + + /** + * Access a property. + * @param key the property's key, not null. + * @return the property's value. + */ + Optional get(String key); + + /** + * Checks if a property is defined. + * @param key the property's key, not null. + * @return true, if the property is existing. + */ + boolean containsKey(String key); + + /** + * Access a property. + * @param key the property's key, not null. + * @return the property's value. + */ + default String getOrDefault(String key, String defaultValue){ + return get(key).orElse(defaultValue); + } + + /** + * Access the set of property keys, defined by this provider. + * @return the key set, never null. + */ + Set keySet(); + + /** + * Get an qualified path to this environment instance, by appending the + * current environment id and type (in backets) with the ones of its parent and so on, e.g. + * root[system].HumanOne[ear].rest[webapp].atsticks[user] + * @return the qualified path of this environment instance + */ + String getContext(); + + /** + * Get the parent context. + * @return the parent context, or null. + */ + Environment getParentEnvironment(); + + /** + * Access the environment as Map. + * @return the Map instance containing the environments properties, never null. + */ + Map toMap(); + + /** + * Get the current {@link org.apache.tamaya.Environment}. The environment is used to determine the current runtime state, which + * is important for returning the correct configuration. + * @return the current Environment, never null. + */ + public static Environment of(){ + return EnvironmentManager.getEnvironment(); + } + + /** + * Get the current root (startup/machine/VM) {@link org.apache.tamaya.Environment}. + * @return the current root Environment, never null. + */ + public static Environment getRootEnvironment(){ + return EnvironmentManager.getRootEnvironment(); + } + + /** + * Evaluate the overall chain of possible environments. + * @return the hierarchy chain of possible Environments. + */ + public static List getEnvironmentTypeOrder(){ + return EnvironmentManager.getEnvironmentTypeOrder(); + } + + /** + * Evaluate the current type chain of environments. + * @return the current type chain of Environments. + */ + public static List getEnvironmentHierarchy(){ + return EnvironmentManager.getEnvironmentHierarchy(); + } + + /** + * Get a environment of the given environment type and context. + * @param environmentType the target type, not null. + * @param contextId the target context, not null. + * @return the corresponding environment, if available. + */ + public static Optional getEnvironment(String environmentType, String contextId){ + return EnvironmentManager.getEnvironment(environmentType, contextId); + } + + /** + * Get the currently known environment contexts of a given environment type. + * @param environmentType the target environment type. + * @return the corresponding environment contexts known, never null. + */ + public static Set getEnvironmentContexts(String environmentType){ + return EnvironmentManager.getEnvironmentContexts(environmentType); + } + + /** + * Allows to check, if the czurrent environment type is one of the current active environment types. + * @param environmentType the environment type to be queried. + * @return true, if the czurrent environment type is one of the current active environment types. + */ + public static boolean isEnvironmentActive(String environmentType){ + return EnvironmentManager.isEnvironmentActive(environmentType); + } + + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/EnvironmentManager.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/EnvironmentManager.java b/api/src/main/java/org/apache/tamaya/EnvironmentManager.java new file mode 100644 index 0000000..7de86a6 --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/EnvironmentManager.java @@ -0,0 +1,136 @@ +/* + * 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; + +import org.apache.tamaya.spi.Bootstrap; +import org.apache.tamaya.spi.EnvironmentManagerSingletonSpi; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + + +/** + * Singleton accessor class for the current environment. + */ +final class EnvironmentManager{ + + private static final EnvironmentManagerSingletonSpi environmentManagerSingletonSpi = loadContextProviderSpi(); + + /** + * Private singleton constructor. + */ + private EnvironmentManager(){} + + /** + * Method to load the environment SPI during initial load. + * @return the EnvironmentProviderSpi SPI, never null. + */ + private static EnvironmentManagerSingletonSpi loadContextProviderSpi(){ + return Bootstrap.getService(EnvironmentManagerSingletonSpi.class); + } + + /** + * Get the current {@link Environment}. The environment is used to determine the current runtime state, which + * is important for returning the correct configuration. + * @return the current Environment, never null. + */ + public static Environment getEnvironment(){ + return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow( + () -> new IllegalStateException("No SPI loaded.") + ).getEnvironment(); + } + + /** + * Get the current root (startup/machine/VM) {@link Environment}. + * @return the current root Environment, never null. + */ + public static Environment getRootEnvironment(){ + return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow( + () -> new IllegalStateException("No SPI loaded.") + ).getRootEnvironment(); + } + + /** + * Evaluate the overall chain of possible environments. + * @return the hierarchy chain of possible Environments. + */ + public static List getEnvironmentTypeOrder(){ + return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow( + () -> new IllegalStateException("No SPI loaded.") + ).getEnvironmentTypeOrder(); + } + + /** + * Evaluate the current type chain of environments. + * @return the current type chain of Environments. + */ + public static List getEnvironmentHierarchy(){ + return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow( + () -> new IllegalStateException("No SPI loaded.") + ).getEnvironmentHierarchy(); + } + + /** + * Get the current environment of the given environment type. + * @param environmentType the target type. + * @return the corresponding environment + * @throws IllegalArgumentException if not such type is present or active. + */ + public static Optional getEnvironment(String environmentType){ + return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow( + () -> new IllegalStateException("No SPI loaded.") + ).getEnvironment(environmentType); + } + + /** + * Get a environment of the given environment type and context. + * @param environmentType the target type, not null. + * @param contextId the target context, not null. + * @return the corresponding environment, if available. + */ + public static Optional getEnvironment(String environmentType, String contextId){ + return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow( + () -> new IllegalStateException("No SPI loaded.") + ).getEnvironment(environmentType, contextId); + } + + /** + * Get the currently known environment contexts of a given environment type. + * @param environmentType the target environment type. + * @return the corresponding environment contexts known, never null. + */ + public static Set getEnvironmentContexts(String environmentType){ + return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow( + () -> new IllegalStateException("No SPI loaded.") + ).getEnvironmentContexts(environmentType); + } + + /** + * Allows to check, if the czurrent environment type is one of the current active environment types. + * @param environmentType the environment type to be queried. + * @return true, if the czurrent environment type is one of the current active environment types. + */ + public static boolean isEnvironmentActive(String environmentType){ + return Optional.ofNullable(environmentManagerSingletonSpi).orElseThrow( + () -> new IllegalStateException("No SPI loaded.") + ).isEnvironmentActive(environmentType); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/MetaInfo.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/MetaInfo.java b/api/src/main/java/org/apache/tamaya/MetaInfo.java new file mode 100644 index 0000000..8be9122 --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/MetaInfo.java @@ -0,0 +1,69 @@ +/* + * 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; + +import java.util.*; + +/** + * Simple class to represent configuration meta information. Metainformation can be related to the holw + * configuration or some if its entries. + */ +public final class MetaInfo{ + + private final Map metaInfo = new HashMap<>(); + + MetaInfo(MetaInfoBuilder builder){ + Objects.requireNonNull(builder); + this.metaInfo.putAll(builder.map); + } + + public static MetaInfo of(String info){ + return MetaInfoBuilder.of(info).build(); + } + + public String get(String key){ + return this.metaInfo.get(key); + + } + public Set keySet(){ + return this.metaInfo.keySet(); + } + + public Map toMap(){ + return Collections.unmodifiableMap(this.metaInfo); + } + + @Override + public String toString(){ + StringBuilder b = new StringBuilder("MetaInfo["); + for(Map.Entry en:metaInfo.entrySet()){ + b.append(escape(en.getKey())).append('=').append(escape(en.getValue())).append(", "); + } + if(!metaInfo.isEmpty()){ + b.setLength(b.length()-2); + } + b.append(']'); + return b.toString(); + } + + static String escape(String val){ + return val.replaceAll("=", "\\\\=").replaceAll("\\[", "\\\\[").replaceAll("]", "\\\\]").replaceAll("\\,", "\\\\,"); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/MetaInfoBuilder.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/MetaInfoBuilder.java b/api/src/main/java/org/apache/tamaya/MetaInfoBuilder.java new file mode 100644 index 0000000..7c527bc --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/MetaInfoBuilder.java @@ -0,0 +1,162 @@ +/* + * 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; + +import java.util.Arrays; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Builder class to create new instances of {@lin MetaInfo}. + */ +public final class MetaInfoBuilder{ + + public static final String METAINFO = "_metainfo"; + public static final String TIMESTAMP = "timestamp"; + public static final String CONTEXT = "context"; + public static final String NAME = "name"; + public static final String INFO = "info"; + public static final String TYPE = "type"; + public static final String SOURCE = "source"; + public static final String ENVIRONMENT = "environment"; + public static final String SOURCE_EXPRESSION = "source-expression"; + + Map map = new ConcurrentHashMap<>(); + + private MetaInfoBuilder(MetaInfo metaInfo){ + if(metaInfo!=null){ + this.map.putAll(metaInfo.toMap()); + } + } + + public static MetaInfoBuilder of(MetaInfo metaInfo){ + return new MetaInfoBuilder(metaInfo); + } + + public static MetaInfoBuilder of(String info){ + return new MetaInfoBuilder(null).setInfo(info); + } + + public static MetaInfoBuilder of(){ + return new MetaInfoBuilder(null); + } + + public MetaInfoBuilder withName(String name){ + Objects.requireNonNull(name); + map.put(NAME, name); + return this; + } + + public MetaInfoBuilder setName(String name){ + Objects.requireNonNull(name); + map.put(NAME, name); + return this; + } + + public MetaInfoBuilder setType(String type){ + Objects.requireNonNull(type); + map.put(TYPE, type); + return this; + } + + public MetaInfoBuilder setInfo(String info){ + Objects.requireNonNull(info); + map.put(INFO, info); + return this; + } + + public MetaInfoBuilder setSources(String... sources){ + Objects.requireNonNull(sources); + map.put(SOURCE, Arrays.toString(sources)); + return this; + } + + public MetaInfoBuilder setMetaInfo(String key, String metaInfo){ + Objects.requireNonNull(metaInfo); + Objects.requireNonNull(key); + map.put(key + '.' + METAINFO, metaInfo); + return this; + } + + public MetaInfoBuilder setMetaInfo(String metaInfo){ + if(metaInfo!=null){ + Objects.requireNonNull(metaInfo); + map.put(METAINFO, metaInfo); + } + return this; + } + + public MetaInfoBuilder setMetaInfo(MetaInfo metaInfo){ + if(metaInfo!=null){ + Objects.requireNonNull(metaInfo); + map.putAll(metaInfo.toMap()); + } + return this; + } + + public MetaInfoBuilder setSourceExpressions(String... sourceExpressions){ + Objects.requireNonNull(sourceExpressions); + map.put(SOURCE_EXPRESSION, Arrays.toString(sourceExpressions)); + return this; + } + + public MetaInfoBuilder setTimestamp(long timestamp){ + map.put(TIMESTAMP, String.valueOf(timestamp)); + return this; + } + + public MetaInfoBuilder setContext(String context){ + Objects.requireNonNull(context); + map.put(CONTEXT, context); + return this; + } + + public MetaInfoBuilder setEnvironment(Environment configurationContext){ + Objects.requireNonNull(configurationContext); + map.put(ENVIRONMENT, configurationContext.toString()); + return this; + } + + public MetaInfoBuilder set(String key, String value){ + Objects.requireNonNull(key); + Objects.requireNonNull(value); + map.put(key, value); + return this; + } + + public MetaInfo build(){ + return new MetaInfo(this); + } + + @Override + public String toString(){ + StringBuilder b = new StringBuilder("MetaInfoBuilder["); + for(Map.Entry en:map.entrySet()){ + b.append(MetaInfo.escape(en.getKey())).append('=').append(MetaInfo.escape(en.getValue())).append(", "); + } + if(!map.isEmpty()){ + b.setLength(b.length()-2); + } + b.append(']'); + return b.toString(); + } + + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/PropertyAdapter.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/PropertyAdapter.java b/api/src/main/java/org/apache/tamaya/PropertyAdapter.java new file mode 100644 index 0000000..6666907 --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/PropertyAdapter.java @@ -0,0 +1,34 @@ +/* + * 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; + + +/** + * Interface for an adapter that converts a configured String into something else. + */ +public interface PropertyAdapter{ + + /** + * Adapt the given configuration value to the required target type. + * @param value + * @return + */ + T adapt(String value); + +} http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/cf59ebbd/api/src/main/java/org/apache/tamaya/PropertyAdapters.java ---------------------------------------------------------------------- diff --git a/api/src/main/java/org/apache/tamaya/PropertyAdapters.java b/api/src/main/java/org/apache/tamaya/PropertyAdapters.java new file mode 100644 index 0000000..2103f60 --- /dev/null +++ b/api/src/main/java/org/apache/tamaya/PropertyAdapters.java @@ -0,0 +1,93 @@ +/* + * 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; + +import org.apache.tamaya.annot.WithPropertyAdapter; +import org.apache.tamaya.spi.Bootstrap; +import org.apache.tamaya.spi.PropertyAdaptersSingletonSpi; +import java.util.Optional; + +/** + * Singleton accessor that provides {@link PropertyAdapter} instance, usable for converting String + * based configuration entries into any other target types. + */ +public final class PropertyAdapters{ + /** The SPI finally registered backing this singleton. */ + private static final PropertyAdaptersSingletonSpi propertyAdaptersSingletonSpi = loadConfigAdapterProviderSpi(); + + /** + * Method that loads the singleton backing bean from the {@link org.apache.tamaya.spi.Bootstrap} component. + * @return the PropertyAdaptersSingletonSpi, never null. + */ + private static PropertyAdaptersSingletonSpi loadConfigAdapterProviderSpi(){ + return Bootstrap.getService(PropertyAdaptersSingletonSpi.class); + } + + /** + * Orivate singleton constructor. + */ + private PropertyAdapters(){} + + /** + * Registers a new PropertyAdapter for the given target type, hereby replacing any existing adapter for + * this type. + * @param targetType The target class, not null. + * @param adapter The adapter, not null. + * @param The target type + * @return any adapter replaced with the new adapter, or null. + */ + public static PropertyAdapter register(Class targetType, PropertyAdapter adapter){ + return Optional.of(propertyAdaptersSingletonSpi).get().register(targetType, adapter); + } + + /** + * Get an adapter converting to the given target type. + * @param targetType the target type class + * @return true, if the given target type is supported. + */ + public static boolean isTargetTypeSupported(Class targetType){ + return Optional.of(propertyAdaptersSingletonSpi).get().isTargetTypeSupported(targetType); + } + + /** + * Get an adapter converting to the given target type. + * @param targetType the target type class + * @param the target type + * @return the corresponding adapter, never null. + * @throws ConfigException if the target type is not supported. + */ + public static PropertyAdapter getAdapter(Class targetType){ + return getAdapter(targetType, null); + } + + /** + * Get an adapter converting to the given target type. + * @param targetType the target type class + * @param annotation the {@link org.apache.tamaya.annot.WithPropertyAdapter} annotation, or null. If the annotation is not null and + * defines an overriding adapter, this instance is created and returned. + * @param the target type + * @return the corresponding adapter, never null. + * @throws ConfigException if the target type is not supported, or the overriding adapter cannot be + * instantiated. + */ + public static PropertyAdapter getAdapter(Class targetType, WithPropertyAdapter annotation){ + return Optional.of(propertyAdaptersSingletonSpi).get().getAdapter(targetType, annotation); + } + +}