Package com.force.sdk.jpa

Source Code of com.force.sdk.jpa.ForceMetaDataManager$ClassInitializer

/**
* 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 static com.force.sdk.jpa.ForceEntityManager.LOGGER;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.OMFContext;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.jpa.metadata.JPAMetaDataManager;
import org.datanucleus.metadata.*;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;

import com.force.sdk.jpa.model.ForceOwner;
import com.force.sdk.jpa.table.ColumnImpl;
import com.force.sdk.jpa.table.TableImpl;
import com.sforce.ws.ConnectionException;

/**
* Custom Metadata Manager so we can control the timing of Force.com object and field creation.
*
* @author Jill Wetzler
*/
public class ForceMetaDataManager extends JPAMetaDataManager {
   
    /**
     * Creates the metadata manager.
     *
     * @param ctxt the object manager factory context
     */
    public ForceMetaDataManager(OMFContext ctxt) {
        super(ctxt);
    }
   
    /**
     * Loads and syncs schema for all persistence units.  The call to the super class will do the main work --
     * everything DataNucleus needs plus the creation of custom SObjects in the user's Force.com organization. The second part
     * is to create the custom fields on each object.  We have to do these separately because the order of object
     * creation is not guaranteed, and this way we can be sure that all lookup fields will be referring to an object
     * that already exists
     * {@inheritDoc}
     */
    @Override
    public FileMetaData[] loadPersistenceUnit(PersistenceUnitMetaData pumd, ClassLoader loader) {
        // Check for a custom force connection name
        if (!omfContext.getPersistenceConfiguration().hasProperty("force.ConnectionName")) {
           
            // Default the force connection name to the persistence unit name.  This will be used to
            // potentially look up connection information (see ForceConnectionFactory.createManagedConnection)
            if (pumd.getName() != null && pumd.getName().length() != 0) {
                omfContext.getPersistenceConfiguration().setProperty("force.ConnectionName", pumd.getName());
            } else if (omfContext.getPersistenceConfiguration().getStringProperty("datanucleus.ConnectionUrl") == null) {
                throw new NucleusUserException("Must specify unit name or connection url");
            }
        }

        ForceStoreManager storeManager = (ForceStoreManager) omfContext.getStoreManager();
        /**
         * DataNucleus does not automatically initialize classes from jars.
         * When we are working with schema and we are using an existing artifact jar we have to
         * forceDN to look at the classes in the jar. To do that, we add the jar into the {@code PersistenceUnitMetaData}
         * as if the jar was included there. However, we should only add the jar if the persistence unit does not already
         * contain explicitly provided classes.
         */
        if ((pumd.getClassNames() == null || pumd.getClassNames().size() == 0)
                && "jar".equals(pumd.getRootURI().getScheme())) {
            try {
                String path = pumd.getRootURI().getSchemeSpecificPart();
                pumd.addJarFile(new URL(path.substring(0, path.length() - 1)));
            } catch (MalformedURLException ue) {
                throw new NucleusUserException(ue.getMessage());
            }
        }
        if (loader == null) {
            loader = Thread.currentThread().getContextClassLoader();
        }
       
        // The ForceOwner entity is provided by force-jpa
        // so make sure it is loaded for all persistence units
        pumd.addClassName(ForceOwner.class.getName());
        FileMetaData[] fileMD = super.loadPersistenceUnit(pumd, loader);
       
        for (FileMetaData md : fileMD) {
            for (int i = 0; i < md.getNoOfPackages(); i++) {
                PackageMetaData pmd = md.getPackage(i);
                for (int j = 0; j < pmd.getNoOfClasses(); j++) {
                    ClassLoaderResolver clr = omfContext.getClassLoaderResolver(null);
                    ClassMetaData cmd = pmd.getClass(j);
                    Class c = null;
                    String className = cmd.getFullClassName();
                    try {
                        if (loader == null) {
                            c = Class.forName(className);
                        } else {
                            c = clr.classForName(className, null, false);
                        }
                    } catch (ClassNotFoundException e) {
                        throw new NucleusException(e.getMessage());
                    }
                   
                    try {
                        createSchema(cmd, clr.classForName(cmd.getFullClassName(), c.getClassLoader()), clr, storeManager);
                    } catch (NucleusException ne) {
                        throw ne;
                    }
                }
            }
        }
       
        // Emit all metadata
        new ClassInitializer() {
            @Override
            void init(AbstractClassMetaData cmd, ForceStoreManager storeManager, ForceManagedConnection mconn)
                throws ConnectionException {
               
                TableImpl table = storeManager.getTable(cmd);
                table.getMetaData(cmd).emit(storeManager, mconn);
            }
        } .initialize(fileMD, storeManager);
       
        // now do the writing
        try {
            storeManager.getSchemaWriter().write(storeManager.createConnection());
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            } else {
                throw new NucleusException(e.getMessage(), e);
            }
        }
       
        // Reload new metadata
        new ClassInitializer() {
            @Override
            void init(AbstractClassMetaData cmd, ForceStoreManager storeManager, ForceManagedConnection mconn)
                throws ConnectionException {
               
                TableImpl table = storeManager.getTable(cmd);
                if (!table.isValid()) {
                    table.refresh(null, mconn);
                }
            }
        } .initialize(fileMD, storeManager);
       
        for (FileMetaData md : fileMD) {
            for (int i = 0; i < md.getNoOfPackages(); i++) {
                PackageMetaData pmd = md.getPackage(i);
                for (int j = 0; j < pmd.getNoOfClasses(); j++) {
                    ClassMetaData cmd = pmd.getClass(j);
                    if (PersistenceUtils.hasNoSchema(cmd) && cmd.getTable() == null) {
                        continue;
                    }
                    try {
                        populateFieldNames(cmd, getOMFContext());
                    } catch (ConnectionException ex) {
                        throw new NucleusException("Exception during initialization", ex);
                    }
                }
            }
        }
       
        return fileMD;
    }
   
    /**
     * Creates all the CustomObjects and CustomFields and registers them with the {@code SchemaWriter}.
     * The SchemaWriter's {@code write()} call is made after calling this method.
     */
    private void createSchema(final ClassMetaData cmd, Class cls, final ClassLoaderResolver clr, ForceStoreManager storeManager) {
        synchronized (cmd) {
            ForceManagedConnection mconn = storeManager.createConnection();
            TableImpl table = storeManager.getTable(cmd);
           
            if (PersistenceUtils.hasNoSchema(cmd)) return; //nothing to create
           
            // only create the table if autoCreateTables is true.
            // create the fields without creating the table if the object is already in the organization
            if (storeManager.isAutoCreateTables() && (!table.getTableAlreadyExistsInOrg()
                    || storeManager.isForDelete()) && !PersistenceUtils.isReadOnlySchema(cmd, true)) {
                table.createTableAndFields(cmd, storeManager, mconn);
            } else if (table.getTableAlreadyExistsInOrg() && !PersistenceUtils.isReadOnlySchema(cmd, false)) {
                table.createFields(cmd, storeManager);
            }
           
            // handle the case where autoCreateTables is disabled but the object does not exist in the organization
            if (!storeManager.isAutoCreateTables() && !table.getTableAlreadyExistsInOrg()) {
                StringBuilder msg = new StringBuilder(256);
                msg.append("Table does not exist in force.com and datanucleus.autoCreateTables is false, table: ")
                   .append(table.getTableName().getForceApiName());
                if (storeManager.isAutoCreateWarnOnError()) {
                    LOGGER.warn(msg.toString());
                } else {
                    throw new NucleusUserException(msg.toString());
                }
            }
        }
    }
   
    /**
     * Initialises the provided FileMetaData so that it's ready for use.
     * We copied this method verbatim from base class only to be
     * able to call the pre/postInitialise methods.
     *
     * @param fileMetaData Collection of {@code FileMetaData}
     * @param clr ClassLoader resolver
     * @throws NucleusUserException thrown if an error occurs during the populate/initialise
     *     of the supplied metadata.
     */
    @Override
    protected void initialiseFileMetaDataForUse(Collection fileMetaData, ClassLoaderResolver clr)
    {
        HashSet<Throwable> exceptions = new HashSet<Throwable>();

        // a). Populate MetaData
        if (NucleusLogger.METADATA.isDebugEnabled()) {
            NucleusLogger.METADATA.debug(LOCALISER.msg("044018"));
        }
        Iterator iter = fileMetaData.iterator();
        while (iter.hasNext()) {
            FileMetaData filemd = (FileMetaData) iter.next();
            if (!filemd.isInitialised()) {
                populateFileMetaData(filemd, clr, null);
            }
        }

        /**
         * Call preInitialise here
         */
        ForceStoreManager storeManager = (ForceStoreManager) omfContext.getStoreManager();
        storeManager.preInitialiseFileMetaData(classMetaDataByClass.values());
       
        // b). Initialise MetaData
        if (NucleusLogger.METADATA.isDebugEnabled()) {
            NucleusLogger.METADATA.debug(LOCALISER.msg("044019"));
        }
        iter = fileMetaData.iterator();
        while (iter.hasNext()) {
            FileMetaData filemd = (FileMetaData) iter.next();
            if (!filemd.isInitialised()) {
                try {
                    initialiseFileMetaData(filemd, clr, null);
                } catch (Exception e) {
                    NucleusLogger.METADATA.error(StringUtils.getStringFromStackTrace(e));
                    exceptions.add(e);
                }
            }
        }
        if (exceptions.size() > 0) {
            throw new NucleusUserException(LOCALISER.msg("044020"),
                exceptions.toArray(new Throwable[exceptions.size()]));
        }
        /**
         * Call postInitialise here
         */
        storeManager.postInitialiseFileMetaData();
    }
   
    private void populateFieldNames(AbstractClassMetaData acmd, OMFContext omf) throws ConnectionException {
        ForceStoreManager storeManager = (ForceStoreManager) omfContext.getStoreManager();
       
        // Grab transactional connection factory
        TableImpl table = storeManager.getTable(acmd);
        // now that we've stored all the Salesforce fields, find the {@code @Entity} definition and register the
        // Java field name on the ColumnImpl.  We'll need this info later
        int[] fieldNumbers =  acmd.getAllMemberPositions();
        if (fieldNumbers != null && fieldNumbers.length > 0) {
            for (int column : fieldNumbers) {
                addColumn(table, acmd.getMetaDataForManagedMemberAtAbsolutePosition(column), omf);
            }
        }
       
        DiscriminatorMetaData dmd = acmd.getDiscriminatorMetaData();
        if (dmd != null && dmd.getColumnName() != null) {
            ColumnImpl col = table.getColumnByForceApiName(dmd.getColumnName());
            if (col != null) {
                String javaFieldName = dmd.getColumnName();
                table.registerJavaColumn(javaFieldName, col);
            }
        }
       
    }
   
    private void addColumn(TableImpl table, AbstractMemberMetaData ammd, OMFContext omf) {
        if (PersistenceUtils.isNonPersistedColumn(ammd)) return;
        if (ammd.getEmbeddedMetaData() != null) {
            for (AbstractMemberMetaData eammd : ammd.getEmbeddedMetaData().getMemberMetaData()) {
                addColumn(table, eammd, omf);
            }
        } else {
            ColumnImpl col = table.getColumnByForceApiName(PersistenceUtils.getForceApiName(ammd, omf));
            if (col != null) {
                String javaFieldName = ammd.getName();
                table.registerJavaColumn(javaFieldName, col);
            }
        }
    }

    /**
     * Inner class for providing initialization methods when entities are first loaded.
     */
    private abstract static class ClassInitializer {

        /**
         * Provides an init method which will be run when the class is initialized.
         *
         * @param cmd the class metadata for the entity being initialized
         * @param storemanager  the store manager
         * @param mconn  managed connection for the Force.com APIs
         * @throws ConnectionException thrown if connecting to the Force.com API fails
         */
        abstract void init(AbstractClassMetaData cmd, ForceStoreManager storemanager, ForceManagedConnection mconn)
            throws ConnectionException;
       
        void initialize(FileMetaData[] fileMD, final ForceStoreManager storeManager) {
            for (FileMetaData md : fileMD) {
                for (int i = 0; i < md.getNoOfPackages(); i++) {
                    PackageMetaData pmd = md.getPackage(i);
                    for (int j = 0; j < pmd.getNoOfClasses(); j++) {
                        ClassMetaData cmd = pmd.getClass(j);
                        if (PersistenceUtils.hasNoSchema(cmd) && cmd.getTable() == null) {
                            continue;
                        }
                        try {
                            ForceManagedConnection mconn = storeManager.createConnection();
                            try {
                                init(cmd, storeManager, mconn);
                            } finally {
                                mconn.close();
                            }
                        } catch (ConnectionException ex) {
                            throw new NucleusException("Exception during initialization", ex);
                        }
                    }
                }
            }
        }
    }
}
TOP

Related Classes of com.force.sdk.jpa.ForceMetaDataManager$ClassInitializer

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.