package com.sforce.async; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.sforce.ws.ConnectionException; import com.sforce.ws.ConnectorConfig; import com.sforce.ws.MessageHandler; import com.sforce.ws.MessageHandlerWithHeaders; import com.sforce.ws.bind.CalendarCodec; import com.sforce.ws.bind.TypeMapper; import com.sforce.ws.parser.PullParserException; import com.sforce.ws.parser.XmlInputStream; import com.sforce.ws.parser.XmlOutputStream; import com.sforce.ws.transport.Transport; import com.sforce.ws.util.FileUtil; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.xml.namespace.QName; public class BulkConnection { public static final String NAMESPACE = "http://www.force.com/2009/06/asyncapi/dataload"; public static final String SESSION_ID = "X-SFDC-Session"; public static final String XML_CONTENT_TYPE = "application/xml"; public static final String CSV_CONTENT_TYPE = "text/csv"; public static final String JSON_CONTENT_TYPE = "application/json"; public static final String ZIP_XML_CONTENT_TYPE = "zip/xml"; public static final String ZIP_CSV_CONTENT_TYPE = "zip/csv"; public static final String ZIP_JSON_CONTENT_TYPE = "zip/json"; public static final QName JOB_QNAME = new QName("http://www.force.com/2009/06/asyncapi/dataload", "jobInfo"); public static final QName BATCH_QNAME = new QName("http://www.force.com/2009/06/asyncapi/dataload", "batchInfo"); public static final QName BATCH_LIST_QNAME = new QName("http://www.force.com/2009/06/asyncapi/dataload", "batchInfoList"); public static final QName ERROR_QNAME = new QName("http://www.force.com/2009/06/asyncapi/dataload", "error"); private ConnectorConfig config; private HashMap headers = new HashMap<>(); public static final TypeMapper typeMapper = new TypeMapper(null, null, false); private static final JsonFactory factory = new JsonFactory((ObjectCodec)new ObjectMapper()); public BulkConnection(ConnectorConfig config) throws AsyncApiException { if (config == null) throw new AsyncApiException("config can not be null", AsyncExceptionCode.ClientInputError); if (config.getRestEndpoint() == null) throw new AsyncApiException("rest endpoint cannot be null", AsyncExceptionCode.ClientInputError); this.config = config; if (config.getSessionId() == null) throw new AsyncApiException("session ID not found", AsyncExceptionCode.ClientInputError); } public JobInfo createJob(String object, String operation) throws AsyncApiException { JobInfo job = new JobInfo(); job.setObject(object); job.setOperation(OperationEnum.valueOf(operation)); return createJob(job); } public JobInfo createJob(JobInfo job) throws AsyncApiException { String endpoint = getRestEndpoint(); endpoint = String.valueOf(endpoint) + "job/"; return createOrUpdateJob(job, endpoint); } public JobInfo createJob(JobInfo job, ContentType contentType) throws AsyncApiException { String endpoint = getRestEndpoint(); endpoint = String.valueOf(endpoint) + "job/"; return createOrUpdateJob(job, endpoint, contentType); } private JobInfo createOrUpdateJob(JobInfo job, String endpoint) throws AsyncApiException { return createOrUpdateJob(job, endpoint, (job.getContentType() == null) ? ContentType.XML : job.getContentType()); } private JobInfo createOrUpdateJob(JobInfo job, String endpoint, ContentType contentType) throws AsyncApiException { try { Transport transport = this.config.createTransport(); if (contentType == ContentType.ZIP_JSON || contentType == ContentType.JSON) { OutputStream out = transport.connect(endpoint, getHeaders("application/json")); serializeToJson(out, job); out.close(); } else { OutputStream out = transport.connect(endpoint, getHeaders("application/xml")); XmlOutputStream xout = new AsyncXmlOutputStream(out, true); job.write(JOB_QNAME, xout, typeMapper); xout.close(); } InputStream in = transport.getContent(); if (transport.isSuccessful()) { if (contentType == ContentType.ZIP_JSON || contentType == ContentType.JSON) return deserializeJsonToObject(in, JobInfo.class); XmlInputStream xin = new XmlInputStream(); xin.setInput(in, "UTF-8"); JobInfo result = new JobInfo(); result.load(xin, typeMapper); return result; } parseAndThrowException(in, contentType); } catch (PullParserException e) { throw new AsyncApiException("Failed to create job ", AsyncExceptionCode.ClientInputError, e); } catch (IOException e) { throw new AsyncApiException("Failed to create job ", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to create job ", AsyncExceptionCode.ClientInputError, e); } return null; } static void parseAndThrowException(InputStream in, ContentType type) throws AsyncApiException { try { AsyncApiException exception; if (type == ContentType.XML || type == ContentType.ZIP_XML || type == ContentType.CSV || type == ContentType.ZIP_CSV) { exception = new AsyncApiException(); XmlInputStream xin = new XmlInputStream(); xin.setInput(in, "UTF-8"); exception.load(xin, typeMapper); } else if (type == ContentType.JSON || type == ContentType.ZIP_JSON) { JsonParser parser = factory.createJsonParser(in); exception = (AsyncApiException)parser.readValueAs(AsyncApiException.class); } else { throw new AsyncApiException("Server error returned in unknown format", AsyncExceptionCode.ClientInputError); } throw exception; } catch (PullParserException e) { AsyncApiException exception; throw new AsyncApiException("Failed to parse exception ", AsyncExceptionCode.ClientInputError, exception); } catch (IOException e) { throw new AsyncApiException("Failed to parse exception", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to parse exception ", AsyncExceptionCode.ClientInputError, e); } } public void addHeader(String headerName, String headerValue) { this.headers.put(headerName, headerValue); } private String getRestEndpoint() { String endpoint = this.config.getRestEndpoint(); endpoint = endpoint.endsWith("/") ? endpoint : (String.valueOf(endpoint) + "/"); return endpoint; } public BatchInfo createBatchFromStream(JobInfo jobInfo, InputStream input) throws AsyncApiException { return createBatchFromStreamImpl(jobInfo, input, false); } public BatchInfo createBatchFromZipStream(JobInfo jobInfo, InputStream zipInput) throws AsyncApiException { return createBatchFromStreamImpl(jobInfo, zipInput, true); } private BatchInfo createBatchFromStreamImpl(JobInfo jobInfo, InputStream input, boolean isZip) throws AsyncApiException { try { String endpoint = getRestEndpoint(); Transport transport = this.config.createTransport(); endpoint = String.valueOf(endpoint) + "job/" + jobInfo.getId() + "/batch"; String contentType = getContentTypeString(jobInfo.getContentType(), isZip); HashMap httpHeaders = getHeaders(contentType); boolean allowZipToBeGzipped = false; OutputStream out = transport.connect(endpoint, httpHeaders, !(!allowZipToBeGzipped && isZip)); FileUtil.copy(input, out); InputStream result = transport.getContent(); if (!transport.isSuccessful()) parseAndThrowException(result, jobInfo.getContentType()); if (jobInfo.getContentType() == ContentType.JSON || jobInfo.getContentType() == ContentType.ZIP_JSON) return deserializeJsonToObject(result, BatchInfo.class); return BatchRequest.loadBatchInfo(result); } catch (IOException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } catch (PullParserException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } } public BatchInfo createBatchFromDir(JobInfo job, InputStream batchContent, File attachmentDir) throws AsyncApiException { List files = FileUtil.listFilesRecursive(attachmentDir, false); Map fileMap = new HashMap<>(files.size()); String rootPath = String.valueOf(attachmentDir.getAbsolutePath()) + File.separator; for (File f : files) { String name = f.getAbsolutePath().replace(rootPath, ""); fileMap.put(name, f); } return createBatchWithFileAttachments(job, batchContent, fileMap); } public BatchInfo createBatchWithFileAttachments(JobInfo jobInfo, InputStream batchContent, File rootDirectory, String... files) throws AsyncApiException { Map fileMap = new HashMap<>(files.length); byte b; int i; String[] arrayOfString; for (i = (arrayOfString = files).length, b = 0; b < i; ) { String fileName = arrayOfString[b]; File f = new File(rootDirectory, fileName); fileMap.put(fileName, f); b++; } return createBatchWithFileAttachments(jobInfo, batchContent, fileMap); } public BatchInfo createBatchWithFileAttachments(JobInfo jobInfo, InputStream batchContent, Map attachedFiles) throws AsyncApiException { Map inputStreamMap = new HashMap<>(attachedFiles.size()); for (Map.Entry entry : attachedFiles.entrySet()) { File file = entry.getValue(); try { inputStreamMap.put(entry.getKey(), new FileInputStream(file)); } catch (IOException e) { throw new AsyncApiException("Failed to create batch. Could not read file : " + file, AsyncExceptionCode.ClientInputError, e); } } return createBatchWithInputStreamAttachments(jobInfo, batchContent, inputStreamMap); } public BatchInfo createBatchWithInputStreamAttachments(JobInfo jobInfo, InputStream batchContent, Map attachments) throws AsyncApiException { if (batchContent != null && attachments.get("request.txt") != null) throw new AsyncApiException("Request content cannot be included as both input stream and attachment", AsyncExceptionCode.ClientInputError); try { String endpoint = getRestEndpoint(); endpoint = String.valueOf(endpoint) + "job/" + jobInfo.getId() + "/batch"; Transport transport = this.config.createTransport(); ZipOutputStream zipOut = new ZipOutputStream(transport.connect(endpoint, getHeaders(getContentTypeString( jobInfo.getContentType(), true)), false)); try { if (batchContent != null) { zipOut.putNextEntry(new ZipEntry("request.txt")); FileUtil.copy(batchContent, zipOut, false); } for (Map.Entry entry : attachments.entrySet()) { zipOut.putNextEntry(new ZipEntry(entry.getKey())); FileUtil.copy(entry.getValue(), zipOut, false); } } finally { zipOut.close(); } InputStream result = transport.getContent(); if (jobInfo.getContentType() == ContentType.JSON || jobInfo.getContentType() == ContentType.ZIP_JSON) return deserializeJsonToObject(result, BatchInfo.class); return BatchRequest.loadBatchInfo(result); } catch (IOException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } catch (PullParserException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } } public BatchInfo createBatchFromForeignCsvStream(JobInfo jobInfo, InputStream input, String charSet) throws AsyncApiException { try { String endpoint = getRestEndpoint(); Transport transport = this.config.createTransport(); endpoint = String.valueOf(endpoint) + "job/" + jobInfo.getId() + "/batch"; String contentType = getContentTypeString(ContentType.CSV, false); if (charSet != null) contentType = String.valueOf(contentType) + ";charset=" + charSet; HashMap httpHeaders = getHeaders(contentType); boolean allowZipToBeGzipped = false; OutputStream out = transport.connect(endpoint, httpHeaders, false); FileUtil.copy(input, out); InputStream result = transport.getContent(); if (!transport.isSuccessful()) parseAndThrowException(result, jobInfo.getContentType()); return BatchRequest.loadBatchInfo(result); } catch (IOException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } catch (PullParserException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } } public void createTransformationSpecFromStream(JobInfo jobInfo, InputStream input) throws AsyncApiException { try { String endpoint = getRestEndpoint(); Transport transport = this.config.createTransport(); endpoint = String.valueOf(endpoint) + "job/" + jobInfo.getId() + "/spec"; String contentType = getContentTypeString(ContentType.CSV, false); HashMap httpHeaders = getHeaders(contentType); boolean allowZipToBeGzipped = false; OutputStream out = transport.connect(endpoint, httpHeaders, false); FileUtil.copy(input, out); InputStream result = transport.getContent(); if (!transport.isSuccessful()) parseAndThrowException(result, jobInfo.getContentType()); } catch (IOException e) { throw new AsyncApiException("Failed to create transformation specification", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to create transformation specification", AsyncExceptionCode.ClientInputError, e); } } private String getContentTypeString(ContentType contentType, boolean isZip) throws AsyncApiException { ContentType ct = (contentType == null) ? ContentType.XML : contentType; if (isZip) { switch (ct) { case ZIP_CSV: return "zip/csv"; case ZIP_XML: return "zip/xml"; case ZIP_JSON: return "zip/json"; } throw new AsyncApiException("Invalid zip content type: " + contentType, AsyncExceptionCode.ClientInputError); } switch (ct) { case XML: return "application/xml"; case null: return "text/csv"; case JSON: return "application/json"; } throw new AsyncApiException("Not expecting zip content type: " + contentType, AsyncExceptionCode.ClientInputError); } private HashMap getHeaders(String contentType) { HashMap newMap = new HashMap<>(); for (Map.Entry entry : this.headers.entrySet()) newMap.put(entry.getKey(), entry.getValue()); newMap.put("Content-Type", contentType); newMap.put("X-SFDC-Session", this.config.getSessionId()); return newMap; } public BatchRequest createBatch(JobInfo job) throws AsyncApiException { try { String endpoint = getRestEndpoint(); Transport transport = this.config.createTransport(); endpoint = String.valueOf(endpoint) + "job/" + job.getId() + "/batch"; ContentType ct = job.getContentType(); if (ct != null && ct != ContentType.XML && ct != ContentType.JSON) throw new AsyncApiException( "This method can only be used with xml or JSON content type", AsyncExceptionCode.ClientInputError); String jobContentType = ""; if (ct == null) { jobContentType = "application/xml"; } else { OutputStream out; switch (ct) { case JSON: jobContentType = "application/json"; out = transport.connect(endpoint, getHeaders(jobContentType)); return new BatchRequest(transport, out); } jobContentType = "application/xml"; } OutputStream outputStream = transport.connect(endpoint, getHeaders(jobContentType)); return new BatchRequest(transport, outputStream); } catch (IOException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException x) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, x); } } public CsvBatchRequest createCsvBatch(JobInfo job) throws AsyncApiException { try { String endpoint = getRestEndpoint(); Transport transport = this.config.createTransport(); endpoint = String.valueOf(endpoint) + "job/" + job.getId() + "/batch"; ContentType ct = job.getContentType(); if (ct != null && ct != ContentType.CSV) throw new AsyncApiException( "This method can only be used with csv content type", AsyncExceptionCode.ClientInputError); OutputStream out = transport.connect(endpoint, getHeaders("text/csv")); return new CsvBatchRequest(transport, out); } catch (IOException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to create batch", AsyncExceptionCode.ClientInputError, e); } } public TransformationSpecRequest createTransformationSpec(JobInfo job) throws AsyncApiException { try { String endpoint = getRestEndpoint(); Transport transport = this.config.createTransport(); endpoint = String.valueOf(endpoint) + "job/" + job.getId() + "/spec"; ContentType ct = job.getContentType(); if (ct != null && ct != ContentType.CSV) throw new AsyncApiException( "This method can only be used with csv content type", AsyncExceptionCode.ClientInputError); boolean allowZipToBeGzipped = false; OutputStream out = transport.connect(endpoint, getHeaders("text/csv"), false); return new TransformationSpecRequest(transport, out); } catch (IOException e) { throw new AsyncApiException("Failed to create transformation spec", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to create transformation spec", AsyncExceptionCode.ClientInputError, e); } } public BatchInfoList getBatchInfoList(String jobId) throws AsyncApiException { return getBatchInfoList(jobId, ContentType.XML); } public BatchInfoList getBatchInfoList(String jobId, ContentType contentType) throws AsyncApiException { try { String endpoint = String.valueOf(getRestEndpoint()) + "job/" + jobId + "/batch/"; URL url = new URL(endpoint); InputStream stream = doHttpGet(url); if (contentType == ContentType.JSON || contentType == ContentType.ZIP_JSON) return deserializeJsonToObject(stream, BatchInfoList.class); XmlInputStream xin = new XmlInputStream(); xin.setInput(stream, "UTF-8"); BatchInfoList result = new BatchInfoList(); result.load(xin, typeMapper); return result; } catch (IOException e) { throw new AsyncApiException("Failed to get batch info list ", AsyncExceptionCode.ClientInputError, e); } catch (PullParserException e) { throw new AsyncApiException("Failed to get batch info list ", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to get batch info list ", AsyncExceptionCode.ClientInputError, e); } } public BatchInfo getBatchInfo(String jobId, String batchId) throws AsyncApiException { return getBatchInfo(jobId, batchId, ContentType.XML); } public BatchInfo getBatchInfo(String jobId, String batchId, ContentType contentType) throws AsyncApiException { try { String endpoint = String.valueOf(getRestEndpoint()) + "job/" + jobId + "/batch/" + batchId; URL url = new URL(endpoint); InputStream stream = doHttpGet(url); if (contentType == ContentType.JSON || contentType == ContentType.ZIP_JSON) return deserializeJsonToObject(stream, BatchInfo.class); XmlInputStream xin = new XmlInputStream(); xin.setInput(stream, "UTF-8"); BatchInfo result = new BatchInfo(); result.load(xin, typeMapper); return result; } catch (IOException e) { throw new AsyncApiException("Failed to parse batch info ", AsyncExceptionCode.ClientInputError, e); } catch (PullParserException e) { throw new AsyncApiException("Failed to parse batch info ", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to parse batch info ", AsyncExceptionCode.ClientInputError, e); } } public BatchResult getBatchResult(String jobId, String batchId) throws AsyncApiException { return getBatchResult(jobId, batchId, ContentType.XML); } public BatchResult getBatchResult(String jobId, String batchId, ContentType contentType) throws AsyncApiException { try { InputStream stream = doHttpGet(buildBatchResultURL(jobId, batchId)); if (contentType == ContentType.JSON || contentType == ContentType.ZIP_JSON) { BatchResult batchResult = new BatchResult(); Result[] results = deserializeJsonToObject(stream, (Class)Result[].class); batchResult.setResult(results); return batchResult; } XmlInputStream xin = new XmlInputStream(); xin.setInput(stream, "UTF-8"); BatchResult result = new BatchResult(); result.load(xin, typeMapper); return result; } catch (PullParserException e) { throw new AsyncApiException("Failed to parse result ", AsyncExceptionCode.ClientInputError, e); } catch (IOException e) { throw new AsyncApiException("Failed to get result ", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to get result ", AsyncExceptionCode.ClientInputError, e); } } public InputStream getBatchResultStream(String jobId, String batchId) throws AsyncApiException { try { String endpoint = String.valueOf(getRestEndpoint()) + "job/" + jobId + "/batch/" + batchId + "/result"; URL url = new URL(endpoint); return doHttpGet(url); } catch (IOException e) { throw new AsyncApiException("Failed to get result ", AsyncExceptionCode.ClientInputError, e); } } public URL buildBatchResultURL(String jobId, String batchId) throws AsyncApiException { try { return new URL(String.valueOf(getRestEndpoint()) + "job/" + jobId + "/batch/" + batchId + "/result"); } catch (MalformedURLException e) { throw new AsyncApiException("Failed to construct URL for getting batch results: " + e.getMessage(), AsyncExceptionCode.ClientInputError, e); } } public InputStream getBatchRequestInputStream(String jobId, String batchId) throws AsyncApiException { try { String endpoint = String.valueOf(getRestEndpoint()) + "job/" + jobId + "/batch/" + batchId + "/request"; URL url = new URL(endpoint); return doHttpGet(url); } catch (IOException e) { throw new AsyncApiException("Failed to get request ", AsyncExceptionCode.ClientInputError, e); } } public QueryResultList getQueryResultList(String jobId, String batchId) throws AsyncApiException { return getQueryResultList(jobId, batchId, ContentType.XML); } public QueryResultList getQueryResultList(String jobId, String batchId, ContentType contentType) throws AsyncApiException { InputStream stream = getBatchResultStream(jobId, batchId); try { if (contentType == ContentType.JSON || contentType == ContentType.ZIP_JSON) { String[] results = deserializeJsonToObject(stream, (Class)String[].class); QueryResultList list = new QueryResultList(); list.setResult(results); return list; } XmlInputStream xin = new XmlInputStream(); xin.setInput(stream, "UTF-8"); QueryResultList result = new QueryResultList(); result.load(xin, typeMapper); return result; } catch (ConnectionException e) { throw new AsyncApiException("Failed to parse query result list ", AsyncExceptionCode.ClientInputError, e); } catch (PullParserException e) { throw new AsyncApiException("Failed to parse query result list ", AsyncExceptionCode.ClientInputError, e); } catch (IOException e) { throw new AsyncApiException("Failed to parse query result list ", AsyncExceptionCode.ClientInputError, e); } } public InputStream getQueryResultStream(String jobId, String batchId, String resultId) throws AsyncApiException { try { return doHttpGet(buildQueryResultURL(jobId, batchId, resultId)); } catch (IOException e) { throw new AsyncApiException("Failed to get result ", AsyncExceptionCode.ClientInputError, e); } } public URL buildQueryResultURL(String jobId, String batchId, String resultId) throws AsyncApiException { try { return new URL(String.valueOf(getRestEndpoint()) + "job/" + jobId + "/batch/" + batchId + "/result" + "/" + resultId); } catch (MalformedURLException e) { throw new AsyncApiException("Failed to construct URL for getting query result: " + e.getMessage(), AsyncExceptionCode.ClientInputError, e); } } private InputStream doHttpGet(URL url) throws IOException, AsyncApiException { InputStream in; HttpURLConnection connection = this.config.createConnection(url, null); SSLContext sslContext = this.config.getSslContext(); if (sslContext != null && connection instanceof HttpsURLConnection) ((HttpsURLConnection)connection).setSSLSocketFactory(sslContext.getSocketFactory()); connection.setRequestProperty("X-SFDC-Session", this.config.getSessionId()); boolean success = true; try { in = connection.getInputStream(); } catch (IOException e) { success = false; in = connection.getErrorStream(); } String encoding = connection.getHeaderField("Content-Encoding"); if ("gzip".equals(encoding)) in = new GZIPInputStream(in); if (this.config.isTraceMessage() || this.config.hasMessageHandlers()) { byte[] bytes = FileUtil.toBytes(in); in = new ByteArrayInputStream(bytes); if (this.config.hasMessageHandlers()) { Iterator it = this.config.getMessagerHandlers(); while (it.hasNext()) { MessageHandler handler = it.next(); if (handler instanceof MessageHandlerWithHeaders) { ((MessageHandlerWithHeaders)handler).handleRequest(url, new byte[0], null); ((MessageHandlerWithHeaders)handler).handleResponse(url, bytes, connection.getHeaderFields()); continue; } handler.handleRequest(url, new byte[0]); handler.handleResponse(url, bytes); } } if (this.config.isTraceMessage()) { this.config.getTraceStream().println(url.toExternalForm()); Map> headers = connection.getHeaderFields(); for (Map.Entry> entry : headers.entrySet()) { StringBuffer sb = new StringBuffer(); List values = entry.getValue(); if (values != null) for (String v : values) sb.append(v); this.config.getTraceStream().println(String.valueOf(entry.getKey()) + ": " + sb.toString()); } this.config.teeInputStream(bytes); } } if (!success) { ContentType type = null; String contentTypeHeader = connection.getContentType(); if (contentTypeHeader != null) if (contentTypeHeader.contains("application/xml")) { type = ContentType.XML; } else if (contentTypeHeader.contains("application/json")) { type = ContentType.JSON; } parseAndThrowException(in, type); } return in; } public JobInfo getJobStatus(String jobId) throws AsyncApiException { return getJobStatus(jobId, ContentType.XML); } public JobInfo getJobStatus(String jobId, ContentType contentType) throws AsyncApiException { try { String endpoint = getRestEndpoint(); endpoint = String.valueOf(endpoint) + "job/" + jobId; URL url = new URL(endpoint); InputStream in = doHttpGet(url); if (contentType == ContentType.JSON || contentType == ContentType.ZIP_JSON) return deserializeJsonToObject(in, JobInfo.class); JobInfo result = new JobInfo(); XmlInputStream xin = new XmlInputStream(); xin.setInput(in, "UTF-8"); result.load(xin, typeMapper); return result; } catch (PullParserException e) { throw new AsyncApiException("Failed to get job status ", AsyncExceptionCode.ClientInputError, e); } catch (IOException e) { throw new AsyncApiException("Failed to get job status ", AsyncExceptionCode.ClientInputError, e); } catch (ConnectionException e) { throw new AsyncApiException("Failed to get job status ", AsyncExceptionCode.ClientInputError, e); } } static void serializeToJson(OutputStream out, Object value) throws IOException { JsonGenerator generator = factory.createJsonGenerator(out); ObjectMapper mapper = new ObjectMapper(); mapper.setDateFormat(CalendarCodec.getDateFormat()); mapper.writeValue(generator, value); } static T deserializeJsonToObject(InputStream in, Class tmpClass) throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); mapper.setTimeZone(TimeZone.getTimeZone("GMT")); return (T)mapper.readValue(in, tmpClass); } public JobInfo abortJob(String jobId) throws AsyncApiException { JobInfo job = new JobInfo(); job.setId(jobId); job.setState(JobStateEnum.Aborted); return updateJob(job); } public JobInfo closeJob(String jobId) throws AsyncApiException { JobInfo job = new JobInfo(); job.setId(jobId); job.setState(JobStateEnum.Closed); return updateJob(job); } public JobInfo updateJob(JobInfo job) throws AsyncApiException { return updateJob(job, ContentType.XML); } public JobInfo updateJob(JobInfo job, ContentType contentType) throws AsyncApiException { String endpoint = getRestEndpoint(); endpoint = String.valueOf(endpoint) + "job/" + job.getId(); return createOrUpdateJob(job, endpoint, contentType); } public ConnectorConfig getConfig() { return this.config; } }