Best practices for developing application code
Architecture and Design
Follow the best practices listed in this table:
Dos | Don'ts |
---|---|
Use the core platform services and extend them, if | Do not introduce new elements to the BMC Helix Innovation Studio container technology stack that run out of processor start threads, such as the following elements:
|
Use the standard platform technology stack | Do not affect the configuration of the technology stack, such as with Jersey, Spring, OSGi, or Jetty configurations. |
Not applicable |
|
Use HTTP integration to access services that use third-party libraries. Introduce new functionality by requesting for enhancements on the platform. Use provided dependencies, without version dependencies, at own risk. | Do not include any third-party libraries for Java. Use only documented, approved third-party libraries for JavaScript that are already provided with the SDK. |
Use Rx APIs to access data on BMC Helix Innovation Studio. You can use the AR REST API to access data on other Remedy servers as an integration. |
|
Supportability
Follow the best practices listed in this table:
Dos | Don'ts |
---|---|
Make sure that the code logs all errors appropriately. Implement debug level logging to For example, catch RxException (e) { ServiceLocator.getLogger().error("Could not calculate service values with " + paramA + ", " + paramB + ": failed because of " + e.getLocalizedMessage()); // Handle the error. } |
|
Persistence
Follow the best practices listed in this table:
Dos | Don'ts |
---|---|
Use the Record Service, including Attachment Fields, or HTTP-based integration | Do not use the file system. For example: Do not read or write from File I/O streams, for resources, configuration, or any other purpose. // DON'T DO THIS BufferedReader br = new BufferedReader(new FileReader(FILENAME)); while ((sCurrentLine = br.readLine()) != null) { // do something with sCurrentLine } |
Threads and Concurrency
Follow the best practices listed in this table:
Dos | Don'ts |
---|---|
Use time-based rules to trigger a workflow in the background. | Do not start threads. For example, do not implement Runnable to start in a thread. (new Thread(new MyRunnable())).start(); Also, do not extend the thread as follows: public class HelloThread extends Thread { .. . . } |
Consider using the Process Service to help synchronize activities without writing a Java code. | Do not use thread synchronization mechanisms, such as sleep or the synchronized keyword. Do not interact with threading APIs for any other reason. Do not use the synchronized keyword, or sleep() for your current thread. public class SynchronizedCounter { private int c = 0; public synchronized void increment() { c++; } public int getCount() { return c; } } |
Avoid code that can take a long time to execute. Instead, break long background processing into smaller actions or processes. Use time-based rules to start each process so that you can configure them to run in parallel. | Do not block threads on any long-running operation.For example, avoid nested loops where the number of iterations is unknown in advance. Additionally, do not block a thread waiting for a long time for a response from a network resource. solveTravellingSalesmanProblem(); |
Use BMC Integration Service to integrate with any third-party systems outside of BMC Helix Innovation Studio. | Do not make any network calls. |
Memory Management
Follow the best practices listed in this table:
Dos | Don'ts |
---|---|
Store information persistently by using Records and Associations or use the platform-provided Cache Service. | Do not allow information to be shared across threads or client requests. Write a code that is stateless. For example:
Do not store information in members of your Service or JAX-RS resource as displayed in the following example: public class MyService implements Service { // DO NOT do this private static int lastRate = 0; // DO NOT do this - A member variable which implements a cache. private Map rateCache; public MyService() { rateCache = new HashMap(); } /** * A service action */ @Action public Integer computeCost( @ActionParameter(name = "id") @NotBlank @NotNull String id, @ActionParameter(name = "hours") @NotBlank @NotNull int hours, @ActionParameter(name = "rate") @NotBlank @NotNull int rate) { if (lastRate != rate) { // DO NOT do this lastRate = rate; ServiceLocator.getLogger().info("rate has changed from " + lastRate + " to " + rate); } // DO NOT do this Integer cost = rateCache.get(id); if (cost == null) { cost = hours * rate; rateCache.put(id, cost); } return cost; } } |
Use DataPageQuery pattern. Requests for data should be chunked using the DataPageQuery pattern (or some other pattern that supports pagination). Furthermore, the processing of data in preparing the response should use RecordInstanceDataPageQuery. Avoid processing an arbitrary amount of data in a single request. // Get a chunk of data (error handling not shown here) DataPageQueryParameters params = new DataPageQueryParameters(50, 0, propertySelections, null, queryPredicatesByName); DataPage result = ServiceLocator.getRecordService().getRecordInstancesByIdDataPage(params); // Process the chunk List<?> records = result.getData(); List<ResultInfo> resultList = new LinkedList<ResultInfo>(); for (Object record : records){ // Construct the object to be returned. String id = (String)mappedRecord.get(ID); resultList.add(new ResultInfo( (HashMap<String, Object>)record ); } // Return the chunk of data return new DataPage(resultList.size(), resultList); | Do not minimize the loading of large amount of data into memory. |
Use the Record Service to persist data between calls. | Do not interact with thread local storage. Thread local storage is just as ineffective as using a static variable. That is because threads are not allocated to a particular tenant and must not share any state. public class MyService implements Service { // DO NOT use ThreadLocal. private ThreadLocal someData; public MyService() { someData = new ThreadLocal(); } // Something that updates thread storage. // DO NOT do this. @Action public void setData(@ActionParameter(name = "data") @NotBlank @NotNull String data) { someData.set(data); } // Something that accesses thread storage. // DO NOT do this. @Action public String getData() { return (String) someData.get(); } } |
HTTP
Follow the best practices listed in this table:
Dos | Don'ts |
---|---|
Follow the standard syntax as shown in the samples: import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import com.bmc.arsys.rx.services.common.RestfulResource; @Path("example/myresource") public class MyResource implements RestfulResource { @GET @Path("/{id}") public String get(@PathParam("id") String id) { // Do some processing with id. String result = something(); return result; } } | Use standard, supported JAX/RS techniques for custom REST interfaces. |
Deploy services as application or library bundles. | Do not include additional servlet containers or legacy Mid TierHTTP services. |
Security
Follow the best practices listed in this table:
Dos | Don'ts |
---|---|
Use the platform permission constructs. | Do not create your own permission enforcement mechanism. For example, do not attempt to parse group lists and implement a new model. |
Use the security context provided by the platform. | Do not attempt authorization impersonation or any other changes to any security context. |