001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: package com.sun.xml.ws.api;
038:
039: import com.sun.istack.NotNull;
040: import com.sun.istack.Nullable;
041: import com.sun.xml.ws.api.message.Packet;
042: import com.sun.xml.ws.util.ReadOnlyPropertyException;
043:
044: import javax.xml.ws.handler.MessageContext;
045: import java.lang.annotation.ElementType;
046: import java.lang.annotation.Inherited;
047: import java.lang.annotation.Retention;
048: import java.lang.annotation.RetentionPolicy;
049: import java.lang.annotation.Target;
050: import java.lang.reflect.Field;
051: import java.lang.reflect.InvocationTargetException;
052: import java.lang.reflect.Method;
053: import java.security.AccessController;
054: import java.security.PrivilegedAction;
055: import java.util.AbstractMap;
056: import java.util.HashMap;
057: import java.util.HashSet;
058: import java.util.Map;
059: import java.util.Map.Entry;
060: import java.util.Set;
061:
062: /**
063: * A set of "properties" that can be accessed via strongly-typed fields
064: * as well as reflexibly through the property name.
065: *
066: * @author Kohsuke Kawaguchi
067: */
068: @SuppressWarnings("SuspiciousMethodCalls")
069: public abstract class PropertySet {
070:
071: /**
072: * Creates a new instance of TypedMap.
073: */
074: protected PropertySet() {
075: }
076:
077: /**
078: * Marks a field on {@link PropertySet} as a
079: * property of {@link MessageContext}.
080: *
081: * <p>
082: * To make the runtime processing easy, this annotation
083: * must be on a public field (since the property name
084: * can be set through {@link Map} anyway, you won't be
085: * losing abstraction by doing so.)
086: *
087: * <p>
088: * For similar reason, this annotation can be only placed
089: * on a reference type, not primitive type.
090: *
091: * @see Packet
092: * @author Kohsuke Kawaguchi
093: */
094: @Inherited
095: @Retention(RetentionPolicy.RUNTIME)
096: @Target({ElementType.FIELD,ElementType.METHOD})
097: public @interface Property {
098: /**
099: * Name of the property.
100: */
101: String[] value();
102: }
103:
104: /**
105: * Represents the list of strongly-typed known propertyies
106: * (keyed by property names.)
107: *
108: * <p>
109: * Just giving it an alias to make the use of this class more fool-proof.
110: */
111: protected static final class PropertyMap extends
112: HashMap<String, Accessor> {
113: }
114:
115: /**
116: * Map representing the Fields and Methods annotated with {@link Property}.
117: * Model of {@link PropertySet} class.
118: *
119: * <p>
120: * At the end of the derivation chain this method just needs to be implemented
121: * as:
122: *
123: * <pre>
124: * private static final PropertyMap model;
125: * static {
126: * model = parse(MyDerivedClass.class);
127: * }
128: * protected PropertyMap getPropertyMap() {
129: * return model;
130: * }
131: * </pre>
132: */
133: protected abstract PropertyMap getPropertyMap();
134:
135: // maybe we can use this some time
136: ///**
137: // * If all the properties defined on this {@link PropertySet} has a certain prefix
138: // * (such as, say, "javax.xml.ws.http."), then return it.
139: // *
140: // * <p>
141: // * Returning a non-null name from this method allows methods like
142: // * {@link #get(Object)} and {@link #put(String, Object)} to work faster.
143: // * This is especially so with {@link DistributedPropertySet}, so implementations
144: // * are encouraged to set a common prefix, as much as possible.
145: // *
146: // * <p>
147: // * Currently, this is used only by {@link DistributedPropertySet}.
148: // *
149: // * @return
150: // * Null if no such common prefix exists. Otherwise string like
151: // * "javax.xml.ws.http." (the dot at the last is usually preferrable,
152: // * so that properties like "javax.xml.ws.https.something" won't match.
153: // */
154: //protected abstract String getPropertyPrefix();
155:
156: /**
157: * This method parses a class for fields and methods with {@link Property}.
158: */
159: protected static PropertyMap parse(final Class clazz) {
160: // make all relevant fields and methods accessible.
161: // this allows runtime to skip the security check, so they runs faster.
162: return AccessController
163: .doPrivileged(new PrivilegedAction<PropertyMap>() {
164: public PropertyMap run() {
165: PropertyMap props = new PropertyMap();
166: for (Class c = clazz; c != null; c = c
167: .getSuperclass()) {
168: for (Field f : c.getDeclaredFields()) {
169: Property cp = f
170: .getAnnotation(Property.class);
171: if (cp != null) {
172: for (String value : cp.value()) {
173: props.put(value,
174: new FieldAccessor(f,
175: value));
176: }
177: }
178: }
179: for (Method m : c.getDeclaredMethods()) {
180: Property cp = m
181: .getAnnotation(Property.class);
182: if (cp != null) {
183: String name = m.getName();
184: assert name.startsWith("get");
185:
186: String setName = 's' + name
187: .substring(1); // getFoo -> setFoo
188: Method setter;
189: try {
190: setter = clazz.getMethod(
191: setName,
192: m.getReturnType());
193: } catch (NoSuchMethodException e) {
194: setter = null; // no setter
195: }
196: for (String value : cp.value()) {
197: props.put(value,
198: new MethodAccessor(m,
199: setter, value));
200: }
201: }
202: }
203: }
204:
205: return props;
206: }
207: });
208: }
209:
210: /**
211: * Represents a typed property defined on a {@link PropertySet}.
212: */
213: protected interface Accessor {
214: String getName();
215:
216: boolean hasValue(PropertySet props);
217:
218: Object get(PropertySet props);
219:
220: void set(PropertySet props, Object value);
221: }
222:
223: static final class FieldAccessor implements Accessor {
224: /**
225: * Field with the annotation.
226: */
227: private final Field f;
228:
229: /**
230: * One of the values in {@link Property} annotation on {@link #f}.
231: */
232: private final String name;
233:
234: protected FieldAccessor(Field f, String name) {
235: this .f = f;
236: f.setAccessible(true);
237: this .name = name;
238: }
239:
240: public String getName() {
241: return name;
242: }
243:
244: public boolean hasValue(PropertySet props) {
245: return get(props) != null;
246: }
247:
248: public Object get(PropertySet props) {
249: try {
250: return f.get(props);
251: } catch (IllegalAccessException e) {
252: throw new AssertionError();
253: }
254: }
255:
256: public void set(PropertySet props, Object value) {
257: try {
258: f.set(props, value);
259: } catch (IllegalAccessException e) {
260: throw new AssertionError();
261: }
262: }
263: }
264:
265: static final class MethodAccessor implements Accessor {
266: /**
267: * Getter method.
268: */
269: private final @NotNull
270: Method getter;
271: /**
272: * Setter method.
273: * Some property is read-only.
274: */
275: private final @Nullable
276: Method setter;
277:
278: /**
279: * One of the values in {@link Property} annotation on {@link #getter}.
280: */
281: private final String name;
282:
283: protected MethodAccessor(Method getter, Method setter,
284: String value) {
285: this .getter = getter;
286: this .setter = setter;
287: this .name = value;
288: getter.setAccessible(true);
289: if (setter != null)
290: setter.setAccessible(true);
291: }
292:
293: public String getName() {
294: return name;
295: }
296:
297: public boolean hasValue(PropertySet props) {
298: return get(props) != null;
299: }
300:
301: public Object get(PropertySet props) {
302: try {
303: return getter.invoke(props);
304: } catch (IllegalAccessException e) {
305: throw new AssertionError();
306: } catch (InvocationTargetException e) {
307: handle(e);
308: return 0; // never reach here
309: }
310: }
311:
312: public void set(PropertySet props, Object value) {
313: if (setter == null)
314: throw new ReadOnlyPropertyException(getName());
315: try {
316: setter.invoke(props, value);
317: } catch (IllegalAccessException e) {
318: throw new AssertionError();
319: } catch (InvocationTargetException e) {
320: handle(e);
321: }
322: }
323:
324: /**
325: * Since we don't expect the getter/setter to throw a checked exception,
326: * it should be possible to make the exception propagation transparent.
327: * That's what we are trying to do here.
328: */
329: private Exception handle(InvocationTargetException e) {
330: Throwable t = e.getTargetException();
331: if (t instanceof Error)
332: throw (Error) t;
333: if (t instanceof RuntimeException)
334: throw (RuntimeException) t;
335: throw new Error(e);
336: }
337: }
338:
339: public final boolean containsKey(Object key) {
340: return get(key) != null;
341: }
342:
343: /**
344: * Gets the name of the property.
345: *
346: * @param key
347: * This field is typed as {@link Object} to follow the {@link Map#get(Object)}
348: * convention, but if anything but {@link String} is passed, this method
349: * just returns null.
350: */
351: public Object get(Object key) {
352: Accessor sp = getPropertyMap().get(key);
353: if (sp != null)
354: return sp.get(this );
355: throw new IllegalArgumentException("Undefined property " + key);
356: }
357:
358: /**
359: * Sets a property.
360: *
361: * <h3>Implementation Note</h3>
362: * This method is slow. Code inside JAX-WS should define strongly-typed
363: * fields in this class and access them directly, instead of using this.
364: *
365: * @throws ReadOnlyPropertyException
366: * if the given key is an alias of a strongly-typed field,
367: * and if the name object given is not assignable to the field.
368: *
369: * @see Property
370: */
371: public Object put(String key, Object value) {
372: Accessor sp = getPropertyMap().get(key);
373: if (sp != null) {
374: Object old = sp.get(this );
375: sp.set(this , value);
376: return old;
377: } else {
378: throw new IllegalArgumentException("Undefined property "
379: + key);
380: }
381: }
382:
383: /**
384: * Checks if this {@link PropertySet} supports a property of the given name.
385: */
386: public boolean supports(Object key) {
387: return getPropertyMap().containsKey(key);
388: }
389:
390: public Object remove(Object key) {
391: Accessor sp = getPropertyMap().get(key);
392: if (sp != null) {
393: Object old = sp.get(this );
394: sp.set(this , null);
395: return old;
396: } else {
397: throw new IllegalArgumentException("Undefined property "
398: + key);
399: }
400: }
401:
402: /**
403: * Lazily created view of {@link Property}s that
404: * forms the core of {@link #createMapView()}.
405: */
406: /*package*/Set<Entry<String, Object>> mapViewCore;
407:
408: /**
409: * Creates a {@link Map} view of this {@link PropertySet}.
410: *
411: * <p>
412: * This map is partially live, in the sense that values you set to it
413: * will be reflected to {@link PropertySet}.
414: *
415: * <p>
416: * However, this map may not pick up changes made
417: * to {@link PropertySet} after the view is created.
418: *
419: * @return
420: * always non-null valid instance.
421: */
422: public final Map<String, Object> createMapView() {
423: final Set<Entry<String, Object>> core = new HashSet<Entry<String, Object>>();
424: createEntrySet(core);
425:
426: return new AbstractMap<String, Object>() {
427: public Set<Entry<String, Object>> entrySet() {
428: return core;
429: }
430: };
431: }
432:
433: /*package*/void createEntrySet(Set<Entry<String, Object>> core) {
434: for (final Entry<String, Accessor> e : getPropertyMap()
435: .entrySet()) {
436: core.add(new Entry<String, Object>() {
437: public String getKey() {
438: return e.getKey();
439: }
440:
441: public Object getValue() {
442: return e.getValue().get(PropertySet.this );
443: }
444:
445: public Object setValue(Object value) {
446: Accessor acc = e.getValue();
447: Object old = acc.get(PropertySet.this);
448: acc.set(PropertySet.this, value);
449: return old;
450: }
451: });
452: }
453: }
454: }
|