Java tutorial
/******************************************************************************* * Copyright (c) 2014 Salesforce.com, inc.. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Salesforce.com, inc. - initial API and implementation ******************************************************************************/ package com.salesforce.ide.core.remote; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import com.google.common.base.Function; import com.google.common.collect.Lists; import com.salesforce.ide.core.factories.FactoryException; import com.salesforce.ide.core.internal.context.ContainerDelegate; import com.salesforce.ide.core.internal.utils.Constants; import com.salesforce.ide.core.internal.utils.ForceExceptionUtils; import com.salesforce.ide.core.internal.utils.Utils; import com.salesforce.ide.core.model.Component; import com.sforce.soap.metadata.AsyncResult; import com.sforce.soap.metadata.DebuggingHeader_element; import com.sforce.soap.metadata.DeployOptions; import com.sforce.soap.metadata.DeployResult; import com.sforce.soap.metadata.DescribeMetadataObject; import com.sforce.soap.metadata.DescribeMetadataResult; import com.sforce.soap.metadata.FileProperties; import com.sforce.soap.metadata.ListMetadataQuery; import com.sforce.soap.metadata.LogInfo; import com.sforce.soap.metadata.MetadataConnection; import com.sforce.soap.metadata.RetrieveRequest; import com.sforce.soap.metadata.RetrieveResult; import com.sforce.ws.ConnectionException; import com.sforce.ws.SoapFaultException; public class MetadataStubExt { private static final Logger logger = Logger.getLogger(MetadataStubExt.class); private Connection connection; private MetadataConnection metadataConnection; //TODO: The connection will already have the api version which we're setting through spring. We can get rid of this. private double apiVersion = 0.0; public MetadataStubExt() { } public Connection getConnection() { return connection; } public MetadataConnection getMetadataConnection() { return metadataConnection; } public void setMetadataConnection(MetadataConnection mc) { metadataConnection = mc; } public int getReadTimeout() { return metadataConnection.getConfig().getReadTimeout(); } public double getApiVersion() { return apiVersion; } public void setApiVersion(double apiVersion) { this.apiVersion = apiVersion; } public void setTimeout(int milliSeconds) { if (metadataConnection != null) { metadataConnection.getConfig().setConnectionTimeout(milliSeconds); metadataConnection.getConfig().setReadTimeout(milliSeconds); } if (logger.isDebugEnabled()) { logger.debug("Timeout set to " + milliSeconds); } } public void initializeMetadataConnection(Connection connection) throws ForceRemoteException { if (connection == null) { logger.warn("Unable to initialize metadata connection - connection is null"); return; } this.connection = connection; if (logger.isDebugEnabled()) { logger.debug("Preparing metadata stub for " + connection.getLogDisplay()); logger.debug("Using metadata server url: " + connection.getMetadataServerUrl()); } try { this.metadataConnection = new MetadataConnection(connection.getMetadataConnectorConfig()); } catch (ConnectionException e) { if (logger.isDebugEnabled()) { logger.debug("Could not set metadata connection with connection: " + connection.getLogDisplay()); } ForceExceptionUtils.handleRemoteException(connection, e); } updateSessionId(connection.getSessionId()); metadataConnection.setCallOptions(connection.getApplication()); if (logger.isDebugEnabled()) { logger.debug("Set client id on metadata stub '" + connection.getApplication() + "'"); } } public void setMetadataDebugHeader(LogInfo[] logInfos) { if (metadataConnection == null) { logger.warn("Unable to set debug level - metadata stub is null"); return; } //Does this do the same thing as below? if we even need it. DebuggingHeader_element header = metadataConnection.getDebuggingHeader(); Map<String, LogInfo> logInfoMap = new HashMap<String, LogInfo>(); //Add old headers if (header != null) { for (LogInfo info : header.getCategories()) { logInfoMap.put(info.getCategory().name(), info); } } //Add new headers if (logInfos != null) { for (LogInfo info : logInfos) { logInfoMap.put(info.getCategory().name(), info); } } //Set the union of the old and new metadataConnection.setDebuggingHeader(logInfoMap.values().toArray(new LogInfo[logInfoMap.size()]), null); if (logger.isDebugEnabled()) { logger.debug("Display current debug headers in MetadataConnection..."); for (LogInfo logInfo : logInfoMap.values()) { logger.debug("Header '" + logInfo.toString() + "' is currently in MetadataConnection"); } } } public String getServerName() { if (connection == null) { return Constants.EMPTY_STRING; } return connection.getMetadataServerUrl(); } public void updateSessionId(String sessionId) { if (metadataConnection != null) { metadataConnection.setSessionHeader(sessionId); if (logger.isDebugEnabled()) { logger.debug("Set session id on metadata stub '" + sessionId + "'"); } } } public AsyncResult retrieve(RetrieveRequest retrieveRequest) throws ForceRemoteException { if (metadataConnection == null) { throw new IllegalArgumentException("Metadata stub cannot be null"); } if (logger.isDebugEnabled()) { logger.debug("Timeout set to " + getReadTimeout() + " milliseconds"); } AsyncResult asyncResult = null; try { asyncResult = metadataConnection.retrieve(retrieveRequest); } catch (ConnectionException e) { ForceExceptionUtils.throwTranslatedException(e, connection); } return asyncResult; } public RetrieveResult checkRetrieveStatus(String asyncProcessId) throws ForceRemoteException { if (metadataConnection == null) { throw new IllegalArgumentException("Metadata stub cannot be null"); } RetrieveResult retrieveResult = null; try { retrieveResult = metadataConnection.checkRetrieveStatus(asyncProcessId); } catch (ConnectionException e) { ForceExceptionUtils.throwTranslatedException(e, connection); } return retrieveResult; } public DeployResult checkDeployStatus(String asyncProcessId) throws ForceRemoteException { if (metadataConnection == null) { throw new IllegalArgumentException("Metadata stub cannot be null"); } DeployResult deployResult = null; try { deployResult = metadataConnection.checkDeployStatus(asyncProcessId, true); } catch (ConnectionException e) { ForceExceptionUtils.throwTranslatedException(e, connection); } return deployResult; } public AsyncResult[] checkStatus(String[] asyncProcessId) throws ForceRemoteException { if (metadataConnection == null) { throw new IllegalArgumentException("Metadata stub cannot be null"); } AsyncResult[] asyncResult = null; try { asyncResult = metadataConnection.checkStatus(asyncProcessId); } catch (ConnectionException e) { ForceExceptionUtils.throwTranslatedException(e, connection); } return asyncResult; } public DescribeMetadataResult describeMetadata(double version) throws ForceRemoteException { if (metadataConnection == null) { throw new IllegalArgumentException("Metadata stub cannot be null"); } if (logger.isInfoEnabled()) { logger.info("Get describe metadata for version " + version); } if (logger.isDebugEnabled()) { logger.debug("Timeout set to " + getReadTimeout() + " milliseconds"); } DescribeMetadataResult describeMetadataResult = null; try { describeMetadataResult = metadataConnection.describeMetadata(version); } catch (ConnectionException e) { ForceExceptionUtils.throwTranslatedException(e, connection); } return describeMetadataResult; } public DescribeMetadataResult describeMetadata() throws ForceRemoteException { return describeMetadata(apiVersion); } public FileProperties[] listMetadata(ListMetadataQuery[] allQueriesArray, IProgressMonitor monitor) throws ForceRemoteException { if (monitor == null) { monitor = new NullProgressMonitor(); } if (metadataConnection == null) { throw new IllegalArgumentException("Metadata stub cannot be null"); } if (logger.isDebugEnabled()) { logger.debug("Querying metadata for FileProperties"); } if (Utils.isEmpty(allQueriesArray)) { return new FileProperties[] {}; } // Remove unsupported components from queries. Set<String> supportedComponents = getSupportedMetadataComponents(); ArrayList<ListMetadataQuery> allQueries = Lists.newArrayList(allQueriesArray); Iterator<ListMetadataQuery> it = allQueries.iterator(); while (it.hasNext()) { ListMetadataQuery query = it.next(); if (!supportedComponents.contains(query.getType())) { it.remove(); } } //break request into 3 queries per api call (api constraint) final int QUERIES_PER_CALL = 3; List<FileProperties> filePropertiesList = new ArrayList<FileProperties>(); try { for (List<ListMetadataQuery> queries : Lists.partition(allQueries, QUERIES_PER_CALL)) { try { filePropertiesList.addAll(getFileProperties(queries, monitor)); } catch (ConnectionException e) { //Invalid type or timeout if (ForceExceptionUtils.isReadTimeoutException(e) || e instanceof SoapFaultException) { filePropertiesList.addAll(tryOneByOne(queries, monitor)); } else { ForceExceptionUtils.throwTranslatedException(e, connection); } } } } catch (MonitorCanceledException e) { // nothing to do, just return } return filePropertiesList.toArray(new FileProperties[filePropertiesList.size()]); } public Set<String> getSupportedMetadataComponents2() throws ForceRemoteException { List<DescribeMetadataObject> metadataObjects = Lists.newArrayList(describeMetadata().getMetadataObjects()); Function<DescribeMetadataObject, String> metadataObjectToName = new Function<DescribeMetadataObject, String>() { @Override public String apply(DescribeMetadataObject metadataObject) { return metadataObject.getXmlName(); } }; HashSet<String> supportedNames = new HashSet<String>(); supportedNames.addAll(Lists.transform(metadataObjects, metadataObjectToName)); return supportedNames; } public Set<String> getSupportedMetadataComponents() throws ForceRemoteException { List<DescribeMetadataObject> metadataObjects = Lists.newArrayList(describeMetadata().getMetadataObjects()); HashSet<String> supportedNames = new HashSet<String>(); for (DescribeMetadataObject object : metadataObjects) { String name = object.getXmlName(); supportedNames.add(name); for (String child : object.getChildXmlNames()) { supportedNames.add(child); } Component component = null; try { component = ContainerDelegate.getInstance().getFactoryLocator().getComponentFactory() .getComponentByComponentType(name); } catch (FactoryException e) { //If an exception is thrown, it isn't supported } //Check the md folder in the bean if (component != null && object.isInFolder()) { supportedNames.add(component.getFolderNameIfFolderTypeMdComponent()); } } return supportedNames; } private List<FileProperties> tryOneByOne(List<ListMetadataQuery> queries, IProgressMonitor monitor) throws MonitorCanceledException, ForceRemoteException { List<FileProperties> filePropertiesSubList = new ArrayList<FileProperties>(); for (List<ListMetadataQuery> listofOneQuery : Lists.partition(Lists.newArrayList(queries), 1)) { try { filePropertiesSubList.addAll(getFileProperties(listofOneQuery, monitor)); } catch (ConnectionException e) { if (e instanceof SoapFaultException) { logger.warn(e.getLocalizedMessage()); } else if (ForceExceptionUtils.isReadTimeoutException(e)) { logTimeout(listofOneQuery.get(0)); } else { ForceExceptionUtils.throwTranslatedException(e, connection); } } } return filePropertiesSubList; } private void logTimeout(ListMetadataQuery query) { logger.warn("Timeout while retrying to retrieve listMetadata for for component type " + query.getType() + (Utils.isNotEmpty(query.getFolder()) ? "[" + query.getFolder() + "]" : "") + " - will skip and continue"); } /** * Get FileProperties for all queries. * @return a non-null array of FileProperties * @throws MonitorCanceledException * @throws ConnectionException */ private List<FileProperties> getFileProperties(List<ListMetadataQuery> queries, IProgressMonitor monitor) throws MonitorCanceledException, ConnectionException { checkMonitorIsCanceled(monitor); logQueries(queries); monitor.subTask(getMonitorMessage(queries)); FileProperties[] tmpFileProperties = null; tmpFileProperties = metadataConnection.listMetadata(queries.toArray(new ListMetadataQuery[queries.size()]), getDefaultApiVersion()); List<FileProperties> properties = arrayToList(tmpFileProperties); logger.debug("Got [" + properties.size() + "] file properties for component types"); checkMonitorIsCanceled(monitor); monitor.worked(queries.size()); return properties; } private Double getDefaultApiVersion() { return new Double(connection.getSalesforceEndpoints().getDefaultApiVersion()); } private void checkMonitorIsCanceled(IProgressMonitor monitor) throws MonitorCanceledException { if (monitor.isCanceled()) { throw new MonitorCanceledException(); } } private ArrayList<FileProperties> arrayToList(FileProperties[] tmpFileProperties) { return tmpFileProperties == null ? Lists.<FileProperties>newArrayList() : Lists.newArrayList(tmpFileProperties); } private String getMonitorMessage(List<ListMetadataQuery> tmpListMetadataQueryList) { final StringBuffer strBuff = new StringBuffer(); Set<String> componentTypes = new HashSet<String>(); for (Iterator<ListMetadataQuery> iterator = tmpListMetadataQueryList.iterator(); iterator.hasNext();) { ListMetadataQuery listMetadataQuery = iterator.next(); if (Utils.isNotEmpty(listMetadataQuery.getFolder())) { componentTypes.add(Utils.getPlural(listMetadataQuery.getType())); } else { componentTypes.add(listMetadataQuery.getType()); } } for (Iterator<String> iterator = componentTypes.iterator(); iterator.hasNext();) { strBuff.append(iterator.next()); if (iterator.hasNext()) { strBuff.append(", "); } } return strBuff.toString() + "..."; } private void logQueries(List<ListMetadataQuery> tmpListMetadataQueryList) { if (logger.isDebugEnabled()) { StringBuffer strBuff = new StringBuffer(); for (Iterator<ListMetadataQuery> iterator = tmpListMetadataQueryList.iterator(); iterator.hasNext();) { ListMetadataQuery listMetadataQuery = iterator.next(); strBuff.append(listMetadataQuery.getType()); if (Utils.isNotEmpty(listMetadataQuery.getFolder())) { strBuff.append(" [").append(listMetadataQuery.getFolder()).append("]"); } if (iterator.hasNext()) { strBuff.append(", "); } } logger.debug("Calling listMetadata for component types\n: " + strBuff.toString()); } } public AsyncResult deploy(byte[] zipFile, DeployOptions deployOptions) throws ForceRemoteException { if (metadataConnection == null) { throw new IllegalArgumentException("Metadata stub cannot be null"); } if (logger.isDebugEnabled()) { logger.debug("Timeout set to " + getReadTimeout() + " milliseconds"); } AsyncResult asyncResult = null; try { asyncResult = metadataConnection.deploy(zipFile, deployOptions); } catch (ConnectionException e) { ForceExceptionUtils.throwTranslatedException(e, connection); } return asyncResult; } public String getLogDisplay() { return connection.getLogDisplay(); } @Override public int hashCode() { final int prime = 31; int result = 1; long temp; temp = Double.doubleToLongBits(apiVersion); result = prime * result + (int) (temp ^ (temp >>> 32)); result = prime * result + ((connection == null) ? 0 : connection.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final MetadataStubExt other = (MetadataStubExt) obj; if (Double.doubleToLongBits(apiVersion) != Double.doubleToLongBits(other.apiVersion)) return false; if (connection == null) { if (other.connection != null) return false; } else if (!connection.equals(other.connection)) return false; return true; } /** * Constructs a <code>String</code> with all attributes in name = value format. * * @return a <code>String</code> representation of this object. */ @Override public String toString() { final String TAB = ", "; StringBuffer retValue = new StringBuffer(); retValue.append("MetadataStubExt ( ").append(super.toString()).append(TAB).append("connection = ") .append(connection != null ? connection.getLogDisplay() : "n/a").append(TAB) .append(", apiVersion = ").append(this.apiVersion).append(TAB).append(", timeout = ") .append(metadataConnection != null ? metadataConnection.getConfig().getReadTimeout() : "n/a") .append(TAB).append(" )"); return retValue.toString(); } public String toStringLite() { final String TAB = ", "; StringBuffer retValue = new StringBuffer(); retValue.append("apiVersion = ").append(this.apiVersion).append(TAB).append("timeout = ") .append(metadataConnection != null ? Utils.timeoutToSecs(metadataConnection.getConfig().getReadTimeout()) : "n/a"); return retValue.toString(); } }