Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3/3] CONNECT command to switch between Orgs #24

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ jdbc:ascendix:salesforce://;sessionId=uniqueIdAssociatedWithTheSession
1. Queries support native SOQL;
2. Nested queries are supported;
3. Request caching support on local drive. Caching supports 2 modes: global and session. Global mode means that the cached result will be accessible for all system users for certain JVM session. Session cache mode works for each Salesforce connection session separately. Both modes cache stores request result while JVM still running but no longer than for 1 hour. The cache mode can be enabled with a prefix of SOQL query. How to use:
=======
* Global cache mode:
```SQL
CACHE GLOBAL SELECT Id, Name FROM Account
Expand All @@ -83,6 +82,15 @@ jdbc:ascendix:salesforce://;sessionId=uniqueIdAssociatedWithTheSession
```SQL
CACHE SESSION SELECT Id, Name FROM Account
```
4. Reconnect to other organization at the same host
```SQL
-- Postgres Notation
CONNECT USER [email protected] IDENTIFIED by "123456"

-- Oracle Notation
CONNECT [email protected]/123456
```
P.S. You need to use the machine host name in the connection url - not MyDomain org host name.

## Limitations
1. The driver is only for read-only purposes now. Insert/udate/delete functionality is not implemented yet.
Expand All @@ -99,6 +107,21 @@ jdbc:ascendix:salesforce://;sessionId=uniqueIdAssociatedWithTheSession
See how it's done in [Salesforce JDBC report sample](docs/birt/Salesforce JDBC sample.rptdesign)


## Configure IntelliJ to use Salesforce JDBC driver

1. [How to add a JDBC driver](https://www.jetbrains.com/help/idea/data-sources-and-drivers-dialog.html)
2. How to set configuration properties for Salesforce JDBC driver.

IntelliJ provides various ways to set parameters for JDBC driver. For example, it can be done with the property binding feature in the data source editor and a report parameter.
Example JDBC Url:

```jdbc:ascendix:salesforce://[email protected]:[email protected]:6109?https=false&api=48.0```

Please check what kind of access do you have to your org - HTTP or HTTPS and the API version to use.
Here is screenshot about results output and autocomplete support for SOQL queries in IntelliJ:

![image](/docs/Autocomplete-SOQL.png)


### Sponsors
[Ascendix Technologies Inc.](https://ascendix.com/) <img src="http://ww1.prweb.com/prfiles/2006/12/12/490667/ascendixlogo.jpg" width=100 align="right"/>
Expand Down
Binary file added docs/Autocomplete-SOQL.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand All @@ -26,13 +27,17 @@
@Slf4j
public class ForceDriver implements Driver {

private static final String SF_JDBC_DRIVER_NAME = "SF JDBC driver";
private static final Logger logger = Logger.getLogger(SF_JDBC_DRIVER_NAME);

private static final String ACCEPTABLE_URL = "jdbc:ascendix:salesforce";
private static final Pattern URL_PATTERN = Pattern.compile("\\A" + ACCEPTABLE_URL + "://(.*)");
private static final Pattern URL_HAS_AUTHORIZATION_SEGMENT = Pattern.compile("\\A" + ACCEPTABLE_URL + "://([^:]+):([^@]+)@([^?]*)([?](.*))?");
private static final Pattern PARAM_STANDARD_PATTERN = Pattern.compile("(([^=]+)=([^&]*)&?)");

static {
try {
logger.info("[ForceDriver] registration");
DriverManager.registerDriver(new ForceDriver());
} catch (Exception e) {
throw new RuntimeException("Failed register ForceDriver: " + e.getMessage(), e);
Expand All @@ -58,14 +63,47 @@ public Connection connect(String url, Properties properties) throws SQLException
info.setUserName(properties.getProperty("user"));
info.setClientName(properties.getProperty("client"));
info.setPassword(properties.getProperty("password"));
info.setClientName(properties.getProperty("client"));
info.setSessionId(properties.getProperty("sessionId"));
info.setSandbox(resolveSandboxProperty(properties));
info.setHttps(resolveBooleanProperty(properties, "https", true));
info.setApiVersion(resolveStringProperty(properties, "api", ForceService.DEFAULT_API_VERSION));
info.setLoginDomain(resolveStringProperty(properties, "loginDomain", ForceService.DEFAULT_LOGIN_DOMAIN));

PartnerConnection partnerConnection = ForceService.createPartnerConnection(info);
return new ForceConnection(partnerConnection);
return new ForceConnection(partnerConnection, (newUrl, userName, userPassword) -> {
logger.info("[ForceDriver] relogin helper ");
Properties newConnStringProps;
try {
newConnStringProps = getConnStringProperties(newUrl);
} catch (Exception e) {
logger.log(Level.WARNING, "[ForceDriver] relogin helper failed - url parsing error", e);
return null;

}
Properties newProperties = new Properties();
properties.putAll(newConnStringProps);

ForceConnectionInfo newInfo = new ForceConnectionInfo();
newInfo.setUserName(userName);
newInfo.setPassword(userPassword);
newInfo.setClientName(newProperties.getProperty("client"));
newInfo.setSessionId(newProperties.getProperty("sessionId"));
newInfo.setSandbox(resolveSandboxProperty(newProperties));
newInfo.setHttps(resolveBooleanProperty(newProperties, "https", true));
newInfo.setApiVersion(resolveStringProperty(newProperties, "api", ForceService.DEFAULT_API_VERSION));
newInfo.setLoginDomain(resolveStringProperty(newProperties, "loginDomain", ForceService.DEFAULT_LOGIN_DOMAIN));

PartnerConnection newPartnerConnection;
try {
newPartnerConnection = ForceService.createPartnerConnection(newInfo);
logger.log(Level.WARNING, "[ForceDriver] relogin helper success="+(newPartnerConnection != null));
return newPartnerConnection;
} catch (ConnectionException e) {
logger.log(Level.WARNING, "[ForceDriver] relogin helper failed", e);
return null;
}
});
} catch (ConnectionException | IOException e) {
throw new SQLException(e);
}
Expand Down Expand Up @@ -99,8 +137,7 @@ private static String resolveStringProperty(Properties properties, String proper
return defaultValue;
}


protected Properties getConnStringProperties(String urlString) throws IOException {
protected static Properties getConnStringProperties(String urlString) throws IOException {
Properties result = new Properties();
String urlProperties = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.ascendix.jdbc.salesforce.statement.ForcePreparedStatement;
import com.ascendix.jdbc.salesforce.metadata.ForceDatabaseMetaData;
import com.sforce.soap.partner.PartnerConnection;
import com.sforce.ws.ConnectionException;

import java.sql.Array;
import java.sql.Blob;
Expand All @@ -23,33 +24,84 @@
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.function.BiFunction;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ForceConnection implements Connection {

@FunctionalInterface
public interface UpdateLoginFunction {

/**
* Applies this function to the given arguments.
*
* @param url the first function argument
* @param user the second function argument
* @param pass the second function argument
* @return the function result
*/
PartnerConnection apply(String url, String user, String pass);
}

private final PartnerConnection partnerConnection;
/** the updated partner connection in case if we want to support relogin command */
private PartnerConnection partnerConnectionUpdated;
/** the function to provide partner connection in case if we want to support relogin command */
UpdateLoginFunction loginHandler;

private final DatabaseMetaData metadata;
private static final String SF_JDBC_DRIVER_NAME = "SF JDBC driver";
private static final Logger logger = Logger.getLogger(SF_JDBC_DRIVER_NAME);

private Map connectionCache = new HashMap<>();
Properties clientInfo = new Properties();

public ForceConnection(PartnerConnection partnerConnection) {
public ForceConnection(PartnerConnection partnerConnection, UpdateLoginFunction loginHandler) {
this.partnerConnection = partnerConnection;
this.metadata = new ForceDatabaseMetaData(this);
this.loginHandler = loginHandler;
}

public PartnerConnection getPartnerConnection() {
if (partnerConnectionUpdated != null) {
return partnerConnectionUpdated;
}
return partnerConnection;
}

public boolean updatePartnerConnection(String url, String userName, String userPass) {
boolean result = false;
String currentUserName = null;
try {
currentUserName = partnerConnection.getUserInfo().getUserName();
} catch (ConnectionException e) {
}
logger.info("[Conn] updatePartnerConnection IMPLEMENTED newUserName="+userName + " oldUserName="+currentUserName + " newUrl="+url);
if (loginHandler != null) {
try {
PartnerConnection newPartnerConnection = loginHandler.apply(url, userName, userPass);
if (newPartnerConnection != null) {
partnerConnectionUpdated = newPartnerConnection;
logger.info("[Conn] updatePartnerConnection UPDATED to newUserName="+userName);
result = true;
} else {
logger.log(Level.SEVERE, "[Conn] updatePartnerConnection UPDATE FAILED to newUserName="+userName+" currentUserName="+currentUserName);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "[Conn] updatePartnerConnection UPDATE FAILED to newUserName="+userName+" currentUserName="+currentUserName, e);
}
}
return result;
}

public DatabaseMetaData getMetaData() {
return metadata;
}

@Override
public PreparedStatement prepareStatement(String soql) {
logger.info("[Conn] prepareStatement IMPLEMENTED "+soql);
return new ForcePreparedStatement(this, soql);
}

Expand Down Expand Up @@ -77,7 +129,7 @@ public boolean isWrapperFor(Class<?> iface) {
@Override
public Statement createStatement() {
logger.info("[Conn] createStatement 1 IMPLEMENTED ");
return null;
return new ForcePreparedStatement(this);
}

@Override
Expand All @@ -100,26 +152,22 @@ public void setAutoCommit(boolean autoCommit) {

@Override
public boolean getAutoCommit() throws SQLException {
// TODO Auto-generated method stub
return false;
return true;
}

@Override
public void commit() throws SQLException {
// TODO Auto-generated method stub

logger.info("[Conn] commit NOT_IMPLEMENTED ");
}

@Override
public void rollback() throws SQLException {
// TODO Auto-generated method stub

logger.info("[Conn] rollback NOT_IMPLEMENTED ");
}

@Override
public void close() throws SQLException {
// TODO Auto-generated method stub

logger.info("[Conn] close NOT_IMPLEMENTED ");
}

@Override
Expand All @@ -143,13 +191,13 @@ public boolean isReadOnly() throws SQLException {
@Override
public void setCatalog(String catalog) throws SQLException {
// TODO Auto-generated method stub

logger.info("[Conn] setCatalog NOT_IMPLEMENTED set to '"+catalog+"'");
}

@Override
public String getCatalog() throws SQLException {
// TODO Auto-generated method stub
return null;
logger.info("[Conn] getCatalog IMPLEMENTED returning "+ForceDatabaseMetaData.DEFAULT_CATALOG);
return ForceDatabaseMetaData.DEFAULT_CATALOG;
}

@Override
Expand Down Expand Up @@ -178,26 +226,26 @@ public void clearWarnings() throws SQLException {

@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
Logger.getLogger(SF_JDBC_DRIVER_NAME).info(Object.class.getEnclosingMethod().getName());
return null;
logger.info("[Conn] createStatement 2 IMPLEMENTED");
return new ForcePreparedStatement(this);
}

@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
throws SQLException {
Logger.getLogger(SF_JDBC_DRIVER_NAME).info(Object.class.getEnclosingMethod().getName());
return null;
logger.info("[Conn] prepareStatement 1 IMPLEMENTED "+sql);
return new ForcePreparedStatement(this, sql);
}

@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
Logger.getLogger(SF_JDBC_DRIVER_NAME).info(Object.class.getEnclosingMethod().getName());
logger.info("[Conn] prepareCall NOT_IMPLEMENTED "+sql);
return null;
}

@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
Logger.getLogger(SF_JDBC_DRIVER_NAME).info(Object.class.getEnclosingMethod().getName());
logger.info("[Conn] getTypeMap NOT_IMPLEMENTED ");
return null;
}

Expand Down Expand Up @@ -233,52 +281,52 @@ public Savepoint setSavepoint(String name) throws SQLException {

@Override
public void rollback(Savepoint savepoint) throws SQLException {
// TODO Auto-generated method stub
logger.info("[Conn] rollback Savepoint NOT_IMPLEMENTED");

}

@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
// TODO Auto-generated method stub
logger.info("[Conn] releaseSavepoint NOT_IMPLEMENTED");

}

@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
Logger.getLogger(SF_JDBC_DRIVER_NAME).info(Object.class.getEnclosingMethod().getName());
return null;
logger.info("[Conn] createStatement 3 NOT_IMPLEMENTED");
return new ForcePreparedStatement(this);
}

@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
Logger.getLogger(SF_JDBC_DRIVER_NAME).info(Object.class.getEnclosingMethod().getName());
logger.info("[Conn] prepareStatement 2 NOT_IMPLEMENTED "+sql );
return null;
}

@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
int resultSetHoldability) throws SQLException {
Logger.getLogger(SF_JDBC_DRIVER_NAME).info(Object.class.getEnclosingMethod().getName());
logger.info("[Conn] prepareCall 2 NOT_IMPLEMENTED "+sql );
return null;
}

@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
Logger.getLogger(SF_JDBC_DRIVER_NAME).info(Object.class.getEnclosingMethod().getName());
logger.info("[Conn] prepareStatement 3 NOT_IMPLEMENTED "+sql );
return null;
}

@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
Logger.getLogger(SF_JDBC_DRIVER_NAME).info(Object.class.getEnclosingMethod().getName());
logger.info("[Conn] prepareStatement 4 NOT_IMPLEMENTED "+sql );
return null;
}

@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
Logger.getLogger(SF_JDBC_DRIVER_NAME).info(Object.class.getEnclosingMethod().getName());
logger.info("[Conn] prepareStatement 5 NOT_IMPLEMENTED "+sql );
return null;
}

Expand Down Expand Up @@ -309,7 +357,8 @@ public SQLXML createSQLXML() throws SQLException {
@Override
public boolean isValid(int timeout) throws SQLException {
// TODO Auto-generated method stub
return false;
logger.info("[Conn] isValid NOT_IMPLEMENTED ");
return true;
}

@Override
Expand Down Expand Up @@ -352,7 +401,7 @@ public Struct createStruct(String typeName, Object[] attributes) throws SQLExcep
@Override
public void setSchema(String schema) throws SQLException {
// TODO Auto-generated method stub

logger.info("[Conn] setSchema NOT_IMPLEMENTED ");
}

@Override
Expand Down
Loading