1 /* 2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.ws.api; 27 28 import com.sun.istack.internal.NotNull; 29 import com.sun.istack.internal.Nullable; 30 import com.sun.xml.internal.ws.util.ReadOnlyPropertyException; 31 32 import java.lang.reflect.Field; 33 import java.lang.reflect.InvocationTargetException; 34 import java.lang.reflect.Method; 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 import java.util.AbstractMap; 38 import java.util.HashMap; 39 import java.util.HashSet; 40 import java.util.Map; 41 import java.util.Map.Entry; 42 import java.util.Set; 43 44 /** 45 * A set of "properties" that can be accessed via strongly-typed fields 46 * as well as reflexibly through the property name. 47 * 48 * @author Kohsuke Kawaguchi 49 */ 50 @SuppressWarnings("SuspiciousMethodCalls") 51 public abstract class PropertySet implements com.sun.xml.internal.org.jvnet.ws.message.PropertySet { 52 53 /** 54 * Creates a new instance of TypedMap. 55 */ 56 protected PropertySet() {} 57 58 /** 59 * Represents the list of strongly-typed known propertyies 60 * (keyed by property names.) 61 * 62 * <p> 63 * Just giving it an alias to make the use of this class more fool-proof. 64 */ 65 protected static final class PropertyMap extends HashMap<String,Accessor> {} 66 67 /** 68 * Map representing the Fields and Methods annotated with {@link Property}. 69 * Model of {@link PropertySet} class. 70 * 71 * <p> 72 * At the end of the derivation chain this method just needs to be implemented 73 * as: 74 * 75 * <pre> 76 * private static final PropertyMap model; 77 * static { 78 * model = parse(MyDerivedClass.class); 79 * } 80 * protected PropertyMap getPropertyMap() { 81 * return model; 82 * } 83 * </pre> 84 */ 85 protected abstract PropertyMap getPropertyMap(); 86 87 // maybe we can use this some time 88 ///** 89 // * If all the properties defined on this {@link PropertySet} has a certain prefix 90 // * (such as, say, "javax.xml.ws.http."), then return it. 91 // * 92 // * <p> 93 // * Returning a non-null name from this method allows methods like 94 // * {@link #get(Object)} and {@link #put(String, Object)} to work faster. 95 // * This is especially so with {@link DistributedPropertySet}, so implementations 96 // * are encouraged to set a common prefix, as much as possible. 97 // * 98 // * <p> 99 // * Currently, this is used only by {@link DistributedPropertySet}. 100 // * 101 // * @return 102 // * Null if no such common prefix exists. Otherwise string like 103 // * "javax.xml.ws.http." (the dot at the last is usually preferrable, 104 // * so that properties like "javax.xml.ws.https.something" won't match. 105 // */ 106 //protected abstract String getPropertyPrefix(); 107 108 /** 109 * This method parses a class for fields and methods with {@link Property}. 110 */ 111 protected static PropertyMap parse(final Class clazz) { 112 // make all relevant fields and methods accessible. 113 // this allows runtime to skip the security check, so they runs faster. 114 return AccessController.doPrivileged(new PrivilegedAction<PropertyMap>() { 115 public PropertyMap run() { 116 PropertyMap props = new PropertyMap(); 117 for( Class c=clazz; c!=null; c=c.getSuperclass()) { 118 for (Field f : c.getDeclaredFields()) { 119 Property cp = f.getAnnotation(Property.class); 120 if(cp!=null) { 121 for(String value : cp.value()) { 122 props.put(value, new FieldAccessor(f, value)); 123 } 124 } 125 } 126 for (Method m : c.getDeclaredMethods()) { 127 Property cp = m.getAnnotation(Property.class); 128 if(cp!=null) { 129 String name = m.getName(); 130 assert name.startsWith("get") || name.startsWith("is"); 131 132 String setName = name.startsWith("is") ? "set"+name.substring(3) : // isFoo -> setFoo 133 's'+name.substring(1); // getFoo -> setFoo 134 Method setter; 135 try { 136 setter = clazz.getMethod(setName,m.getReturnType()); 137 } catch (NoSuchMethodException e) { 138 setter = null; // no setter 139 } 140 for(String value : cp.value()) { 141 props.put(value, new MethodAccessor(m, setter, value)); 142 } 143 } 144 } 145 } 146 147 return props; 148 } 149 }); 150 } 151 152 /** 153 * Represents a typed property defined on a {@link PropertySet}. 154 */ 155 protected interface Accessor { 156 String getName(); 157 boolean hasValue(PropertySet props); 158 Object get(PropertySet props); 159 void set(PropertySet props, Object value); 160 } 161 162 static final class FieldAccessor implements Accessor { 163 /** 164 * Field with the annotation. 165 */ 166 private final Field f; 167 168 /** 169 * One of the values in {@link Property} annotation on {@link #f}. 170 */ 171 private final String name; 172 173 protected FieldAccessor(Field f, String name) { 174 this.f = f; 175 f.setAccessible(true); 176 this.name = name; 177 } 178 179 public String getName() { 180 return name; 181 } 182 183 public boolean hasValue(PropertySet props) { 184 return get(props)!=null; 185 } 186 187 public Object get(PropertySet props) { 188 try { 189 return f.get(props); 190 } catch (IllegalAccessException e) { 191 throw new AssertionError(); 192 } 193 } 194 195 public void set(PropertySet props, Object value) { 196 try { 197 f.set(props,value); 198 } catch (IllegalAccessException e) { 199 throw new AssertionError(); 200 } 201 } 202 } 203 204 static final class MethodAccessor implements Accessor { 205 /** 206 * Getter method. 207 */ 208 private final @NotNull Method getter; 209 /** 210 * Setter method. 211 * Some property is read-only. 212 */ 213 private final @Nullable Method setter; 214 215 /** 216 * One of the values in {@link Property} annotation on {@link #getter}. 217 */ 218 private final String name; 219 220 protected MethodAccessor(Method getter, Method setter, String value) { 221 this.getter = getter; 222 this.setter = setter; 223 this.name = value; 224 getter.setAccessible(true); 225 if(setter!=null) 226 setter.setAccessible(true); 227 } 228 229 public String getName() { 230 return name; 231 } 232 233 public boolean hasValue(PropertySet props) { 234 return get(props)!=null; 235 } 236 237 public Object get(PropertySet props) { 238 try { 239 return getter.invoke(props); 240 } catch (IllegalAccessException e) { 241 throw new AssertionError(); 242 } catch (InvocationTargetException e) { 243 handle(e); 244 return 0; // never reach here 245 } 246 } 247 248 public void set(PropertySet props, Object value) { 249 if(setter==null) 250 throw new ReadOnlyPropertyException(getName()); 251 try { 252 setter.invoke(props,value); 253 } catch (IllegalAccessException e) { 254 throw new AssertionError(); 255 } catch (InvocationTargetException e) { 256 handle(e); 257 } 258 } 259 260 /** 261 * Since we don't expect the getter/setter to throw a checked exception, 262 * it should be possible to make the exception propagation transparent. 263 * That's what we are trying to do here. 264 */ 265 private Exception handle(InvocationTargetException e) { 266 Throwable t = e.getTargetException(); 267 if(t instanceof Error) 268 throw (Error)t; 269 if(t instanceof RuntimeException) 270 throw (RuntimeException)t; 271 throw new Error(e); 272 } 273 } 274 275 276 public final boolean containsKey(Object key) { 277 return get(key)!=null; 278 } 279 280 /** 281 * Gets the name of the property. 282 * 283 * @param key 284 * This field is typed as {@link Object} to follow the {@link Map#get(Object)} 285 * convention, but if anything but {@link String} is passed, this method 286 * just returns null. 287 */ 288 public Object get(Object key) { 289 Accessor sp = getPropertyMap().get(key); 290 if(sp!=null) 291 return sp.get(this); 292 throw new IllegalArgumentException("Undefined property "+key); 293 } 294 295 /** 296 * Sets a property. 297 * 299 * This method is slow. Code inside JAX-WS should define strongly-typed 300 * fields in this class and access them directly, instead of using this. 301 * 302 * @throws ReadOnlyPropertyException 303 * if the given key is an alias of a strongly-typed field, 304 * and if the name object given is not assignable to the field. 305 * 306 * @see Property 307 */ 308 public Object put(String key, Object value) { 309 Accessor sp = getPropertyMap().get(key); 310 if(sp!=null) { 311 Object old = sp.get(this); 312 sp.set(this,value); 313 return old; 314 } else { 315 throw new IllegalArgumentException("Undefined property "+key); 316 } 317 } 318 319 /** 320 * Checks if this {@link PropertySet} supports a property of the given name. 321 */ 322 public boolean supports(Object key) { 323 return getPropertyMap().containsKey(key); 324 } 325 326 public Object remove(Object key) { 327 Accessor sp = getPropertyMap().get(key); 328 if(sp!=null) { 329 Object old = sp.get(this); 330 sp.set(this,null); 331 return old; 332 } else { 333 throw new IllegalArgumentException("Undefined property "+key); 334 } 335 } 336 337 338 /** 339 * Lazily created view of {@link Property}s that 340 * forms the core of {@link #createMapView()}. 341 */ 342 /*package*/ Set<Entry<String,Object>> mapViewCore; 343 344 /** 345 * Creates a {@link Map} view of this {@link PropertySet}. 346 * 347 * <p> 348 * This map is partially live, in the sense that values you set to it 349 * will be reflected to {@link PropertySet}. 350 * 351 * <p> 352 * However, this map may not pick up changes made 353 * to {@link PropertySet} after the view is created. 354 * 355 * @return 356 * always non-null valid instance. 357 */ 358 public final Map<String,Object> createMapView() { 359 final Set<Entry<String,Object>> core = new HashSet<Entry<String,Object>>(); 360 createEntrySet(core); 361 362 return new AbstractMap<String, Object>() { 363 public Set<Entry<String,Object>> entrySet() { 364 return core; 365 } 366 }; 367 } 368 369 /*package*/ void createEntrySet(Set<Entry<String,Object>> core) { 370 for (final Entry<String, Accessor> e : getPropertyMap().entrySet()) { 371 core.add(new Entry<String, Object>() { 372 public String getKey() { 373 return e.getKey(); 374 } 375 376 public Object getValue() { 377 return e.getValue().get(PropertySet.this); 378 } 379 380 public Object setValue(Object value) { 381 Accessor acc = e.getValue(); 382 Object old = acc.get(PropertySet.this); 383 acc.set(PropertySet.this,value); 384 return old; 385 } 386 }); 387 } 388 } 389 } | 1 /* 2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.ws.api; 27 28 import java.util.Map; 29 import java.util.Set; 30 import java.util.Map.Entry; 31 32 /** 33 * Placeholder for backwards compatibility. 34 * 35 * @deprecated Use com.oracle.webservices.internal.api.message.PropertySet instead. 36 * @author snajper 37 */ 38 public abstract class PropertySet extends com.oracle.webservices.internal.api.message.BasePropertySet { 39 /** 40 * Represents the list of strongly-typed known properties 41 * (keyed by property names.) 42 * 43 * <p> 44 * Just giving it an alias to make the use of this class more fool-proof. 45 * @deprecated 46 */ 47 protected static class PropertyMap extends com.oracle.webservices.internal.api.message.BasePropertySet.PropertyMap {} 48 49 /** 50 * @deprecated 51 */ 52 protected static PropertyMap parse(final Class clazz) { 53 com.oracle.webservices.internal.api.message.BasePropertySet.PropertyMap pm = com.oracle.webservices.internal.api.message.BasePropertySet.parse(clazz); 54 PropertyMap map = new PropertyMap(); 55 map.putAll(pm); 56 return map; 57 } 58 59 /** 60 * Gets the name of the property. 61 * 62 * @param key 63 * This field is typed as {@link Object} to follow the {@link Map#get(Object)} 64 * convention, but if anything but {@link String} is passed, this method 65 * just returns null. 66 */ 67 public Object get(Object key) { 68 Accessor sp = getPropertyMap().get(key); 69 if(sp!=null) 70 return sp.get(this); 71 throw new IllegalArgumentException("Undefined property "+key); 72 } 73 74 /** 75 * Sets a property. 76 * 78 * This method is slow. Code inside JAX-WS should define strongly-typed 79 * fields in this class and access them directly, instead of using this. 80 * 81 * @throws ReadOnlyPropertyException 82 * if the given key is an alias of a strongly-typed field, 83 * and if the name object given is not assignable to the field. 84 * 85 * @see Property 86 */ 87 public Object put(String key, Object value) { 88 Accessor sp = getPropertyMap().get(key); 89 if(sp!=null) { 90 Object old = sp.get(this); 91 sp.set(this,value); 92 return old; 93 } else { 94 throw new IllegalArgumentException("Undefined property "+key); 95 } 96 } 97 98 public boolean supports(Object key) { 99 return getPropertyMap().containsKey(key); 100 } 101 102 public Object remove(Object key) { 103 Accessor sp = getPropertyMap().get(key); 104 if(sp!=null) { 105 Object old = sp.get(this); 106 sp.set(this,null); 107 return old; 108 } else { 109 throw new IllegalArgumentException("Undefined property "+key); 110 } 111 } 112 113 protected void createEntrySet(Set<Entry<String,Object>> core) { 114 for (final Entry<String, Accessor> e : getPropertyMap().entrySet()) { 115 core.add(new Entry<String, Object>() { 116 public String getKey() { 117 return e.getKey(); 118 } 119 120 public Object getValue() { 121 return e.getValue().get(PropertySet.this); 122 } 123 124 public Object setValue(Object value) { 125 Accessor acc = e.getValue(); 126 Object old = acc.get(PropertySet.this); 127 acc.set(PropertySet.this,value); 128 return old; 129 } 130 }); 131 } 132 } 133 134 protected abstract PropertyMap getPropertyMap(); 135 } |