Improper resource management examples
The following examples illustrate poor resource management practices. We’ll examine specific lines of code to understand what is inefficient and why the code needs improvement.
Code sample: Update operation
Here is a sample Update operation that demonstrates some poor practices regarding input streams.
Example: Update operation
/**
* Sample update operation that demonstrates some poor practices regarding input streams. This is based on actual public
* connector submissions. This is intended as an example of what NOT to do.
*/
public class BadInputStreamSampleOperation extends BaseUpdateOperation {
private static final Logger LOG = LogUtil.getLogger(BadInputStreamSampleOperation.class);
protected BadInputStreamSampleOperation(OperationContext context) {
super(context);
}
@Override
protected void executeUpdate(UpdateRequest request, OperationResponse response) {
SampleClient<InputStream, InputStream> client = new SampleClient<>();
for (ObjectData input : request) {
// convert the input data to a string
String inputString = toString(input.getData());
// log the input data string
LOG.info(inputString);
// use the client to send the input data to the target system and return the response as an input stream
InputStream outputData = client.send(new ByteArrayInputStream(inputString.getBytes()));
// convert the output to a string
String outputString = toString(outputData);
// add a result for this input with the response as a payload
response.addResult(input, OperationStatus.SUCCESS, "0", "ok", PayloadUtil.toPayload(outputString));
}
}
/**
* Utility method to convert an entire input stream to a string. Since this just an example (and you probably
* shouldn't do this anyway), it just returns null but you can assume a proper conversion.
*
* @param input the input stream
* @return string containing the entire contents of the stream
*/
private static String toString(InputStream input) {
return null;
}
}
Inefficient input stream
// convert the input data to a string
String inputString = toString(input.getData());
Why does this code need improvement?
- The
getData()method opens an input stream containing the entire contents of the input document. - All resources need to be released when they are no longer required. In this code, the stream is passed directly to a method that does not close and release the resource. Since the execute method has no handle for the stream, it can’t close it either.
- The stream is converted to a string which is held in memory. Since the string could potentially be quite large, loading it into memory can consume the heap and crash the JVM.
Inefficient logging
// log the input data string
LOG.info(inputString);
Why does this code need improvement?
- The document logger, obtained using
input.getLogger(), and the operation logger, obtained usingresponse.getLogger(), should be preferred over the container log. - The code is attempting to log the entire input document. This is problematic due to the same memory consumption issues as the input stream example. It’s also unnecessary as there are more efficient alternatives. For example, if the connector operation tracks input documents, the document contents are available in Process Reporting. Additionally, most client libraries have the option to enable trace logging to efficiently capture this information during troubleshooting.
See the Connector troubleshooting topic for more information about connector trace logging.
Creating an inefficient stream
// use the client to send the input data to the target system and return the response as an input stream
InputStream outputData = client.send(new ByteArrayInputStream(inputString.getBytes()));
Why does this code need improvement?
- The client accepts an input stream and returns an output stream. It is more efficient to send the original stream directly to the client. Instead, this code creates a new, less efficient stream using the bytes of the previously created string.
- Byte arrays and input streams backed by byte arrays (like
ByteArrayInputStream) are problematic for the same reason as strings: the content is held entirely in memory and large data can consume the heap.
Inefficient output stream handling
// convert the output to a string
String outputString = toString(outputData);
Why does this code need improvement?
- Similar to the input data example, the line of code potentially loads a large amount of data into memory.
- While the method does have a handle for the output data stream, it makes no attempt to release the resource.
Unstructured strings
// add a result for this input with the response as a payload
response.addResult(input, OperationStatus.SUCCESS, "0", "ok", PayloadUtil.toPayload(outputString));
note
There is something this code does well. It adds the required response for the input document. If you do not provide a response for an input, the Atom considers the input “lost” because the connector never acknowledged it by adding a response.
Why does this code need improvement?
- The code illustrates another common mistake: unstructured strings result in poor payloads. For example, if the string contains “success”, which has a relatively small memory footprint, there is no room to expand the response in the future. A better solution is to create a simple JSON or XML payload. You can add additional attributes in the future without breaking an existing process that expects an unstructured string value.
- The code lacks error handling. Any exception that occur leaves any remaining documents unprocessed and the Atom is assign the FAILURE operation status to the documents. However, in most scenarios, an error for one input does not indicate that the remaining documents couldn’t be successfully processed.
- The lack of a
finallyblock means there is no way to guarantee that any open resources (in this example, the input and output data streams) are properly released.
Was this topic helpful?