1 /* 2 * Copyright (c) 1997, 2011, 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.bind.v2.runtime.reflect; 27 28 import java.lang.reflect.Field; 29 import java.lang.reflect.InvocationTargetException; 30 import java.lang.reflect.Method; 31 import java.lang.reflect.Modifier; 32 import java.lang.reflect.Type; 33 import java.util.Arrays; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.List; 38 import java.util.Arrays; 39 import java.util.logging.Level; 40 import java.util.logging.Logger; 41 42 import javax.xml.bind.JAXBElement; 43 import javax.xml.bind.annotation.adapters.XmlAdapter; 44 45 import com.sun.istack.internal.Nullable; 46 import com.sun.xml.internal.bind.Util; 47 import com.sun.xml.internal.bind.api.AccessorException; 48 import com.sun.xml.internal.bind.api.JAXBRIContext; 49 import com.sun.xml.internal.bind.v2.model.core.Adapter; 50 import com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder; 51 import com.sun.xml.internal.bind.v2.model.nav.Navigator; 52 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; 53 import com.sun.xml.internal.bind.v2.runtime.reflect.opt.OptimizedAccessorFactory; 54 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader; 55 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Receiver; 56 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; 57 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; 58 import com.sun.istack.internal.Nullable; 59 60 import org.xml.sax.SAXException; 61 62 /** 63 * Accesses a particular property of a bean. 64 * <p/> 65 * <p/> 66 * This interface encapsulates the access to the actual data store. 67 * The intention is to generate implementations for a particular bean 68 * and a property to improve the performance. 69 * <p/> 70 * <p/> 71 * Accessor can be used as a receiver. Upon receiving an object 72 * it sets that to the field. 73 * 74 * @author Kohsuke Kawaguchi (kk@kohsuke.org) 75 * @see Accessor.FieldReflection 76 * @see TransducedAccessor 77 */ 78 public abstract class Accessor<BeanT, ValueT> implements Receiver { 79 80 public final Class<ValueT> valueType; 81 82 public Class<ValueT> getValueType() { 83 return valueType; 84 } 85 86 protected Accessor(Class<ValueT> valueType) { 87 this.valueType = valueType; 88 } 89 90 /** 91 * Returns the optimized version of the same accessor. 92 * 93 * @param context The {@link JAXBContextImpl} that owns the whole thing. 94 * (See {@link RuntimeModelBuilder#context}.) 95 * @return At least the implementation can return <tt>this</tt>. 96 */ 97 public Accessor<BeanT, ValueT> optimize(@Nullable JAXBContextImpl context) { 98 return this; 99 } 100 101 102 /** 103 * Gets the value of the property of the given bean object. 104 * 105 * @param bean must not be null. 106 * @throws AccessorException if failed to set a value. For example, the getter method 107 * may throw an exception. 108 * @since 2.0 EA1 109 */ 110 public abstract ValueT get(BeanT bean) throws AccessorException; 111 112 /** 113 * Sets the value of the property of the given bean object. 114 * 115 * @param bean must not be null. 116 * @param value the value to be set. Setting value to null means resetting 117 * to the VM default value (even for primitive properties.) 118 * @throws AccessorException if failed to set a value. For example, the setter method 119 * may throw an exception. 120 * @since 2.0 EA1 121 */ 122 public abstract void set(BeanT bean, ValueT value) throws AccessorException; 123 124 125 /** 126 * Sets the value without adapting the value. 127 * <p/> 128 * This ugly entry point is only used by JAX-WS. 129 * See {@link JAXBRIContext#getElementPropertyAccessor} 130 */ 131 public Object getUnadapted(BeanT bean) throws AccessorException { 132 return get(bean); 133 } 134 135 /** 136 * Returns true if this accessor wraps an adapter. 137 * <p/> 138 * This method needs to be used with care, but it helps some optimization. 139 */ 140 public boolean isAdapted() { 141 return false; 142 } 143 144 /** 145 * Sets the value without adapting the value. 146 * <p/> 147 * This ugly entry point is only used by JAX-WS. 148 * See {@link JAXBRIContext#getElementPropertyAccessor} 149 */ 150 public void setUnadapted(BeanT bean, Object value) throws AccessorException { 151 set(bean, (ValueT) value); 152 } 153 154 public void receive(UnmarshallingContext.State state, Object o) throws SAXException { 155 try { 156 set((BeanT) state.target, (ValueT) o); 157 } catch (AccessorException e) { 158 Loader.handleGenericException(e, true); 159 } catch (IllegalAccessError iae) { 160 // throw UnmarshalException instead IllegalAccesssError | Issue 475 161 Loader.handleGenericError(iae); 162 } 163 } 164 165 private static List<Class> nonAbstractableClasses = Arrays.asList(new Class[]{ 166 Object.class, 167 java.util.Calendar.class, 168 javax.xml.datatype.Duration.class, 169 javax.xml.datatype.XMLGregorianCalendar.class, 170 java.awt.Image.class, 171 javax.activation.DataHandler.class, 172 javax.xml.transform.Source.class, 173 java.util.Date.class, 174 java.io.File.class, 175 java.net.URI.class, 176 java.net.URL.class, 177 Class.class, 178 String.class, 179 javax.xml.transform.Source.class} 180 ); 181 182 public boolean isValueTypeAbstractable() { 183 return !nonAbstractableClasses.contains(getValueType()); 184 } 185 186 /** 187 * Wraps this {@link Accessor} into another {@link Accessor} 188 * and performs the type adaption as necessary. 189 */ 190 public final <T> Accessor<BeanT, T> adapt(Class<T> targetType, final Class<? extends XmlAdapter<T, ValueT>> adapter) { 191 return new AdaptedAccessor<BeanT, ValueT, T>(targetType, this, adapter); 192 } 193 194 public final <T> Accessor<BeanT, T> adapt(Adapter<Type, Class> adapter) { 195 return new AdaptedAccessor<BeanT, ValueT, T>( 196 (Class<T>) Navigator.REFLECTION.erasure(adapter.defaultType), 197 this, 198 adapter.adapterType); 199 } 200 201 /** 202 * Flag that will be set to true after issueing a warning 203 * about the lack of permission to access non-public fields. 204 */ 205 private static boolean accessWarned = false; 206 207 208 /** 209 * {@link Accessor} that uses Java reflection to access a field. 210 */ 211 public static class FieldReflection<BeanT, ValueT> extends Accessor<BeanT, ValueT> { 212 public final Field f; 213 214 private static final Logger logger = Util.getClassLogger(); 215 216 public FieldReflection(Field f) { 217 this(f, false); 218 } 219 220 public FieldReflection(Field f, boolean supressAccessorWarnings) { 221 super((Class<ValueT>) f.getType()); 222 this.f = f; 223 224 int mod = f.getModifiers(); 225 if (!Modifier.isPublic(mod) || Modifier.isFinal(mod) || !Modifier.isPublic(f.getDeclaringClass().getModifiers())) { 226 try { 227 // attempt to make it accessible, but do so in the security context of the calling application. 228 // don't do this in the doPrivilege block, as that would create a security hole for anyone 229 // to make any field accessible. 230 f.setAccessible(true); 231 } catch (SecurityException e) { 232 if ((!accessWarned) && (!supressAccessorWarnings)) { 233 // this happens when we don't have enough permission. 234 logger.log(Level.WARNING, Messages.UNABLE_TO_ACCESS_NON_PUBLIC_FIELD.format( 235 f.getDeclaringClass().getName(), 236 f.getName()), 237 e); 238 } 239 accessWarned = true; 240 } 241 } 242 } 243 244 public ValueT get(BeanT bean) { 245 try { 246 return (ValueT) f.get(bean); 247 } catch (IllegalAccessException e) { 248 throw new IllegalAccessError(e.getMessage()); 249 } 250 } 251 252 public void set(BeanT bean, ValueT value) { 253 try { 254 if (value == null) 255 value = (ValueT) uninitializedValues.get(valueType); 256 f.set(bean, value); 257 } catch (IllegalAccessException e) { 258 throw new IllegalAccessError(e.getMessage()); 259 } 260 } 261 262 @Override 263 public Accessor<BeanT, ValueT> optimize(JAXBContextImpl context) { 264 if (context != null && context.fastBoot) 265 // let's not waste time on doing this for the sake of faster boot. 266 return this; 267 Accessor<BeanT, ValueT> acc = OptimizedAccessorFactory.get(f); 268 if (acc != null) 269 return acc; 270 else 271 return this; 272 } 273 } 274 275 /** 276 * Read-only access to {@link Field}. Used to handle a static field. 277 */ 278 public static final class ReadOnlyFieldReflection<BeanT, ValueT> extends FieldReflection<BeanT, ValueT> { 279 public ReadOnlyFieldReflection(Field f, boolean supressAccessorWarnings) { 280 super(f, supressAccessorWarnings); 281 } 282 public ReadOnlyFieldReflection(Field f) { 283 super(f); 284 } 285 286 @Override 287 public void set(BeanT bean, ValueT value) { 288 // noop 289 } 290 291 @Override 292 public Accessor<BeanT, ValueT> optimize(JAXBContextImpl context) { 293 return this; 294 } 295 } 296 297 298 /** 299 * {@link Accessor} that uses Java reflection to access a getter and a setter. 300 */ 301 public static class GetterSetterReflection<BeanT, ValueT> extends Accessor<BeanT, ValueT> { 302 public final Method getter; 303 public final Method setter; 304 305 private static final Logger logger = Util.getClassLogger(); 306 307 public GetterSetterReflection(Method getter, Method setter) { 308 super( 309 (Class<ValueT>) (getter != null ? 310 getter.getReturnType() : 311 setter.getParameterTypes()[0])); 312 this.getter = getter; 313 this.setter = setter; 314 315 if (getter != null) 316 makeAccessible(getter); 317 if (setter != null) 318 makeAccessible(setter); 319 } 320 321 private void makeAccessible(Method m) { 322 if (!Modifier.isPublic(m.getModifiers()) || !Modifier.isPublic(m.getDeclaringClass().getModifiers())) { 323 try { 324 m.setAccessible(true); 325 } catch (SecurityException e) { 326 if (!accessWarned) 327 // this happens when we don't have enough permission. 328 logger.log(Level.WARNING, Messages.UNABLE_TO_ACCESS_NON_PUBLIC_FIELD.format( 329 m.getDeclaringClass().getName(), 330 m.getName()), 331 e); 332 accessWarned = true; 333 } 334 } 335 } 336 337 public ValueT get(BeanT bean) throws AccessorException { 338 try { 339 return (ValueT) getter.invoke(bean); 340 } catch (IllegalAccessException e) { 341 throw new IllegalAccessError(e.getMessage()); 342 } catch (InvocationTargetException e) { 343 throw handleInvocationTargetException(e); 344 } 345 } 346 347 public void set(BeanT bean, ValueT value) throws AccessorException { 348 try { 349 if (value == null) 350 value = (ValueT) uninitializedValues.get(valueType); 351 setter.invoke(bean, value); 352 } catch (IllegalAccessException e) { 353 throw new IllegalAccessError(e.getMessage()); 354 } catch (InvocationTargetException e) { 355 throw handleInvocationTargetException(e); 356 } 357 } 358 359 private AccessorException handleInvocationTargetException(InvocationTargetException e) { 360 // don't block a problem in the user code 361 Throwable t = e.getTargetException(); 362 if (t instanceof RuntimeException) 363 throw (RuntimeException) t; 364 if (t instanceof Error) 365 throw (Error) t; 366 367 // otherwise it's a checked exception. 368 // I'm not sure how to handle this. 369 // we can throw a checked exception from here, 370 // but because get/set would be called from so many different places, 371 // the handling would be tedious. 372 return new AccessorException(t); 373 } 374 375 @Override 376 public Accessor<BeanT, ValueT> optimize(JAXBContextImpl context) { 377 if (getter == null || setter == null) 378 // if we aren't complete, OptimizedAccessor won't always work 379 return this; 380 if (context != null && context.fastBoot) 381 // let's not waste time on doing this for the sake of faster boot. 382 return this; 383 384 Accessor<BeanT, ValueT> acc = OptimizedAccessorFactory.get(getter, setter); 385 if (acc != null) 386 return acc; 387 else 388 return this; 389 } 390 } 391 392 /** 393 * A version of {@link GetterSetterReflection} that doesn't have any setter. 394 * <p/> 395 * <p/> 396 * This provides a user-friendly error message. 397 */ 398 public static class GetterOnlyReflection<BeanT, ValueT> extends GetterSetterReflection<BeanT, ValueT> { 399 public GetterOnlyReflection(Method getter) { 400 super(getter, null); 401 } 402 403 @Override 404 public void set(BeanT bean, ValueT value) throws AccessorException { 405 throw new AccessorException(Messages.NO_SETTER.format(getter.toString())); 406 } 407 } 408 409 /** 410 * A version of {@link GetterSetterReflection} thaat doesn't have any getter. 411 * <p/> 412 * <p/> 413 * This provides a user-friendly error message. 414 */ 415 public static class SetterOnlyReflection<BeanT, ValueT> extends GetterSetterReflection<BeanT, ValueT> { 416 public SetterOnlyReflection(Method setter) { 417 super(null, setter); 418 } 419 420 @Override 421 public ValueT get(BeanT bean) throws AccessorException { 422 throw new AccessorException(Messages.NO_GETTER.format(setter.toString())); 423 } 424 } 425 426 /** 427 * Gets the special {@link Accessor} used to recover from errors. 428 */ 429 @SuppressWarnings("unchecked") 430 public static <A, B> Accessor<A, B> getErrorInstance() { 431 return ERROR; 432 } 433 434 private static final Accessor ERROR = new Accessor<Object, Object>(Object.class) { 435 public Object get(Object o) { 436 return null; 437 } 438 439 public void set(Object o, Object o1) { 440 } 441 }; 442 443 /** 444 * {@link Accessor} for {@link JAXBElement#getValue()}. 445 */ 446 public static final Accessor<JAXBElement, Object> JAXB_ELEMENT_VALUE = new Accessor<JAXBElement, Object>(Object.class) { 447 public Object get(JAXBElement jaxbElement) { 448 return jaxbElement.getValue(); 449 } 450 451 public void set(JAXBElement jaxbElement, Object o) { 452 jaxbElement.setValue(o); 453 } 454 }; 455 456 /** 457 * Uninitialized map keyed by their classes. 458 */ 459 private static final Map<Class, Object> uninitializedValues = new HashMap<Class, Object>(); 460 461 static { 462 /* 463 static byte default_value_byte = 0; 464 static boolean default_value_boolean = false; 465 static char default_value_char = 0; 466 static float default_value_float = 0; 467 static double default_value_double = 0; 468 static int default_value_int = 0; 469 static long default_value_long = 0; 470 static short default_value_short = 0; 471 */ 472 uninitializedValues.put(byte.class, Byte.valueOf((byte) 0)); 473 uninitializedValues.put(boolean.class, false); 474 uninitializedValues.put(char.class, Character.valueOf((char) 0)); 475 uninitializedValues.put(float.class, Float.valueOf(0)); 476 uninitializedValues.put(double.class, Double.valueOf(0)); 477 uninitializedValues.put(int.class, Integer.valueOf(0)); 478 uninitializedValues.put(long.class, Long.valueOf(0)); 479 uninitializedValues.put(short.class, Short.valueOf((short) 0)); 480 } 481 482 }