The Artima Developer Community
Sponsored Link

Weblogs Forum
Object Adapter based on Dynamic Proxy

4 replies on 1 page. Most recent reply: Jul 23, 2008 4:09 PM by Axan Org

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 4 replies on 1 page
Heinz Kabutz

Posts: 46
Nickname: drbean
Registered: May, 2003

Object Adapter based on Dynamic Proxy (View in Weblogs)
Posted: May 11, 2005 5:40 AM
Reply to this message Reply
Summary
The Object Adapter Design Pattern is often used for modifying an interface into another interface that the client expects. A drawback with this pattern is that you need to implement all the methods of the target interface. In this newsletter, solve the problem with dynamice proxies
Advertisement

In the latest upgrade of my website (many more to come soon) I have added an RSS feed for The Java(tm) Specialists' Newsletter for those who would prefer receiving the newsletter via RSS rather than as an email. The RSS feed will always only have the most current newsletter on it, but the old issues will, as always, still be available for free on our website.

Object Adapter based on Dynamic Proxy

A few weeks ago, I presented my Design Patterns Course to a wonderfully inspiring audience in Austria. One of the three Doctors of Computer Science in the course, Dr Klaus Wiederaenders suggested an approach to solve a problem that had been bugging me with the Object Adapter Pattern.

There are two different types of Adapter Design Patterns: Object Adapter and Class Adapter. The Object Adapter has the advantage that it can be used to adapt a whole hierarchy of objects, whereas the Class Adapter has the advantage that you do not need to override all the methods.

Application of Object Adapter

Java 5 has a neat little feature, that is not widely known. You can change the return type of overridden methods. For example, clone() now returns the correct type of the object, so you do not need to downcast the result anymore.

One of my annoyances with Java has been that Collection.toArray() returns an Object[] and not the correct type. Say you have a collection containing Strings, then you have to pass a String[] into the toArray() method. This seems clumsy to me. It would have been nice if this had been changed in Java 5. However, generics cannot solve the problem due to erasure. There is no handle to the type of generic, once the code has been compiled. You therefore have to change the construction of the collection object to also have a handle to the class of the generic type.

My first solution was to write a Class Adapter, which extended java.util.ArrayList with my adapter, which I called BetterArrayList. I have put these classes in a package so that we can do static imports later on:

package com.maxoft.tjsn.util;

import java.lang.reflect.Array;
import java.util.*;

public class BetterArrayList extends ArrayList {
  private final Class valueType;
  public BetterArrayList(int initialCapacity, Class valueType) {
    super(initialCapacity);
    this.valueType = valueType;
  }
  public BetterArrayList(Class valueType) {
    this.valueType = valueType;
  }
  public BetterArrayList(Collectionextends T> ts,
                         Class valueType) {
    super(ts);
    this.valueType = valueType;
  }
  // You can modify the return type of an overridden method in
  // Java 5, with some restrictions.
  public T[] toArray() {
    return toArray((T[]) Array.newInstance(valueType, size()));
  }
}

We can now use this in our code instead of the ArrayList, and then we do not need to have such an awkward syntax for converting it to a type-safe array. We have to pass the class object into the constructor, but the compiler checks that it is the correct class object for the generic type.

package com.maxoft.tjsn.util;

public class BetterArrayListTest {
  public static void main(String[] args) {
    BetterArrayList names =
        new BetterArrayList(String.class);
    names.add("Wolfgang");
    names.add("Leander");
    names.add("Klaus");
    names.add("Reinhard");
    String[] nameArray = names.toArray();
    for (String s : nameArray) {
      System.out.println(s);
    }
  }
}

This would be a reasonable solution if we only ever wanted to use ArrayLists. However, we have to write a class adapter for every collection class that we might want to use.

Here is a new interface, called the BetterCollection, that extends the Collection interface, and changes the return type of the toArray() method:

package com.maxoft.tjsn.util;

import java.util.Collection;

public interface BetterCollection  extends Collection {
  T[] toArray();
}

We can then implement that interface in an object adapter (note how much more code this is!):

package com.maxoft.tjsn.util;

import java.lang.reflect.Array;
import java.util.*;

public class BetterCollectionObjectAdapter
    implements BetterCollection {
  private final Collection adaptee;
  private final Class valueType;
  public BetterCollectionObjectAdapter(Collection adaptee,
                                       Class valueType) {
    this.adaptee = adaptee;
    this.valueType = valueType;
  }
  public T[] toArray() {
    return adaptee.toArray((T[]) Array.newInstance(valueType,
        adaptee.size()));
  }
  // this is a typical problem with the Object Adapter Design
  // Pattern - you have implement all the methods :-(
  public int size() {
    return adaptee.size();
  }
  public boolean isEmpty() {
    return adaptee.isEmpty();
  }
  public boolean contains(Object o) {
    return adaptee.contains(o);
  }
  public Iterator iterator() {
    return adaptee.iterator();
  }
  public  T[] toArray(T[] ts) {
    return adaptee.toArray(ts);
  }
  public boolean add(T t) {
    return adaptee.add(t);
  }
  public boolean remove(Object o) {
    return adaptee.remove(o);
  }
  public boolean containsAll(Collection c) {
    return adaptee.containsAll(c);
  }
  public boolean addAll(Collection ts) {
    return adaptee.addAll(ts);
  }
  public boolean removeAll(Collection c) {
    return adaptee.removeAll(c);
  }
  public boolean retainAll(Collection c) {
    return adaptee.retainAll(c);
  }
  public void clear() {
    adaptee.clear();
  }
}

We can use this as an adapter for any type of collection, for example:

package com.maxoft.tjsn.util;

import java.util.LinkedList;

public class BetterCollectionTest {
  public static void main(String[] args) {
    BetterCollection names =
      new BetterCollectionObjectAdapter(
        new LinkedList(), String.class);
    names.add("Wolfgang");
    names.add("Leander");
    names.add("Klaus");
    names.add("Reinhard");
    String[] nameArray = names.toArray();
    for (String s : nameArray) {
      System.out.println(s);
    }
  }
}
  

This solution works, but I dont like being exposed to changes in the interface. Should Sun ever decide to add a new method to the Collection interface, our class would not compile anymore. Also, it is a lot of code to implement all those methods, and to support the extended interfaces of List, Set, SortedSet, etc., I would need to again write other adapters. I know the chance of Sun changing java.util.Collection is rather remote, but I did have this experience a few times with the java.sql.Connection interface that I had adapted.

Dynamic Object Adapter using Dynamic Proxies

Now that we have seen the problem, let's examine the solution based on dynamic proxies (with thanks to Dr Klaus Wiederaenders for the idea). In the past I have written several object adapters based on interfaces. Besides being a lot of boring work, we experience pain new methods are added to the interface. It can easily occur that your object adapter then only works for one specific version of Java.

The first piece of the puzzle is a DynamicObjectAdapterFactory. This contains the method adapt, which takes an adaptee (the object that we are adapting), the target (the interface that we want to return) and the adapter (the object that contains methods which override adaptee behaviour). We then create a dynamic proxy of the target interface. The invocation handler gets called whenever a method is called on the dymanic proxy. Each of the declared methods in the adapter is put into a map using an identifier that is based on the name and parameter list of the method. This way, there does not have to be an inheritance relationship between the adapter and the target.

package com.maxoft.tjsn.util;

import java.lang.reflect.*;
import java.util.*;

public class DynamicObjectAdapterFactory {
  public static  T adapt(final Object adaptee,
                            final Class target,
                            final Object adapter) {
    return (T) Proxy.newProxyInstance(
        Thread.currentThread().getContextClassLoader(),
        new Class[]{target},
        new InvocationHandler() {
          private Map adaptedMethods =
              new HashMap();
          // initializer block - find all methods in adapter object
          {
            Method[] methods = adapter.getClass().getDeclaredMethods();
            for (Method m : methods) {
              adaptedMethods.put(new MethodIdentifier(m), m);
            }
          }
          public Object invoke(Object proxy, Method method,
                               Object[] args) throws Throwable {
            try {
              Method other = adaptedMethods.get(
                  new MethodIdentifier(method));
              if (other != null) {
                return other.invoke(adapter, args);
              } else {
                return method.invoke(adaptee, args);
              }
            } catch (InvocationTargetException e) {
              throw e.getTargetException();
            }
          }
        });
  }

  private static class MethodIdentifier {
    private final String name;
    private final Class[] parameters;
    public MethodIdentifier(Method m) {
      name = m.getName();
      parameters = m.getParameterTypes();
    }
    // we can save time by assuming that we only compare against
    // other MethodIdentifier objects
    public boolean equals(Object o) {
      MethodIdentifier mid = (MethodIdentifier) o;
      return name.equals(mid.name) &&
          Arrays.equals(parameters, mid.parameters);
    }
    public int hashCode() {
      return name.hashCode();
    }
  }
}
  

I have used Java 5 generics in the dynamic object adapter factory, but the solution would also work with JDK 1.3. Here is how I would use the dynamic object adapter factory to make an object adapter for my BetterCollection. Note that this code generates a compiler warning, which we may safely ignore. This type of code is what the @SuppressWarnings annotation is meant for, but it seems to not have been implemented in the compiler.

package com.maxoft.tjsn.util;

import java.lang.reflect.Array;
import java.util.Collection;
import static com.maxoft.tjsn.util.DynamicObjectAdapterFactory.*;

public class BetterCollectionFactory {
  public static  BetterCollection asBetterCollection(
      final Collection adaptee, final Class valueType) {
    return adapt(adaptee,
        BetterCollection.class,
        // this anonymous inner class contains the method that
        // we want to adapt
        new Object() {
          public T[] toArray() {
            return adaptee.toArray((T[]) Array.newInstance(
                valueType, adaptee.size()));
          }
          // Whilst we are at it, we could also make it into a
          // checked collection, see java.util.Collections for
          // an example.
          public boolean add(T o) {
            if (!valueType.isInstance(o))
              throw new ClassCastException("Attempt to insert " +
                  o.getClass() +
                  " value into collection with value type " + valueType);
            return adaptee.add(o);
          }
          // addAll left as an exercise for the reader :-)
        });
  }
}
  

Here is how we would use this BetterCollectionFactory. The static imports in Java 5 save us some typing. We can simply write asBetterCollection() and it then wraps our collection with the BetterCollection using the adapter factory.

package com.maxoft.tjsn.util;

import java.util.*;
import static com.maxoft.tjsn.util.BetterCollectionFactory.*;

public class BestCollectionTest {
  public static void main(String[] args) {
    BetterCollection names = asBetterCollection(
        new ArrayList(), String.class);
    names.add("Wolfgang");
    names.add("Leander");
    names.add("Klaus");
    names.add("Reinhard");
    String[] nameArray = names.toArray();
    for (String s : nameArray) {
      System.out.println(s);
    }
  }
}
  

Next Thursday (19th May 2005) I am scheduled to present a talk at the University of Crete on Design Patterns, specifically on how to use dynamic proxies in Java to autogenerate code for virtual proxies and security proxies. Please send me an email if you are in or near Iraklion, Crete, Greece next week and would like to attend.

Kind regards

Heinz


Gregg Wonderly

Posts: 317
Nickname: greggwon
Registered: Apr, 2003

Re: Object Adapter based on Dynamic Proxy Posted: May 13, 2005 4:32 PM
Reply to this message Reply
The dynamic proxy facilities in Java 1.4 and later are very powerful. The uses that you show here, using a hierarchy of objects for creating an adapter for overiding implementations is a very, very powerful way to replace a method in any object with a different implementation. Clearly it doesn't resolve the issue of data disparity between different objects that provide the methods, but there is some power, as you show, in this capability.

Combined with the fact that this dynamic proxy could be mobile code and the referred-to objects could be remove services, one can visualize how the smart-proxy mechanism empowers the developer to solve problems in unique and often powerful ways.

Heinz Kabutz

Posts: 46
Nickname: drbean
Registered: May, 2003

Re: Object Adapter based on Dynamic Proxy Posted: May 25, 2005 2:38 AM
Reply to this message Reply
Copying the HTML into the Artima Weblog messed up my generics code, as pointed out by Ahmed Magdub.

Please look at the original on http://www.javaspecialists.co.za/archive/Issue108.html

Heinz

razzar mohammed

Posts: 2
Nickname: jackb
Registered: May, 2006

Re: Object Adapter based on Dynamic Proxy Posted: May 26, 2006 8:07 PM
Reply to this message Reply
I found there are several restrictions on the parameters that can be passed to Proxy.getProxyClass:

* All of the Class objects in the interfaces array must represent interfaces, not classes or primitive types.
* No two elements in the interfaces array may refer to identical Class objects.
* All of the interface types must be visible by name through the specified class loader.

However, incredibly powerful.

Axan Org

Posts: 1
Nickname: axan
Registered: Jul, 2008

Re: Object Adapter based on Dynamic Proxy Posted: Jul 23, 2008 4:09 PM
Reply to this message Reply
I was thinking about how to override method return type and you give me the answer, I have another problem you may already have the solution or idea for..

I would like to make a proxy of an interface with another interface that is identical to the adapted one except that I want to wrap all the return types.

I may mannualy write my wrapped interface, but I look for a solution to create my wrapped interface dynamicaly.

ex:
Dinamycaly wrap this interface:

interface MustBeAdapted
{
   void iReturnNothingAndThatIsEmbarassing(int arg);
   Boolean iReturnABoolean();
}

to :
interface DynamicalyNamed
{
   Future<Void> iReturnNothingAndThatIsEmbarassing(int arg);
   Future<Boolean> iReturnABoolean();
}

Flat View: This topic has 4 replies on 1 page
Topic: Vise - A Testing/Refactoring Tool for Java Previous Topic   Next Topic Topic: Simplyfing Java Generics by Eliminating Wildcards


Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us