1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/oracle/webservices/internal/api/message/BasePropertySet.java Wed Apr 27 01:27:09 2016 +0800 1.3 @@ -0,0 +1,548 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.oracle.webservices.internal.api.message; 1.30 + 1.31 +import com.sun.istack.internal.NotNull; 1.32 +import com.sun.istack.internal.Nullable; 1.33 + 1.34 +import java.lang.reflect.Field; 1.35 +import java.lang.reflect.InvocationTargetException; 1.36 +import java.lang.reflect.Method; 1.37 +import java.security.AccessController; 1.38 +import java.security.PrivilegedAction; 1.39 +import java.util.AbstractMap; 1.40 +import java.util.HashMap; 1.41 +import java.util.HashSet; 1.42 +import java.util.Map; 1.43 +import java.util.Map.Entry; 1.44 +import java.util.Set; 1.45 + 1.46 + 1.47 +/** 1.48 + * A set of "properties" that can be accessed via strongly-typed fields 1.49 + * as well as reflexibly through the property name. 1.50 + * 1.51 + * @author Kohsuke Kawaguchi 1.52 + */ 1.53 +@SuppressWarnings("SuspiciousMethodCalls") 1.54 +public abstract class BasePropertySet implements PropertySet { 1.55 + 1.56 + /** 1.57 + * Creates a new instance of TypedMap. 1.58 + */ 1.59 + protected BasePropertySet() { 1.60 + } 1.61 + 1.62 + private Map<String,Object> mapView; 1.63 + 1.64 + /** 1.65 + * Represents the list of strongly-typed known properties 1.66 + * (keyed by property names.) 1.67 + * 1.68 + * <p> 1.69 + * Just giving it an alias to make the use of this class more fool-proof. 1.70 + */ 1.71 + protected static class PropertyMap extends HashMap<String,Accessor> { 1.72 + 1.73 + // the entries are often being iterated through so performance can be improved 1.74 + // by their caching instead of iterating through the original (immutable) map each time 1.75 + transient PropertyMapEntry[] cachedEntries = null; 1.76 + 1.77 + PropertyMapEntry[] getPropertyMapEntries() { 1.78 + if (cachedEntries == null) { 1.79 + cachedEntries = createPropertyMapEntries(); 1.80 + } 1.81 + return cachedEntries; 1.82 + } 1.83 + 1.84 + private PropertyMapEntry[] createPropertyMapEntries() { 1.85 + final PropertyMapEntry[] modelEntries = new PropertyMapEntry[size()]; 1.86 + int i = 0; 1.87 + for (final Entry<String, Accessor> e : entrySet()) { 1.88 + modelEntries[i++] = new PropertyMapEntry(e.getKey(), e.getValue()); 1.89 + } 1.90 + return modelEntries; 1.91 + } 1.92 + 1.93 + } 1.94 + 1.95 + /** 1.96 + * PropertyMapEntry represents a Map.Entry in the PropertyMap with more efficient access. 1.97 + */ 1.98 + static public class PropertyMapEntry { 1.99 + public PropertyMapEntry(String k, Accessor v) { 1.100 + key = k; value = v; 1.101 + } 1.102 + String key; 1.103 + Accessor value; 1.104 + } 1.105 + 1.106 + /** 1.107 + * Map representing the Fields and Methods annotated with {@link PropertySet.Property}. 1.108 + * Model of {@link PropertySet} class. 1.109 + * 1.110 + * <p> 1.111 + * At the end of the derivation chain this method just needs to be implemented 1.112 + * as: 1.113 + * 1.114 + * <pre> 1.115 + * private static final PropertyMap model; 1.116 + * static { 1.117 + * model = parse(MyDerivedClass.class); 1.118 + * } 1.119 + * protected PropertyMap getPropertyMap() { 1.120 + * return model; 1.121 + * } 1.122 + * </pre> 1.123 + */ 1.124 + protected abstract PropertyMap getPropertyMap(); 1.125 + 1.126 + /** 1.127 + * This method parses a class for fields and methods with {@link PropertySet.Property}. 1.128 + */ 1.129 + protected static PropertyMap parse(final Class clazz) { 1.130 + // make all relevant fields and methods accessible. 1.131 + // this allows runtime to skip the security check, so they runs faster. 1.132 + return AccessController.doPrivileged(new PrivilegedAction<PropertyMap>() { 1.133 + @Override 1.134 + public PropertyMap run() { 1.135 + PropertyMap props = new PropertyMap(); 1.136 + for (Class c=clazz; c!=null; c=c.getSuperclass()) { 1.137 + for (Field f : c.getDeclaredFields()) { 1.138 + Property cp = f.getAnnotation(Property.class); 1.139 + if(cp!=null) { 1.140 + for(String value : cp.value()) { 1.141 + props.put(value, new FieldAccessor(f, value)); 1.142 + } 1.143 + } 1.144 + } 1.145 + for (Method m : c.getDeclaredMethods()) { 1.146 + Property cp = m.getAnnotation(Property.class); 1.147 + if(cp!=null) { 1.148 + String name = m.getName(); 1.149 + assert name.startsWith("get") || name.startsWith("is"); 1.150 + 1.151 + String setName = name.startsWith("is") ? "set"+name.substring(2) : // isFoo -> setFoo 1.152 + 's' +name.substring(1); // getFoo -> setFoo 1.153 + Method setter; 1.154 + try { 1.155 + setter = clazz.getMethod(setName,m.getReturnType()); 1.156 + } catch (NoSuchMethodException e) { 1.157 + setter = null; // no setter 1.158 + } 1.159 + for(String value : cp.value()) { 1.160 + props.put(value, new MethodAccessor(m, setter, value)); 1.161 + } 1.162 + } 1.163 + } 1.164 + } 1.165 + 1.166 + return props; 1.167 + } 1.168 + }); 1.169 + } 1.170 + 1.171 + /** 1.172 + * Represents a typed property defined on a {@link PropertySet}. 1.173 + */ 1.174 + protected interface Accessor { 1.175 + String getName(); 1.176 + boolean hasValue(PropertySet props); 1.177 + Object get(PropertySet props); 1.178 + void set(PropertySet props, Object value); 1.179 + } 1.180 + 1.181 + static final class FieldAccessor implements Accessor { 1.182 + /** 1.183 + * Field with the annotation. 1.184 + */ 1.185 + private final Field f; 1.186 + 1.187 + /** 1.188 + * One of the values in {@link Property} annotation on {@link #f}. 1.189 + */ 1.190 + private final String name; 1.191 + 1.192 + protected FieldAccessor(Field f, String name) { 1.193 + this.f = f; 1.194 + f.setAccessible(true); 1.195 + this.name = name; 1.196 + } 1.197 + 1.198 + @Override 1.199 + public String getName() { 1.200 + return name; 1.201 + } 1.202 + 1.203 + @Override 1.204 + public boolean hasValue(PropertySet props) { 1.205 + return get(props)!=null; 1.206 + } 1.207 + 1.208 + @Override 1.209 + public Object get(PropertySet props) { 1.210 + try { 1.211 + return f.get(props); 1.212 + } catch (IllegalAccessException e) { 1.213 + throw new AssertionError(); 1.214 + } 1.215 + } 1.216 + 1.217 + @Override 1.218 + public void set(PropertySet props, Object value) { 1.219 + try { 1.220 + f.set(props,value); 1.221 + } catch (IllegalAccessException e) { 1.222 + throw new AssertionError(); 1.223 + } 1.224 + } 1.225 + } 1.226 + 1.227 + static final class MethodAccessor implements Accessor { 1.228 + /** 1.229 + * Getter method. 1.230 + */ 1.231 + private final @NotNull Method getter; 1.232 + /** 1.233 + * Setter method. 1.234 + * Some property is read-only. 1.235 + */ 1.236 + private final @Nullable Method setter; 1.237 + 1.238 + /** 1.239 + * One of the values in {@link Property} annotation on {@link #getter}. 1.240 + */ 1.241 + private final String name; 1.242 + 1.243 + protected MethodAccessor(Method getter, Method setter, String value) { 1.244 + this.getter = getter; 1.245 + this.setter = setter; 1.246 + this.name = value; 1.247 + getter.setAccessible(true); 1.248 + if (setter!=null) { 1.249 + setter.setAccessible(true); 1.250 + } 1.251 + } 1.252 + 1.253 + @Override 1.254 + public String getName() { 1.255 + return name; 1.256 + } 1.257 + 1.258 + @Override 1.259 + public boolean hasValue(PropertySet props) { 1.260 + return get(props)!=null; 1.261 + } 1.262 + 1.263 + @Override 1.264 + public Object get(PropertySet props) { 1.265 + try { 1.266 + return getter.invoke(props); 1.267 + } catch (IllegalAccessException e) { 1.268 + throw new AssertionError(); 1.269 + } catch (InvocationTargetException e) { 1.270 + handle(e); 1.271 + return 0; // never reach here 1.272 + } 1.273 + } 1.274 + 1.275 + @Override 1.276 + public void set(PropertySet props, Object value) { 1.277 + if(setter==null) { 1.278 + throw new ReadOnlyPropertyException(getName()); 1.279 + } 1.280 + try { 1.281 + setter.invoke(props,value); 1.282 + } catch (IllegalAccessException e) { 1.283 + throw new AssertionError(); 1.284 + } catch (InvocationTargetException e) { 1.285 + handle(e); 1.286 + } 1.287 + } 1.288 + 1.289 + /** 1.290 + * Since we don't expect the getter/setter to throw a checked exception, 1.291 + * it should be possible to make the exception propagation transparent. 1.292 + * That's what we are trying to do here. 1.293 + */ 1.294 + private Exception handle(InvocationTargetException e) { 1.295 + Throwable t = e.getTargetException(); 1.296 + if (t instanceof Error) { 1.297 + throw (Error)t; 1.298 + } 1.299 + if (t instanceof RuntimeException) { 1.300 + throw (RuntimeException)t; 1.301 + } 1.302 + throw new Error(e); 1.303 + } 1.304 + } 1.305 + 1.306 + 1.307 + /** 1.308 + * Class allowing to work with PropertySet object as with a Map; it doesn't only allow to read properties from 1.309 + * the map but also to modify the map in a way it is in sync with original strongly typed fields. It also allows 1.310 + * (if necessary) to store additional properties those can't be found in strongly typed fields. 1.311 + * 1.312 + * @see com.sun.xml.internal.ws.api.PropertySet#asMap() method 1.313 + */ 1.314 + final class MapView extends HashMap<String, Object> { 1.315 + 1.316 + // flag if it should allow store also different properties 1.317 + // than the from strongly typed fields 1.318 + boolean extensible; 1.319 + 1.320 + MapView(boolean extensible) { 1.321 + super(getPropertyMap().getPropertyMapEntries().length); 1.322 + this.extensible = extensible; 1.323 + initialize(); 1.324 + } 1.325 + 1.326 + public void initialize() { 1.327 + // iterate (cached) array instead of map to speed things up ... 1.328 + PropertyMapEntry[] entries = getPropertyMap().getPropertyMapEntries(); 1.329 + for (PropertyMapEntry entry : entries) { 1.330 + super.put(entry.key, entry.value); 1.331 + } 1.332 + } 1.333 + 1.334 + @Override 1.335 + public Object get(Object key) { 1.336 + Object o = super.get(key); 1.337 + if (o instanceof Accessor) { 1.338 + return ((Accessor) o).get(BasePropertySet.this); 1.339 + } else { 1.340 + return o; 1.341 + } 1.342 + } 1.343 + 1.344 + @Override 1.345 + public Set<Entry<String, Object>> entrySet() { 1.346 + Set<Entry<String, Object>> entries = new HashSet<Entry<String, Object>>(); 1.347 + for (String key : keySet()) { 1.348 + entries.add(new SimpleImmutableEntry<String, Object>(key, get(key))); 1.349 + } 1.350 + return entries; 1.351 + } 1.352 + 1.353 + @Override 1.354 + public Object put(String key, Object value) { 1.355 + 1.356 + Object o = super.get(key); 1.357 + if (o != null && o instanceof Accessor) { 1.358 + 1.359 + Object oldValue = ((Accessor) o).get(BasePropertySet.this); 1.360 + ((Accessor) o).set(BasePropertySet.this, value); 1.361 + return oldValue; 1.362 + 1.363 + } else { 1.364 + 1.365 + if (extensible) { 1.366 + return super.put(key, value); 1.367 + } else { 1.368 + throw new IllegalStateException("Unknown property [" + key + "] for PropertySet [" + 1.369 + BasePropertySet.this.getClass().getName() + "]"); 1.370 + } 1.371 + } 1.372 + } 1.373 + 1.374 + @Override 1.375 + public void clear() { 1.376 + for (String key : keySet()) { 1.377 + remove(key); 1.378 + } 1.379 + } 1.380 + 1.381 + @Override 1.382 + public Object remove(Object key) { 1.383 + Object o; 1.384 + o = super.get(key); 1.385 + if (o instanceof Accessor) { 1.386 + ((Accessor)o).set(BasePropertySet.this, null); 1.387 + } 1.388 + return super.remove(key); 1.389 + } 1.390 + } 1.391 + 1.392 + @Override 1.393 + public boolean containsKey(Object key) { 1.394 + Accessor sp = getPropertyMap().get(key); 1.395 + if (sp != null) { 1.396 + return sp.get(this) != null; 1.397 + } 1.398 + return false; 1.399 + } 1.400 + 1.401 + /** 1.402 + * Gets the name of the property. 1.403 + * 1.404 + * @param key 1.405 + * This field is typed as {@link Object} to follow the {@link Map#get(Object)} 1.406 + * convention, but if anything but {@link String} is passed, this method 1.407 + * just returns null. 1.408 + */ 1.409 + @Override 1.410 + public Object get(Object key) { 1.411 + Accessor sp = getPropertyMap().get(key); 1.412 + if (sp != null) { 1.413 + return sp.get(this); 1.414 + } 1.415 + throw new IllegalArgumentException("Undefined property "+key); 1.416 + } 1.417 + 1.418 + /** 1.419 + * Sets a property. 1.420 + * 1.421 + * <h3>Implementation Note</h3> 1.422 + * This method is slow. Code inside JAX-WS should define strongly-typed 1.423 + * fields in this class and access them directly, instead of using this. 1.424 + * 1.425 + * @throws ReadOnlyPropertyException 1.426 + * if the given key is an alias of a strongly-typed field, 1.427 + * and if the name object given is not assignable to the field. 1.428 + * 1.429 + * @see Property 1.430 + */ 1.431 + @Override 1.432 + public Object put(String key, Object value) { 1.433 + Accessor sp = getPropertyMap().get(key); 1.434 + if(sp!=null) { 1.435 + Object old = sp.get(this); 1.436 + sp.set(this,value); 1.437 + return old; 1.438 + } else { 1.439 + throw new IllegalArgumentException("Undefined property "+key); 1.440 + } 1.441 + } 1.442 + 1.443 + /** 1.444 + * Checks if this {@link PropertySet} supports a property of the given name. 1.445 + */ 1.446 + @Override 1.447 + public boolean supports(Object key) { 1.448 + return getPropertyMap().containsKey(key); 1.449 + } 1.450 + 1.451 + @Override 1.452 + public Object remove(Object key) { 1.453 + Accessor sp = getPropertyMap().get(key); 1.454 + if(sp!=null) { 1.455 + Object old = sp.get(this); 1.456 + sp.set(this,null); 1.457 + return old; 1.458 + } else { 1.459 + throw new IllegalArgumentException("Undefined property "+key); 1.460 + } 1.461 + } 1.462 + 1.463 + /** 1.464 + * Creates a {@link Map} view of this {@link PropertySet}. 1.465 + * 1.466 + * <p> 1.467 + * This map is partially live, in the sense that values you set to it 1.468 + * will be reflected to {@link PropertySet}. 1.469 + * 1.470 + * <p> 1.471 + * However, this map may not pick up changes made 1.472 + * to {@link PropertySet} after the view is created. 1.473 + * 1.474 + * @deprecated use newer implementation {@link PropertySet#asMap()} which produces 1.475 + * readwrite {@link Map} 1.476 + * 1.477 + * @return 1.478 + * always non-null valid instance. 1.479 + */ 1.480 + @Deprecated 1.481 + @Override 1.482 + public final Map<String,Object> createMapView() { 1.483 + final Set<Entry<String,Object>> core = new HashSet<Entry<String,Object>>(); 1.484 + createEntrySet(core); 1.485 + 1.486 + return new AbstractMap<String, Object>() { 1.487 + @Override 1.488 + public Set<Entry<String,Object>> entrySet() { 1.489 + return core; 1.490 + } 1.491 + }; 1.492 + } 1.493 + 1.494 + /** 1.495 + * Creates a modifiable {@link Map} view of this {@link PropertySet}. 1.496 + * <p/> 1.497 + * Changes done on this {@link Map} or on {@link PropertySet} object work in both directions - values made to 1.498 + * {@link Map} are reflected to {@link PropertySet} and changes done using getters/setters on {@link PropertySet} 1.499 + * object are automatically reflected in this {@link Map}. 1.500 + * <p/> 1.501 + * If necessary, it also can hold other values (not present on {@link PropertySet}) - 1.502 + * {@see PropertySet#mapAllowsAdditionalProperties} 1.503 + * 1.504 + * @return always non-null valid instance. 1.505 + */ 1.506 + @Override 1.507 + public Map<String, Object> asMap() { 1.508 + if (mapView == null) { 1.509 + mapView = createView(); 1.510 + } 1.511 + return mapView; 1.512 + } 1.513 + 1.514 + protected Map<String, Object> createView() { 1.515 + return new MapView(mapAllowsAdditionalProperties()); 1.516 + } 1.517 + 1.518 + /** 1.519 + * Used when constructing the {@link MapView} for this object - it controls if the {@link MapView} servers only to 1.520 + * access strongly typed values or allows also different values 1.521 + * 1.522 + * @return true if {@link Map} should allow also properties not defined as strongly typed fields 1.523 + */ 1.524 + protected boolean mapAllowsAdditionalProperties() { 1.525 + return false; 1.526 + } 1.527 + 1.528 + protected void createEntrySet(Set<Entry<String,Object>> core) { 1.529 + for (final Entry<String, Accessor> e : getPropertyMap().entrySet()) { 1.530 + core.add(new Entry<String, Object>() { 1.531 + @Override 1.532 + public String getKey() { 1.533 + return e.getKey(); 1.534 + } 1.535 + 1.536 + @Override 1.537 + public Object getValue() { 1.538 + return e.getValue().get(BasePropertySet.this); 1.539 + } 1.540 + 1.541 + @Override 1.542 + public Object setValue(Object value) { 1.543 + Accessor acc = e.getValue(); 1.544 + Object old = acc.get(BasePropertySet.this); 1.545 + acc.set(BasePropertySet.this,value); 1.546 + return old; 1.547 + } 1.548 + }); 1.549 + } 1.550 + } 1.551 +}