Skip to main content

ADF Best Practices: Let's do it better

We may happen to implement things in the wrong way and see an expected output, which may break or degrade the performance of the application later. I planned to blog the best practices i come across everyday and from resources i follow. Also i'll be more than happy if you can contribute to this project or correct me through comments:

1. Do not prefix managed bean EL expression every-time with a specific scope:

Sometimes it's good to not specify scope when referencing managed beans in expression language. Never prefix for default servlet scope (requestScope, sessionScope and applicationScope). By prefixing the scope you bypass the JavaServer Faces managed bean facility(handles creation and initializing of managed beans). When scope is prefixed it will only look for in-memory objects and does not instantiate managed beans possibly throwing an NPE. ADF specific scopes (backingBeanScope, viewScope and pageFlowScope) should've a prefix. These scopes are handled by the ADF controller, which takes care of managed bean instantiation.

Example :

 #{sessionScope.beanName.propertyName} → #{beanName.propertyName}

2. Use the right scope:

Using the right scope is really important as its directly proportional to the memory usage. Always use the smallest scope possible. You should try to avoid using the default servlet scopes or plan for a minimum usage. For example, sessionScope is mostly used to store user context but we can find scenarios where it's used to pass/ reuse values between task-flows in which TF parameters should be used instead. Also keep in mind that all sessionScope data is shared across browser tabs.

3. Invoking PL/SQL function or Procedure:

A very common requirement is to call a stored function or procedure to handle business logic or to commit data to database. This should be done by calling the function and procedure in model layer and invoke the model layer method using page level bindings. Here's a tutorial with a simple demo to do this.

4. Entity object and View object must have a primary key:

Framework defines primary key for Business components if they're generated from tables but for Read only view objects and transient view objects, a primary key will not be defined by default. Now you've to manually select the "Key attribute" check box for all primary key attributes on that View object. ADF uses primary key for selection and other processes so if primary key is ignored you may face unexpected behavior in table selection, lov etc..

5. Never set immediate=true on editable components:

immediate property is used to skip validation and immediately submit form, mostly used for cancel/ reset functionality on input forms. However this is available on some input components as well using immediate for these components should be avoided.

6. Do not generate Impl classes without a need:

Impl classes for EO, VO and AM should be generated only when needed. This helps keep the application code size reasonable. Definition classes are very rarely needed so you should almost never see them in the application.

7. Use type safe APIs to access Entity objects and View Objects directly:

When you've generated the Impl classes for your EO/ VO. To set/ access data it's better to use type-safe APIs instead of generic framework APIs.

Without using type-safe APIs you access an attribute by calling:

row.getAttribute("UserId");

or

row.getAttribute(0);

Developers rarely use this numerical approach so when you access using row.getAttribute("UserId") the framework internally fetches the attribute index based on the attribute name before accessing the attribute. This lookup can be avoided by generating and using the type-safe methods in *ObjectImpl classes. Also as they are type-safe these methods ensure an operation is working on right kind of row data with compile-time checking of the correct datatype usage.

Number userId = (Number) row.getAttribute("UserId");

can be simplified to:

Number userId = row.getUserId();

8. Never cache ApplicationModule references:

Caching of resources is common with developers to improve performance of the application but caching AM reference in a backing bean gives undesired results. As ApplicationModules are pooled resources, there's no guarantee you'll be using the same ApplicationModule instance every-time. So using cached AM instance may result in NPE or can provide wrong data. These issues are hard to find as they occur only when the system is under stress.

9. Iterate rows using createRowSetIterator():

There are multiple ways to iterate over View object's rows as in below:


  • ViewObjectImpl.getAllRowsInRange();
  • ViewObjectImpl.getRowSet();
  • ViewObjectImpl.getRowSetIterator();
  • ViewObjectImpl.createRowSetIterator();

All the above will work but using createRowSetIterator() is always the best option as it will not effect the selected  row on the View object and the most important thing:
Never forget to close the RowSetIterator

10. Do not save state in AppModuleImpl:

Creating class objects for loggers or any util classes within AppModuleImpl is fine but never store any custom fields like user roles or any preferences info etc... this creates data vulnerability and unpredictable application behavior. As AM instances are pooled resources users may see wrong data from other user sessions.

11. Cut the overhead:

It's very common to see queries with "SELECT * FROM" in view objects which gets many unused columns instead get the right data for performance as adding columns is easy.

12. Take Serialization seriously:

A must do thing ignored by most of us is to implement the Java Serializable interface for beans in viewScope, pageFlowScope and sessionScope. This always gets overlooked because your application works fine unless it's deployed to a cluster environment with fail over. When a fail over occurs in a clustered environment state from one cluster should be transferred to the other. To perform this beans in View , Page Flow and Session scope must implement Serializable interface.
Fail over will not occur between the start and the end of a request so beans with request and backing bean scope need not be Serializable and these request scoped cannot be serialized because they contain bindings to UIComponent objects which are not serializable.

*To implement Serializable you have to make sure no component bindings are created for beans registered in View, Page Flow or Session Scope.

13. Avoid UI Component binding if not keep limit binding to minimum scoped beans:

Creating binding for UI components in managed bean is developers favorite choice to get complete control of component but we happen to bind it to wrong places most of the times. Any binding for UI components shouldn't be made with beans greater than requestScope or backingBeanScope because:


  1. UI components are not serializable so your application may break in a clustered environment.
  2. UI component tree may not be released correctly which significantly increases the memory load of your application.
If its mandatory to be declared in a bean with pageFlowScope make sure you declare the binding as transient which solves only first issue and not recommended. To address both issues use: 

org.apache.myfaces.trinidad.util.ComponentReference

Even the above usage can create some terrible issues with bean in sessionScope so you should always think of avoiding UI component binding.






Comments

Popular posts from this blog

Spring Boot - RestTemplate PATCH request fix

  In Spring Boot, you make a simple http request as below: 1. Define RestTemplate bean @Bean public RestTemplate restTemplate () { return new RestTemplate (); } 2. Autowire RestTemplate wherever you need to make Http calls @Autowire private RestTemplate restTemplate ; 3. Use auto-wired RestTemplate to make the Http call restTemplate . exchange ( "http://localhost:8080/users" , HttpMethod . POST , httpEntity , String . class ); Above setup works fine for all Http calls except PATCH. The following exception occurs if you try to make a PATCH request as above Exception: I / O error on PATCH request for \ "http://localhost:8080/users\" : Invalid HTTP method: PATCH ; nested exception is java . net . ProtocolException : Invalid HTTP method: PATCH Cause: Above exception happens because of the HttpURLConnection used by default in Spring Boot RestTemplate which is provided by the standard JDK HTTP library. More on this at this  bug Fix: This can b...

RADUS#4 - Caching the response in REST API's

  Caching in spring boot app: Caching can be used to provide a performance boost to your application users by avoiding the business logic processing involved again and again, load on your DB, requests to external systems if the users request data that's not changed frequently Different types of caching: We'll be focusing more on in-memory caching in this post i listed other options available to have an idea. In-memory caching You'll have a key-value data stores that stores the response of the request after it is served for the first time There are multiple systems like Redis, Memcached that do this distributed caching very well By default Spring provides concurrent hashmap as default cache, but you can override CacheManager to register external cache providers. Database caching Web server caching Dependencies needed: Maven < dependency > < groupId > org . springframework . boot </ groupId > < artifactId > spring - boot - starter - cache ...

Set BIND VARIABLE and EXECUTE QUERY programmatically in ADF

A very common scenario in ADF is to set a bind variable and execute query programmatically within AMImpl/ VOImpl classes. Here's a simple way to do this: To set bind variable for all rowsets:       ViewObjectImpl someVO = this.getSomeViewObject();       VariableValueManager vMngr = someVO.ensureVariableManager();        vMngr.setVariableValue("DefinedBindVariable",value);        someVO,executeQuery(); To set bind variable for default rowset:          ViewObjectImpl someVO = this.getSomeViewObject();          someVO.setNamedWhereClauseParam("DefinedBindVariable",value);          someVO,executeQuery();