All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openjpa.persistence.PersistenceMetaDataDefaults Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.    
 */
package org.apache.openjpa.persistence;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
import javax.persistence.Basic;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import javax.persistence.Transient;

import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.lib.util.J2DoPriv5Helper;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.log.Log;
import org.apache.openjpa.meta.AbstractMetaDataDefaults;
import org.apache.openjpa.meta.ClassMetaData;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueMetaData;
import static org.apache.openjpa.persistence.PersistenceStrategy.*;
import org.apache.openjpa.util.MetaDataException;
import org.apache.openjpa.conf.OpenJPAConfiguration;

/**
 * JPA-based metadata defaults.
 *
 * @author Patrick Linskey
 * @author Abe White
 * @nojavadoc
 */
public class PersistenceMetaDataDefaults
    extends AbstractMetaDataDefaults {

    private boolean _allowsMultipleMethodsForSameCallback = false;

    private static final Localizer _loc = Localizer.forPackage
        (PersistenceMetaDataDefaults.class);

    private static final Map _strats =
        new HashMap();
    private static final Set _ignoredAnnos = new HashSet();

    static {
        _strats.put(Basic.class, BASIC);
        _strats.put(ManyToOne.class, MANY_ONE);
        _strats.put(OneToOne.class, ONE_ONE);
        _strats.put(Embedded.class, EMBEDDED);
        _strats.put(EmbeddedId.class, EMBEDDED);
        _strats.put(OneToMany.class, ONE_MANY);
        _strats.put(ManyToMany.class, MANY_MANY);
        _strats.put(Persistent.class, PERS);
        _strats.put(PersistentCollection.class, PERS_COLL);
        _strats.put(PersistentMap.class, PERS_MAP);

        _ignoredAnnos.add(DetachedState.class.getName());
        _ignoredAnnos.add(PostLoad.class.getName());
        _ignoredAnnos.add(PostPersist.class.getName());
        _ignoredAnnos.add(PostRemove.class.getName());
        _ignoredAnnos.add(PostUpdate.class.getName());
        _ignoredAnnos.add(PrePersist.class.getName());
        _ignoredAnnos.add(PreRemove.class.getName());
        _ignoredAnnos.add(PreUpdate.class.getName());
    }

    public PersistenceMetaDataDefaults() {
        setCallbackMode(CALLBACK_RETHROW | CALLBACK_ROLLBACK |
            CALLBACK_FAIL_FAST);
        setDataStoreObjectIdFieldUnwrapped(true);
    }

    /**
     * Return the code for the strategy of the given member. Return null if
     * no strategy.
     */
    public static PersistenceStrategy getPersistenceStrategy
        (FieldMetaData fmd, Member member) {
        if (member == null)
            return null;
        AnnotatedElement el = (AnnotatedElement) member;
        if (((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
            .isAnnotationPresentAction(el, Transient.class))).booleanValue())
            return TRANSIENT;
        if (fmd != null
            && fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
            return null;

        // look for persistence strategy in annotation table
        PersistenceStrategy pstrat = null;
        for (Annotation anno : el.getDeclaredAnnotations()) {
            if (pstrat != null && _strats.containsKey(anno.annotationType()))
                throw new MetaDataException(_loc.get("already-pers", member));
            if (pstrat == null)
                pstrat = _strats.get(anno.annotationType());
        }
        if (pstrat != null)
            return pstrat;

        Class type;
        int code;
        if (fmd != null) {
            type = fmd.getType();
            code = fmd.getTypeCode();
        } else if (member instanceof Field) {
            type = ((Field) member).getType();
            code = JavaTypes.getTypeCode(type);
        } else {
            type = ((Method) member).getReturnType();
            code = JavaTypes.getTypeCode(type);
        }

        switch (code) {
            case JavaTypes.ARRAY:
                if (type == byte[].class
                    || type == char[].class
                    || type == Byte[].class
                    || type == Character[].class)
                    return BASIC;
                break;
            case JavaTypes.BOOLEAN:
            case JavaTypes.BOOLEAN_OBJ:
            case JavaTypes.BYTE:
            case JavaTypes.BYTE_OBJ:
            case JavaTypes.CHAR:
            case JavaTypes.CHAR_OBJ:
            case JavaTypes.DOUBLE:
            case JavaTypes.DOUBLE_OBJ:
            case JavaTypes.FLOAT:
            case JavaTypes.FLOAT_OBJ:
            case JavaTypes.INT:
            case JavaTypes.INT_OBJ:
            case JavaTypes.LONG:
            case JavaTypes.LONG_OBJ:
            case JavaTypes.SHORT:
            case JavaTypes.SHORT_OBJ:
            case JavaTypes.STRING:
            case JavaTypes.BIGDECIMAL:
            case JavaTypes.BIGINTEGER:
            case JavaTypes.DATE:
                return BASIC;
            case JavaTypes.OBJECT:
                if (Enum.class.isAssignableFrom(type))
                    return BASIC;
                break;
        }

        //### EJB3: what if defined in XML?
        if (((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
            .isAnnotationPresentAction(type, Embeddable.class))).booleanValue())
            return EMBEDDED;
        if (Serializable.class.isAssignableFrom(type))
            return BASIC;
        return null;
    }
    
    /** 
     * Flags if multiple methods of the same class can handle the same 
     * callback event.
     */
    public boolean getAllowsMultipleMethodsForSameCallback() {
        return _allowsMultipleMethodsForSameCallback;
    }
    
    /** 
     * Flags if multiple methods of the same class can handle the same 
     * callback event.
     */
    public void setAllowsMultipleMethodsForSameCallback(boolean flag) {
        _allowsMultipleMethodsForSameCallback = flag;
    }

    /**
     * Auto-configuration method for the default access type of base classes 
     * with ACCESS_UNKNOWN
     */
    public void setDefaultAccessType(String type) {
        if (type == null)
            return;
        if ("PROPERTY".equals(type.toUpperCase()))
            setDefaultAccessType(ClassMetaData.ACCESS_PROPERTY);
        else
            setDefaultAccessType(ClassMetaData.ACCESS_FIELD);
    }

    @Override
    public void populate(ClassMetaData meta, int access) {
        super.populate(meta, access);
        meta.setDetachable(true);
        // do not call get*Fields as it will lock down the fields.
    }

    @Override
    protected void populate(FieldMetaData fmd) {
        setCascadeNone(fmd);
        setCascadeNone(fmd.getKey());
        setCascadeNone(fmd.getElement());
    }

    /**
     * Turns off auto cascading of persist, refresh, attach.
     */
    static void setCascadeNone(ValueMetaData vmd) {
        vmd.setCascadePersist(ValueMetaData.CASCADE_NONE);
        vmd.setCascadeRefresh(ValueMetaData.CASCADE_NONE);
        vmd.setCascadeAttach(ValueMetaData.CASCADE_NONE);
    }

    @Override
    protected int getAccessType(ClassMetaData meta) {
        return getAccessType(meta.getDescribedType());
    }

    /**
     * Recursive helper to determine access type based on annotation placement.
     */
    private int getAccessType(Class cls) {
        // traversed entire hierarchy without finding annotations
        if (cls == null || cls == Object.class)
            return ClassMetaData.ACCESS_UNKNOWN;

        int access = 0;
        if (annotated((Field[]) AccessController.doPrivileged(
            J2DoPriv5Helper.getDeclaredFieldsAction(cls))).size() > 0)
            access |= ClassMetaData.ACCESS_FIELD;
        if (annotated((Method[]) AccessController.doPrivileged(
            J2DoPriv5Helper.getDeclaredMethodsAction(cls))).size() > 0
            || cls.isInterface()) // OpenJPA managed ifaces must use prop access
            access |= ClassMetaData.ACCESS_PROPERTY;
        return getAccessType(cls.getSuperclass()) | access;
    }

    @Override
    protected List getFieldAccessNames(ClassMetaData meta) {
        return annotated((Field[]) AccessController.doPrivileged(
            J2DoPriv5Helper.getDeclaredFieldsAction(meta.getDescribedType())));
    }

    @Override
    protected List getPropertyAccessNames(ClassMetaData meta) {
        return annotated((Method[]) AccessController.doPrivileged(
            J2DoPriv5Helper.getDeclaredMethodsAction(meta.getDescribedType())));
    }

    /**
     * Return the members of members that have persistence
     * annotations.
     */
    private static List annotated(AnnotatedElement[] members) {
        Annotation[] annos;
        String name;
        List annotated = new ArrayList(members.length);
        for (int i = 0; i < members.length; i++) {
            annos = (Annotation[]) AccessController.doPrivileged(J2DoPriv5Helper
                .getAnnotationsAction(members[i]));
            for (int j = 0; j < annos.length; j++) {
                name = annos[j].annotationType().getName();
                if ((name.startsWith("javax.persistence.")
                    || name.startsWith("org.apache.openjpa.persistence."))
                    && !_ignoredAnnos.contains(name))
                    annotated.add(members[i]);
            }
        }
        return annotated;
    }

    protected boolean isDefaultPersistent(ClassMetaData meta, Member member,
        String name) {
        int mods = member.getModifiers();
        if (Modifier.isTransient(mods))
            return false;

        if (member instanceof Method) {
            try {
                // check for setters for methods
                Method setter = (Method) AccessController.doPrivileged(
                    J2DoPriv5Helper.getDeclaredMethodAction(
                        meta.getDescribedType(), "set" +
                        StringUtils.capitalize(name), new Class[] { 
                            ((Method) member).getReturnType() }));
                if (setter == null && !isAnnotatedTransient(member)) {
                    logNoSetter(meta, name, null);
                    return false;
                }
            } catch (Exception e) {
                // e.g., NoSuchMethodException
                if (!isAnnotatedTransient(member))
                    logNoSetter(meta, name, e);
                return false;
            }
        }

        PersistenceStrategy strat = getPersistenceStrategy(null, member);
        if (strat == null || strat == PersistenceStrategy.TRANSIENT)
            return false;
        return true;
	}

    private boolean isAnnotatedTransient(Member member) {
        return member instanceof AnnotatedElement
            && ((Boolean) AccessController.doPrivileged(J2DoPriv5Helper
                .isAnnotationPresentAction(((AnnotatedElement) member),
                    Transient.class))).booleanValue();
    }

    private void logNoSetter(ClassMetaData meta, String name, Exception e) {
        Log log = meta.getRepository().getConfiguration()
            .getLog(OpenJPAConfiguration.LOG_METADATA);
        if (log.isWarnEnabled())
            log.warn(_loc.get("no-setter-for-getter", name,
                meta.getDescribedType().getName()));
        else if (log.isTraceEnabled())
            // log the exception, if any, if we're in trace-level debugging
            log.warn(_loc.get("no-setter-for-getter", name,
                meta.getDescribedType().getName()), e);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy