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