Thursday, March 20, 2014

Adding Additional Type Support to Configuration

Adding Additional Type Support to Configuration

In my previous blog I discussed roughly what configuration is. I also stopped on a working assumption that configuration mainly is a Map<String,String> type with additional metadata. In this blog I would like to elaborate, how non literal types can be supported. Summarizing I would define the following requirements:
  • it should be possible to access configuration as non literal type
  • all types contained in java.lang should be supported.
  • nevertheless arbitrary other types should also be enabled
  • it should be possible to register "converters"
  • it should also be possible to pass a matching "converter" programmatically
First of all we have to think about, what kind of functionality we want to add here to the basic Configuration interface (this is also the reason why converter is written in italic face above).
Basically adding type support requires a configuration entry's value, that is a String to be compatible with some arbitrary type:
 "...allows the interface of an existing class to be used from another interface."
This is exactly the GoF's adapter pattern. So let as define an adapter:

@FunctionalInterface
public interface PropertyAdapter<T>{
  <T> T adapt(String value);
}

Now we can extend
Configuration to allow access on configuration using such an adapter:

public interface Configuration{
   ...
   <T> T getAdapted(String key, PropertyAdapter<T> adapter);

}

Obviously this is a very minimalistic approach. Now thinking on all basic types defined in java.lang, it is a good idea to add corresponding convenience methods:


public interface Configuration{
   ...
   Character getCharacter(String key);
   Byte getByte(String key);
   Short getShort(String key);
   Integer getInteger(String key);
   Long getLong(String key);
   Float getFloat(String key);
   Double getDouble(String key);     

}

By default, I would suggest throwing a RuntimeException, if a value is missing, is a good idea, so these methods never will return null values. Additinoally it might b e a good idea to let also default values to be returned, so we add also the following methods:


   Character getCharacterOrDefault(String key,
                                  
Character defaultValue);
   Byte getByteOrDefault(String key, Byte defaultValue);
   Short getShortOrDefault(String key, Short defaultValue);
   Integer getIntegerOrDefault(String key, Integer defaultValue);
   Long getLongOrDefault(String key, Long defaultValue);
   Float getFloatOrDefault(String key, Float defaultValue);
   Double getDoubleOrDefault(String key, Double defaultValue);
   <T> T getAdaptedOrDefault(String key, Adapter<T> adapter,
                             T defaultValue
);
 

With the above signatures passing null as a default value is completely valid. So one might write:

Byte myNumber = config.getByte("minNumber", null);
if(myNumber==null){
   // do whatever needed
}

Though such code above is quite common, it might be worthwil to think on additional utility functionality, e.g. using JDK 8 features:

Configurator configurator = Configurator.of(config);
configurator.forByte("minNumber", MyNumbers::configure};

Though it would be interesting to investigate this area more, I would like to go one step back and ask, if a single String configuration value is always enough to create/implement every type of adapted interface. We have seen that a configuration entry is basically
  • a configuration key
  • a configuration value
  • configuration entry metadata
So to cover that we would extend our interface slightly as follows:

@FunctionalInterface
public interface PropertyAdapter<T>{
  <T> T adapt(String key, String value,
              Map<String,String> metadata);
}
 
But this is still not optimal, we would be much more flexible by passing the Configuration instance, on which the adapter is used, to the adapter implementation:

@FunctionalInterface
public interface PropertyAdapter<T>{
  <T> T adapt(String key, Configuration config);
}

More generally the above is simply a specialization of a query against a configuration, but just for one specific key:

@FunctionalInterface
public interface ConfigurationQuery<T> {
   T queryFrom(Configuration config);
}

Now these concepts can be useful, when thinking on collection support. Image the following configuration:

foo.a=aValue
foo.b=bValue
foo.c=cValue

Given this configuration foo can be adapted in different ways:
  • It can be adapted to a Map with a=aValue, b=bValue, c=cValue
  • But it can also be mapped to a List (interpreting a,b,c as ordering predicate) with aValue, bValue, cValue.
  • Similarly it could also be mapped to a tree:
              (root)
             /  |   \
           a    b    c
           |    |    |   
      aValue  bValue cValue

With the extended adapter definition all that is possible.

With that our Configuration is already very flexible, for example think on the following entry: 

javax.persistence.unit.MyUnit=https://myconfigserver.net/myApp/persistence/MyUnit.xml?myinstance=${instance.host} 

This entry could reference a persistence.xml, that is provided remotely. With ${instance.host} EL could be used to enable also dynamic aspects included into the configuration.
Now an adapter might return a String, containing the descriptor file, but this would load the configuration completely into memory. As an alternate we might allow to return an InputStream:

InputStream myUnitConfigStream = config.getAdapted(
                           "javax.persistence.unit.MyUnit",

                           URLResolver.of());


URLResolver would implements hereby an adapter that creates an URL and tries to load it, returning the InputStream. It is accessed using a static factory method:

public final class URLResolver implements PropertyAdapter<InputStream>{

  private static final URLResolver INSTANCE = new URLResolver();








  private URLResolver(){}

  
  public InputStream adapt(String key, Configuration config){
    try{
      URL url = new URL(config.getValueOrDefault(key, null));
      if(url!=null){
        return url.openStream();
      }

    }
    catch (Exception e){
      // log error

    }
    return null;
  }

  public static URLResolver of(){
    return INSTANCE;
  }

}
  
So we have seen that with rather small additional to the Configuration interface, we already have gained much flexibility, what we can do with it. Thinking on the new features of Java 8 configuration will be for sure get much more fun than it was in the past.
I would be interested, on what you think would be useful scenarios, using the mechanism presented in this blog. So you are invited to leave your comments and ideas!



12 comments:

  1. Interesting post. which i wondered about this issue so thanks for posting and very good article which is a really very nice and useful article. Thank you
    Data Science Course in Noida

    ReplyDelete
  2. I really enjoyed reading this post and keep up the good work and let me know when you can post more articles or where I can find out more on the topic.
    Data Science Online Course

    ReplyDelete
  3. Nice Post thank you very much for sharing such a useful information and will definitely saved and revisit your site and i have bookmarked to check out new things frm your post.
    Data Science Course

    ReplyDelete
  4. Excellent work done by you once again here and this is just the reason why I’ve always liked your work with amazing writing skills and you display them in every article. Keep it going!
    Data Analytics Courses in Hyderabad

    ReplyDelete
  5. I like viewing this web page which comprehend the price of delivering the excellent useful resource free of charge and truly adored reading your posting. Thank you!
    Data Science Certification Course

    ReplyDelete
  6. Just a shine from you here and have never expected anything less from you and have not disappointed me at all which i guess you will continue the quality work. Great post.
    Data Science Training in Gurgaon

    ReplyDelete
  7. Excellent post to make this blog more wonderful, attractive and cool stuff you have. Thank You.
    Data Science Course in India with Placements

    ReplyDelete
  8. I just got to this amazing site not long ago was actually captured with the piece of resources you have got here and big thumbs up for making such wonderful blog page!
    Data Scientist Course

    ReplyDelete
  9. Informative Post. The information you have posted is very useful and sites you have referred was good. Thanks for sharing.
    Data Science Course with Placement

    ReplyDelete
  10. You have done a great job and will definitely dig it and personally recommend to my friends. Thank You.
    Data Science Online Training

    ReplyDelete