src/share/jaxws_classes/com/oracle/webservices/internal/api/message/BasePropertySet.java

Tue, 09 Apr 2013 14:51:13 +0100

author
alanb
date
Tue, 09 Apr 2013 14:51:13 +0100
changeset 368
0989ad8c0860
child 374
72e03566f0a6
permissions
-rw-r--r--

8010393: Update JAX-WS RI to 2.2.9-b12941
Reviewed-by: alanb, erikj
Contributed-by: miroslav.kos@oracle.com, martin.grebac@oracle.com

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
     5  *
     6  * The contents of this file are subject to the terms of either the GNU
     7  * General Public License Version 2 only ("GPL") or the Common Development
     8  * and Distribution License("CDDL") (collectively, the "License").  You
     9  * may not use this file except in compliance with the License.  You can
    10  * obtain a copy of the License at
    11  * http://glassfish.java.net/public/CDDL+GPL_1_1.html
    12  * or packager/legal/LICENSE.txt.  See the License for the specific
    13  * language governing permissions and limitations under the License.
    14  *
    15  * When distributing the software, include this License Header Notice in each
    16  * file and include the License file at packager/legal/LICENSE.txt.
    17  *
    18  * GPL Classpath Exception:
    19  * Oracle designates this particular file as subject to the "Classpath"
    20  * exception as provided by Oracle in the GPL Version 2 section of the License
    21  * file that accompanied this code.
    22  *
    23  * Modifications:
    24  * If applicable, add the following below the License Header, with the fields
    25  * enclosed by brackets [] replaced by your own identifying information:
    26  * "Portions Copyright [year] [name of copyright owner]"
    27  *
    28  * Contributor(s):
    29  * If you wish your version of this file to be governed by only the CDDL or
    30  * only the GPL Version 2, indicate your decision by adding "[Contributor]
    31  * elects to include this software in this distribution under the [CDDL or GPL
    32  * Version 2] license."  If you don't indicate a single choice of license, a
    33  * recipient has the option to distribute your version of this file under
    34  * either the CDDL, the GPL Version 2 or to extend the choice of license to
    35  * its licensees as provided above.  However, if you add GPL Version 2 code
    36  * and therefore, elected the GPL Version 2 license, then the option applies
    37  * only if the new code is made subject to such option by the copyright
    38  * holder.
    39  */
    41 package com.oracle.webservices.internal.api.message;
    43 import com.sun.istack.internal.NotNull;
    44 import com.sun.istack.internal.Nullable;
    46 import java.lang.reflect.Field;
    47 import java.lang.reflect.InvocationTargetException;
    48 import java.lang.reflect.Method;
    49 import java.security.AccessController;
    50 import java.security.PrivilegedAction;
    51 import java.util.AbstractMap;
    52 import java.util.HashMap;
    53 import java.util.HashSet;
    54 import java.util.Map;
    55 import java.util.Map.Entry;
    56 import java.util.Set;
    59 /**
    60  * A set of "properties" that can be accessed via strongly-typed fields
    61  * as well as reflexibly through the property name.
    62  *
    63  * @author Kohsuke Kawaguchi
    64  */
    65 @SuppressWarnings("SuspiciousMethodCalls")
    66 public abstract class BasePropertySet implements PropertySet {
    68     /**
    69      * Creates a new instance of TypedMap.
    70      */
    71     protected BasePropertySet() {
    72     }
    74     private Map<String,Object> mapView;
    76     /**
    77      * Represents the list of strongly-typed known properties
    78      * (keyed by property names.)
    79      *
    80      * <p>
    81      * Just giving it an alias to make the use of this class more fool-proof.
    82      */
    83     protected static class PropertyMap extends HashMap<String,Accessor> {
    85         // the entries are often being iterated through so performance can be improved
    86         // by their caching instead of iterating through the original (immutable) map each time
    87         transient PropertyMapEntry[] cachedEntries = null;
    89         PropertyMapEntry[] getPropertyMapEntries() {
    90             if (cachedEntries == null) {
    91                 cachedEntries = createPropertyMapEntries();
    92             }
    93             return cachedEntries;
    94         }
    96         private PropertyMapEntry[] createPropertyMapEntries() {
    97             final PropertyMapEntry[] modelEntries = new PropertyMapEntry[size()];
    98             int i = 0;
    99             for (final Entry<String, Accessor> e : entrySet()) {
   100                 modelEntries[i++] = new PropertyMapEntry(e.getKey(), e.getValue());
   101             }
   102             return modelEntries;
   103         }
   105     }
   107     /**
   108      * PropertyMapEntry represents a Map.Entry in the PropertyMap with more efficient access.
   109      */
   110     static public class PropertyMapEntry {
   111         public PropertyMapEntry(String k, Accessor v) {
   112             key = k; value = v;
   113         }
   114         String key;
   115         Accessor value;
   116     }
   118     /**
   119      * Map representing the Fields and Methods annotated with {@link PropertySet.Property}.
   120      * Model of {@link PropertySet} class.
   121      *
   122      * <p>
   123      * At the end of the derivation chain this method just needs to be implemented
   124      * as:
   125      *
   126      * <pre>
   127      * private static final PropertyMap model;
   128      * static {
   129      *   model = parse(MyDerivedClass.class);
   130      * }
   131      * protected PropertyMap getPropertyMap() {
   132      *   return model;
   133      * }
   134      * </pre>
   135      */
   136     protected abstract PropertyMap getPropertyMap();
   138     /**
   139      * This method parses a class for fields and methods with {@link PropertySet.Property}.
   140      */
   141     protected static PropertyMap parse(final Class clazz) {
   142         // make all relevant fields and methods accessible.
   143         // this allows runtime to skip the security check, so they runs faster.
   144         return AccessController.doPrivileged(new PrivilegedAction<PropertyMap>() {
   145             @Override
   146             public PropertyMap run() {
   147                 PropertyMap props = new PropertyMap();
   148                 for (Class c=clazz; c!=null; c=c.getSuperclass()) {
   149                     for (Field f : c.getDeclaredFields()) {
   150                         Property cp = f.getAnnotation(Property.class);
   151                         if(cp!=null) {
   152                             for(String value : cp.value()) {
   153                                 props.put(value, new FieldAccessor(f, value));
   154                             }
   155                         }
   156                     }
   157                     for (Method m : c.getDeclaredMethods()) {
   158                         Property cp = m.getAnnotation(Property.class);
   159                         if(cp!=null) {
   160                             String name = m.getName();
   161                             assert name.startsWith("get") || name.startsWith("is");
   163                             String setName = name.startsWith("is") ? "set"+name.substring(2) : // isFoo -> setFoo
   164                                                                      's'  +name.substring(1);  // getFoo -> setFoo
   165                             Method setter;
   166                             try {
   167                                 setter = clazz.getMethod(setName,m.getReturnType());
   168                             } catch (NoSuchMethodException e) {
   169                                 setter = null; // no setter
   170                             }
   171                             for(String value : cp.value()) {
   172                                 props.put(value, new MethodAccessor(m, setter, value));
   173                             }
   174                         }
   175                     }
   176                 }
   178                 return props;
   179             }
   180         });
   181     }
   183     /**
   184      * Represents a typed property defined on a {@link PropertySet}.
   185      */
   186     protected interface Accessor {
   187         String getName();
   188         boolean hasValue(PropertySet props);
   189         Object get(PropertySet props);
   190         void set(PropertySet props, Object value);
   191     }
   193     static final class FieldAccessor implements Accessor {
   194         /**
   195          * Field with the annotation.
   196          */
   197         private final Field f;
   199         /**
   200          * One of the values in {@link Property} annotation on {@link #f}.
   201          */
   202         private final String name;
   204         protected FieldAccessor(Field f, String name) {
   205             this.f = f;
   206             f.setAccessible(true);
   207             this.name = name;
   208         }
   210         @Override
   211         public String getName() {
   212             return name;
   213         }
   215         @Override
   216         public boolean hasValue(PropertySet props) {
   217             return get(props)!=null;
   218         }
   220         @Override
   221         public Object get(PropertySet props) {
   222             try {
   223                 return f.get(props);
   224             } catch (IllegalAccessException e) {
   225                 throw new AssertionError();
   226             }
   227         }
   229         @Override
   230         public void set(PropertySet props, Object value) {
   231             try {
   232                 f.set(props,value);
   233             } catch (IllegalAccessException e) {
   234                 throw new AssertionError();
   235             }
   236         }
   237     }
   239     static final class MethodAccessor implements Accessor {
   240         /**
   241          * Getter method.
   242          */
   243         private final @NotNull Method getter;
   244         /**
   245          * Setter method.
   246          * Some property is read-only.
   247          */
   248         private final @Nullable Method setter;
   250         /**
   251          * One of the values in {@link Property} annotation on {@link #getter}.
   252          */
   253         private final String name;
   255         protected MethodAccessor(Method getter, Method setter, String value) {
   256             this.getter = getter;
   257             this.setter = setter;
   258             this.name = value;
   259             getter.setAccessible(true);
   260             if (setter!=null) {
   261                 setter.setAccessible(true);
   262             }
   263         }
   265         @Override
   266         public String getName() {
   267             return name;
   268         }
   270         @Override
   271         public boolean hasValue(PropertySet props) {
   272             return get(props)!=null;
   273         }
   275         @Override
   276         public Object get(PropertySet props) {
   277             try {
   278                 return getter.invoke(props);
   279             } catch (IllegalAccessException e) {
   280                 throw new AssertionError();
   281             } catch (InvocationTargetException e) {
   282                 handle(e);
   283                 return 0;   // never reach here
   284             }
   285         }
   287         @Override
   288         public void set(PropertySet props, Object value) {
   289             if(setter==null) {
   290                 throw new ReadOnlyPropertyException(getName());
   291             }
   292             try {
   293                 setter.invoke(props,value);
   294             } catch (IllegalAccessException e) {
   295                 throw new AssertionError();
   296             } catch (InvocationTargetException e) {
   297                 handle(e);
   298             }
   299         }
   301         /**
   302          * Since we don't expect the getter/setter to throw a checked exception,
   303          * it should be possible to make the exception propagation transparent.
   304          * That's what we are trying to do here.
   305          */
   306         private Exception handle(InvocationTargetException e) {
   307             Throwable t = e.getTargetException();
   308             if (t instanceof Error) {
   309                 throw (Error)t;
   310             }
   311             if (t instanceof RuntimeException) {
   312                 throw (RuntimeException)t;
   313             }
   314             throw new Error(e);
   315         }
   316     }
   319     /**
   320      * Class allowing to work with PropertySet object as with a Map; it doesn't only allow to read properties from
   321      * the map but also to modify the map in a way it is in sync with original strongly typed fields. It also allows
   322      * (if necessary) to store additional properties those can't be found in strongly typed fields.
   323      *
   324      * @see com.sun.xml.internal.ws.api.PropertySet#asMap() method
   325      */
   326     final class MapView extends HashMap<String, Object> {
   328         // flag if it should allow store also different properties
   329         // than the from strongly typed fields
   330         boolean extensible;
   332         MapView(boolean extensible) {
   333                 super(getPropertyMap().getPropertyMapEntries().length);
   334             this.extensible = extensible;
   335             initialize();
   336         }
   338         public void initialize() {
   339             // iterate (cached) array instead of map to speed things up ...
   340             PropertyMapEntry[] entries = getPropertyMap().getPropertyMapEntries();
   341             for (PropertyMapEntry entry : entries) {
   342                 super.put(entry.key, entry.value);
   343             }
   344         }
   346         @Override
   347         public Object get(Object key) {
   348             Object o = super.get(key);
   349             if (o instanceof Accessor) {
   350                 return ((Accessor) o).get(BasePropertySet.this);
   351             } else {
   352                 return o;
   353             }
   354         }
   356         @Override
   357         public Set<Entry<String, Object>> entrySet() {
   358             Set<Entry<String, Object>> entries = new HashSet<Entry<String, Object>>();
   359             for (String key : keySet()) {
   360                 entries.add(new SimpleImmutableEntry<String, Object>(key, get(key)));
   361             }
   362             return entries;
   363         }
   365         @Override
   366         public Object put(String key, Object value) {
   368             Object o = super.get(key);
   369             if (o != null && o instanceof Accessor) {
   371                 Object oldValue = ((Accessor) o).get(BasePropertySet.this);
   372                 ((Accessor) o).set(BasePropertySet.this, value);
   373                 return oldValue;
   375             } else {
   377                 if (extensible) {
   378                     return super.put(key, value);
   379                 } else {
   380                     throw new IllegalStateException("Unknown property [" + key + "] for PropertySet [" +
   381                             BasePropertySet.this.getClass().getName() + "]");
   382                 }
   383             }
   384         }
   386         @Override
   387         public void clear() {
   388             for (String key : keySet()) {
   389                 remove(key);
   390             }
   391         }
   393         @Override
   394         public Object remove(Object key) {
   395             Object o;
   396             o = super.get(key);
   397             if (o instanceof Accessor) {
   398                 ((Accessor)o).set(BasePropertySet.this, null);
   399             }
   400             return super.remove(key);
   401         }
   402     }
   404     @Override
   405     public boolean containsKey(Object key) {
   406         Accessor sp = getPropertyMap().get(key);
   407         if (sp != null) {
   408             return sp.get(this) != null;
   409         }
   410         return false;
   411     }
   413     /**
   414      * Gets the name of the property.
   415      *
   416      * @param key
   417      *      This field is typed as {@link Object} to follow the {@link Map#get(Object)}
   418      *      convention, but if anything but {@link String} is passed, this method
   419      *      just returns null.
   420      */
   421     @Override
   422     public Object get(Object key) {
   423         Accessor sp = getPropertyMap().get(key);
   424         if (sp != null) {
   425             return sp.get(this);
   426         }
   427         throw new IllegalArgumentException("Undefined property "+key);
   428     }
   430     /**
   431      * Sets a property.
   432      *
   433      * <h3>Implementation Note</h3>
   434      * This method is slow. Code inside JAX-WS should define strongly-typed
   435      * fields in this class and access them directly, instead of using this.
   436      *
   437      * @throws ReadOnlyPropertyException
   438      *      if the given key is an alias of a strongly-typed field,
   439      *      and if the name object given is not assignable to the field.
   440      *
   441      * @see Property
   442      */
   443     @Override
   444     public Object put(String key, Object value) {
   445         Accessor sp = getPropertyMap().get(key);
   446         if(sp!=null) {
   447             Object old = sp.get(this);
   448             sp.set(this,value);
   449             return old;
   450         } else {
   451             throw new IllegalArgumentException("Undefined property "+key);
   452         }
   453     }
   455     /**
   456      * Checks if this {@link PropertySet} supports a property of the given name.
   457      */
   458     @Override
   459     public boolean supports(Object key) {
   460         return getPropertyMap().containsKey(key);
   461     }
   463     @Override
   464     public Object remove(Object key) {
   465         Accessor sp = getPropertyMap().get(key);
   466         if(sp!=null) {
   467             Object old = sp.get(this);
   468             sp.set(this,null);
   469             return old;
   470         } else {
   471             throw new IllegalArgumentException("Undefined property "+key);
   472         }
   473     }
   475     /**
   476      * Creates a {@link Map} view of this {@link PropertySet}.
   477      *
   478      * <p>
   479      * This map is partially live, in the sense that values you set to it
   480      * will be reflected to {@link PropertySet}.
   481      *
   482      * <p>
   483      * However, this map may not pick up changes made
   484      * to {@link PropertySet} after the view is created.
   485      *
   486      * @deprecated use newer implementation {@link PropertySet#asMap()} which produces
   487      * readwrite {@link Map}
   488      *
   489      * @return
   490      *      always non-null valid instance.
   491      */
   492     @Deprecated
   493     @Override
   494     public final Map<String,Object> createMapView() {
   495         final Set<Entry<String,Object>> core = new HashSet<Entry<String,Object>>();
   496         createEntrySet(core);
   498         return new AbstractMap<String, Object>() {
   499             @Override
   500             public Set<Entry<String,Object>> entrySet() {
   501                 return core;
   502             }
   503         };
   504     }
   506     /**
   507      * Creates a modifiable {@link Map} view of this {@link PropertySet}.
   508      * <p/>
   509      * Changes done on this {@link Map} or on {@link PropertySet} object work in both directions - values made to
   510      * {@link Map} are reflected to {@link PropertySet} and changes done using getters/setters on {@link PropertySet}
   511      * object are automatically reflected in this {@link Map}.
   512      * <p/>
   513      * If necessary, it also can hold other values (not present on {@link PropertySet}) -
   514      * {@see PropertySet#mapAllowsAdditionalProperties}
   515      *
   516      * @return always non-null valid instance.
   517      */
   518     @Override
   519     public Map<String, Object> asMap() {
   520         if (mapView == null) {
   521             mapView = createView();
   522         }
   523         return mapView;
   524     }
   526     protected Map<String, Object> createView() {
   527         return new MapView(mapAllowsAdditionalProperties());
   528     }
   530     /**
   531      * Used when constructing the {@link MapView} for this object - it controls if the {@link MapView} servers only to
   532      * access strongly typed values or allows also different values
   533      *
   534      * @return true if {@link Map} should allow also properties not defined as strongly typed fields
   535      */
   536     protected boolean mapAllowsAdditionalProperties() {
   537         return false;
   538     }
   540     protected void createEntrySet(Set<Entry<String,Object>> core) {
   541         for (final Entry<String, Accessor> e : getPropertyMap().entrySet()) {
   542             core.add(new Entry<String, Object>() {
   543                 @Override
   544                 public String getKey() {
   545                     return e.getKey();
   546                 }
   548                 @Override
   549                 public Object getValue() {
   550                     return e.getValue().get(BasePropertySet.this);
   551                 }
   553                 @Override
   554                 public Object setValue(Object value) {
   555                     Accessor acc = e.getValue();
   556                     Object old = acc.get(BasePropertySet.this);
   557                     acc.set(BasePropertySet.this,value);
   558                     return old;
   559                 }
   560             });
   561         }
   562     }
   563 }

mercurial