net.sf.farrago.namespace.sfdc.SfdcDataServer.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.farrago.namespace.sfdc.SfdcDataServer.java

Source

/*
// Licensed to DynamoBI Corporation (DynamoBI) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  DynamoBI 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 net.sf.farrago.namespace.sfdc;

import com.sforce.soap.partner.*;
import com.sforce.soap.partner.fault.*;

import java.net.URL;

import java.rmi.RemoteException;

import java.sql.SQLException;

import java.util.*;

import javax.xml.rpc.ServiceException;

import net.sf.farrago.namespace.*;
import net.sf.farrago.namespace.impl.MedAbstractDataServer;
import net.sf.farrago.namespace.sfdc.resource.*;
import net.sf.farrago.type.FarragoTypeFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.eigenbase.oj.rel.*;
import org.eigenbase.rel.*;
import org.eigenbase.relopt.*;
import org.eigenbase.reltype.*;
import org.eigenbase.sql.type.*;

/**
 * SfdcDataServer provides an implementation of the {@link FarragoMedDataServer}
 * interface.
 *
 * @author Sunny Choi
 * @version $Id$
 */
class SfdcDataServer extends MedAbstractDataServer {
    //~ Static fields/initializers ---------------------------------------------

    // ~ Static fields/initializers --------------------------------------------

    public static final String PROP_USER_NAME = "USER_NAME";
    public static final String PROP_PASSWORD = "PASSWORD";
    public static final String PROP_EXTRA_VARCHAR_PRECISION = "VARCHAR_FIELD_EXTRA_PRECISION";
    public static final String PROP_ENDPOINT_URL = "ENDPOINT_URL";

    protected static final int DEFAULT_EXTRA_VARCHAR_PRECISION = 128;
    protected static final Log log = LogFactory.getLog(SfdcDataServer.class);

    //~ Instance fields --------------------------------------------------------

    // ~ Instance fields -------------------------------------------------------

    String username;
    String password;
    int varcharPrecision = DEFAULT_EXTRA_VARCHAR_PRECISION;
    String endpoint;
    SoapBindingStub binding;

    private Date nextLoginTime;

    //~ Constructors -----------------------------------------------------------

    // ~ Constructors ----------------------------------------------------------

    SfdcDataServer(String serverMofId, Properties props) {
        super(serverMofId, props);
    }

    //~ Methods ----------------------------------------------------------------

    // ~ Methods ---------------------------------------------------------------

    void initialize() throws SQLException {
        Properties props = getProperties();
        this.username = props.getProperty(PROP_USER_NAME);
        this.password = props.getProperty(PROP_PASSWORD);
        this.endpoint = props.getProperty(PROP_ENDPOINT_URL);
        try {
            int precision = Integer.parseInt(props.getProperty(PROP_EXTRA_VARCHAR_PRECISION));
            if (precision >= 0) {
                this.varcharPrecision = precision;
            }
        } catch (NumberFormatException ne) {
            // ignore
        }
        nextLoginTime = new Date();
        login(this.username, this.password, null);
    }

    // implement FarragoMedDataServer
    public FarragoMedNameDirectory getNameDirectory() throws SQLException {
        return new SfdcNameDirectory(this, FarragoMedMetadataQuery.OTN_SCHEMA);
    }

    // implement FarragoMedDataServer
    public FarragoMedColumnSet newColumnSet(String[] localName, Properties tableProps,
            FarragoTypeFactory typeFactory, RelDataType rowType, Map columnPropMap) throws SQLException {
        String objectName = tableProps.getProperty(SfdcColumnSet.PROP_OBJECT);
        if (objectName == null) {
            objectName = getObjectName(localName);
        }

        String updatedFields = null;
        String updatedTypes = null;
        RelDataType srcRowType = deriveRowType(typeFactory, objectName);
        RelDataType mappedRowType = srcRowType;

        if (rowType == null) {
            rowType = mappedRowType;
        } else {
            if (mappedRowType != null) {
                Object[] updatedRowType = updateRowType(typeFactory, rowType, mappedRowType);
                mappedRowType = (RelDataType) updatedRowType[0];
                updatedFields = (String) updatedRowType[1];
                updatedTypes = (String) updatedRowType[2];
            }
        }

        if (mappedRowType == null) {
            return null;
        }

        boolean getAllFields = false;
        String fields = null;
        if (updatedFields == null) {
            fields = getFields(objectName);
        } else {
            String[] updatedFieldsArray = updatedFields.split(",");
            if (!SfdcPushDownRule.validProjection(updatedFieldsArray)) {
                getAllFields = true;
                fields = getFields(objectName);
                mappedRowType = deriveRowType(typeFactory, objectName);
            } else {
                fields = updatedFields;
            }
        }

        String types = null;
        if ((updatedTypes == null) || getAllFields) {
            types = getTypes(objectName, typeFactory);
        } else {
            types = updatedTypes;
        }
        return new SfdcColumnSet(this, localName, mappedRowType, rowType, srcRowType, objectName, fields, types);
    }

    // implement FarragoMedDataServer
    public Object getRuntimeSupport(Object param) throws SQLException {
        // minimum session timeout is 30 minutes.
        // Re-login every 20 minutes.
        if (param == null) {
            Date now = new Date();
            if ((this.binding == null) || (nextLoginTime.getTime() < now.getTime())) {
                login(this.username, this.password, null);
            }
            return this.binding;
        } else {
            if (param instanceof SoapBindingStub) {
                login(this.username, this.password, (SoapBindingStub) param);
                return this.binding;
            }
            return this;
        }
    }

    // implement FarragoMedDataServer
    public void registerRules(RelOptPlanner planner) {
        super.registerRules(planner);

        // delete rules
        planner.addRule(new SfdcDeleteRule(
                new RelOptRuleOperand(FilterRel.class,
                        new RelOptRuleOperand[] { new RelOptRuleOperand(ProjectRel.class,
                                new RelOptRuleOperand[] { new RelOptRuleOperand(SfdcUdxRel.class) }) }),
                "filter on proj"));

        planner.addRule(new SfdcDeleteRule(new RelOptRuleOperand(FilterRel.class,
                new RelOptRuleOperand[] { new RelOptRuleOperand(SfdcUdxRel.class) }), "filter"));

        // pushdown rules

        // case 1: projection on top of a filter (with push down projection)
        // ie: filtering on variables which are not in projection
        planner.addRule(new SfdcPushDownRule(
                new RelOptRuleOperand(ProjectRel.class,
                        new RelOptRuleOperand[] { new RelOptRuleOperand(FilterRel.class,
                                new RelOptRuleOperand[] { new RelOptRuleOperand(ProjectRel.class,
                                        new RelOptRuleOperand[] { new RelOptRuleOperand(SfdcUdxRel.class) }) }) }),
                "proj on filter on proj"));

        // case 2: filter with push down projection
        // ie: proj only has values which are already in filter expression
        planner.addRule(new SfdcPushDownRule(
                new RelOptRuleOperand(FilterRel.class,
                        new RelOptRuleOperand[] { new RelOptRuleOperand(ProjectRel.class,
                                new RelOptRuleOperand[] { new RelOptRuleOperand(SfdcUdxRel.class) }) }),
                "filter on proj"));

        // case 3: filter with no projection to push down.
        // ie: select *
        planner.addRule(new SfdcPushDownRule(new RelOptRuleOperand(FilterRel.class,
                new RelOptRuleOperand[] { new RelOptRuleOperand(SfdcUdxRel.class) }), "filter"));

        // case 4: only projection, no filter
        planner.addRule(new SfdcPushDownRule(new RelOptRuleOperand(ProjectRel.class,
                new RelOptRuleOperand[] { new RelOptRuleOperand(SfdcUdxRel.class) }), "proj"));
    }

    // implement FarragoAllocation
    public void closeAllocation() {
        super.closeAllocation();
    }

    RelDataType createRowType(FarragoTypeFactory typeFactory, RelDataType[] types, String[] names) {
        return typeFactory.createStructType(types, names);
    }

    public String getClientIdFromJNDI()
    // throws NamingException
    {
        String clientId = "";

        /*
         * Context initCtx = new InitialContext(); try { Context envCtx =
         * (Context) initCtx.lookup(LeJndiNames.LE_JNDI_LOOKUP_NAME); clientId =
         * (String) envCtx.lookup(LeJndiNames.SFDC_CLIENT_ID); } catch
         * (NamingException e) { log.warn("Unable to read " +
         * LeJndiNames.SFDC_CLIENT_ID +
         * " from JNDI.  This had better be a development environment."); }
         */
        return clientId;
    }

    private void login(String user, String pass, SoapBindingStub bStub) {
        if (bStub != null) {
            this.binding = bStub;
        } else {
            try {
                if ((this.endpoint != null) && !this.endpoint.trim().equals("")) {
                    this.binding = (SoapBindingStub) new ServiceLocatorGzip().getSoap(new URL(this.endpoint));
                } else {
                    this.binding = (SoapBindingStub) new ServiceLocatorGzip().getSoap();
                }
            } catch (ServiceException se) {
                throw SfdcResource.instance().SfdcBinding_ServiceException.ex(se.getMessage());
            } catch (Exception ex) {
                log.error("Error logging into SFDC", ex);
                throw SfdcResource.instance().SfdcLoginFault.ex(ex.toString());
            }
        }

        // Set timeout 2 hours
        this.binding.setTimeout(1000 * 60 * 60 * 2);

        LoginResult loginResult = null;
        try {
            /*
             * EncryptDecryptUtil encryptDecryptUtil =
             * EncryptDecryptUtil.getInstance(); // no encryption during test if
             * (encryptDecryptUtil.isEncrypted(pass)) {
             * encryptDecryptUtil.initKeyFromJNDI(); pass =
             * encryptDecryptUtil.decrypt(pass); } if
             * (encryptDecryptUtil.isEncrypted(user)) {
             * encryptDecryptUtil.initKeyFromJNDI(); user =
             * encryptDecryptUtil.decrypt(user); }
             */
            // AppExchange API ClientID
            // CallOptions co = new CallOptions();
            // co.setClient(getClientIdFromJNDI());
            // binding.setHeader(new SforceServiceLocator().getServiceName()
            // .getNamespaceURI(), "CallOptions", co);

            loginResult = this.binding.login(user, pass);
            log.info(SfdcResource.instance().LoggedInMsg.str());
        } catch (LoginFault lf) {
            throw SfdcResource.instance().SfdcLoginFault.ex(lf.getExceptionMessage());
        } catch (UnexpectedErrorFault uef) {
            throw SfdcResource.instance().SfdcLoginFault.ex(uef.getExceptionMessage());
        } catch (RemoteException re) {
            throw SfdcResource.instance().SfdcLoginFault.ex(re.toString());
        } catch (Exception ex) {
            log.error("Error logging into SFDC", ex);
            throw SfdcResource.instance().SfdcLoginFault.ex(ex.toString());
        }

        // set the session header for subsequent call authentication
        binding._setProperty(SoapBindingStub.ENDPOINT_ADDRESS_PROPERTY, loginResult.getServerUrl());

        // Create a new session header object and set the session id to that
        // returned by the login
        SessionHeader sh = new SessionHeader();
        sh.setSessionId(loginResult.getSessionId());
        binding.setHeader(new SforceServiceLocator().getServiceName().getNamespaceURI(), "SessionHeader", sh);
        Date now = new Date();
        nextLoginTime = new Date(now.getTime() + (20 * 60 * 1000));
    }

    private String getFields(String objectName) throws SQLException {
        if (objectName.endsWith("_LOV") || objectName.endsWith("_deleted")) {
            return null;
        }
        String allFieldNames = null;
        try {
            DescribeSObjectResult describeSObjectResult = getEntityDescribe(objectName);

            // check the name
            if ((describeSObjectResult != null) && describeSObjectResult.getName().equals(objectName)) {
                com.sforce.soap.partner.Field[] fields = describeSObjectResult.getFields();
                for (int i = 0; i < fields.length; i++) {
                    if (i == 0) {
                        allFieldNames = fields[i].getName();
                    } else {
                        allFieldNames = allFieldNames + "," + fields[i].getName();
                    }
                }
            } else {
                throw SfdcResource.instance().InvalidObjectException.ex(objectName);
            }
        } catch (InvalidSObjectFault io) {
            throw SfdcResource.instance().InvalidObjectException.ex(objectName);
        } catch (RemoteException re) {
            throw SfdcResource.instance().QueryException.ex(objectName, re.toString());
        }
        return allFieldNames;
    }

    private String getTypes(String objectName, FarragoTypeFactory typeFactory) throws SQLException {
        if (objectName.endsWith("_LOV") || objectName.endsWith("_deleted")) {
            return null;
        }
        String[] fieldNames = null;
        String types = null;
        try {
            DescribeSObjectResult describeSObjectResult = getEntityDescribe(objectName);

            // check the name
            if ((describeSObjectResult != null) && describeSObjectResult.getName().equals(objectName)) {
                com.sforce.soap.partner.Field[] allFields = describeSObjectResult.getFields();
                fieldNames = new String[allFields.length];
                for (int i = 0; i < allFields.length; i++) {
                    fieldNames[i] = allFields[i].getName();
                    if (i == 0) {
                        types = ((SfdcNameDirectory) getNameDirectory()).toRelType(allFields[i], typeFactory)
                                .toString();
                    } else {
                        types = types + "," + ((SfdcNameDirectory) getNameDirectory())
                                .toRelType(allFields[i], typeFactory).toString();
                    }
                }
            } else {
                throw SfdcResource.instance().InvalidObjectException.ex(objectName);
            }
        } catch (InvalidSObjectFault io) {
            throw SfdcResource.instance().InvalidObjectException.ex(objectName);
        } catch (RemoteException re) {
            throw SfdcResource.instance().QueryException.ex(objectName, re.toString());
        }
        return types;
    }

    private RelDataType deriveRowType(FarragoTypeFactory typeFactory, String objectName) throws SQLException {
        String[] fieldNames = null;
        RelDataType[] types = null;
        try {
            if (objectName.endsWith("_deleted")) {
                types = new RelDataType[] {
                        typeFactory.createTypeWithNullability(
                                typeFactory.createSqlType(SqlTypeName.VARCHAR, 25 + this.varcharPrecision), true),
                        typeFactory.createTypeWithNullability(typeFactory.createSqlType(SqlTypeName.TIMESTAMP,
                                SqlTypeName.TIMESTAMP.getDefaultPrecision()), true) };
                fieldNames = new String[] { "Id", "DeleteStamp" };
            } else if (objectName.endsWith("_LOV")) {
                types = new RelDataType[] {
                        typeFactory.createTypeWithNullability(
                                typeFactory.createSqlType(SqlTypeName.VARCHAR, 25 + this.varcharPrecision), true),
                        typeFactory.createTypeWithNullability(typeFactory.createSqlType(SqlTypeName.VARCHAR,
                                SfdcNameDirectory.MAX_PRECISION + this.varcharPrecision), true) };
                fieldNames = new String[] { "Field", "Value" };
            } else {
                DescribeSObjectResult describeSObjectResult = getEntityDescribe(objectName);

                // check the name
                if ((describeSObjectResult != null) && describeSObjectResult.getName().equals(objectName)) {
                    com.sforce.soap.partner.Field[] fields = describeSObjectResult.getFields();
                    fieldNames = new String[fields.length];
                    types = new RelDataType[fields.length];
                    for (int i = 0; i < fields.length; i++) {
                        fieldNames[i] = fields[i].getName();
                        types[i] = ((SfdcNameDirectory) getNameDirectory()).toRelType(fields[i], typeFactory);
                    }
                } else {
                    throw SfdcResource.instance().InvalidObjectException.ex(objectName);
                }
            }
        } catch (InvalidSObjectFault io) {
            throw SfdcResource.instance().InvalidObjectException.ex(objectName);
        } catch (RemoteException re) {
            throw SfdcResource.instance().QueryException.ex(objectName, re.toString());
        }

        return createRowType(typeFactory, types, fieldNames);
    }

    private Object[] updateRowType(FarragoTypeFactory typeFactory, RelDataType currRowType,
            RelDataType srcRowType) {
        String fieldNames = "";
        String typeNames = "";
        ArrayList fieldsVector = new ArrayList();
        ArrayList typesVector = new ArrayList();

        HashMap<String, RelDataType> srcMap = new HashMap();
        for (RelDataTypeField srcField : srcRowType.getFieldList()) {
            srcMap.put(srcField.getName(), srcField.getType());
        }

        for (RelDataTypeField currField : currRowType.getFieldList()) {
            RelDataType type;
            if (((type = srcMap.get(currField.getName())) != null)
                    && SqlTypeUtil.canCastFrom(currField.getType(), type, true)) {
                if (fieldNames.equals("")) {
                    fieldNames = currField.getName();
                    typeNames = type.toString();
                } else {
                    fieldNames = fieldNames.concat(",").concat(currField.getName());
                    typeNames = typeNames.concat(",").concat(type.toString());
                }
                fieldsVector.add(currField.getName());
                typesVector.add(type);
            }
        }

        typesVector.trimToSize();
        fieldsVector.trimToSize();
        RelDataType[] types = (RelDataType[]) typesVector.toArray(new RelDataType[typesVector.size()]);
        String[] fields = (String[]) fieldsVector.toArray(new String[fieldsVector.size()]);

        RelDataType rowType = createRowType(typeFactory, types, fields);
        return new Object[] { rowType, fieldNames, typeNames };
    }

    private String getObjectName(String[] localName) {
        assert (localName.length == 3);
        return localName[localName.length - 1];
    }

    /**
     * method to get all SObjects
     */
    public DescribeGlobalResult getEntityTypes() throws RemoteException, SQLException {
        return ((SoapBindingStub) getRuntimeSupport(null)).describeGlobal();
    }

    /**
     * method describing SObjects
     */
    public DescribeSObjectResult getEntityDescribe(String type) throws RemoteException, SQLException {
        return ((SoapBindingStub) getRuntimeSupport(null)).describeSObject(type);
    }

    public int getVarcharPrecision() {
        return this.varcharPrecision;
    }
}

// End SfdcDataServer.java