ohair@286: /*
aefimov@650: * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
ohair@286: *
ohair@286: * This code is free software; you can redistribute it and/or modify it
ohair@286: * under the terms of the GNU General Public License version 2 only, as
ohair@286: * published by the Free Software Foundation. Oracle designates this
ohair@286: * particular file as subject to the "Classpath" exception as provided
ohair@286: * by Oracle in the LICENSE file that accompanied this code.
ohair@286: *
ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT
ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
ohair@286: * version 2 for more details (a copy is included in the LICENSE file that
ohair@286: * accompanied this code).
ohair@286: *
ohair@286: * You should have received a copy of the GNU General Public License version
ohair@286: * 2 along with this work; if not, write to the Free Software Foundation,
ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
ohair@286: *
ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@286: * or visit www.oracle.com if you need additional information or have any
ohair@286: * questions.
ohair@286: */
ohair@286:
ohair@286: package com.sun.xml.internal.bind.v2.runtime.reflect;
ohair@286:
ohair@286: import java.lang.reflect.Field;
ohair@286: import java.lang.reflect.InvocationTargetException;
ohair@286: import java.lang.reflect.Method;
ohair@286: import java.lang.reflect.Modifier;
ohair@286: import java.lang.reflect.Type;
ohair@286: import java.util.Arrays;
ohair@286: import java.util.HashMap;
ohair@286: import java.util.List;
ohair@286: import java.util.Map;
ohair@286: import java.util.logging.Level;
ohair@286: import java.util.logging.Logger;
ohair@286:
ohair@286: import javax.xml.bind.JAXBElement;
ohair@286: import javax.xml.bind.annotation.adapters.XmlAdapter;
ohair@286:
ohair@286: import com.sun.istack.internal.Nullable;
ohair@286: import com.sun.xml.internal.bind.Util;
ohair@286: import com.sun.xml.internal.bind.api.AccessorException;
ohair@286: import com.sun.xml.internal.bind.api.JAXBRIContext;
ohair@286: import com.sun.xml.internal.bind.v2.model.core.Adapter;
ohair@286: import com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder;
ohair@286: import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
ohair@286: import com.sun.xml.internal.bind.v2.runtime.reflect.opt.OptimizedAccessorFactory;
ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader;
ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Receiver;
ohair@286: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
ohair@286:
ohair@286: import org.xml.sax.SAXException;
ohair@286:
ohair@286: /**
ohair@286: * Accesses a particular property of a bean.
ohair@286: *
ohair@286: *
ohair@286: * This interface encapsulates the access to the actual data store.
ohair@286: * The intention is to generate implementations for a particular bean
ohair@286: * and a property to improve the performance.
ohair@286: *
ohair@286: *
ohair@286: * Accessor can be used as a receiver. Upon receiving an object
ohair@286: * it sets that to the field.
ohair@286: *
ohair@286: * @author Kohsuke Kawaguchi (kk@kohsuke.org)
ohair@286: * @see Accessor.FieldReflection
ohair@286: * @see TransducedAccessor
ohair@286: */
ohair@286: public abstract class Accessor implements Receiver {
ohair@286:
ohair@286: public final Class valueType;
ohair@286:
ohair@286: public Class getValueType() {
ohair@286: return valueType;
ohair@286: }
ohair@286:
ohair@286: protected Accessor(Class valueType) {
ohair@286: this.valueType = valueType;
ohair@286: }
ohair@286:
ohair@286: /**
ohair@286: * Returns the optimized version of the same accessor.
ohair@286: *
ohair@286: * @param context The {@link JAXBContextImpl} that owns the whole thing.
ohair@286: * (See {@link RuntimeModelBuilder#context}.)
ohair@286: * @return At least the implementation can return this.
ohair@286: */
ohair@286: public Accessor optimize(@Nullable JAXBContextImpl context) {
ohair@286: return this;
ohair@286: }
ohair@286:
ohair@286:
ohair@286: /**
ohair@286: * Gets the value of the property of the given bean object.
ohair@286: *
ohair@286: * @param bean must not be null.
ohair@286: * @throws AccessorException if failed to set a value. For example, the getter method
ohair@286: * may throw an exception.
ohair@286: * @since 2.0 EA1
ohair@286: */
ohair@286: public abstract ValueT get(BeanT bean) throws AccessorException;
ohair@286:
ohair@286: /**
ohair@286: * Sets the value of the property of the given bean object.
ohair@286: *
ohair@286: * @param bean must not be null.
ohair@286: * @param value the value to be set. Setting value to null means resetting
ohair@286: * to the VM default value (even for primitive properties.)
ohair@286: * @throws AccessorException if failed to set a value. For example, the setter method
ohair@286: * may throw an exception.
ohair@286: * @since 2.0 EA1
ohair@286: */
ohair@286: public abstract void set(BeanT bean, ValueT value) throws AccessorException;
ohair@286:
ohair@286:
ohair@286: /**
ohair@286: * Sets the value without adapting the value.
ohair@286: *
ohair@286: * This ugly entry point is only used by JAX-WS.
ohair@286: * See {@link JAXBRIContext#getElementPropertyAccessor}
ohair@286: */
ohair@286: public Object getUnadapted(BeanT bean) throws AccessorException {
ohair@286: return get(bean);
ohair@286: }
ohair@286:
ohair@286: /**
ohair@286: * Returns true if this accessor wraps an adapter.
ohair@286: *
ohair@286: * This method needs to be used with care, but it helps some optimization.
ohair@286: */
ohair@286: public boolean isAdapted() {
ohair@286: return false;
ohair@286: }
ohair@286:
ohair@286: /**
ohair@286: * Sets the value without adapting the value.
ohair@286: *
ohair@286: * This ugly entry point is only used by JAX-WS.
ohair@286: * See {@link JAXBRIContext#getElementPropertyAccessor}
ohair@286: */
ohair@286: public void setUnadapted(BeanT bean, Object value) throws AccessorException {
ohair@286: set(bean, (ValueT) value);
ohair@286: }
ohair@286:
ohair@286: public void receive(UnmarshallingContext.State state, Object o) throws SAXException {
ohair@286: try {
aefimov@650: set((BeanT) state.getTarget(), (ValueT) o);
ohair@286: } catch (AccessorException e) {
ohair@286: Loader.handleGenericException(e, true);
ohair@286: } catch (IllegalAccessError iae) {
ohair@286: // throw UnmarshalException instead IllegalAccesssError | Issue 475
ohair@286: Loader.handleGenericError(iae);
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: private static List nonAbstractableClasses = Arrays.asList(new Class[]{
ohair@286: Object.class,
ohair@286: java.util.Calendar.class,
ohair@286: javax.xml.datatype.Duration.class,
ohair@286: javax.xml.datatype.XMLGregorianCalendar.class,
ohair@286: java.awt.Image.class,
ohair@286: javax.activation.DataHandler.class,
ohair@286: javax.xml.transform.Source.class,
ohair@286: java.util.Date.class,
ohair@286: java.io.File.class,
ohair@286: java.net.URI.class,
ohair@286: java.net.URL.class,
ohair@286: Class.class,
ohair@286: String.class,
ohair@286: javax.xml.transform.Source.class}
ohair@286: );
ohair@286:
ohair@286: public boolean isValueTypeAbstractable() {
ohair@286: return !nonAbstractableClasses.contains(getValueType());
ohair@286: }
ohair@286:
ohair@286: /**
alanb@368: * Checks if it is not builtin jaxb class
alanb@368: * @param clazz to be checked
alanb@368: * @return true if it is NOT builtin class
alanb@368: */
alanb@368: public boolean isAbstractable(Class clazz) {
alanb@368: return !nonAbstractableClasses.contains(clazz);
alanb@368: }
alanb@368:
alanb@368: /**
ohair@286: * Wraps this {@link Accessor} into another {@link Accessor}
ohair@286: * and performs the type adaption as necessary.
ohair@286: */
ohair@286: public final Accessor adapt(Class targetType, final Class extends XmlAdapter> adapter) {
ohair@286: return new AdaptedAccessor(targetType, this, adapter);
ohair@286: }
ohair@286:
ohair@286: public final Accessor adapt(Adapter adapter) {
ohair@286: return new AdaptedAccessor(
mkos@450: (Class) Utils.REFLECTION_NAVIGATOR.erasure(adapter.defaultType),
ohair@286: this,
ohair@286: adapter.adapterType);
ohair@286: }
ohair@286:
ohair@286: /**
ohair@286: * Flag that will be set to true after issueing a warning
ohair@286: * about the lack of permission to access non-public fields.
ohair@286: */
ohair@286: private static boolean accessWarned = false;
ohair@286:
ohair@286:
ohair@286: /**
ohair@286: * {@link Accessor} that uses Java reflection to access a field.
ohair@286: */
ohair@286: public static class FieldReflection extends Accessor {
ohair@286: public final Field f;
ohair@286:
ohair@286: private static final Logger logger = Util.getClassLogger();
ohair@286:
ohair@286: public FieldReflection(Field f) {
ohair@286: this(f, false);
ohair@286: }
ohair@286:
ohair@286: public FieldReflection(Field f, boolean supressAccessorWarnings) {
ohair@286: super((Class) f.getType());
ohair@286: this.f = f;
ohair@286:
ohair@286: int mod = f.getModifiers();
ohair@286: if (!Modifier.isPublic(mod) || Modifier.isFinal(mod) || !Modifier.isPublic(f.getDeclaringClass().getModifiers())) {
ohair@286: try {
ohair@286: // attempt to make it accessible, but do so in the security context of the calling application.
ohair@286: // don't do this in the doPrivilege block, as that would create a security hole for anyone
ohair@286: // to make any field accessible.
ohair@286: f.setAccessible(true);
ohair@286: } catch (SecurityException e) {
ohair@286: if ((!accessWarned) && (!supressAccessorWarnings)) {
ohair@286: // this happens when we don't have enough permission.
ohair@286: logger.log(Level.WARNING, Messages.UNABLE_TO_ACCESS_NON_PUBLIC_FIELD.format(
ohair@286: f.getDeclaringClass().getName(),
ohair@286: f.getName()),
ohair@286: e);
ohair@286: }
ohair@286: accessWarned = true;
ohair@286: }
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: public ValueT get(BeanT bean) {
ohair@286: try {
ohair@286: return (ValueT) f.get(bean);
ohair@286: } catch (IllegalAccessException e) {
ohair@286: throw new IllegalAccessError(e.getMessage());
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: public void set(BeanT bean, ValueT value) {
ohair@286: try {
ohair@286: if (value == null)
ohair@286: value = (ValueT) uninitializedValues.get(valueType);
ohair@286: f.set(bean, value);
ohair@286: } catch (IllegalAccessException e) {
ohair@286: throw new IllegalAccessError(e.getMessage());
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: @Override
ohair@286: public Accessor optimize(JAXBContextImpl context) {
ohair@286: if (context != null && context.fastBoot)
ohair@286: // let's not waste time on doing this for the sake of faster boot.
ohair@286: return this;
ohair@286: Accessor acc = OptimizedAccessorFactory.get(f);
ohair@286: if (acc != null)
ohair@286: return acc;
ohair@286: else
ohair@286: return this;
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: /**
ohair@286: * Read-only access to {@link Field}. Used to handle a static field.
ohair@286: */
ohair@286: public static final class ReadOnlyFieldReflection extends FieldReflection {
ohair@286: public ReadOnlyFieldReflection(Field f, boolean supressAccessorWarnings) {
ohair@286: super(f, supressAccessorWarnings);
ohair@286: }
ohair@286: public ReadOnlyFieldReflection(Field f) {
ohair@286: super(f);
ohair@286: }
ohair@286:
ohair@286: @Override
ohair@286: public void set(BeanT bean, ValueT value) {
ohair@286: // noop
ohair@286: }
ohair@286:
ohair@286: @Override
ohair@286: public Accessor optimize(JAXBContextImpl context) {
ohair@286: return this;
ohair@286: }
ohair@286: }
ohair@286:
ohair@286:
ohair@286: /**
ohair@286: * {@link Accessor} that uses Java reflection to access a getter and a setter.
ohair@286: */
ohair@286: public static class GetterSetterReflection extends Accessor {
ohair@286: public final Method getter;
ohair@286: public final Method setter;
ohair@286:
ohair@286: private static final Logger logger = Util.getClassLogger();
ohair@286:
ohair@286: public GetterSetterReflection(Method getter, Method setter) {
ohair@286: super(
ohair@286: (Class) (getter != null ?
ohair@286: getter.getReturnType() :
ohair@286: setter.getParameterTypes()[0]));
ohair@286: this.getter = getter;
ohair@286: this.setter = setter;
ohair@286:
ohair@286: if (getter != null)
ohair@286: makeAccessible(getter);
ohair@286: if (setter != null)
ohair@286: makeAccessible(setter);
ohair@286: }
ohair@286:
ohair@286: private void makeAccessible(Method m) {
ohair@286: if (!Modifier.isPublic(m.getModifiers()) || !Modifier.isPublic(m.getDeclaringClass().getModifiers())) {
ohair@286: try {
ohair@286: m.setAccessible(true);
ohair@286: } catch (SecurityException e) {
ohair@286: if (!accessWarned)
ohair@286: // this happens when we don't have enough permission.
ohair@286: logger.log(Level.WARNING, Messages.UNABLE_TO_ACCESS_NON_PUBLIC_FIELD.format(
ohair@286: m.getDeclaringClass().getName(),
ohair@286: m.getName()),
ohair@286: e);
ohair@286: accessWarned = true;
ohair@286: }
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: public ValueT get(BeanT bean) throws AccessorException {
ohair@286: try {
ohair@286: return (ValueT) getter.invoke(bean);
ohair@286: } catch (IllegalAccessException e) {
ohair@286: throw new IllegalAccessError(e.getMessage());
ohair@286: } catch (InvocationTargetException e) {
ohair@286: throw handleInvocationTargetException(e);
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: public void set(BeanT bean, ValueT value) throws AccessorException {
ohair@286: try {
ohair@286: if (value == null)
ohair@286: value = (ValueT) uninitializedValues.get(valueType);
ohair@286: setter.invoke(bean, value);
ohair@286: } catch (IllegalAccessException e) {
ohair@286: throw new IllegalAccessError(e.getMessage());
ohair@286: } catch (InvocationTargetException e) {
ohair@286: throw handleInvocationTargetException(e);
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: private AccessorException handleInvocationTargetException(InvocationTargetException e) {
ohair@286: // don't block a problem in the user code
ohair@286: Throwable t = e.getTargetException();
ohair@286: if (t instanceof RuntimeException)
ohair@286: throw (RuntimeException) t;
ohair@286: if (t instanceof Error)
ohair@286: throw (Error) t;
ohair@286:
ohair@286: // otherwise it's a checked exception.
ohair@286: // I'm not sure how to handle this.
ohair@286: // we can throw a checked exception from here,
ohair@286: // but because get/set would be called from so many different places,
ohair@286: // the handling would be tedious.
ohair@286: return new AccessorException(t);
ohair@286: }
ohair@286:
ohair@286: @Override
ohair@286: public Accessor optimize(JAXBContextImpl context) {
ohair@286: if (getter == null || setter == null)
ohair@286: // if we aren't complete, OptimizedAccessor won't always work
ohair@286: return this;
ohair@286: if (context != null && context.fastBoot)
ohair@286: // let's not waste time on doing this for the sake of faster boot.
ohair@286: return this;
ohair@286:
ohair@286: Accessor acc = OptimizedAccessorFactory.get(getter, setter);
ohair@286: if (acc != null)
ohair@286: return acc;
ohair@286: else
ohair@286: return this;
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: /**
ohair@286: * A version of {@link GetterSetterReflection} that doesn't have any setter.
ohair@286: *
ohair@286: *
ohair@286: * This provides a user-friendly error message.
ohair@286: */
ohair@286: public static class GetterOnlyReflection extends GetterSetterReflection {
ohair@286: public GetterOnlyReflection(Method getter) {
ohair@286: super(getter, null);
ohair@286: }
ohair@286:
ohair@286: @Override
ohair@286: public void set(BeanT bean, ValueT value) throws AccessorException {
ohair@286: throw new AccessorException(Messages.NO_SETTER.format(getter.toString()));
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: /**
ohair@286: * A version of {@link GetterSetterReflection} thaat doesn't have any getter.
ohair@286: *
ohair@286: *
ohair@286: * This provides a user-friendly error message.
ohair@286: */
ohair@286: public static class SetterOnlyReflection extends GetterSetterReflection {
ohair@286: public SetterOnlyReflection(Method setter) {
ohair@286: super(null, setter);
ohair@286: }
ohair@286:
ohair@286: @Override
ohair@286: public ValueT get(BeanT bean) throws AccessorException {
ohair@286: throw new AccessorException(Messages.NO_GETTER.format(setter.toString()));
ohair@286: }
ohair@286: }
ohair@286:
ohair@286: /**
ohair@286: * Gets the special {@link Accessor} used to recover from errors.
ohair@286: */
ohair@286: @SuppressWarnings("unchecked")
ohair@286: public static Accessor getErrorInstance() {
ohair@286: return ERROR;
ohair@286: }
ohair@286:
ohair@286: private static final Accessor ERROR = new Accessor