Package com.force.sdk.jpa

Source Code of com.force.sdk.jpa.ForceMetaDataListener

/**
* Copyright (c) 2011, salesforce.com, inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* that the following conditions are met:
*
*    Redistributions of source code must retain the above copyright notice, this list of conditions and the
*    following disclaimer.
*
*    Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
*    the following disclaimer in the documentation and/or other materials provided with the distribution.
*
*    Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or
*    promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

package com.force.sdk.jpa;

import java.util.Calendar;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.*;
import org.datanucleus.util.Localiser;

import com.force.sdk.jpa.annotation.ForceAnnotationReader;
import com.force.sdk.jpa.schema.ForceClassMetaData;

/**
*
* Customized version of a {@code MetaDataListener} that does all the registration and
* setup needed when an application starts.  The object schema is validated
* and cached.
*
* @author Fiaz Hossain
*/
public class ForceMetaDataListener implements MetaDataListener {

    /** Localiser for messages. */
    protected static final Localiser LOCALISER = Localiser.getInstance(
        "org.datanucleus.Localisation", ForceStoreManager.class.getClassLoader());

    private ForceStoreManager storeManager;
    private ForceAnnotationReader annotationReader;
   
    /**
     *
     * Create a {@code MetaDataListener} and store off the store manager, instantiates the
     * annotation reader.
     *
     * @param storeManager  the store manager with the persistence properties that should be used
     */
    ForceMetaDataListener(ForceStoreManager storeManager) {
        this.storeManager = storeManager;
        this.annotationReader = new ForceAnnotationReader(storeManager.getMetaDataManager());
    }
   
    /**
     * This method is called when an entity is loaded during startup. There is
     * some basic validation that happens here as well as the caching of objects
     * (@Entity classes).  Registration of fields happens in the {@link ForceMetaDataManager}
     *
     * @param cmd  the class metadata to load
     */
    @Override
    public void loaded(AbstractClassMetaData cmd) {
        if (cmd.getIdentityType() == IdentityType.DATASTORE && !cmd.isEmbeddedOnly())
        {
            throw new InvalidMetaDataException(LOCALISER, "force.DatastoreID", cmd.getFullClassName());
        }
        /**
         * Annotations do not work at field level. We hack that in here for now.
         */
        updateAnnotations(cmd);

        /**
         * Validate that the annotations and extensions are supported
         */
        validateExtensions(cmd);
       
        /**
         * Load object schema and register it --creation happens in the ForceMetaDataManager
         */
        registerObjectSchema(cmd);
    }

    /**
     * This is a lot of hackery. Basically, force the class to go through ForceAnnotationReader
     * and create a fake class that has Force.com annotations read in as
     * extension data. We then transfer the extension data from the fake class to the actual one.
     *
     * @param cmd - actual class metadata that will get updated with Force.com annotations
     */
    private void updateAnnotations(AbstractClassMetaData cmd) {
        ClassLoaderResolver clr = storeManager.getOMFContext().getClassLoaderResolver(storeManager.getClass().getClassLoader());
        Class<?> clazz = clr.classForName(cmd.getFullClassName());
        AbstractClassMetaData cmdNew = annotationReader.getMetaDataForClass(clazz, cmd.getPackageMetaData(), clr);
        ExtensionMetaData[] extensions = cmdNew.getExtensions();
        if (extensions != null && extensions.length > 0) {
            for (ExtensionMetaData e : extensions) {
                if (ForceStoreManager.FORCE_KEY.equals(e.getVendorName())) {
                    cmd.addExtension(ForceStoreManager.FORCE_KEY, e.getKey(), e.getValue());
                }
            }
        }
        for (AbstractMemberMetaData ammdNew : ((ForceClassMetaData) cmdNew).getMembers()) {
            AbstractMemberMetaData ammd = cmd.getMetaDataForMember(ammdNew.getName());
            for (ExtensionMetaData e : ammdNew.getExtensions()) {
                ammd.addExtension(e.getVendorName(), e.getKey(), e.getValue());
            }
        }
    }
   
    /**
     * Performs basic validations on the class metadata right after it's been loaded.
     *
     * @param cmd  the class metadata to validate
     */
    private void validateExtensions(AbstractClassMetaData cmd) {
        // We do not support Secondary tables (yet)
        if (cmd.getJoinMetaData() != null && cmd.getJoinMetaData().length > 0) {
            throw new NucleusUserException("Secondary tables are not supported by Force.com datastore."
                                            + " Offending entity: " + cmd.getFullClassName());
        }
        // We do not support @PrimaryKeyJoinColumn
        if (cmd.getPrimaryKeyMetaData() != null) {
            throw new NucleusUserException("@PrimaryKeyJoinColumn is not supported by Force.com datastore."
                                            + " Offending entity: " + cmd.getFullClassName());
        }
        // We do not support @UniqueConstraint
        if (cmd.getUniqueMetaData() != null && cmd.getUniqueMetaData().length > 0) {
            throw new NucleusUserException("@UniqueConstraint is not supported by Force.com datastore."
                                            + " Offending entity: " + cmd.getFullClassName());
        }
        if (cmd.getInheritanceMetaData() != null) {
            // We only support SingleTable inheritance strategy
            if (cmd.getInheritanceMetaData().getStrategyForTree() != null
                    && !cmd.getInheritanceMetaData().getStrategyForTree().equals("SINGLE_TABLE")) {
                throw new NucleusUserException("Only SINGLE_TABLE inheritance strategy supported by Force.com datastore."
                                                + " Offending entity: " + cmd.getFullClassName());
            }
            // We do not support Attribute or Operation overrides
            if (cmd.getOverriddenMembers() != null && cmd.getOverriddenMembers().length > 0) {
                throw new NucleusUserException("@AttributeOverride or @AssociationOverride"
                                                + " is not supported by Force.com datastore."
                                                + " Offending entity: " + cmd.getFullClassName());
            }
        }
       
        AbstractClassMetaData superClass = cmd.getSuperAbstractClassMetaData();
        // This means we're expecting the super class to be persisted to its own table
        if (superClass != null && superClass.getInheritanceMetaData().getStrategy() != InheritanceStrategy.SUBCLASS_TABLE) {
           
            // We only allow discriminator column in base class
            if (cmd.getDiscriminatorMetaData() != null && cmd.getDiscriminatorMetaData().getColumnMetaData() != null) {
                throw new NucleusUserException("@DiscriminatorColumn is only supported for base entity classes. "
                        + "Offending entity: " + cmd.getFullClassName() + " Super class: " + superClass.getFullClassName());
            }
            // We do not allow table rename on subclasses
            if (cmd.getTable() != null) {
                throw new NucleusUserException("@Table is only supported for base entity classes. "
                        + "Offending entity: " + cmd.getFullClassName() + " Super class: " + superClass.getFullClassName());
            }
            // We cannot have an ID on subclass, there is already one on the base class
            if (cmd.getPKMemberPositions().length > 1) {
                throw new NucleusUserException("@Id is only supported for base entity classes. "
                        + "Offending entity: " + cmd.getFullClassName() + " Super class: " + superClass.getFullClassName());
            }
        } else if (superClass == null && !cmd.isEmbeddedOnly()) {
            // This section is for non-Embedded non-Subclasses i.e. Baseclasses with IDs
            // id field has to be single string and called id
            if (cmd.getPKMemberPositions().length != 1) {
                throw new NucleusUserException("Only single string column primary keys supported as ID by Force.com datastore."
                                                + " Offending entity: " + cmd.getFullClassName());
            } else {
                AbstractMemberMetaData ammd = cmd.getMetaDataForManagedMemberAtPosition(cmd.getPKMemberPositions()[0]);
                checkColumnOrFieldName("id", ammd, ammd.getName(), String.class,
                        "Primay field name should be ID. Offending entity: ", "ID field type should be String."
                        + " Offending entity: ", cmd);
               
                // The only supported strategy is Identity
                if (ammd.getValueStrategy() == null || ammd.getValueStrategy() != IdentityStrategy.IDENTITY) {
                    throw new NucleusUserException("@Id column requires value generation"
                                                    + " @GeneratedValue(strategy = GenerationType.IDENTITY)."
                                                    + " Offending entity: " + cmd.getFullClassName());
                }
            }
        }

        // Embedded only objects cannot have a table specified
        if (cmd.isEmbeddedOnly() && cmd.getTable() != null) {
            throw new NucleusUserException("Embedded objects cannot have table specification."
                                            + " Offending entity: " + cmd.getFullClassName());
        }
        // Version field must be called lastModified and have type Calendar or GregorianCalendar
        if (cmd.getVersionMetaData() != null) {
            checkColumnOrFieldName("lastModifiedDate", cmd.getMetaDataForMember(cmd.getVersionMetaData().getFieldName()),
                cmd.getVersionMetaData().getFieldName(), Calendar.class,
                "Version field name should be lastModifiedDate. Offending entity: ",
                "Version field type should be Calendar or GregorianCalendar. Offending entity: ", cmd);
        }
    }
   
    private static void checkColumnOrFieldName(String expectedName, AbstractMemberMetaData ammd, String fieldName, Class<?> clazz,
            String message1, String message2, AbstractClassMetaData cmd) {
        ColumnMetaData[] columnMD = ammd.getColumnMetaData();
        if (columnMD != null && columnMD.length > 0 && columnMD[0].getName() != null) {
            if (!expectedName.equalsIgnoreCase(columnMD[0].getName())) {
                throw new NucleusUserException(message1 + cmd.getFullClassName());
            }
        } else if (!expectedName.equalsIgnoreCase(fieldName)) {
            throw new NucleusUserException(message1 + cmd.getFullClassName());
        }
        if (!clazz.isAssignableFrom(ammd.getType())) {
            throw new NucleusUserException(message2 + cmd.getFullClassName());
        }
    }
   
    private void registerObjectSchema(AbstractClassMetaData cmd) {
        // We skip embedded and MappedSuperclass as they will inherit schema from the base class
        if (PersistenceUtils.hasNoSchema(cmd)) return;
       
        if (PersistenceUtils.isVirtualSchema(cmd)) {
            storeManager.addVirtualTable(cmd);
            return;
        }
       
        ForceManagedConnection mconn = null;
        try {
            mconn = storeManager.createConnection();
            storeManager.addTable(cmd, mconn);
        } catch (Exception e) {
            throw new NucleusException(e.getMessage(), e);
        } finally {
            if (mconn != null) {
                mconn.close();
            }
        }
    }
}
TOP

Related Classes of com.force.sdk.jpa.ForceMetaDataListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.