Java tutorial
/** * Copyright 2015 Anaplan Inc. * * Licensed 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.md file for the specific language governing permissions and * limitations under the License. */ package com.anaplan.connector; import com.anaplan.client.AnaplanAPIException; import com.anaplan.client.CellReader; import com.anaplan.client.ExportMetadata; import com.anaplan.client.ServerFile; import com.anaplan.connector.connection.AnaplanConnection; import com.anaplan.connector.exceptions.AnaplanOperationException; import com.anaplan.connector.utils.OperationStatus; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; /** * Translates Anaplan task results into connector friendly responses. Provides * constructors corresponding to the expected states of a completed Anaplan * server task, utilities to convert these into DI platform responses. * * Payload generated here form the data output from the Anaplan connector. In * the case of an operation success or partial success, this is written out as * Current Data for a downstream connector to consume; in the case of complete * failure an exception is thrown to force the data-flow to stop. */ public class MulesoftAnaplanResponse implements Serializable { private static Logger logger = LogManager.getLogger(MulesoftAnaplanResponse.class.getName()); private static final long serialVersionUID = 1L; private final String responseMessage; private final ServerFile serverFile; private final ExportMetadata exportMetadata; private final OperationStatus status; private final Throwable exception; /** * Constructor. * * @param responseMessage Response message, usually export data. * @param status status object. * @param serverFile ServerFile object returned from API. * @param exportMetaData Meta data for Export data. * @param failureCause Any server error during exporting data. */ private MulesoftAnaplanResponse(String responseMessage, OperationStatus status, ServerFile serverFile, ExportMetadata exportMetaData, Throwable failureCause) { this.responseMessage = responseMessage; this.status = status; this.serverFile = serverFile; this.exportMetadata = exportMetaData; this.exception = failureCause; logger.info("Created {}", this); } /** Getters */ public OperationStatus getStatus() { return status; } public String getResponseMessage() { return responseMessage; } public ServerFile getServerFile() { return serverFile; } public ExportMetadata getExportMetadata() { return exportMetadata; } public Throwable getException() { return exception; } /** * Uses the server cell-reader handler to read the server response contents * and then write it to a string-buffer to be returned as a response. * * TODO: Use CSVPrinter to create the response. Check * DO_NOT_REBASE_writeResponseOptimization for suggested changes. * * @param cellReader CellReader object to write server response. * @return The string response. * @throws AnaplanAPIException Exception thrown when reading rows of data. * @throws IOException Exception thrown when reading rows of data. */ private String writeResponse(CellReader cellReader) throws AnaplanAPIException, IOException { StringBuilder sb = new StringBuilder(); final String header = StringUtils.join(cellReader.getHeaderRow(), ','); sb.append(header); logger.debug("{}", header); String dataLine = StringUtils.join(cellReader.readDataRow(), ','); String[] dataLineArr; while (dataLine != null) { sb.append("\n").append(dataLine); dataLineArr = cellReader.readDataRow(); dataLine = (dataLineArr == null) ? null : StringUtils.join(dataLineArr, ','); } logger.debug("finished writing file"); return sb.toString(); } /** * Finalizes the result sent back from the server and returns it as a * string. * * @param serverFile ServerFile object. * @throws IOException Exception thrown when reading rows of data. * @throws AnaplanAPIException Exception thrown when reading rows of data. */ private String responseServerFile(ServerFile serverFile) throws IOException, AnaplanAPIException { if (serverFile == null) { throw new AnaplanAPIException("Response is empty: " + getStatus()); } final CellReader cellReader = serverFile.getDownloadCellReader(); return writeResponse(cellReader); } /** * Reads the export-data from the registered Serverfile object and emits * the data as a string into the Mulesoft platform for the next * connector down the data-flow pipeline. * * @param connection Anaplan API connection object. * @throws IOException IO exception * @throws AnaplanAPIException * @throws AnaplanOperationException */ public String writeExportData(AnaplanConnection connection) throws IOException, AnaplanAPIException, AnaplanOperationException { if (getStatus() != OperationStatus.SUCCESS) { if (getException() == null) { responseFail(connection, getResponseMessage()); } else { responseEpicFail(connection, getException(), getResponseMessage()); } } return responseServerFile(getServerFile()); } @Override public String toString() { StringBuilder strBuffer = new StringBuilder(); strBuffer.append("AnaplanResponse with status "); strBuffer.append(this.status == null ? "null" : this.status.toString()); strBuffer.append("; message: "); strBuffer.append(this.responseMessage == null ? "null" : this.responseMessage); strBuffer.append("; serverfile "); strBuffer.append(this.serverFile == null ? "null" : this.serverFile.getName()); return strBuffer.toString(); } /** * Currently logs the error provided in 'reason' and creates a general * Log4j.error() message. * * @param connection Anaplan API connection * @param reason Reason description for failed response. */ public static void responseFail(AnaplanConnection connection, String reason) { logger.error("Aborting operation for all documents in request: {}", reason); } /** * Usually as a last resort to error-logging, whenever the cause of the * error is unknown and everything needs to be brought to a grinding halt. * In order to stop the flow, the Throwable is wrapped into a * AnaplanOperationException. * * @param connection Anaplan API connection. * @param e Throwable object containing Failure info * @param reason Reason for Epic Fail * @throws AnaplanOperationException Internal operation exception for * signalling epic fail. */ public static void responseEpicFail(AnaplanConnection connection, Throwable e, String reason) throws AnaplanOperationException { final String msg; if (reason == null) { msg = "Unexpected operation error: Generating OperationResponse " + "error for " + e.getMessage(); } else { msg = reason + ": " + e.getMessage(); } logger.error(msg, e); // for stack trace throw new AnaplanOperationException(e.getMessage()); } /** * Success response constructor whenever an export operation succeeds. * * @param responseMessage Response message from server for succesful export. * @param exportOutput Export output ServerFile object. * @param exportMetadata Export metadata. * @return Response object containing export data and details. * @throws IllegalArgumentException Thrown if null export output exists. */ public static MulesoftAnaplanResponse exportSuccess(String responseMessage, ServerFile exportOutput, ExportMetadata exportMetadata) throws IllegalArgumentException { if (exportOutput == null) { logger.error("Discarding response for task: {}", responseMessage); throw new IllegalArgumentException("Output cannot be null for a " + "successful export"); } else { return new MulesoftAnaplanResponse(responseMessage, OperationStatus.SUCCESS, exportOutput, exportMetadata, null); } } /** * Failure response constructor for data exports from Anaplan, whenever a * failure is encountered during a data-export. Most likely thrown due to * invalid Workspace-ID, Model-ID or Export-action-ID. * * @param responseMessage Response message for export failure. * @param exportMetadata Metadata for export/ * @param cause Throwable with info regarding export failure. * @return Response object containing server details of Export failure. */ public static MulesoftAnaplanResponse exportFailure(String responseMessage, ExportMetadata exportMetadata, Throwable cause) { return new MulesoftAnaplanResponse(responseMessage, OperationStatus.FAILURE, null, exportMetadata, cause); } /** * Success response constructor to signal a successful Import operation. * * @param responseMessage Response message from import success. * @param serverFile Object containing details of import. * @return Response object containing all response details regarding import. */ public static MulesoftAnaplanResponse importSuccess(String responseMessage, ServerFile serverFile) { return new MulesoftAnaplanResponse(responseMessage, OperationStatus.SUCCESS, serverFile, null, null); } /** * Failure response constructor used to signal a failure during an import * operation into Anaplan, which usually is because of malformed input data. * * @param responseMessage Response message from Import failure. * @param cause Throwable with info regarding import failure. * @return Response object containing server details of Import failure. */ public static MulesoftAnaplanResponse importFailure(String responseMessage, Throwable cause) { return new MulesoftAnaplanResponse(responseMessage, OperationStatus.FAILURE, null, null, cause); } /** * Response constructor used when an Import operation fails and the server * provides a failure-dump to help debug. * * @param responseMessage Response message from Import failure. * @param failDump Failure dump log from server for debugging purposes. * @return Response object containing server details of import failure. */ public static MulesoftAnaplanResponse importWithFailureDump(String responseMessage, ServerFile failDump) { return new MulesoftAnaplanResponse(responseMessage, OperationStatus.APPLICATION_ERROR, failDump, null, null); } /** * Success response constructor to signal a successful generic action * operation, such as a successful Delete operation or an M2M operation. * * @param responseMessage Success response message from Execute-Action. * @return Response object containing execute-action success details. */ public static MulesoftAnaplanResponse executeActionSuccess(String responseMessage) { return new MulesoftAnaplanResponse(responseMessage, OperationStatus.SUCCESS, null, null, null); } /** * Failure response constructor from the connector whenever a generic * execute operation fails. * * @param responseMessage Failure response message from Execute-Action. * @param cause Throwable with failed execute action details. * @return Response object containing failed execute-action details. */ public static MulesoftAnaplanResponse executeActionFailure(String responseMessage, Throwable cause) { return new MulesoftAnaplanResponse(responseMessage, OperationStatus.FAILURE, null, null, cause); } /** * Success response constructor from the connector whenever a Process operation * succeeds on the server. * * @param responseMessage Success response message from running Process. * @return Response object containing Process success response details. */ public static MulesoftAnaplanResponse runProcessSuccess(String responseMessage) { return new MulesoftAnaplanResponse(responseMessage, OperationStatus.SUCCESS, null, null, null); } /** * Failure response constructor from the connector whenever a Process * operation fails on the server. * * @param responseMessage Failure response message from server. * @param cause Throwable containing failure details. * @return Response object containing Process failure response details. */ public static MulesoftAnaplanResponse runProcessFailure(String responseMessage, Throwable cause) { return new MulesoftAnaplanResponse(responseMessage, OperationStatus.FAILURE, null, null, cause); } /** * Fetches dump file contents from Server response if any, otherwise returns * an empty string. * * TODO: Move this to Anaplan-Connect. * * @return Response message from Dump-file. */ public String getDumpFileContents() { StringBuilder dumpFileContents = new StringBuilder(); if (this.serverFile != null) { InputStream dumpFileStream; try { dumpFileStream = serverFile.getDownloadStream(); if (dumpFileStream != null) { byte[] buffer = new byte[5]; int read; do { if ((read = dumpFileStream.read(buffer)) > 0) { dumpFileContents.append(new String(buffer)); } } while (read != -1); } } catch (IOException | AnaplanAPIException e) { logger.info("No Dump file found. Proceeding..."); } } return dumpFileContents.toString(); } }