23 * questions. |
23 * questions. |
24 */ |
24 */ |
25 |
25 |
26 package com.sun.xml.internal.ws.api; |
26 package com.sun.xml.internal.ws.api; |
27 |
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; |
28 import java.util.Map; |
|
29 import java.util.Set; |
41 import java.util.Map.Entry; |
30 import java.util.Map.Entry; |
42 import java.util.Set; |
|
43 |
31 |
44 /** |
32 /** |
45 * A set of "properties" that can be accessed via strongly-typed fields |
33 * Placeholder for backwards compatibility. |
46 * as well as reflexibly through the property name. |
|
47 * |
34 * |
48 * @author Kohsuke Kawaguchi |
35 * @deprecated Use com.oracle.webservices.internal.api.message.PropertySet instead. |
|
36 * @author snajper |
49 */ |
37 */ |
50 @SuppressWarnings("SuspiciousMethodCalls") |
38 public abstract class PropertySet extends com.oracle.webservices.internal.api.message.BasePropertySet { |
51 public abstract class PropertySet implements com.sun.xml.internal.org.jvnet.ws.message.PropertySet { |
|
52 |
|
53 /** |
39 /** |
54 * Creates a new instance of TypedMap. |
40 * Represents the list of strongly-typed known properties |
55 */ |
|
56 protected PropertySet() {} |
|
57 |
|
58 /** |
|
59 * Represents the list of strongly-typed known propertyies |
|
60 * (keyed by property names.) |
41 * (keyed by property names.) |
61 * |
42 * |
62 * <p> |
43 * <p> |
63 * Just giving it an alias to make the use of this class more fool-proof. |
44 * Just giving it an alias to make the use of this class more fool-proof. |
|
45 * @deprecated |
64 */ |
46 */ |
65 protected static final class PropertyMap extends HashMap<String,Accessor> {} |
47 protected static class PropertyMap extends com.oracle.webservices.internal.api.message.BasePropertySet.PropertyMap {} |
66 |
48 |
67 /** |
49 /** |
68 * Map representing the Fields and Methods annotated with {@link Property}. |
50 * @deprecated |
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 */ |
51 */ |
111 protected static PropertyMap parse(final Class clazz) { |
52 protected static PropertyMap parse(final Class clazz) { |
112 // make all relevant fields and methods accessible. |
53 com.oracle.webservices.internal.api.message.BasePropertySet.PropertyMap pm = com.oracle.webservices.internal.api.message.BasePropertySet.parse(clazz); |
113 // this allows runtime to skip the security check, so they runs faster. |
54 PropertyMap map = new PropertyMap(); |
114 return AccessController.doPrivileged(new PrivilegedAction<PropertyMap>() { |
55 map.putAll(pm); |
115 public PropertyMap run() { |
56 return map; |
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 } |
57 } |
279 |
58 |
280 /** |
59 /** |
281 * Gets the name of the property. |
60 * Gets the name of the property. |
282 * |
61 * |
332 } else { |
108 } else { |
333 throw new IllegalArgumentException("Undefined property "+key); |
109 throw new IllegalArgumentException("Undefined property "+key); |
334 } |
110 } |
335 } |
111 } |
336 |
112 |
337 |
113 protected void createEntrySet(Set<Entry<String,Object>> core) { |
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()) { |
114 for (final Entry<String, Accessor> e : getPropertyMap().entrySet()) { |
371 core.add(new Entry<String, Object>() { |
115 core.add(new Entry<String, Object>() { |
372 public String getKey() { |
116 public String getKey() { |
373 return e.getKey(); |
117 return e.getKey(); |
374 } |
118 } |