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.server;
038:
039: import com.sun.istack.Nullable;
040: import com.sun.xml.ws.api.server.InstanceResolver;
041: import com.sun.xml.ws.api.server.ResourceInjector;
042: import com.sun.xml.ws.api.server.WSEndpoint;
043: import com.sun.xml.ws.resources.ServerMessages;
044: import com.sun.xml.ws.util.localization.Localizable;
045:
046: import javax.annotation.Resource;
047: import javax.xml.ws.WebServiceContext;
048: import javax.xml.ws.WebServiceException;
049: import java.lang.annotation.Annotation;
050: import java.lang.reflect.Field;
051: import java.lang.reflect.InvocationTargetException;
052: import java.lang.reflect.Method;
053: import java.lang.reflect.Modifier;
054: import java.security.AccessController;
055: import java.security.PrivilegedAction;
056: import java.util.ArrayList;
057: import java.util.Collection;
058: import java.util.List;
059:
060: /**
061: * Partial implementation of {@link InstanceResolver} with
062: * convenience methods to do the resource injection.
063: *
064: * @author Kohsuke Kawaguchi
065: */
066: abstract class AbstractInstanceResolver<T> extends InstanceResolver<T> {
067:
068: /**
069: * Encapsulates which field/method the injection is done,
070: * and performs the injection.
071: */
072: protected static interface InjectionPlan<T, R> {
073: void inject(T instance, R resource);
074:
075: /**
076: * Gets the number of injections to be performed.
077: */
078: int count();
079: }
080:
081: /**
082: * Injects to a field.
083: */
084: protected static class FieldInjectionPlan<T, R> implements
085: InjectionPlan<T, R> {
086: private final Field field;
087:
088: public FieldInjectionPlan(Field field) {
089: this .field = field;
090: }
091:
092: public void inject(final T instance, final R resource) {
093: AccessController
094: .doPrivileged(new PrivilegedAction<Object>() {
095: public Object run() {
096: try {
097: if (!field.isAccessible()) {
098: field.setAccessible(true);
099: }
100: field.set(instance, resource);
101: return null;
102: } catch (IllegalAccessException e) {
103: throw new ServerRtException(
104: "server.rt.err", e);
105: }
106: }
107: });
108: }
109:
110: public int count() {
111: return 1;
112: }
113: }
114:
115: /**
116: * Injects to a method.
117: */
118: protected static class MethodInjectionPlan<T, R> implements
119: InjectionPlan<T, R> {
120: private final Method method;
121:
122: public MethodInjectionPlan(Method method) {
123: this .method = method;
124: }
125:
126: public void inject(T instance, R resource) {
127: invokeMethod(method, instance, resource);
128: }
129:
130: public int count() {
131: return 1;
132: }
133: }
134:
135: /**
136: * Combines multiple {@link InjectionPlan}s into one.
137: */
138: private static class Compositor<T, R> implements
139: InjectionPlan<T, R> {
140: private final InjectionPlan<T, R>[] children;
141:
142: public Compositor(Collection<InjectionPlan<T, R>> children) {
143: this .children = children.toArray(new InjectionPlan[children
144: .size()]);
145: }
146:
147: public void inject(T instance, R res) {
148: for (InjectionPlan<T, R> plan : children)
149: plan.inject(instance, res);
150: }
151:
152: public int count() {
153: int r = 0;
154: for (InjectionPlan<T, R> plan : children)
155: r += plan.count();
156: return r;
157: }
158: }
159:
160: protected static ResourceInjector getResourceInjector(
161: WSEndpoint endpoint) {
162: ResourceInjector ri = endpoint.getContainer().getSPI(
163: ResourceInjector.class);
164: if (ri == null)
165: ri = ResourceInjector.STANDALONE;
166: return ri;
167: }
168:
169: /**
170: * Helper for invoking a method with elevated privilege.
171: */
172: protected static void invokeMethod(final @Nullable
173: Method method, final Object instance, final Object... args) {
174: if (method == null)
175: return;
176: AccessController.doPrivileged(new PrivilegedAction<Void>() {
177: public Void run() {
178: try {
179: if (!method.isAccessible()) {
180: method.setAccessible(true);
181: }
182: method.invoke(instance, args);
183: } catch (IllegalAccessException e) {
184: throw new ServerRtException("server.rt.err", e);
185: } catch (InvocationTargetException e) {
186: throw new ServerRtException("server.rt.err", e);
187: }
188: return null;
189: }
190: });
191: }
192:
193: /**
194: * Finds the method that has the given annotation, while making sure that
195: * there's only at most one such method.
196: */
197: protected final @Nullable
198: Method findAnnotatedMethod(Class clazz,
199: Class<? extends Annotation> annType) {
200: boolean once = false;
201: Method r = null;
202: for (Method method : clazz.getDeclaredMethods()) {
203: if (method.getAnnotation(annType) != null) {
204: if (once)
205: throw new ServerRtException(ServerMessages
206: .ANNOTATION_ONLY_ONCE(annType));
207: if (method.getParameterTypes().length != 0)
208: throw new ServerRtException(ServerMessages
209: .NOT_ZERO_PARAMETERS(method));
210: r = method;
211: once = true;
212: }
213: }
214: return r;
215: }
216:
217: /**
218: * Creates an {@link InjectionPlan} that injects the given resource type to the given class.
219: *
220: * @param isStatic
221: * Only look for static field/method
222: *
223: */
224: protected static <T, R> InjectionPlan<T, R> buildInjectionPlan(
225: Class<? extends T> clazz, Class<R> resourceType,
226: boolean isStatic) {
227: List<InjectionPlan<T, R>> plan = new ArrayList<InjectionPlan<T, R>>();
228:
229: for (Field field : clazz.getDeclaredFields()) {
230: Resource resource = field.getAnnotation(Resource.class);
231: if (resource != null) {
232: if (isInjectionPoint(resource, field.getType(),
233: ServerMessages
234: .localizableWRONG_FIELD_TYPE(field
235: .getName()), resourceType)) {
236:
237: if (isStatic
238: && !Modifier.isStatic(field.getModifiers()))
239: throw new WebServiceException(ServerMessages
240: .STATIC_RESOURCE_INJECTION_ONLY(
241: resourceType, field));
242:
243: plan.add(new FieldInjectionPlan<T, R>(field));
244: }
245: }
246: }
247:
248: for (Method method : clazz.getDeclaredMethods()) {
249: Resource resource = method.getAnnotation(Resource.class);
250: if (resource != null) {
251: Class[] paramTypes = method.getParameterTypes();
252: if (paramTypes.length != 1)
253: throw new ServerRtException(ServerMessages
254: .WRONG_NO_PARAMETERS(method));
255: if (isInjectionPoint(resource, paramTypes[0],
256: ServerMessages
257: .localizableWRONG_PARAMETER_TYPE(method
258: .getName()), resourceType)) {
259:
260: if (isStatic
261: && !Modifier
262: .isStatic(method.getModifiers()))
263: throw new WebServiceException(ServerMessages
264: .STATIC_RESOURCE_INJECTION_ONLY(
265: resourceType, method));
266:
267: plan.add(new MethodInjectionPlan<T, R>(method));
268: }
269: }
270: }
271:
272: return new Compositor<T, R>(plan);
273: }
274:
275: /**
276: * Returns true if the combination of {@link Resource} and the field/method type
277: * are consistent for {@link WebServiceContext} injection.
278: */
279: private static boolean isInjectionPoint(Resource resource,
280: Class fieldType, Localizable errorMessage,
281: Class resourceType) {
282: Class t = resource.type();
283: if (t.equals(Object.class)) {
284: return fieldType.equals(resourceType);
285: } else if (t.equals(resourceType)) {
286: if (fieldType.isAssignableFrom(resourceType)) {
287: return true;
288: } else {
289: // type compatibility error
290: throw new ServerRtException(errorMessage);
291: }
292: }
293: return false;
294: }
295: }
|