Tuesday, November 3, 2015

Oracle Cloud JCS-SX: Sending Email

Recently I was trying to deploy an ADF application on Oracle PaaS (JCS-SX) environemnt. I wanted to send email using our smtp server. I wrote java mail apis but it failed with AccessControlException as java mail apis are blocked in JCSSX. But then there is another way that works  -->  ''APEX_MAIL" apis

With Oracle JCS environment we get ODC (Oracle Database Cloud) as well. We can use it to send notification. We can use APEX_MAIL package on this database to send email. Good thing it does not need any smtp server either.

As described in http://sanjeev-technology.blogspot.in/2015/04/cloud-sending-email-from-oracle-paas.html, we can write below code to send email using apex_mail


declare
    l_to  varchar2(200)  := 'fromemail@email.com';  -- change to your email address;
    l_from varchar2(200) :=  'toemail@email.com'; -- change to a real senders email address
    l_body      varchar2(4000) := 'To view the content of this message, please use an HTML enabled mail client.' ;
    l_body_html varchar2(4000)  := '<html><body><p> This is test email </p> </body></html>';
    l_subject varchar2(200);
begin
    APEX_MAIL.SEND (
        p_to        => l_to,
        p_from      => l_from,
        p_body      => l_body,
        p_body_html => l_body_html,
        p_subj      => l_subject );

    APEX_MAIL.PUSH_QUEUE;

end;


If we call above api from Java, it will not work. The limitation is that we must be in APEX context to use above apis. To overcome that we need to set security-group-id first. To set security-group-id, you must know your workspace. To know workspace, you can run below sql in SQL Workshop.

select apex_util.find_workspace((select apex_application.get_security_group_id from dual)) from dual;


Once you have workspace name, you can change above code so that it can work from outside APEX context. In this blog I am going to create a REST service to send email and they I am going to invoke that service from Chrome - Advanced Rest Client Application.

Here are the steps to do it.

1. Open ODC (Oracle Database Cloud) console and navigate to SQL Workshop. Select REST Services.

2. Click on Create button to create a new REST service
While creating REST service in ODC you need to define three things
a. Module: Define module as shown below

b. Template: Define template as shown below

c. Resource Handler: Define Resource Handler as shown below

With that your REST service is ready. 

Now lets call it from outside APEX context. As it is REST service, I will be using 'Advanced REST client' of chrome browser. You can download this plugin and install in browser.


Below are screenshots for SUCCESS and ERROR

As I can invoke it from Advanced REST client I should be invoke this service from any third party including java application. We can deploy such application in Oracle PaaS JCS-SX and send email.


Last thing I would like to do is call this REST service from ADF application and deploy ADF application on JCS-SX environment. I would not go in details about how to deploy and create ADF application for cloud. 

1. I have a jspx page, which looks like
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:h="http://java.sun.com/jsf/html"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <jsp:directive.page contentType="text/html;charset=UTF-8"/>
  <f:view>
    <af:document id="d1">
      <af:form id="f1">
        <af:panelGroupLayout id="pgl1" layout="vertical">
          <af:panelFormLayout id="pfl1">
            <af:inputText label="To" id="it1"
                          value="#{pageFlowScope.pMessageTo}"/>
            <af:inputText label="From" id="it2"
                          value="#{pageFlowScope.pMessageFrom}"/>
            <af:inputText label="Subject" id="it3"
                          value="#{pageFlowScope.pMessageSubject}"/>
            <af:inputText label="Body" id="rte1"
                               value="#{pageFlowScope.pMessageBody}" rows="5"
                          columns="40"/>
            <f:facet name="footer">
              <af:panelGroupLayout id="pgl2" halign="center"
                                   styleClass="AFStretchWidth">
                <af:commandButton text="Send" id="cb1"
                                  actionListener="#{backingBeanScope.MessageBean.sendMessage}"/>
              </af:panelGroupLayout>
            </f:facet>
          </af:panelFormLayout>
        </af:panelGroupLayout>
      </af:form>
    </af:document>
  </f:view>
</jsp:root>

2. Create a bean (MessageBean) and associate it with backingbean scope

3. Write below code in bean:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;

import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;

import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

public class MessageBean {
    public MessageBean() {
        super();
    }
    
    
    
    private String sendNotification(String to, String from, String textBody, String htmlBody, String subject){
        String output = null;
        try {

                
            URL url = new URL("https://<oracle_cloud_server>.com/apex/myservice/sendmail/");
            HttpURLConnection conn = (HttpURLConnection)url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Accept", "application/json");            
            conn.setRequestProperty( "User-Agent", "Mozilla/5.0" );
            conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");            
            conn.setDoOutput(true);
            conn.setDoInput(true);
            
            String payload =   "{\"TO\":\""+to+"\"," +    "\"FROM\":\""+from+"\"," +    "\"TEXT_BODY\":\""+textBody+"\", " +
                                "\"HTML_BODY\":\""+htmlBody+"\", " +     "\"SUBJECT\":\""+subject+"\"}";
            conn.setRequestProperty("Content-Length", String.valueOf(payload.getBytes().length));
            
            OutputStream writer = conn.getOutputStream();
            writer.write(payload.getBytes());
            writer.flush();
            writer .close();
            System.out.println(payload);
            if (conn.getResponseCode() != 200) {
                throw new RuntimeException("Failed : HTTP error code : " +
                                           conn.getResponseCode());
            }

            BufferedReader br =  new BufferedReader(new InputStreamReader((conn.getInputStream())));


            System.out.println("Output from Server .... \n");
            while ((output = br.readLine()) != null) {
                System.out.println(output);
            }           
            conn.disconnect();

        } catch (MalformedURLException e) {
            e.printStackTrace();

        } catch (IOException e) {
            e.printStackTrace();
        }

        return output;
    }

    public void sendMessage(ActionEvent actionEvent) {
        // Add event code here...
        String to = (String)evaluateEL("#{pageFlowScope.pMessageTo}");
        String from = (String)evaluateEL("#{pageFlowScope.pMessageFrom}");
        String subject = (String)evaluateEL("#{pageFlowScope.pMessageSubject}");
        String body = (String)evaluateEL("#{pageFlowScope.pMessageBody}");
        
        sendNotification(to, from, null, body, subject);
    }
            
    public static Object evaluateEL(String el) {
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ELContext elContext = facesContext.getELContext();
    ExpressionFactory expressionFactory =
    facesContext.getApplication().getExpressionFactory();
    ValueExpression exp =
    expressionFactory.createValueExpression(elContext, el,
    Object.class);

    return exp.getValue(elContext);
    }

}

Deploy this application on cloud and see the output as below





NOTE: There are lots shortcomings in this ADF code. You can use RichTextEditor for email. In this code you can't have special characters as I am just appending your message and forming json object. You may want to create a java object with properties (to,from,subject,textBody,htmlBody) and then convert it to json string using a third part library (jackson etc). You also may want to move sendNotification to model side and expose it as datacontrol. You also may want to remove hardcoding of service url and request properties. Definitely you want to do better error handling. BUT then this is just to a Blog entry.

Wednesday, August 19, 2015

ADF: Move using Tab key in AF:Table

Problem description: Let say we have a af:table, which has some editable columns. We can use tab keys to move between these editable fields. Normal tab key moves horizontally first in same row and if there is no more editable item in row, it will jump to next row. Problem is when it jumps to new row, it does not make that row current. Now if you edit the value and have a value change listener, you may not get correct row in model.
To demonstrate the problem let us create following objects
1. Updateable EmpEO/EmpVO/EmployeeAM in our model project. EO can be based on Employee table of HR schema.
2. Drag and drop EmpVO on a jspx page (say ADFTableTabNavigation.jspx) and create a af:table. Make EmployeeId outputtext and first-name, last-name, email as editable input-text.
3. Have a bean say ADFTableTabNavigationBean.
4. Bind table with bean (say #{backingBeanScope.ADFTableTabNavigationBean.employeeTable})
3. Have auto-submit and value-change-listener on first-name, which executes a bean method (say method changeFirstName)
4. changeFirstName code should look like
    public void changeFirstName(ValueChangeEvent valueChangeEvent) {
        // Add event code here...
        String newValue = valueChangeEvent.getNewValue().toString();
        System.out.println("New First Name: " + newValue);
        JUCtrlHierBinding adfModel = (JUCtrlHierBinding)((CollectionModel)getEmployeeTable().getValue()).getWrappedData();
        DCIteratorBinding dciter = adfModel.getDCIteratorBinding();
        NavigatableRowIterator nav=dciter.getNavigatableRowIterator();
        Row row = nav.getCurrentRow();
        System.out.println("EmployeeId modified: " + row.getAttribute("EmployeeId"));
        
    }

Whenever you change first name bean method is going to print, new value of first name and employeeid or row, which is modified.

Now with this let us run our page.

Here is the behaviour that we notice.
1. If I select a row first using mouse and then change first name, it correctly prints new value and empolyeeid.
2. If I am on row1 (say employee id = 100) and then use tab key to move. Once I reach to next row (say employeeid=101), framework does not change current row. If I edit first-name, bean prints new-value of first name correctly but employeeid incorrectly. It shows employeeid of previous row (100).

At times this behaviour can lead to unexpected results because our model codes getCurrentRow will return wrong row, which is not modified. To correct it I tried following approach.

Solution:

To solve this problem we just need two steps
1. Add a custom attribute with input-text FirstName.
2. Change current-row of table before doing any processing.

--------------------------------------------------------------------------------------------------------------

1. Add a custom attribute with input-text FirstName: For this just add below code in your FirstName input text.
<af:inputText value="#{row.bindings.FirstName.inputValue}"
                          label="#{bindings.EmpVO.hints.FirstName.label}"
                          required="#{bindings.EmpVO.hints.FirstName.mandatory}"
                          columns="#{bindings.EmpVO.hints.FirstName.displayWidth}"
                          maximumLength="#{bindings.EmpVO.hints.FirstName.precision}"
                          shortDesc="#{bindings.EmpVO.hints.FirstName.tooltip}"
                          id="it2" autoSubmit="true"
                          valueChangeListener="#{backingBeanScope.ADFTableTabNavigationBean.changeFirstName}">
              <f:validator binding="#{row.bindings.FirstName.validator}"/>
              <f:attribute name="rowKey" value="#{row.EmployeeId}"/>
            </af:inputText>

This code adds a custom attribute in your input text, which holds the value of employeeid. Which means all first name input text in table actually has an attribute (rowKey), which is holding value of employeeid. Anytime if I get hold of inputtext component, I can get value of employeeid associated with it.


2. Change current-row of table before doing any processing: Now in bean method write a new mothod makeRowCurrent as shown below
    private void makeRowCurrent(ValueChangeEvent valueChangeEvent){
        oracle.jbo.domain.Number employeeId = (oracle.jbo.domain.Number)valueChangeEvent.getComponent().getAttributes().get("rowKey");
        JUCtrlHierBinding adfModel = (JUCtrlHierBinding)((CollectionModel)getEmployeeTable().getValue()).getWrappedData();
        DCIteratorBinding dciter = adfModel.getDCIteratorBinding();
        NavigatableRowIterator nav=dciter.getNavigatableRowIterator();
        Object[] objKey = new Object[1];
        objKey[0] = employeeId;
        Key key = new Key(objKey);
        nav.setCurrentRow(nav.getRow(key));
    }

You can change above method if you have different datatype of primary key or if you have composite key as primary key.

Now just call this method from your valuechangelistener
    public void changeFirstName(ValueChangeEvent valueChangeEvent) {
        // Add event code here...
        String newValue = valueChangeEvent.getNewValue().toString();
        System.out.println("New First Name: " + newValue);
        makeRowCurrent(valueChangeEvent);
        JUCtrlHierBinding adfModel = (JUCtrlHierBinding)((CollectionModel)getEmployeeTable().getValue()).getWrappedData();
        DCIteratorBinding dciter = adfModel.getDCIteratorBinding();
        NavigatableRowIterator nav=dciter.getNavigatableRowIterator();
        Row row = nav.getCurrentRow();
        System.out.println("EmployeeId modified: " + row.getAttribute("EmployeeId"));
        
    }

3. Run application and test it: You will find that you can successfully navigate using tab key and whenever you change first name, you get correct employeeid as current row.


--------------------------------------------------------------------------------------------------------------------
Effectively what we are doing: We are associating a custom attribute (rowKey) with all input-text (firstname). This custom attribute holds the value of row's primary key (employeeid). In makeRowCurrent method we try to get hold of inputtext and its corresponding custom attribute (rowKey). We construct key using employeeid and setcurrentrow using that key. Once current row is set correctly we can always know which employee record is actually modified by end user.

NOTE:
1. I searched over net and found that there could be other solutions also like handling tabkey client event and then making row current. I found it a bit too much of work to resolve simple issue.
2. One issue with this approach is that when you switch between rows, you are not making row current. We only make row current if we try to edit a field (firstname). If I don't edit field and try to navigate between rows, my current row still remains old row. I believe that is good only because if I try to to make row current every time I reach to a new row using tab, I need to unnecessary fire client event (tab key) and server event even though there is effectively no change. This will slow down tab navigation.
3. If you have composite key as primary key, you can change makeRowCurrent method as
private void makeRowCurrent(ValueChangeEvent valueChangeEvent){
        oracle.jbo.domain.Number key1 = (oracle.jbo.domain.Number)valueChangeEvent.getComponent().getAttributes().get("rowKey1");
oracle.jbo.domain.Number key2 = (oracle.jbo.domain.Number)valueChangeEvent.getComponent().getAttributes().get("rowKey2");
oracle.jbo.domain.Number key3 = (oracle.jbo.domain.Number)valueChangeEvent.getComponent().getAttributes().get("rowKey3");
        JUCtrlHierBinding adfModel = (JUCtrlHierBinding)((CollectionModel)getEmployeeTable().getValue()).getWrappedData();
        DCIteratorBinding dciter = adfModel.getDCIteratorBinding();
        NavigatableRowIterator nav=dciter.getNavigatableRowIterator();
        Object[] objKey = new Object[3];
        objKey[0] = key1;
        objKey[1] = key2;
        objKey[2] = key3;
        Key key = new Key(objKey);
        nav.setCurrentRow(nav.getRow(key));
    }

4. valueChangeEvent.getComponent().processUpdate() does not change current row so that approach does not work here.


That's it.

Sunday, August 16, 2015

ADF: Monitor performance of VO query using base-classes

Problem Description: In ADF we use VO at multiple places. For example we can use a VO as view-instance in AM or ViewAccessor in EO/VO. There are different events which can execute these VOs
For example if I drag and drop a VO-view-instance on a page, it will get executed automatically. Same thing about view accessor, they gets executed when needed by framework. Because framework is taking care of VO execution at many places, we need to double sure about followings
1. Which all VOs are getting executed when I do some activity (like load a page/click a button etc)
2. What is exact query that framework has formed?
3. What are bind parameters? All parameters are binded or some blank query is firing.

In this blog I am trying to implement a central approach, which will tell me about all that information.

Solution:
Our solution is quite simple
1. Create baseclasses for your model project
2. Use those baseclasses in your VOs/EOs
3. Override executeQuery method of baseclass and generate above required information

Let us implement it

1. Create baseclasses for your model project: It is always a good practice to create baseclasses for your model project. Base classes are nothing but few classes, which you create in your model project and then all EOs/VOs/AMs extend those classes. Your baseclasses extend framework provided classes for example EntityImpl, VOImpl, VORowImpl, AMImpl etc. If you don't have base classes, your EOs/VOs/AMs directly extend framework provided classes.


To create base classes you need do following steps
a. Create base classes: If we have only one model project, we can create base classes in that project itself. But if we want to reuse our base classes in multiple model project, we can create a new project for base classes and add it as a library in other model project. In this example I am using model project to create base classes.
To create base classes you just need to create normal java classes but you need to extend framework provided base classes. Although there are total 8 base classes you can create but we generally create 5 baseclasses. One for EOs, One for VOs, One for VORows, One for AMs. Let say I want to have following base classes

EOs: com.san.blog.model.baseclasses.MyEntityImpl  extends oracle.jbo.server.EntityImpl
VOs: com.san.blog.model.baseclasses.MyVOImpl      extends oracle.jbo.server.ViewObjectImpl
VORows: com.san.blog.model.baseclasses.MyVORowImpl  extends oracle.jbo.server.ViewRowImpl
AMs: com.san.blog.model.baseclasses.MyApplicationModuleImpl extends oracle.jbo.server.ApplicationModuleImpl

NOTE: To extend oracle.jbo.server classes you must have ADF Model Runtime library added in your model project.

After creating these classes your project will look something like

Code in these files will look something like
package com.san.blog.model.baseclasses;
import oracle.jbo.server.EntityImpl;
public class MyEntityImpl extends EntityImpl {
    public MyEntityImpl() {
        super();
    }
}

package com.san.blog.model.baseclasses;
import oracle.jbo.server.ViewDefImpl;
import oracle.jbo.server.ViewObjectImpl;
public class MyViewObjectImpl extends ViewObjectImpl {
    public MyViewObjectImpl(String string, ViewDefImpl viewDefImpl) {
        super(string, viewDefImpl);
    }
    public MyViewObjectImpl() {
        super();
    }
}



package com.san.blog.model.baseclasses;
import oracle.jbo.server.ViewRowImpl;
public class MyViewRowImpl extends ViewRowImpl {
    public MyViewRowImpl() {
        super();
    }
}


package com.san.blog.model.baseclasses;
import oracle.jbo.server.ApplicationModuleImpl;
public class MyApplicationModuleImpl extends ApplicationModuleImpl {
    public MyApplicationModuleImpl() {
        super();
    }
}

b. Change settings to recognize your custom classes as base classes: For this you need to navigate to Tools > Preferences > Business Components > Base Classes and change framework provided classes to your custom classes
Another place where you can change base classes is model project properties > Business Components > Base Classes. I think this approach is better as base class information resides in jpr file.

Now if you create any EO/VO/AM, they will be extending baseclasses you just created and your custom base-classes are extending framework provided base-classes so you will get all framework functionality. But you can change/add functionality in your base-classes and that will be applicable in all your ADF model objects.

NOTE: If you forget to mention base classes initially and want to implement it on already existing project you need to follow above steps and then make minor modification in each EOs/VOs/AMs so that system updates your EO/VO/AM files with correct base-classes. For example you can add transient attribute in EO/VO and then remove it.

2. Use Base-classes in EOs/VOs: Next thing we want to use these base classes. Let us create an employee form. In which we can have "update employee" functionality. We would like to create LOV for department, we would also like to have a validation for department in EmployeeEO. Let us have following objects:
EmployeeEO: Based on Employee table.
EmployeeVO: Updateable VO based on EmployeeEO.
DeptVVO: Validation VO to validate department
DeptLOVVO: VO to create department LOV
EmployeeAM: Application module

After creating these objects your project should look like

Add validation for department in DeptEO using DeptVVO. Also create an LOV for department in EmpVO using DeptLOVVO. Add EmpVO as a view instance in EmployeeAM. Once done your model is ready.

Now Create a page in ViewController project and drag and drop EmpVO from datacontrol. Select a form with Submit and Navigation button. Once done your flow is complete.

Our aim is when we navigate between pages, system should log each VO query that its firing and should report if any VO query is taking time. Here is the code that you can write in your base class MyViewObjectImpl.java to achieve this.

    public void executeQueryForCollection(Object rowset, Object[] params, int noUserParams) {   
            long startTime = 0L;
            long stopTime = 0L;
            long elapsedTime = 0L;
            if (_logger.isLoggable(Level.INFO)) {
                _logger.info("Executing VO: " + this.getFullName() + " Started.");
                startTime = System.currentTimeMillis();
            }
            
            super.executeQueryForCollection(rowset, params, noUserParams);             
            
            if (_logger.isLoggable(Level.INFO)) {
                stopTime = System.currentTimeMillis();
                elapsedTime = stopTime - startTime;
                if (elapsedTime > 100) {
                    _logger.info("********************SLOW QUERY HAS BEEN DETECTED ******************");
                    _logger.info("VO: " + this.getFullName());
                    _logger.info("Query: " + this.getQuery());
                    _logger.info("Total time taken: " + elapsedTime + "millisec");
    
                    if (params != null) {
                        _logger.info("Bind variables");
                        for (Object param : params) {
                            Object[] value = (Object[])param;
                            _logger.info("Name: " + value[0] + " Value: " +  value[1]);
                        }
                    }
    
    
                } 
                _logger.info("Executing VO: " + this.getFullName() + " Finished. Total time taken: " + elapsedTime);
            }
                   
        }

Now if I run my page and navigate between rows, change department and commit. I see various VOs are getting executed but all queries are getting logged continuously. 


On Page Load
<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM.EmpVO Started.
<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM.EmpVO Finished. Total time taken: 1
<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM._LOCAL_VIEW_USAGE_com_san_blog_model_vo_EmpVO_DeptLOVVO Started.
<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM._LOCAL_VIEW_USAGE_com_san_blog_model_vo_EmpVO_DeptLOVVO Finished. Total time taken: 1
On Next Click
<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM._LOCAL_VIEW_USAGE_com_san_blog_model_vo_EmpVO_DeptLOVVO Started.
<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM._LOCAL_VIEW_USAGE_com_san_blog_model_vo_EmpVO_DeptLOVVO Finished. Total time taken: 0
On changing department and commit
<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM.__LOCAL_VIEW_USAGE_com_san_blog_model_vo_EmpVO_DeptLOVVO_findByVC_8fc_LOV_DepartmentId__lov__filterlist__vcr___ Started.
<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM.__LOCAL_VIEW_USAGE_com_san_blog_model_vo_EmpVO_DeptLOVVO_findByVC_8fc_LOV_DepartmentId__lov__filterlist__vcr___ Finished. Total time taken: 0
<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM.DeptVVO_66_findByKey_ Started.

<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM.DeptVVO_66_findByKey_ Finished. Total time taken: 1

If there is any slow query (more than 100 millisec), logger will write query with bind parameters as

<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM.EmpVO Started.
<MyViewObjectImpl> <executeQueryForCollection> ********************SLOW QUERY HAS BEEN DETECTED ******************
<MyViewObjectImpl> <executeQueryForCollection> VO: EmployeeAM.EmpVO
<MyViewObjectImpl> <executeQueryForCollection> Query: SELECT EmpEO.EMPLOYEE_ID,         EmpEO.FIRST_NAME,         EmpEO.LAST_NAME,         EmpEO.EMAIL,         EmpEO.PHONE_NUMBER,         EmpEO.HIRE_DATE,         EmpEO.JOB_ID,         EmpEO.SALARY,         EmpEO.COMMISSION_PCT,         EmpEO.MANAGER_ID,         EmpEO.DEPARTMENT_ID FROM EMP EmpEO
<MyViewObjectImpl> <executeQueryForCollection> Total time taken: 102millisec
<MyViewObjectImpl> <executeQueryForCollection> Executing VO: EmployeeAM.EmpVO Finished. Total time taken: 102

NOTE: Logger is enabled for info level only because we do not want logging everytime. It should only be enabled if some user reports slow performance and we want to check VO query performances. To see logs you need to enable INFO level of logging for MyViewObjectImpl file. To do that while running application in integrated weblogic, you can select 'Configure Oracle Diagnostic Logging' in jdev as shown below.


That's it: Now you have VO monitoring system, which keeps track of slow running queries. 

Sunday, June 28, 2015

ADF Tip: PanelGroupLayout Avoid inner Divs [Any component to create single div]

Problem Description: At times I want only a single div in my html. PanelGroupLayout(vertical) can be used to create a div but then if I put any component in it, all will be surrounded by additional div. I don't want that. But there is no problem with PanelGroupLayout(Vertical). As name suggest vertical layout so it needs to surround inner components with a div. So the problem is to get an ADF component, which only generates a div thats it. No impact on inner component. I do not think there is any other simple component available in ADF, which can only generate a div. I would have been helpful but seems like there is none. All are complex components and even simplest panelgrouplayout add additional complexity.


Solution: I could not get a perfect solution for this but below approach helps me.

Surround inner components of PanelGroupLayout-Vertical with a af:group.
af:group does not generate any html.


Thats it.

Weblogic J2EE security using web.xml and weblogic.xml (Quick scenarios)

In a J2EE project we can use web.xml and weblogic.xml to secure application.
To secure a J2EE application you need to perform following tasks

1. Create a login page
2. Secure resources (urls) by application roles
3. Map application roles with LDAP groups



In this blog I will be creating a J2EE application in jdeveloper and secure it. 
Let say I have a bare minimal initial application with only web.xml as shown below.



[NOTE: JDEV creates JSF/EJB related artefacts by default when you create a J2EE project but I have deleted them and removed their libraries]




First thing we need to do is create a login page. In J2EE we can have 3 kinds of login configuration.

a. Use Http Basic login: In this case browser provides in built login challenge (username/password). This is good for quick testing but may not be suitable for production. To enable this kind of login, you just need to add following lines in your code.
<login-config>
  <auth-method>BASIC</auth-method>
</login-config>


b. Use SSO: If Single Sign On is configured we can use below entry in webl.xml.
<login-config>
  <auth-method>CLIENT-CERT</auth-method>
</login-config>

c. Use Custom Login page: If you want to have your own login page you can configure web.xml as
<login-config>
  <auth-method>FORM</auth-method>
  <form-login-config>
    <form-login-page>/pages/Login.html</form-login-page>
    <form-error-page>/pages/Error.html</form-error-page>
  </form-login-config>
</login-config>

We will create a custom login page. 

Login.html (created under public_html/pages folder) looks like
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"></meta>
    <title>Login</title>
  </head>
  <body>
  <form method="POST" action="j_security_check">
  User Name: <input type="text" name="j_username"></input>
  Password: <input type="password" name="j_password"></input>
  <Input type="Submit" value="Login"></input>
  </form>
  </body>
</html>


Effectively you just need to call j_security_check with j_username and j_password value.
Error page could have below code
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"></meta>
    <title>Login</title>
  </head>
  <body>
  <p style="color: red; font-weight: bold">Error: Invalid username or password </p>
  <form method="POST" action="j_security_check">
  User Name: <input type="text" name="j_username"></input>
  Password: <input type="password" name="j_password"></input>
  <Input type="Submit" value="Login"></input>
  </form>
  </body>

</html>

With that our login module is set. Now lets secure our resources (pages)



Now before setting our security in web.xml I would like to comment on application role and enterprise role (group).
There are two kinds of roles, one is Application role and other is Enterprise role (aka group). In our J2EE app we should define application role and use them. For example if few pages are build for HR, we can define an application role as 'HRAppRole'. In our coding we should only refer this role. We should secure all our pages using this role only.

User/Password and their actual roles are maintained in LDAP directories. These roles are called Enterprise role or group. We should not refer these roles (group) in our application. Instead we should map Application role and Enterprise role (group) in weblogic.xml file.

Advantage of this approach is that code is not dependent of LDAP roles. If tomorrow we need to change LDAP group name, impact on code will be minimal. We just need to change mapping. We can also deploy same code against different LDAP. With deployment plan we can even change mapping of weblogic.xml at deployment time.



Let say we have application role as 'HRAppRole' and enterprise role as 'HR'. We need to have following entries for these roles
  1. Define HRAppRole in web.xml
            <security-role>
               <role-name>HRAppRole</role-name>
           </security-role>


        2. Define mapping of HRAppRole and HR in weblogic.xml

    <security-role-assignment>
         <role-name>HRAppRole</role-name>
         <principal-name>HR</principal-name>
     </security-role-assignment>

We have another scenario of accessing a page by all valid users. For that also we need to define an application role 'valid-users' and map it with group 'users' as
web.xml:
          <security-role>
               <role-name>valid-users</role-name>
           </security-role>

weblogic.xml:
        <security-role-assignment> 
            <role-name>valid-users</role-name>  
            <principal-name>users</principal-name>  
        </security-role-assignment>  
      
With that knowledge let us try to secure our pages.




    Next step is to create few pages and set security for them. Effectively there could be following possible scenarios.
    • Pages, which are accessible by everyone. No login should be needed for such pages. Let us have a PublicPage.html in this category.
    • Pages, which are accessible by valid users. Such pages needs login but user need not to have any specific role to access such pages. Only valid username/password is good enough. Let us have AllValidUserPage.html for this category.
    <security-constraint>
          <web-resource-collection>
            <web-resource-name>AllValidUserPage</web-resource-name>
            <url-pattern>pages/AllValidUserPage.html</url-pattern>
            <http-method>GET</http-method>
            <http-method>POST</http-method>
         </web-resource-collection>
         <!-- you can repeat web-resource-collection tag -->
         <auth-constraint>
            <role-name>valid-users</role-name>
         </auth-constraint>
      </security-constraint>
    • Pages, which are accessible by users with specific role. For example only HR can access such pages. Let us have a RoleSpecificPage.html for this category.

      <security-constraint>
         <web-resource-collection>
             <web-resource-name>RoleSpecificPage</web-resource-name>
             <url-pattern>pages/RoleSpecificPage.html</url-pattern>
             <http-method>GET</http-method>
             <http-method>POST</http-method>
          </web-resource-collection>
          <auth-constraint>
              <role-name>HRAppRole</role-name>
          </auth-constraint>
       </security-constraint>
    • Page, can be accessed by anybody but few components (like button etc) should only be visible if user has a specific role
    For this usecase you need to have application role defined in web.xml and corresponding mapping in weblogic.xml only. You don't need <security-constraint> tag in web.xml. You need to secure component by using request.isUserInRole("HRAppRole") expression. For example if I have jsp I can put below lines to hide a button
    <% if( request.isUserInRole("HRAppRole")) { %>
    <a href="#" onclick="ShowEditPopup()"> Link only for HR</a>
    <% } %>
                          
    • Pages, which can not be accessed by anybody. Let us have RestrictedPage.html for this category


        <security-constraint>
          <display-name>excluded</display-name>
          <web-resource-collection>
              <web-resource-name>Restricted Pages</web-resource-name>
              <url-pattern>pages/RestrictedPage.html</url-pattern>
          </web-resource-collection>  
       <auth-constraint /> <!--blank auth-constraint -->
    </security-constraint>


    Code wise I do not have anything special in above mentioned html files. Just hard coded text to describe that page.


    For all above scenarios, effectively my web.xml looks like
    <?xml version = '1.0' encoding = 'windows-1252'?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
             version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
      <security-constraint>
        <web-resource-collection>
          <web-resource-name>AllValidUserPage</web-resource-name>
          <url-pattern>pages/AllValidUserPage.html</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
        </web-resource-collection>
        <!-- you can repeat web-resource-collection tag -->
        <auth-constraint>
          <role-name>valid-users</role-name>
        </auth-constraint>
      </security-constraint>
      <security-constraint>
        <web-resource-collection>
          <web-resource-name>RoleSpecificPage</web-resource-name>
          <url-pattern>pages/RoleSpecificPage.html</url-pattern>
          <http-method>GET</http-method>
          <http-method>POST</http-method>
        </web-resource-collection>
        <auth-constraint>
          <role-name>HRAppRole</role-name>
        </auth-constraint>
      </security-constraint>
      <security-constraint>
        <display-name>excluded</display-name>
        <web-resource-collection>
          <web-resource-name>Restricted Pages</web-resource-name>
          <url-pattern>pages/RestrictedPage.html</url-pattern>
        </web-resource-collection>
        <auth-constraint/>
      </security-constraint>
      <security-role>
        <role-name>HRAppRole</role-name>
      </security-role>
      <security-role>
        <role-name>valid-users</role-name>
      </security-role>
      <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
          <form-login-page>/pages/Login.html</form-login-page>
          <form-error-page>/pages/Error.html</form-error-page>
        </form-login-config>
      </login-config>
    </web-app>

     weblogic.xml looks like
    <?xml version = '1.0' encoding = 'windows-1252'?>
    <weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                      xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-web-app http://www.bea.com/ns/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd"
                      xmlns="http://www.bea.com/ns/weblogic/weblogic-web-app">
         <security-role-assignment>
              <role-name>HRAppRole</role-name>
              <principal-name>HR</principal-name>
         </security-role-assignment>
         <security-role-assignment>
              <role-name>valid-users</role-name>
              <principal-name>users</principal-name>
         </security-role-assignment>
    </weblogic-web-app>

    NOT TIME FOR TESTING:

    Before that we need to create few test users and assign them groups in LDAP. We will use embedded ldap of weblogic. Follow these steps 

    1. Start weblogic server and login into console.
    2. Navigate to Security Realm > myrealm > Users and Groups
    3. Select Group tab and click New to create new group. Create a group with name HR

    4. Create two test users, userA with HR access and userB with no group assigned.


    Now type in following urls

    URL Result
    http://127.0.0.1:7101//pages/PublicPage.html NO login screen appears and user can directly view it.
    http://127.0.0.1:7101//pages/AllValidUserPage.html Login screen appears and both userA/userB can view it. Wrong username/password takes us to error page.
    http://127.0.0.1:7101//pages/RoleSpecificPage.html Login screen appears and but only userA can view it. Wrong username/password takes us to error page.
    http://127.0.0.1:7101//pages/RestrictedPage.html Login screen appears and neither userA nor userB can access it. Wrong username/password takes us to error page.

    Thats it. Key points that we got are
    a. How to setup Login pages (web.xml and Login.html)
    b. What is application role and enterprise group
    c. How to defined an application role in web.xml
    d. How to map it with enterprise group in weblogic.xml
    e. A specific group 'users' is assigned by default to all valid users.
    f. How to secure a page
           which needs to be accessed by all valid users
           which needs to be accessed by only users of certain group
           which can not be accessed by anybody