fr.ign.cogit.geoxygene.datatools.ojb.GeOxygeneBrokerHelper.java Source code

Java tutorial

Introduction

Here is the source code for fr.ign.cogit.geoxygene.datatools.ojb.GeOxygeneBrokerHelper.java

Source

/*
 * This file is part of the GeOxygene project source files. GeOxygene aims at
 * providing an open framework which implements OGC/ISO specifications for the
 * development and deployment of geographic (GIS) applications. It is a open
 * source contribution of the COGIT laboratory at the Institut Gographique
 * National (the French National Mapping Agency). See:
 * http://oxygene-project.sourceforge.net Copyright (C) 2005 Institut
 * Gographique National This library is free software; you can redistribute it
 * and/or modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the License,
 * or any later version. This library is distributed in the hope that it will be
 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details. You should have received a copy of
 * the GNU Lesser General Public License along with this library (see file
 * LICENSE if present); if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

package fr.ign.cogit.geoxygene.datatools.ojb;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.StringTokenizer;

import org.apache.commons.lang.SystemUtils;
import org.apache.log4j.Logger;
import org.apache.ojb.broker.Identity;
import org.apache.ojb.broker.PBKey;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.PersistenceBrokerException;
import org.apache.ojb.broker.VirtualProxy;
import org.apache.ojb.broker.accesslayer.IndirectionHandler;
import org.apache.ojb.broker.core.ValueContainer;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.FieldDescriptor;
import org.apache.ojb.broker.metadata.MetadataException;
import org.apache.ojb.broker.metadata.MetadataManager;
import org.apache.ojb.broker.metadata.fieldaccess.PersistentField;
import org.apache.ojb.broker.platforms.Platform;
import org.apache.ojb.broker.platforms.PlatformFactory;
import org.apache.ojb.broker.platforms.PlatformOracle9iImpl;
import org.apache.ojb.broker.platforms.PlatformOracleImpl;
import org.apache.ojb.broker.platforms.PlatformPostgreSQLImpl;
import org.apache.ojb.broker.query.Criteria;
import org.apache.ojb.broker.query.MtoNQuery;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.query.QueryByCriteria;
import org.apache.ojb.broker.query.QueryBySQL;
import org.apache.ojb.broker.query.ReportQueryByCriteria;
import org.apache.ojb.broker.query.ReportQueryByMtoNCriteria;
import org.apache.ojb.broker.util.sequence.SequenceManagerException;

/**
 * Redefinition de la classe org.apache.ojb.util.BrokerHelper d'OJB, permettant
 * d'appeler une methode "javaToSql(Object, Connection) pour ecrire les
 * structures dans Oracle. Par rapport a la version originale de BrokerHelper :
 * les imports ont ete reorganises, le constructeur renomme, un parametre
 * connection ajoute dans la signature de getValuesForObject (ligne 352), un
 * ajout dans la methode getValuesForObject (ligne 375), un parametre connection
 * ajoute dans getAllRwValues, un parametre connection ajoute dans
 * getNonKeyRwValues, un parametre connection ajoute dans getKeyValues, un
 * parametre connection ajoute dans getKeyValues. Les 4 dernieres modifs se font
 * suite a des erreurs de compile, suite au premier ajout dans
 * getValuesForObject.
 * 
 * AB 11 juillet 2005 : <br>
 * Utilisation des noms de classes et de la rflection pour permettre la
 * compilation spare pour Oracle. <br>
 * Patch pour permettre l'utilisation de la meme classe de "FieldConversion"
 * pour Oracle et Postgis.
 * 
 * @author Thierry Badard & Arnaud Braun
 * @version 1.1
 */
public class GeOxygeneBrokerHelper {

    /**
     * The Logger.
     */
    static Logger logger = Logger.getLogger(GeOxygeneBrokerHelper.class.getName());

    // AJOUT pour GeOxygene --------------------------------------------------
    // Nom des classes relatives  Oracle,
    // en String pour permettre la compilation spare
    private final String GeomGeOxygene2Oracle_CLASS_NAME = "fr.ign.cogit.geoxygene.datatools.oracle" + //$NON-NLS-1$
            ".GeomGeOxygene2Oracle"; //$NON-NLS-1$
    private final String GeomGeOxygene2Postgis_CLASS_NAME = "fr.ign.cogit.geoxygene.datatools.postgis" + //$NON-NLS-1$
            ".GeomGeOxygene2Postgis"; //$NON-NLS-1$
    private Method geomGeOxygene2OracleMethod;
    private Method geomGeOxygene2PostgisMethod;
    // SGBD
    private Platform m_platform;
    // FIN AJOUT pour GeOxygene ----------------------------------------------

    public static final String REPOSITORY_NAME_SEPARATOR = "#"; //$NON-NLS-1$
    private PersistenceBroker m_broker;

    public GeOxygeneBrokerHelper(PersistenceBroker broker) {
        this.m_broker = broker;

        // AJOUT pour GeOxygene ---------------------------------------------
        // Definition du SGBD
        this.m_platform = PlatformFactory
                .getPlatformFor(this.m_broker.serviceConnectionManager().getConnectionDescriptor());

        // ORACLE
        if (this.m_platform instanceof PlatformOracle9iImpl || this.m_platform instanceof PlatformOracleImpl) {
            try {
                Class<?> geomGeOxygene2OracleClass = Class.forName(this.GeomGeOxygene2Oracle_CLASS_NAME);
                this.geomGeOxygene2OracleMethod = geomGeOxygene2OracleClass.getMethod("javaToSql", //$NON-NLS-1$
                        new Class[] { Object.class, Connection.class });
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (this.m_platform instanceof PlatformPostgreSQLImpl) {
            try {
                Class<?> geomGeOxygene2PostgisClass = Class.forName(this.GeomGeOxygene2Postgis_CLASS_NAME);
                this.geomGeOxygene2PostgisMethod = geomGeOxygene2PostgisClass.getMethod("javaToSql", //$NON-NLS-1$
                        new Class[] { Object.class });
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            GeOxygeneBrokerHelper.logger.fatal("Unhandled platform"); //$NON-NLS-1$
            GeOxygeneBrokerHelper.logger.fatal("Program will stop"); //$NON-NLS-1$
            System.exit(0);
        }
        // FIN AJOUT pour GeOxygene ------------------------------------------
    }

    /**
     * splits up the name string and extract db url, user name and password and
     * build a new PBKey instance - the token '#' is used to separate the
     * substrings.
     * @throws PersistenceBrokerException if given name was <code>null</code>
     */
    public static PBKey extractAllTokens(String name) {
        if (name == null) {
            throw new PersistenceBrokerException("Could not extract PBKey, given argument is 'null'"); //$NON-NLS-1$
        }
        String user = null;
        String passwd = null;
        StringTokenizer tok = new StringTokenizer(name, GeOxygeneBrokerHelper.REPOSITORY_NAME_SEPARATOR);
        String dbName = tok.nextToken();
        if (tok.hasMoreTokens()) {
            user = tok.nextToken();
            if (user != null && user.trim().equals("")) { //$NON-NLS-1$
                user = null;
            }
        }
        if (tok.hasMoreTokens()) {
            if (user != null) {
                passwd = tok.nextToken();
            }
        }
        if (user != null && passwd == null) {
            passwd = ""; //$NON-NLS-1$
        }
        PBKey key = new PBKey(dbName, user, passwd);
        return key;
    }

    /**
     * Check if the user of the given PBKey was <code>null</code>, if so we try to
     * get user/password from the jdbc-connection-descriptor matching the given
     * PBKey.getAlias().
     */
    public static PBKey crossCheckPBKey(PBKey key) {
        if (key.getUser() == null) {
            PBKey defKey = MetadataManager.getInstance().connectionRepository()
                    .getStandardPBKeyForJcdAlias(key.getAlias());
            if (defKey != null) {
                return defKey;
            }
        }
        return key;
    }

    /**
     * Answer the real ClassDescriptor for anObj ie. aCld may be an Interface of
     * anObj, so the cld for anObj is returned.
     */
    protected ClassDescriptor getRealClassDescriptor(ClassDescriptor aCld, Object anObj) {
        ClassDescriptor result;
        if (aCld.getClassOfObject() == anObj.getClass()) {
            result = aCld;
        } else {
            result = aCld.getRepository().getDescriptorFor(anObj.getClass());
        }
        return result;
    }

    /**
     * returns an Array with an Objects PK VALUES if convertToSql is true, any
     * associated java-to-sql conversions are applied. If the Object is a Proxy or
     * a VirtualProxy NO conversion is necessary.
     * 
     * @param objectOrProxy
     * @param convertToSql
     * @return Object[]
     * @throws PersistenceBrokerException
     */
    public ValueContainer[] getKeyValues(ClassDescriptor cld, Object objectOrProxy, boolean convertToSql,
            Connection conn) throws PersistenceBrokerException {
        /*
         * arminw Check it out. Because the isProxyClass method is costly and most
         * objects aren't proxies, I add a instanceof check before. Is every Proxy a
         * instance of Proxy?
         */
        if ((objectOrProxy instanceof Proxy) && Proxy.isProxyClass(objectOrProxy.getClass())) {
            IndirectionHandler handler;
            handler = (IndirectionHandler) Proxy.getInvocationHandler(objectOrProxy);
            return this.getKeyValues(cld, handler.getIdentity(), convertToSql); // BRJ:
                                                                                // convert
                                                                                // Identity
        } else if (objectOrProxy instanceof VirtualProxy) {
            IndirectionHandler handler;
            handler = VirtualProxy.getIndirectionHandler((VirtualProxy) objectOrProxy);
            return this.getKeyValues(cld, handler.getIdentity(), convertToSql); // BRJ:
                                                                                // convert
                                                                                // Identity
        } else {
            ClassDescriptor realCld = this.getRealClassDescriptor(cld, objectOrProxy);
            return this.getValuesForObject(realCld.getPkFields(), objectOrProxy, convertToSql, conn);
        }
    }

    /**
     * Return key Values of an Identity.
     * @param cld
     * @param oid
     * @return Object[]
     * @throws PersistenceBrokerException
     */
    public ValueContainer[] getKeyValues(ClassDescriptor cld, Identity oid) throws PersistenceBrokerException {
        return this.getKeyValues(cld, oid, true);
    }

    /**
     * Return key Values of an Identity.
     * @param cld
     * @param oid
     * @param convertToSql
     * @return Object[]
     * @throws PersistenceBrokerException
     */
    public ValueContainer[] getKeyValues(ClassDescriptor cld, Identity oid, boolean convertToSql)
            throws PersistenceBrokerException {
        FieldDescriptor[] pkFields = cld.getPkFields();
        ValueContainer[] result = new ValueContainer[pkFields.length];
        Object[] pkValues = oid.getPrimaryKeyValues();
        try {
            for (int i = 0; i < result.length; i++) {
                FieldDescriptor fd = pkFields[i];
                Object cv = pkValues[i];
                if (convertToSql) {
                    // BRJ : apply type and value mapping
                    cv = fd.getFieldConversion().javaToSql(cv);
                }
                result[i] = new ValueContainer(cv, fd.getJdbcType());
            }
        } catch (Exception e) {
            throw new PersistenceBrokerException("Can't generate primary key values for " + //$NON-NLS-1$
                    "given Identity " //$NON-NLS-1$
                    + oid, e);
        }
        return result;
    }

    /**
     * returns an Array with an Objects PK VALUES, with any java-to-sql
     * FieldConversion applied. If the Object is a Proxy or a VirtualProxy NO
     * conversion is necessary.
     * 
     * @param objectOrProxy
     * @return Object[]
     * @throws PersistenceBrokerException
     */
    public ValueContainer[] getKeyValues(ClassDescriptor cld, Object objectOrProxy, Connection conn)
            throws PersistenceBrokerException {
        return this.getKeyValues(cld, objectOrProxy, true, conn);
    }

    /**
     * Return true if aValue is regarded as null. <br>
     * null, Number(0) or empty String
     * @param aValue
     * @return true if aValue is regarded as null, false otherwise
     */
    private boolean isNull(Object aValue) {
        return ((aValue == null) || ((aValue instanceof Number) && (((Number) aValue).longValue() == 0))
                || ((aValue instanceof String) && (((String) aValue).length() == 0)));
    }

    /**
     * Get an autoincremented value that has already had a field conversion run on
     * it.
     * <p>
     * The data type of the value that is returned by this method is compatible
     * with the java-world. The return value has <b>NOT</b> been run through a
     * field conversion and converted to a corresponding sql-type.
     * 
     * @throws MetadataException if there is an erros accessing obj field values
     */
    protected Object getAutoIncrementValue(FieldDescriptor fd, Object obj, Object cv) {
        if (this.isNull(cv)) {
            PersistentField f = fd.getPersistentField();
            try {
                // lookup SeqMan for a value matching db column an
                // fieldconversion
                Object result = this.m_broker.serviceSequenceManager().getUniqueValue(fd);
                // reflect autoincrement value back into object
                f.set(obj, result);
                return result;
            } catch (MetadataException e) {
                throw new PersistenceBrokerException("Error while trying to autoincrement " + //$NON-NLS-1$
                        "field " //$NON-NLS-1$
                        + f.getDeclaringClass() + "#" //$NON-NLS-1$
                        + f.getName(), e);
            } catch (SequenceManagerException e) {
                throw new PersistenceBrokerException("Could not get key value", //$NON-NLS-1$
                        e);
            }
        }
        return cv;
    }

    /**
     * Get the values of the fields for an obj.
     * @param fields
     * @param obj
     * @throws PersistenceBrokerException
     */
    public ValueContainer[] getValuesForObject(FieldDescriptor[] fields, Object obj, boolean convertToSql,
            Connection conn) throws PersistenceBrokerException {
        ValueContainer[] result = new ValueContainer[fields.length];

        for (int i = 0; i < fields.length; i++) {
            FieldDescriptor fd = fields[i];
            Object cv = fd.getPersistentField().get(obj);

            // handle autoincrement attributes if not filled
            if (fd.isAutoIncrement()) {
                // getAutoIncrementValue returns a value that is
                // properly typed for the java-world. This value
                // needs to be converted to it's corresponding
                // sql type so that the entire result array contains
                // objects that are properly typed for sql.
                cv = this.getAutoIncrementValue(fd, obj, cv);
            }
            if (convertToSql) {
                // apply type and value conversion

                // DEBUT AJOUT POUR GeOxygene ----------------------
                // Gestion des gomtrie
                if (fd.getFieldConversion() instanceof GeomGeOxygene2Dbms) {
                    // ORACLE
                    if (this.m_platform instanceof PlatformOracle9iImpl
                            || this.m_platform instanceof PlatformOracleImpl) {
                        try {
                            cv = this.geomGeOxygene2OracleMethod.invoke(fd.getFieldConversion(),
                                    new Object[] { cv, conn });
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } // POSTGIS
                    if (this.m_platform instanceof PlatformPostgreSQLImpl) {
                        try {
                            cv = this.geomGeOxygene2PostgisMethod.invoke(fd.getFieldConversion(),
                                    new Object[] { cv });
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }

                } else {
                    // FIN AJOUT POUR GeOxygene-----------------------------
                    // Types non gomtriques
                    cv = fd.getFieldConversion().javaToSql(cv);
                }
            }
            // create ValueContainer
            result[i] = new ValueContainer(cv, fd.getJdbcType());
        }
        return result;
    }

    /**
     * returns an Array with an Objects NON-PK VALUES (READ/WRITE only).
     * @throws MetadataException if there is an erros accessing o field values
     */
    public ValueContainer[] getNonKeyRwValues(ClassDescriptor cld, Object obj, Connection conn)
            throws PersistenceBrokerException {
        ClassDescriptor realCld = this.getRealClassDescriptor(cld, obj);
        return this.getValuesForObject(realCld.getNonPkRwFields(), obj, true, conn);
    }

    /**
     * returns an array containing values for all the Objects attribute
     * (READ/WRITE only).
     * @throws MetadataException if there is an erros accessing obj field values
     */
    public ValueContainer[] getAllRwValues(ClassDescriptor cld, Object obj, Connection conn)
            throws PersistenceBrokerException {
        ClassDescriptor realCld = this.getRealClassDescriptor(cld, obj);
        return this.getValuesForObject(realCld.getAllRwFields(), obj, true, conn);
    }

    /**
     * Extract a value array of the given {@link ValueContainer} array.
     * @param containers
     * @return a value array
     */
    public Object[] extractValueArray(ValueContainer[] containers) {
        Object[] result = new Object[containers.length];
        for (int i = 0; i < containers.length; i++) {
            result[i] = containers[i].getValue();
        }
        return result;
    }

    /**
     * returns true if the primary key fields are valid, else false. PK fields are
     * valid if each of them is either an OJB managed attribute (autoincrement or
     * locking) or if it contains a valid non-null value
     * @param fieldDescriptors the array of PK fielddescriptors
     * @param pkValues the array of PK values
     * @return boolean
     */
    public boolean assertValidPkFields(FieldDescriptor[] fieldDescriptors, Object[] pkValues) {
        int fieldDescriptorSize = fieldDescriptors.length;
        for (int i = 0; i < fieldDescriptorSize; i++) {
            /*
             * a pk field is valid if it is either managed by OJB (autoincrement or
             * locking) or if it does contain a valid non-null value.
             */
            if (!(fieldDescriptors[i].isAutoIncrement() || fieldDescriptors[i].isLocking()
                    || this.assertValidPkValue(pkValues[i]))) {
                return false;
            }
        }
        return true;
    }

    /**
     * returns true if a value is non-null, STring instances are also checked, if
     * they are non-empty.
     * @param pkValue the value to check
     * @return boolean
     */
    private boolean assertValidPkValue(Object pkValue) {
        // null as value of a primary key is not acceptable
        if (pkValue == null) {
            return false;
        }
        if (pkValue instanceof String) {
            // the toString() method on a String-object is maybe faster
            // than the downcast to String. Also use length() to test
            // if a String empty or not, this is faster than the comparing
            // a String-object with an empty string using the equals()-method.
            if (pkValue.toString().trim().length() == 0) {
                return false;
            }
        }
        return true;
    }

    /**
     * Build a Count-Query based on aQuery.
     * @param aQuery
     * @return a Query
     */
    public Query getCountQuery(Query aQuery) {
        if (aQuery instanceof QueryBySQL) {
            return this.getCountQuery((QueryBySQL) aQuery);
        }
        return this.getCountQuery((QueryByCriteria) aQuery);
    }

    /**
     * Create a Count-Query for QueryBySQL.
     * @param aQuery
     * @return Count-Query for QueryBySQL
     */
    private Query getCountQuery(QueryBySQL aQuery) {
        String countSql = aQuery.getSql();
        int fromPos = countSql.toUpperCase().indexOf(" FROM "); //$NON-NLS-1$
        if (fromPos >= 0) {
            countSql = "select count(*)" //$NON-NLS-1$
                    + countSql.substring(fromPos);
        }
        int orderPos = countSql.toUpperCase().indexOf(" ORDER BY "); //$NON-NLS-1$
        if (orderPos >= 0) {
            countSql = countSql.substring(0, orderPos);
        }
        return new QueryBySQL(aQuery.getSearchClass(), countSql);
    }

    /**
     * Create a Count-Query for QueryByCriteria.
     * @param aQuery
     * @return Count-Query for QueryByCriteria
     */
    private Query getCountQuery(QueryByCriteria aQuery) {
        Class<?> searchClass = aQuery.getSearchClass();
        ReportQueryByCriteria countQuery;
        Criteria countCrit = null;
        FieldDescriptor[] pkFields = this.m_broker.getClassDescriptor(searchClass).getPkFields();
        String[] columns = new String[pkFields.length];

        // build a ReportQuery based on query orderby needs to be cleared
        if (aQuery.getCriteria() != null) {
            countCrit = aQuery.getCriteria().copy(false, false, false);
        }

        // BRJ: add a column for each pkField, make it distinct if query is
        // distinct
        // TBD check if it really works for multiple keys ?
        for (int i = 0; i < pkFields.length; i++) {
            if (aQuery.isDistinct()) {
                columns[i] = "count(distinct " //$NON-NLS-1$
                        + pkFields[i].getAttributeName() + ")"; //$NON-NLS-1$
            } else {
                columns[i] = "count(" //$NON-NLS-1$
                        + pkFields[i].getAttributeName() + ")"; //$NON-NLS-1$
            }
        }
        // BRJ: we have to preserve indirection table !
        if (aQuery instanceof MtoNQuery) {
            MtoNQuery mnQuery = (MtoNQuery) aQuery;
            ReportQueryByMtoNCriteria mnReportQuery = new ReportQueryByMtoNCriteria(searchClass, columns,
                    countCrit);
            mnReportQuery.setIndirectionTable(mnQuery.getIndirectionTable());
            countQuery = mnReportQuery;
        } else {
            countQuery = new ReportQueryByCriteria(searchClass, columns, countCrit);
        }
        return countQuery;
    }

    public static String buildMessageString(Object obj, Object value, Field field) {
        String eol = SystemUtils.LINE_SEPARATOR;
        StringBuffer buf = new StringBuffer();
        buf.append(eol + "object class[ " //$NON-NLS-1$
                + (obj != null ? obj.getClass().getName() : null)).append(eol + "target field: " //$NON-NLS-1$
                        + (field != null ? field.getName() : null))
                .append(eol + "target field type: " //$NON-NLS-1$
                        + (field != null ? field.getType() : null))
                .append(eol + "object value class: " //$NON-NLS-1$
                        + (value != null ? value.getClass().getName() : null))
                .append(eol + "object value: " //$NON-NLS-1$
                        + (value != null ? value : null))
                .append("]"); //$NON-NLS-1$
        return buf.toString();
    }
}