Dec 20, 2013

Recently i ran into a situation where i had to specify a default value for bind variable in ViewAccessor  and access the value in the lookup view object. Upon analysis, I found that when i accessed the value using bind variable accessor, the value was always null but in the overridden executeQueryForCollection method i was always receiving the default value, which i had specified, in the params parameter. Needless to say i did not want to access the params object for accessing the bind value, so to fix the issue i changed the RowLevelBinds property of the view accessor to false. Note that this property should only be used if your rowset of the lookup does not change depending upon the current rows’ attribute which essentially means that if you have to fetch a bunch of data like let’s say a list of all organizations and you want to avoid re-querying of the data based on your current rows’ (the view object row where you are using the lookup) attributes you are better off by using the RowLevelBinds=false.

Doing so leads to the following benefits

a) Avoids re-querying of the database b)More important, in my case be able to access the default bind value that i had specified on the viewaccessor using bind variable accessor.

 

Note: At this point of time i do not know whether this is a bug or the intended behaviour that causes the default value to be discarded when using the bind variable accessor.

Posted on Friday, December 20, 2013 by Unknown

Dec 14, 2013

ADF provides strong transaction handling mechanisms for database based applications which are backed by entity and view objects. But what if you have a application which does not use database ? How do you then detect changes to your data ?.  Let’s say you have a web service backed programmatic view objects and you have extended your application to work without a database and user saves some data and clicks on update record ? How would you then verify a) whether the data has changed b) handle commits and rollback c) warn user of uncommitted data changes

In this post i will share the solution to above problems.

1) Detecting changes: If you are using data controls and bindings then you can detect changes when the user submits the data by using the following snippet of code. Note that doing this is very relevant as you do not want to call the backend web service or execute some update code where no action is required.

DCBindingContainer dcBindingContainer=(DCBindingContainer)
BindingContext.getCurrent().getCurrentBindingsEntry();
if(dcBindingContainer.getDataControl().isTransactionModified()){
// Handle update calls to your service here
}
else{
//well nothing to do here maybe notify the user of the same
JSFUtils.addFacesInformationMessage("No change detected");
}


2. Handling Commit or Rollback: Now to handle commits or rollback you can use the following code snippet also note that you must rollback or commit the transaction manually.



//To commit the transaction
dcBindingContainer.getDataControl().commitTransaction();
//To rollback the transaction
dcBindingContainer.getDataControl().rollbackTransaction();


3. Implementing Uncommitted Data Changes warning: To prevent navigation between regions you can implement your own logic on a similar lines as shown in the following code snippet. The backing bean snippet is shown below followed by the test page code.



public class NavBacking implements Serializable {
private static final long serialVersionUID = 1L;
//this will hold the value of task flow id being passed in the setter
private String newTaskFlowId;
//this is where the value of task flow id is picked from
private String taskFlowId

public void setTaskFlowId(String taskFlowId) {
//here we will override the setter to set the taskflow value into our own variable for the time being
this.newTaskFlowId=taskFlowId;

}
/**
* This method is used to check whether the transaction is dirty or not
* and then it launches a popup to confirm from user whether he wants to
* discard the changes or not
* @return
*/
public String checkChanges() {
DCBindingContainer dcBindingContainer=(DCBindingContainer)
BindingContext.getCurrent().getCurrentBindingsEntry();
if(dcBindingContainer.getDataControl().isTransactionModified()){
/**
*check placed here to confirm whether the oldtaskflow id and new taskflow id are same
*and if they are it allows the change
*
*/

if(!taskFlowId.equals(newTaskFlowId)){
FacesContext context = FacesContext.getCurrentInstance();
ExtendedRenderKitService erks =
Service.getRenderKitService(context, ExtendedRenderKitService.class);
//show popup
erks.addScript(context,"AdfPage.PAGE.findComponent('"+popupId +"').show();");
}
}
else{
this.taskFlowId=newTaskFlowId;
}

return null;
}
/**
* If the user insists on discarding changes rollback the transaction
* @return
*/
public String okAction() {
DCBindingContainer dcBindingContainer=(DCBindingContainer)
BindingContext.getCurrent().getCurrentBindingsEntry();
dcBindingContainer.getDataControl().rollbackTransaction();
this.taskFlowId=newTaskFlowId;
return null;
}
}

//Unbounded taskflow page which is used to call the bounded taskflow

<af:popup id="pp3" animate="default" childCreation="deferred"  clientComponent="true">
<af:dialog id="dg1" closeIconVisible="false" type="none"
title="Uncommitted Data Warning" >
<f:facet name="buttonBar">
<af:toolbar id="tb1">
<af:commandButton id="cb1"
text="OK" immediate="true" action="#{viewScope.NavBacking.okAction}"/>
<af:commandButton id="cb2"
text="CANCEL" immediate="true" action=" "/>
</af:toolbar>
</f:facet>
<af:inputText id="opt1" readOnly="true" wrap="hard"
value="You have made some changes are you sure you want to continue"/>
<af:spacer id="sp2" height="5"/>
</af:dialog>
</af:popup>
// now you have to ensure that each command link enforces a call to checkChanges

<af:commandLink text="pageName"
visible="true" immediate="true"
action="#{viewScope.NavBacking.checkChanges}"
id="cl2">
<af:setPropertyListener type="action"
from="taskflowIdGoesHere"
to="#{viewScope.NavBacking.taskFlowId}"/>
</af:commandLink>

Note: For this code to work your bounded taskflow must share the datacontrol with the calling unbounded taskflow.

Screenshots:-







Links and references:-



Checking Dirty Data by Jobinesh

Posted on Saturday, December 14, 2013 by Unknown

Nov 14, 2013

ADF essentials release does not include the two IDM jar files that are used for OPSS integration (these are included with normal ADF release) .  Although to use OPSS APIs’ features one does not require a porting effort. I just hope that Oracle can offer these with the ADF essentials release. Here are the reasons why Oracle should do it.

  1. OPSS API’s are Directory server vendor agnostic: The same code works for OID as it works for OpenLdap and Active Directory (you only have to switch the providers).
  2. OPSS API’s are container agnostic: The same code runs on weblogic as on glassfish
  3. ADF essentials lacks a security framework; these API’s could be used to fill the gap.

The following snippet is based on a earlier post of mine which used OID as a provider, this snippet however, is configured for open ldap provider.

package model;

import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.logging.Level;

import oracle.adf.share.ADFContext;
import oracle.adf.share.logging.ADFLogger;
import oracle.adf.share.security.SecurityContext;
import oracle.jbo.JboException;

import oracle.security.idm.ComplexSearchFilter;
import oracle.security.idm.IMException;
import oracle.security.idm.Identity;
import oracle.security.idm.IdentityStore;


import oracle.security.idm.IdentityStoreFactory;
import oracle.security.idm.IdentityStoreFactoryBuilder;
import oracle.security.idm.ModProperty;
import oracle.security.idm.ObjectNotFoundException;
import oracle.security.idm.OperationNotSupportedException;
import oracle.security.idm.Role;
import oracle.security.idm.RoleManager;
import oracle.security.idm.RoleProfile;
import oracle.security.idm.SearchFilter;
import oracle.security.idm.SearchParameters;
import oracle.security.idm.SearchResponse;
import oracle.security.idm.SimpleSearchFilter;
import oracle.security.idm.User;
import oracle.security.idm.UserManager;
import oracle.security.idm.UserProfile;
import oracle.security.idm.providers.oid.OIDIdentityStoreFactory;
import oracle.security.idm.providers.openldap.OLdapIdentityStoreFactory;
/**
*This class can be used to perform operation on OpenLdap using OPSS API
* @author Ramandeep Nanda
*/

public class LDAPOperations {
public static final ADFLogger LDAPLogger=ADFLogger.createADFLogger(LDAPOperations.class);
private static final ResourceBundle rb =
ResourceBundle.getBundle("model.myresourcebundle");
/**
*
* @return The store instance for OpenLDAP store
*/
public static IdentityStore getStoreInstance(){
return IdentityStoreConfigurator.initializeDefaultStore();
}
public static IdentityStoreFactory getIdentityStoreFactory(){
return IdentityStoreConfigurator.idStoreFactory;
}


/**
* Assigns the specified role to the user
* @param roleName the role to assign
* @param userName the user to assign role to
*/
public static void assignRoleToUser(String roleName,String userName){
String methodName=Thread.currentThread().getStackTrace()[1].getMethodName();
IdentityStore store = LDAPOperations.getStoreInstance();
try {
Role role= store.searchRole(IdentityStore.SEARCH_BY_NAME,roleName);
User user= store.searchUser(userName);
RoleManager rm=store.getRoleManager();
if(!rm.isGranted(role, user.getPrincipal())){
rm.grantRole(role, user.getPrincipal());
}

} catch (IMException e) {
LDAPLogger.severe("Exception in "+methodName + "Could not assign role ["+roleName+"] to the user ["+userName +"] because of " +e.getMessage() +" ", e);
throw new JboException("Could not assign role ["+roleName+"] to the user ["+userName +"] due to "+e.getMessage());

}
finally {
try{
store.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}
}
/**
* Assigns the specified role to the user
* @param roleNames the roles to assign
* @param userName the user to assign role to
* @return the set of users who are assigned roles
*/
public static Set assignRolesToUser(Set roleNames,String userName){
Set rolesAssigned=new HashSet();

String methodName=Thread.currentThread().getStackTrace()[1].getMethodName();
IdentityStore store = LDAPOperations.getStoreInstance();
String roleName=null;
try {
User user= store.searchUser(userName);
Principal userPrincipal=user.getPrincipal();
RoleManager rm=store.getRoleManager();
Iterator it=roleNames.iterator();
while(it.hasNext()){
roleName=(String)it.next();
Role role= store.searchRole(IdentityStore.SEARCH_BY_NAME,roleName);
if(!rm.isGranted(role, user.getPrincipal())){
rm.grantRole(role,userPrincipal);
rolesAssigned.add(roleName);
}
}
} catch (IMException e) {

LDAPLogger.severe("Exception in "+methodName + "Could not assign role ["+roleName+"] to the user ["+userName +"] because of " +e.getMessage() +" ", e);
throw new JboException("Could not assign role ["+roleName+"] to the user ["+userName +"] due to "+e.getMessage());


}
finally {
try{
store.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}

return rolesAssigned;
}
/**
* Assigns the specified role to the user
* @param roleName the role to assign
* @param users the users to assign role to
* @return The users who are assigned the role
*/
public static Set assignRoleToUsers(String roleName,Map users){
Set usersAssigned=new HashSet();
String methodName=Thread.currentThread().getStackTrace()[1].getMethodName();
IdentityStore store = LDAPOperations.getStoreInstance();

Set entrySet = users.entrySet();
Iterator it=entrySet.iterator();
String userName=null;

try {
Role role= store.searchRole(IdentityStore.SEARCH_BY_NAME,roleName);
RoleManager rm=store.getRoleManager();
while(it.hasNext()){
Map.Entry entry=(Map.Entry)it.next();
userName=(String)entry.getKey();
User user= store.searchUser(userName);
if(!rm.isGranted(role, user.getPrincipal())){
rm.grantRole(role, user.getPrincipal());
usersAssigned.add(user);
}
}
} catch (IMException e) {
LDAPLogger.severe("Exception in "+methodName + "Could not assign role ["+roleName+"] to the user ["+userName +"] because of " +e.getMessage() +" ", e);
}
finally {
try{
store.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}
return usersAssigned;
}

//revoke sample below It is similar to the above mentioned assign case so mentioning a sample operation

/**
* To remove the role from user
* @param roleName the role to remove/ revoke
* @param userName the user from which to revoke role
*/
public static void removeRoleFromUser(String roleName,String userName){
String methodName=Thread.currentThread().getStackTrace()[1].getMethodName();
IdentityStore store = LDAPOperations.getStoreInstance();
try {
Role role= store.searchRole(IdentityStore.SEARCH_BY_NAME,roleName);

User user= store.searchUser(userName);
RoleManager rm=store.getRoleManager();
if(rm.isGranted(role, user.getPrincipal())){
rm.revokeRole(role, user.getPrincipal());
}
} catch (IMException e) {
LDAPLogger.severe("Exception in "+methodName + "Could not revoke role ["+roleName+"] from the user ["+userName +"] because of " +e.getMessage() +" ", e);
throw new JboException("Could not remove role ["+roleName+"] from the user ["+userName +"] due to "+e.getMessage());

}
finally {
try{
store.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}
}
public static void dropUserWithRoles(String userId){
UserManager um = null;
IdentityStore store=null;
User newUser = null;
try {
store = LDAPOperations.getStoreInstance();
User user = store.searchUser(IdentityStore.SEARCH_BY_NAME, userId);
um=store.getUserManager();
if (user != null) {
//drop user if already present
um.dropUser(user);
RoleManager rm = store.getRoleManager();
Principal userPrincipal= user.getPrincipal();
SearchResponse resp=rm.getGrantedRoles(userPrincipal, true);
while(resp.hasNext()){
rm.revokeRole((Role)resp.next(), user.getPrincipal());
}
}
}
catch (IMException e) {
LDAPLogger.info("[dropUser]" +

e);

}
finally {
try{
store.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}
}
public static void dropUser(String userId){
UserManager um = null;
User newUser = null;
IdentityStore store=null;

try {
store = LDAPOperations.getStoreInstance();
User user = store.searchUser(IdentityStore.SEARCH_BY_NAME, userId);
um=store.getUserManager();
if (user != null) {
//drop user if already present
um.dropUser(user);
}
}
catch (IMException e) {
LDAPLogger.info("[dropUser]" +
e);

}
finally {
try{
store.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}
}

/**
* Gets the userProfile of the logged in user
* @param approverUser
* @return
*/
public static oracle.security.idm.UserProfile getUserProfile(String approverUser) {
IdentityStore store = LDAPOperations.getStoreInstance();
oracle.security.idm.UserProfile profile=null;
try {
User user= store.searchUser(approverUser);
profile=user.getUserProfile();

} catch (IMException e) {
LDAPLogger.info("Could not find user in OID with supplied Id"+approverUser);
throw new JboException(e.getMessage());
}
finally {
try{
store.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}
return profile;
}
/**
* Gets all the roles
* @return
*/
public static List getAllRoles(){
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
List returnList=new ArrayList();
IdentityStore store = LDAPOperations.getStoreInstance();

try{
SimpleSearchFilter filter=store.getSimpleSearchFilter(RoleProfile.NAME,SimpleSearchFilter.TYPE_EQUAL,null);
String wildCardChar=filter.getWildCardChar();
// Here the default_role is a property this is just a placeholder can be any pattern you want to search
filter.setValue(wildCardChar+rb.getString("DEFAULT_ROLE")+wildCardChar);
SearchParameters parameters=new SearchParameters(filter,SearchParameters.SEARCH_ROLES_ONLY) ;
SearchResponse resp=store.searchRoles(Role.SCOPE_ANY,parameters);
while(resp.hasNext()){
Role role=(Role)resp.next();
String tempRole=role.getPrincipal().getName();
returnList.add(tempRole);
}
store.close();
}catch(IMException e){
LDAPLogger.severe("Exception in "+methodName + " " +e.getMessage() +" ", e);
throw new JboException(e.getMessage());
}
finally {
try{
store.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}

return returnList;
}
/**
* Fetches all the roles assigned to the user
* @param userName
* @return
*/
public static List getAllUserRoles(String userName, String searchPath) {
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
List returnList=new ArrayList();
IdentityStoreFactory storeFactory = LDAPOperations.getIdentityStoreFactory();
IdentityStore store=null;
String[] userSearchBases= {rb.getString(searchPath)};
String[] groupSearchBases= {rb.getString("group.search.bases")};
Hashtable storeEnv=new Hashtable();
storeEnv.put(OLdapIdentityStoreFactory.ADF_IM_SUBSCRIBER_NAME,rb.getString("oidsubscribername"));
storeEnv.put(OLdapIdentityStoreFactory.RT_USER_SEARCH_BASES,userSearchBases);
storeEnv.put(OLdapIdentityStoreFactory.RT_GROUP_SEARCH_BASES,groupSearchBases);

try{
store = storeFactory.getIdentityStoreInstance(storeEnv);
User user= store.searchUser(IdentityStore.SEARCH_BY_NAME,userName);
RoleManager mgr=store.getRoleManager();
SearchResponse resp= mgr.getGrantedRoles(user.getPrincipal(), false);
while(resp.hasNext()){
String name= resp.next().getName();
returnList.add(name);
}

}catch(IMException e){
LDAPLogger.severe("Exception in "+methodName + " " +e.getMessage() +" ", e);
throw new JboException(e.getMessage());
}
finally {
try{
store.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}

return returnList;
}

/**
*Use to change the passoword for logged in user It uses ADF Security Context to get logged in user
*
**/
public static void changePasswordForUser(String oldPassword,String newPassword, String userName){
String methodName =
java.lang.Thread.currentThread().getStackTrace()[1].getMethodName();
SecurityContext securityContext =
ADFContext.getCurrent().getSecurityContext();
String user = securityContext.getUserName();
IdentityStore oidStore=null;
oidStore = LDAPOperations.getStoreInstance();
try {
UserManager uMgr = oidStore.getUserManager();
User authUser =
uMgr.authenticateUser(user, oldPassword.toCharArray());

if (authUser != null) {
UserProfile profile = authUser.getUserProfile();

profile.setPassword( oldPassword.toCharArray(),
newPassword.toCharArray());
}
} catch (IMException e) {
if (LDAPLogger.isLoggable(Level.SEVERE)) {
LDAPLogger.severe("[" + methodName +
"] Exception occured due to " + e.getCause(),
e);
}
throw new JboException(e.getMessage());
}
finally {
try{
oidStore.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}


}
/**
* It is used to generate a random password
*@code{
* generateRandomPassword();
* }
* @return
*/
public static String generateRandomPassword() {
String password =
Long.toHexString(Double.doubleToLongBits(Math.random()));
int passLength = password.length();
if (passLength >= 8) {
password = password.substring(passLength - 8, passLength);
}

return password;
}

/**
* Resets the password for user
*
**/
public static void resetPasswordForUser(String userId)
{
String methodName =
java.lang.Thread.currentThread().getStackTrace()[1].getMethodName();
IdentityStore oidStore = LDAPOperations.getStoreInstance();
User user = null;
try {
user = oidStore.searchUser(userId);
if (user != null) {
UserProfile userProfile = user.getUserProfile();
List passwordValues =
userProfile.getProperty("userpassword").getValues();
ModProperty prop =
new ModProperty("PASSWORD", passwordValues.get(0),
ModProperty.REMOVE);
userProfile.setProperty(prop);
String randomPassword = generateRandomPassword();
userProfile.setPassword(null, randomPassword.toCharArray());
}
} catch (IMException e) {
LDAPLogger.severe("[" + methodName + "]" +
"Exception occured due to ", e);

}
finally {
try{
oidStore.close();
}
catch (IMException e) {
LDAPLogger.severe("Exception occured in closing store");
}
}

}


/**
* This nested private class is used for configuring and initializing a store instance
* @author Ramandeep Nanda
*/
private static final class IdentityStoreConfigurator {
private static final IdentityStoreFactory idStoreFactory=initializeFactory();


private static IdentityStoreFactory initializeFactory(){
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
IdentityStoreFactoryBuilder builder = new
IdentityStoreFactoryBuilder();
IdentityStoreFactory oidFactory = null;
try {
Hashtable factEnv = new Hashtable();
factEnv.put(OLdapIdentityStoreFactory.ST_SECURITY_PRINCIPAL,rb.getString("oidusername"));
factEnv.put(OLdapIdentityStoreFactory.ST_SECURITY_CREDENTIALS, rb.getString("oiduserpassword"));
factEnv.put(OLdapIdentityStoreFactory.ST_SUBSCRIBER_NAME,rb.getString("oidsubscribername"));
factEnv.put(OLdapIdentityStoreFactory.ST_LDAP_URL,rb.getString("ldap.url"));
factEnv.put(OLdapIdentityStoreFactory.ST_USER_NAME_ATTR,rb.getString("username.attr"));
oidFactory = builder.getIdentityStoreFactory("oracle.security.idm.providers.openldap.OLdapIdentityStoreFactory", factEnv);
}
catch (IMException e) {
LDAPLogger.severe("Exception in "+methodName + " " +e.getMessage() +" ", e);
//re throw exception here
}
return oidFactory;
}
private static IdentityStore initializeDefaultStore(){
IdentityStore store=null;
String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();
String[] userSearchBases= {rb.getString("user.search.bases")};
String[] groupCreateBases= {rb.getString("group.search.bases")};
String []usercreate={rb.getString("user.create.bases")};
String [] groupClass={rb.getString("GROUP_CLASSES")};
Hashtable storeEnv=new Hashtable();
storeEnv.put(OLdapIdentityStoreFactory.ADF_IM_SUBSCRIBER_NAME,rb.getString("oidsubscribername"));
storeEnv.put(OLdapIdentityStoreFactory.RT_USER_SEARCH_BASES,userSearchBases);
storeEnv.put(OLdapIdentityStoreFactory.RT_GROUP_SEARCH_BASES,groupCreateBases);
storeEnv.put(OLdapIdentityStoreFactory.RT_USER_CREATE_BASES,usercreate);
storeEnv.put(OLdapIdentityStoreFactory.RT_USER_SELECTED_CREATEBASE,rb.getString("user.create.bases"));
storeEnv.put(OLdapIdentityStoreFactory.RT_GROUP_OBJECT_CLASSES,groupClass);
try{
store = model.LDAPOperations.IdentityStoreConfigurator.idStoreFactory.getIdentityStoreInstance(storeEnv);
}
catch (IMException e) {
LDAPLogger.severe("Exception in "+methodName + " " +e.getMessage() +" ", e);
// re throw exception here

}
return store;

}


}
}


The resource bundle properties used in this code are mentioned below.



ldap.url=ldap://localhost:389
user.create.bases=ou=Users,dc=ramannanda,dc=blogspot,dc=com
username.attr=uid
oidusername=cn=Manager,dc=ramannanda,dc=blogspot,dc=com
GROUP_CLASSES=groupOfUniqueNames
#not safe
oiduserpassword=oracle
user.search.bases=ou=users,dc=ramannanda,dc=blogspot,dc=com
group.search.bases=cn=groups,dc=ramannanda,dc=blogspot,dc=com
oidsubscribername=dc=ramannanda,dc=blogspot,dc=com


Here’s the link to earlier post: OPSS API's with OID



 



Let’s hope they offer the use of these API’s at a nominal cost or include it with ADF essentials for free.

Posted on Thursday, November 14, 2013 by Unknown

Oct 13, 2013

To trace an existing J2EE application (or a legacy application,legacy here means the ones still not using CDI) at database layer is not easy, especially if that application does not have any reference to the user whom you want to trace. A cumbersome way would be to pass the user name or id from the view layer to each method you call on model layer and then pass it further down to class method from which you obtain the database connection. But, there is a far easy solution that i am going to discuss in this post. The solution that i am going to discuss can be used to enable database tracing for any legacy application and that too far easily.

The major issue with the existing applications is that they cannot access HttpSession from the model layer and hence cannot obtain the user id or user name of the user. To overcome this scenario we can use ThreadLocal class or any implementation of it (in this post i am going to use slf4j MDC class). A ThreadLocal variable is local to the currently executing thread and it cannot be altered by a concurrent thread,so we can use this variable to store the user information. But in case of web applications, during a user’s session, it is most likely that each of his/her request will be handled by a separate thread, So to ensure that the user’s information is kept stored in ThreadLocal variable, we can use a filter which can take the user id from the HttpSession variable and store it in the ThreadLocal variable. Also to avoid memory leaks we can remove the variable once a request is completed. Once this variable is stored it can be accessed from any class that is called by this thread, hence we easily achieve the goal of getting the information we need to enable the trace at database layer. The following code snippets show how it can be achieved.

The Filter Class :-

import org.slf4j.MDC;

public class UserIdInjectingFilter implements Filter{

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

HttpSession session=((HttpServletRequest)request).getSession(false);

if(session!=null){
if(session.getAttribute("userID")!=null){
//Here we populate the MDC
MDC.put("userID", (String)session.getAttribute("userID"));

}
}
chain.doFilter(request, response);

finally{
//Be sure to remove it, will cause memory leaks and permgen out of space errors. if not done so
MDC.remove("userID");
}


}

The central database connection management class methods:-

.....

private Connection connection=null;

Connection getDBConnection(){

CallableStatement cs=null;

try{
Context ctx=new InitialContext();
Context initContext = new InitialContext();
DataSource ds=(DataSource)initContext.lookup("jdbc/TestDS");
connection=ds.getConnection();
//get the value from thread local variable
String userId=MDC.get("userID");

cs=connection.prepareCall("begin set_cid(?,?,?); end;");
cs.setString(1, userId);
String invokingMethodName=Thread.currentThread().getStackTrace()[3].getMethodName();
String invokingClassName=Thread.currentThread().getStackTrace()[3].getClassName();
cs.setString(2,invokingClassName);
cs.setString(3,invokingMethodName);
cs.executeUpdate();
}catch(NamingException nameEx){
// handle exception here
}

// Be Specific :-)

catch(Exception sqlEx){
// catch your exception here
}

finally{

try {
cs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
return connection;
}

/**
*Before closing the connection unset the bunch of identifiers
*/
public void closeConnection(){
//Bunch of close statements
if(connection != null && connection.isClosed()==false){
CallableStatement cs=connection.prepareCall("begin clear_cid(); end;");
cs.executeUpdate();
cs.close();
connection.close();
}
}catch(SQLException sqlEx){
// handle your exception here
}


}
.....

The PL/SQL procedures to set the identifiers:

create or replace procedure set_cid(p_cid varchar2,p_module_id varchar2,p_method_id varchar2)
is
begin
DBMS_APPLICATION_INFO.SET_CLIENT_INFO (p_cid);
DBMS_APPLICATION_INFO.SET_MODULE (p_module_id,p_method_id);

end set_cid;


create or replace procedure clear_cid
is
begin
DBMS_APPLICATION_INFO.SET_CLIENT_INFO (' ');
DBMS_APPLICATION_INFO.SET_MODULE ('','');

end clear_cid;

The query to see the details:-

select client_info,module,action from v$session


 


Hope this helps !

Posted on Sunday, October 13, 2013 by Unknown

Oct 3, 2013

I had developed this utility on swing back in 2009. This utility can be used to schedule shutdown, lock, sleep, hibernate, reboot and log off operations in windows.

Earlier this utility was based on external dependencies and due to this dependency the utility was not working anymore . Now I have created two versions of the utility Shutdown-basic* and Shutdown jar. The difference lies in the component used to select the date and time for scheduling the operation.

Shutdown.jar:  This utility utilizes a advanced swing component to select date and time.  As this swing component is for trial version only, using it after the trial period might give you exception during launch, but the functionality will still work.

Shutdownbasic.jar:  This is a standalone utility which does not use any proprietary swing component, so it will work without issues.

Using the application:-

  • The jar is runnable, so just launch the application by opening command prompt at the application folder and invoking "java -jar shutdown.jar" (without the quotes) or “java –jar shutdownbasic.jar” command or you can set it to open with java by default.
  • Select the time and date for the scheduling the operation.
  • Select the operation from the drop down box that you want to execute and click on the submit button.
  • One can always abort the current operation in between by clicking on the abort button.
  • The label at the bottom will display the time left for the execution of the operation.

 

Download the application from the link below:-








Following are the snapshots of the application:-


shutdown utility shutdown utility2

shutdown utility3 shutdown utility4



Note:- Its always recommended to create a shortcut for running jar files. So just create a shortcut and in the target prefix the command 'javaw -jar' and save the shortcut.

Posted on Thursday, October 03, 2013 by Unknown

Aug 13, 2013

To determine code quality of an application,lot of tools are available such as PMD, FindBugs,etc. During the product development life-cycle there are various phases such as unit testing, code coverage analysis, performance testing ,and code quality analysis. SonarQube is one such open source tool that aggregates the aforementioned metrics into a single dashboard through which one can see, manage and iteratively improve upon such aspects. SonarQube has a plug-in based architecture and you can use this tool to analyze different languages within the same project and can maintain a central repository to track changes, assign and resolve issues.

In this post i will share an example of code analysis done by SonarQube for an ADF project, I have configured MySQL database as a repository for the SonarQube application. I have also extended the existing rule sets by adding a XPATH rule; This rule is based on PMD xpath template that flags the use of getRowSetIterator() method. The steps for running the analysis are mentioned below.
  1. Assuming sonar-runner in your classpath and SONAR_RUNNER_HOME  is configured, create sonar-project.properties in the application directory of your application as shown below :-
    # required metadata
    sonar.projectKey=com.blogspot.ramannanda.MasterDetail
    sonar.projectName=ADF Project
    sonar.projectVersion=1.0
     
    # optional description
    sonar.projectDescription=Master Detail Example
    
    #project modules
    sonar.modules=Model,ViewController
    
    # The value of the property must be the key of the language.
    sonar.language=java
    
    # Encoding of the source code
    sonar.sourceEncoding=UTF-8



  2. Create module specific sonar-project.properties files as shown below
    #For model project
    # required metadata
    sonar.projectName=Model
    sonar.projectVersion=1.0
     
    # path to source directories (required)
    sonar.sources=src
     
    # optional description
    sonar.projectDescription=Model for Master Detail
    
    #path to binaries
    sonar.binaries=classes 

    For viewcontroller project the configuration is similar as shown below

    # required metadata
    sonar.projectName=ViewController
    sonar.projectVersion=1.0
     
    # path to source directories (required)
    sonar.sources=src
    
    #optional description
    sonar.projectDescription=ViewController project for 
    
    #path to binaries
    sonar.binaries=classes 



  3. Just type the sonar-runner command in the application directory and analysis will be performed, after which you can open the application and view the analysis for the project


Extending the rule sets:-


Now i will provide an example of how to extend  the application with a custom XPATH rule.



  1. Go to Quality Profile menu and select a java profile


  2. Search for xpath rule; there might be two disabled rules. I have chosen the PMD xpath template rule, as it is currently the supported rule for java and expressions can easily be checked using PMDDesigner utility. The screenshot is shown below.SonarQube _Rule


  3. The xpath expression for the rule is shown in the below snippet.
    //PrimaryExpression/PrimaryPrefix/Name[contains(@Image,'getRowSetIterator')]



  4. Activate the rule and run the analysis with sonar-runner utility; After running the analysis we can see the rule violations for the project.


sonarqube_custom_pmd_rule





As can be seen from the screen-shot, we also have the option to assign the issue to an individual, browse the source code, see how long a issue has been opened, etc. So all in all this tool is a good place to maintain the metrics about the project. Probably, in future, people can extend the functionality further for ADF application by creating more rule sets applicable to ADF application.





References:-


http://www.sonarqube.org/


http://pmd.sourceforge.net/pmd-5.0.5/xpathruletutorial.html


Some Screens:


SonarQube - ADFProject


SonarQube_treemap

Posted on Tuesday, August 13, 2013 by Unknown

Apr 22, 2013

Developers while creating the applications often ignore guidelines of closing result sets which is the most common cause of memory leaks. In ADF applications it is the RowSetIterators that need to be closed . This scenario can easily be simulated under load testing using jmeter and turning on memory sampling in JVisualVM.  In this post i will share an example of a one page application in which there is a depiction of master detail relationship b/w a department and its employees. There is a disabled af:inputtext on the fragment that is bound to a backing bean and its getter method has code for iterating the employee table and calculating the total employee salary. Initially i have created a secondary row set iterator and left it open just to examine the memory leaks under load that occur due to this open reference. The code in the backing bean is shown below.

    public Number getSum() {
DCBindingContainer bc=(DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
EmpVOImpl empVoImpl=(EmpVOImpl)bc.findIteratorBinding("EmpVO1Iterator").getViewObject();
Number totalAmount=new Number(0);
RowSetIterator it= empVoImpl.createRowSetIterator(null);
while(it.hasNext()){
EmpVORowImpl row=(EmpVORowImpl)it.next();
if(row.getSal()!=null){
totalAmount=row.getSal().add(totalAmount);
}
}
it.closeRowSetIterator();
this.sum=totalAmount;
return sum;
}


So to identify that you have open iterator issue, open the JVisualVM’s sampler tab and turn on memory sampling, when you do so you would see many instances of different types of classes but to identify open iterator issue you need to focus on ViewRowSetIteratorImpl instances and then to your project specific VOImpl classes through which you obtained the iterator in the first place. When ever you create a new RowSetIterator you are returned a ViewRowSetIteratorImpl class instance. As is obvious you would expect the number of instances to increase as you open the RowSetIterator instances and do not close them. Now when i ran the application under simulated load with JMeter with open iterator reference i could see the instances for ViewRowSetIteratorImpl class increase in number very quickly. So then i took a heap dump with JVisualVM and then clicked on the ViewRowSetIteratorImpl class in classes tab to open the instance view tab, then upon selecting an instance and querying for the nearest GC root (in the reference section) i could see that EmpVOImpl was holding a reference to the ViewRowSetIteratorImpl instance. You can also use OQL support in JVisualVM to find out the references which are keeping the object alive.



select  heap.livepaths(u,false) from oracle.jbo.server.ViewRowSetImpl u


adfgcroot



livepaths



After opening the EmpVOImpl instance by clicking on the node, I expanded the mviewrowset (it is a variable which holds ViewRowSetImpl  instance) and then further expanding its mViews property i could see a number of ViewRowSetIteratorImpl instances as shown below.



empvoimplholdingviewrowsetiteratorimpl



So to resolve this issue all i had to do was to close the open iterator by calling the closeRowSetIterator method on the RowSetIterator instance.



There are other alternatives also for identifying the problem in your source code by turning on profiling in JVisualVM but profiling on a production system is not recommended way of even approaching the issue.



The comparison b/w the application with open iterators and closed iterators is shown below.



 



adfmemoryleak nomemoryleakiteratorclose



In this application JMeter was used to simulate the load with loop controller configured to select different master rows so that upon selection change and partial refresh the getter method for the inputtext in backing bean was called again and again to increase the iterator instances.



jmeterconf



The application and jmx file can be downloaded from below links




  1. JMeter test plan


  2. Application

Posted on Monday, April 22, 2013 by Unknown

Apr 19, 2013

A user on OTN forum was trying to extend the AdfcExceptionHandler in which he was trying to do redirect to an error page and facing Response Already Committed exception. On examining the scenario i could see that the phase in which the exception handler was being called was RENDER_RESPONSE and by this time it is too late to do a redirect as part of the response has already been submitted (one can check this by using response.iscommitted() method). So redirects/forwards should never be issued from the AdfcExceptionHandler in a conventional way.

The question also arises why someone needs to extend the AdfcExceptionHandler  in the first place, but, we need to do this as default exception handling will not handle exceptions that occur during render response phase for example: let’s say that during the application execution the connectivity to the DB goes down, the exceptions raised then will not be handled by the default exception handling mechanism.

So we cannot use conventional redirects but we have to do redirects somehow, the solution then is to use javascript redirect from the exception handler. The code for the exception handler is shown below.

package com.blogspot.ramannanda.view.handlers;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import oracle.adf.view.rich.context.ExceptionHandler;
import oracle.adfinternal.controller.application.AdfcExceptionHandler;
import oracle.jbo.DMLException;
import org.apache.myfaces.trinidad.render.ExtendedRenderKitService;
import org.apache.myfaces.trinidad.util.Service;


public class MyAppUIErrorHandler extends AdfcExceptionHandler {
public MyAppUIErrorHandler() {
super();
}


@Override
public void handleException(FacesContext facesContext, Throwable throwable,
PhaseId phaseId) throws Throwable {
ExternalContext ctx =
FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request = (HttpServletRequest)ctx.getRequest();
if (phaseId.getOrdinal() == PhaseId.RENDER_RESPONSE.getOrdinal()) {
//handle null pointer or sqlexception or runtime DMLException that might occur
if (throwable instanceof DMLException ||
throwable instanceof SQLException ||
throwable instanceof NullPointerException) {
String contextPath = request.getContextPath();
FacesContext context = FacesContext.getCurrentInstance();
StringBuffer script = new StringBuffer();
//set the window.location.href property this causes the redirect.
script.append("window.location.href= '").append(contextPath).append("/error.html';");
ExtendedRenderKitService erks =
Service.getRenderKitService(context,
ExtendedRenderKitService.class);
erks.addScript(context, script.toString());
//encoding script is required as just adding script does not work here
erks.encodeScripts(context);
return;
} else {
super.handleException(facesContext, throwable, phaseId);
}

}


}
}


So by using this code we are doing redirect to error.html page, I am also checking for phaseId here as the handler will be called for each phase in turn and we can do the handling in render response phase.



Note: Note that i have used encodeScripts method after adding the script and this is required because this method outputs any required scripts by RenderKit which in our case tends to be the script we added. 



 



adferrorhandling

Posted on Friday, April 19, 2013 by Unknown