Saturday, June 28, 2014

How to check AMX page is fully loaded or not

Sometimes you might requires to manipulate the HTML elements after the page is fully loaded. In HTML file it an be checked using window.onload or deviceready but same doesn't guarantees the appropriate ADF Mobile state.

In ADF mobile using showpagecomplete event on the handlePageShown callback function it can be checked.
<script>
        function handlePageShown() {
          console.log("Page is shown!");
        }
        document.addEventListener("showpagecomplete", handlePageShown, false);
</script>
The showpagecomplete event guarantees the appropriate ADF Mobile state; other browser and third-party events, such as load and Cordova's deviceready, may not.

In my next article I will show the usage of showpagecomplete event with example.

Friday, June 27, 2014

ADF Mobile - Get network status dynamically

While working on ADF mobile application with offline/online capabilities, before calling webservices network has to be checked whether it is "connected" or "disconnected".

In my previous article Offline Data Synchronization for ADF Mobile, I was using the javascript to evaluate the network connectivity and store the connection status in pageFlowScope variable. But after subsequent checks pageFlowScope variable was not refreshing properly. Please see the oracle forum for more info,  ADF Mobile - Get network status.

Steps to check network status dynamically:-

1. Java Script to check the network status
 /**
  * Method to check the network status
  */
  application.checkConnection = function () {
     var isConnected;
     var connectionType = navigator.network.connection.type;
     var states = {
      };
     states[Connection.UNKNOWN] = 'Unknown connection';
     states[Connection.ETHERNET] = 'Ethernet connection';
     states[Connection.WIFI] = 'WiFi connection';
     states[Connection.CELL_2G] = 'Cell 2G connection';
     states[Connection.CELL_3G] = 'Cell 3G connection';
     states[Connection.CELL_4G] = 'Cell 4G connection';
     states[Connection.NONE] = 'No network connection';
     if (connectionType == Connection.NONE || connectionType == Connection.UNKNOWN) {
        isConnected = false;
     }else {
        isConnected = true;
     }
     var obj = {connectionType : connectionType, isConnected : isConnected};
     return obj;
  }
2. In managed bean call the application.checkConnection method
public void checkNetworkStatusAction(ActionEvent actionEvent) {
String networkDetails = (String)AdfmfContainerUtilities.invokeContainerJavaScriptFunction(AdfmfJavaUtilities.getFeatureName(),"application.checkConnection",new Object[] { });
   try {
        JSONObject obj = new JSONObject(networkDetails);
        String connectionType = (String)obj.getString("connectionType");
        boolean isConnected = (boolean)obj.getBoolean("isConnected");
        setIsConnected(isConnected);
        setConnectionType(connectionType);
    } catch (JSONException e) {
        throw new AdfException("Error while trying to connect to network", AdfException.ERROR);
    }
}
Application screen looks like below when it deployed to Android device, When there is no network available in the device.


Enable the Mobile Data or Wireless and click on the Check button, Connection Type and Network Status will be displayed on the screen.


You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.4.0]

Thursday, March 6, 2014

ADF Mobile - Popup at center of the window

Sometimes you may want to position amx:popup at the center of the window. At this moment, in 11.1.2.4.0 there is no declarative solution for this usecase, In this post I'm sharing a code snippet that I noticed from Denis Tyrell(Oracle) for the same.

You can align the amx:showPopupBehavior with overlapTop property, this indicates how the popup should be aligned relative to the panelPage and then in the amx:popup code you can set the inlineStyle as below:
<amx:popup id="p1" autoDismiss="true"
     inlineStyle="position:relative;margin-top:#{(deviceScope.hardware.screen.availableHeight-200)/2}px;margin-left:#{(deviceScope.hardware.screen.availableHeight*10)/100}px;margin-right:#{(deviceScope.hardware.screen.availableHeight*10)/100}px;width:#{deviceScope.hardware.screen.availableWidth};height:200px;">
    <amx:outputText value="Popup at center of the window" id="ot4"/>
</amx:popup>
Application screen looks like below when it deployed to IOS simulator.

Sunday, March 2, 2014

ADF Mobile - Reading custom properties from adf-config.xml

Take a scenario where you have integrated the push notification feature into mobile application and application needs to be shipped to client, to work push notification feature you need Sender ID for both Android and iOS and client want's to use their own Sender Id.

Next question will be where can we store the custom Sender Id in the application so that the client can edit. Credit goes to Srini Indla(Oracle), I have extracted the idea based on one of his forum reply and below is one of the way.

Custom values can be stored in add-config.xml file under <adf:adf-properties-child> tag, add-config.xml is used to configure application-level settings, including the Configuration Service parameters and read more on ADF Mobile Configuration Service Usage. The advantage of storing the properties and accessing it via the adf-confg.xml is that the values can be accessible using the EL expression at run time.

Below is a sample adf-confif.xml with custom properties:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<adf-config xmlns="http://xmlns.oracle.com/adf/config" xmlns:config="http://xmlns.oracle.com/bc4j/configuration"
            xmlns:adf="http://xmlns.oracle.com/adf/config/properties">
  <adf-adfm-config xmlns="http://xmlns.oracle.com/adfm/config">
    <defaults changeEventPolicy="ppr" useBindVarsForViewCriteriaLiterals="true"/>
    <startup>
      <amconfig-overrides>
        <config:Database jbo.locking.mode="optimistic"/>
      </amconfig-overrides>
    </startup>
  </adf-adfm-config>
  <adf:adf-properties-child xmlns="http://xmlns.oracle.com/adf/config/properties">
    <adf-property name="adfAppUID" value="MobileAdfConfig-1564"/>
    <adf-property name="username" value="Weblogic"/>
    <adf-property name="password" value="Weblogic1"/>
  </adf:adf-properties-child>
</adf-config>
In Amx page you can access the above custom defined properties using EL expression.
For ex:  "#{applicationScope.configuration.username}"

Below is the sample code to access custom properties from adf-config.xml in managed bean.
ValueExpression userNameVe =
            AdfmfJavaUtilities.getValueExpression("#{applicationScope.configuration.username}", String.class);
String userName = (String)userNameVe.getValue(AdfmfJavaUtilities.getAdfELContext());

ValueExpression passwordVe =
            AdfmfJavaUtilities.getValueExpression("#{applicationScope.configuration.password}", String.class);
String password = (String)passwordVe.getValue(AdfmfJavaUtilities.getAdfELContext());

ADF Mobile - How to add a record using Constructor option

It’s been a while since my last post, but it doesn't mean that new discoveries were not happening. Well to be fair, I have been really busy with various responsibilities, I hope to re-take blogging again it into my daily routine. Now let’s get into business!

This article is a continuation of "ADF Mobile - Access Device Native SQLite Database to Store Data" and here I will try to explain a way of creating the record using the Constructor option with Bean DataControl.

You can download the sample workspace from here
[Runs with Oracle JDeveloper 11.1.2.4.0]

Implementation Steps:- 

Create an ADF Mobile Application, expand the ViewController project. Locate and expand the Application Sources folder, create a Department.java file and add the below code.
public class Departments {
    protected int departmentId;
    protected String departmentName;
    protected int locationId;
    private transient PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.addPropertyChangeListener(l);
    }

    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.removePropertyChangeListener(l);
    }

    public Departments() {
        super();
    }

    public String getKey() {
        Integer i = new Integer(departmentId);
        return i.toString();
    }

    public Departments(int departmentId, String departmentName, int locationId) {
        this.setDepartmentId(departmentId);
        this.setDepartmentName(departmentName);
        this.setLocationId(locationId);
    }


    Departments(Departments newDepartments) {
        this.setDepartmentId(newDepartments.getDepartmentId());
        this.setDepartmentName(newDepartments.getDepartmentName());
        this.setLocationId(newDepartments.getLocationId());
    }

    public void setDepartmentId(int departmentId) {
        int oldDepartmentId = this.departmentId;
        this.departmentId = departmentId;
        propertyChangeSupport.firePropertyChange("departmentId", oldDepartmentId, departmentId);
    }


    public int getDepartmentId() {
        return departmentId;
    }

    public void setLocationId(int locationId) {
        int oldLocationId = this.locationId;
        this.locationId = locationId;
        propertyChangeSupport.firePropertyChange("locationId", oldLocationId, locationId);
    }

    public int getLocationId() {
        return locationId;
    }

    public void setDepartmentName(String departmentName) {
        String oldDepartmentName = this.departmentName;
        this.departmentName = departmentName;
        propertyChangeSupport.firePropertyChange("departmentName", oldDepartmentName, departmentName);
    }

    public String getDepartmentName() {
        return departmentName;
    }
}
Create DepartmentList.java file and add the below code. Create DataControl based on DepartmentList.java file.
public class DepartmentsList {
    private static List s_departments = null;
    private Departments editDepartment = new Departments();
    private transient ProviderChangeSupport providerChangeSupport = new ProviderChangeSupport(this);

    private transient PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);


    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.addPropertyChangeListener(l);
    }

    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.removePropertyChangeListener(l);
    }

    public void addProviderChangeListener(ProviderChangeListener l) {
        providerChangeSupport.addProviderChangeListener(l);
    }

    public void removeProviderChangeListener(ProviderChangeListener l) {
        providerChangeSupport.removeProviderChangeListener(l);
    }

    public DepartmentsList() {
        if (s_departments == null) {
            s_departments = new ArrayList();
            Execute();
        }
    }

    public void Execute() {
        Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentsList.class, "Execute",
                  "!!!!!!!!!!!!!!!!!In DEPARTMENT Execute!!!!!!!!!!!!!!!!!!!!!!!!!");
        try {
            Connection conn = DBConnectionFactory.getConnection();
            s_departments.clear();
            conn.setAutoCommit(false);
            PreparedStatement stat = conn.prepareStatement("SELECT * from DEPARTMENTS ORDER BY DEPARTMENT_NAME");
            ResultSet rs = stat.executeQuery();
            Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentsList.class, "Execute",
                      "!!!!!!!!!!!!!!!!!Query Executed!!!!!!!!!!!!!!!!!!!!!!!!!");
            while (rs.next()) {
                Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentsList.class, "Execute",
                          "!!!!!!!!!!!!!!!!!Adding Department!!!!!!!!!!!!!!!!!!!!!!!!!");
                int departmentId = rs.getInt("DEPARTMENT_ID");
                String departmentName = rs.getString("DEPARTMENT_NAME");
                int locationId = rs.getInt("LOCATION_ID");
                s_departments.add(new Departments(departmentId, departmentName, locationId));
            }
            rs.close();
            providerChangeSupport.fireProviderRefresh("departments");
        } catch (SQLException e) {
            System.err.println(e.getMessage());
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    public Departments[] getDepartments() {
        Departments d[] = null;
        d = (Departments[])s_departments.toArray(new Departments[s_departments.size()]);
        return d;
    }


    public void persistDepartments(int departmentId, String departmentName, int locationId) {
        Connection conn;
        try {
            conn = DBConnectionFactory.getConnection();
            conn.setAutoCommit(false);
            String insertSQL = "Insert into DEPARTMENTS (DEPARTMENT_ID,DEPARTMENT_NAME,LOCATION_ID) values (?,?,?)";
            PreparedStatement pStmt = conn.prepareStatement(insertSQL);
            pStmt.setInt(1, departmentId);
            pStmt.setString(2, departmentName);
            pStmt.setInt(3, locationId);
            pStmt.execute();
            conn.commit();

            editDepartment.setDepartmentId(departmentId);
            editDepartment.setDepartmentName(departmentName);
            editDepartment.setLocationId(locationId);

            s_departments.add(0, editDepartment);
            providerChangeSupport.fireProviderCreate("departments", editDepartment.getKey(), editDepartment);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
In below section I will not show all screen creation, here I'm interested in DeptAdd.amx page. Take a look at the DepartmentList data control in the below image, drag and drop DepartmentsList->Constructors->com.oracle.hr.Departments->Departments as ADF Mobile Parameter Form.   


Next drag and drop DepartmentsList->persistDepartment->Method as ADF Mobile Button and in Edit Action Binding window map the parameter binding values as shown below.


Application screen looks like below when it deployed to IOS simulator. Displaying the Departments List is fetched from SQLite, click on the Add button to create new department record.


Enter the details in the department form and click on Save button. The Department information will saved to the database and moved to Dept List screen and department list will get updated by newly added department record.


Monday, October 21, 2013

ADF Mobile - Integrating Signature Capture

This article provide an example of how to write a custom user control to collect and store of an Electronic Signature created on a Android devices using stylus or a finger on the touch screen. 

I have integrated the SignaturePad jQuery plugin for assisting in the creation of an HTML5 canvas based signature pad. Records the drawn signature in JSON format in SQLite database for later regeneration.

Application screen looks like below when it deployed and run on Android Device/Emulator. Displays the Employee list on the screen.


Clicking on any employee will take you to the selected Employee details page, click on View Signature button will open up the popup, there is no signature since database doesn't have signature value for this employee.


Next click on the Edit button will navigate to the Employee details page, clicking on Capture Signature button will open up the popup and Signature Pad jQuery plugin will transform an HTML form into a signature pad. User can use stylus or a finger on the touch screen to do signature and click the Save button to save the signature value(JSON format) in SQLite database.


I wasn't able to draw signature of King properly in android emulator :). Save button will navigate back to Employee details and recorded signature value in database will be used to regenerate the signature on Signature Pad.


You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11.1.2.4.0]

In this sample application I'm storing the signature value into database, even we can store captured signature value as Image format.  For more information and option visit Signature Pad.

Friday, September 6, 2013

Jdeveloper 12c - EJB with Rest WebService and Filtering Data Collection

In Jdeveloper 12c there is a support for Rest WebService based on EJB and its only for Java Service Facade not for Session Facade. You can create Rest WebService just right click on the Java Service Facade and in context menu click on Create RESTful Service.

In this blog entry I'm interested in constructing dynamic data collection based on the URI, query-string parameters and send Rest WebService response. Having sensible resource names or paths (e.g., /departments/10 instead of /api?type=departments&id=10) improves the clarity of what a given request does. Using URL query-string parameters is fantastic for filtering, sorting, limit, pagination or dynamic fields. Here HTTP GET method is used to retrieve (or read) a representation of a resource. Read more on Rest Webservice.

The resources are based on EJB Java Service Facade. I tried adding GET parameters (PathParam/QueryParam) to a collection in order to add functionality such as filtering, sorting, filetering fields, limit. I have written logic in Java Service Facade for few resources that looks as below:

HTTP Verb Resource Naming (URI) Entire Collection (e.g. departments)
GET RestApplication/resources/model/departments Return list of departments.
GET RestApplication/resources/model/departments?offset=1 Return individual department object.
GET RestApplication/resources/model/departments?limit=4&childValue=false Return list of departments based on limit, if childValue passed as false response will not contains employeeList object.
GET RestApplication/resources/model/departments?limit=4&childValue=false&orderBy=departmentId
&orderType=DESC
Return list of departments based on ascending/descending order.
GET RestApplication/resources/model/departments?limit=4&childValue=false
&fields=departmentId,departmentName
Return list of departments with selected departments fields dynamically.
GET RestApplication/resources/model/departments/20 Return department object by departementId.

You can download the sample workspace from here
[Runs with Oracle JDeveloper 12.1.2.0.0]

JDeveloper provides WADL supports to run the REST WebService, below are the examples.
  • GET http://127.0.0.1:7101/RestApplication/resources/model/departments - Return list of departments
  • GET http://127.0.0.1:7101/RestApplication/resources/model/departments?offset=1 - Return department object.
  • GET http://127.0.0.1:7101/RestApplication/resources/model/departments?limit=4&childValue=false - Return list of departments based on limit, if childValue passed as false response will not contains employeeList object.
  • GET http://127.0.0.1:7101/RestApplication/resources/model/departments?limit=4&childValue=false&orderBy=departmentId&orderType=DESC - Return list of departments based on ascending/descending order.
  • GET http://127.0.0.1:7101/RestApplication/resources/model/departments?limit=4&childValue=false&fields=departmentId,departmentName - Return list of departments with dynamic departments fields.
  • GET http://127.0.0.1:7101/RestApplication/resources/model/departments/20 - Return department object by departementId.

Below Java Service Facade code explains how to create resources and filter the data collection for above scenarios.
@Path("model")
public class JavaServiceFacade {
    private final EntityManager em;
    private String XMLResponse;
    private String fields = null;
    private boolean childValue;

    public JavaServiceFacade() {
        final EntityManagerFactory emf = Persistence.createEntityManagerFactory("Model-1");
        em = emf.createEntityManager();
    }

    /**
     * @param childValue
     * @param offset
     * @param limit
     * @param fields
     * @param orderBy
     * @param orderType
     * @return
     */
    @GET
    @Produces("application/xml")
    @Path("/departments")
    public String getDepartmentsFindAll(@DefaultValue("true") @QueryParam("childValue") boolean childValue,
                                        @DefaultValue("0") @QueryParam("offset") int offset,
                                        @DefaultValue("0") @QueryParam("limit") int limit,
                                        @QueryParam("fields") String fields, @QueryParam("orderBy") String orderBy,
                                        @QueryParam("orderType") String orderType) {
        this.childValue = childValue;
        this.fields = fields;

        Query query = null;
        if (offset > 0) {
            query =
                em.createNamedQuery("Departments.findAll", Departments.class).setFirstResult(offset).setMaxResults(1);
            this.generateXMlResponse(query, false);
        } else if (orderBy != null) {
            String qlString = "select o from Departments o order by o." + orderBy;
            if (orderType != null)
                qlString += " " + orderType;
            query = em.createQuery(qlString, Departments.class).setMaxResults(limit);
            this.generateXMlResponse(query, false);
        } else {
            query = em.createNamedQuery("Departments.findAll", Departments.class).setMaxResults(limit);
            this.generateXMlResponse(query, false);
        }
        return this.XMLResponse;
    }

    /**
     * @param departmentId
     * @param fields
     * @param childValue
     * @return
     */
    @GET
    @Produces("application/xml")
    @Path("/departments/{departmentId}")
    public String getDepartmentById(@PathParam("departmentId") int departmentId, @QueryParam("fields") String fields,
                                    @DefaultValue("true") @QueryParam("childValue") boolean childValue) {
        this.childValue = childValue;
        this.fields = fields;
        Query query =
            em.createNamedQuery("Departments.ByDeptId", Departments.class).setParameter("bind_departmentId",
                                                                                        departmentId);
        this.generateXMlResponse(query, true);
        return this.XMLResponse;
    }

    /**
     * @param query
     * @param singleResult
     */
    public void generateXMlResponse(Query query, boolean singleResult) {
        if (singleResult == true) {
            List deptResultList = query.getResultList();
            if (deptResultList.size() > 0) {
                this.XMLResponse = "";
                Iterator deptListIterator = deptResultList.iterator();
                while (deptListIterator.hasNext()) {
                    this.XMLResponse += "";
                    Departments dept = (Departments) deptListIterator.next();
                    this.constructXML(dept.getClass(), dept, false);
                    if (this.childValue == true) {
                        List empList = dept.getEmployeesList1();
                        for (int j = 0; j < empList.size(); j++) {
                            Employees emp = empList.get(j);
                            this.XMLResponse += "";
                            this.constructXML(emp.getClass(), emp, true);
                            this.XMLResponse += "";
                        }
                    }
                    this.XMLResponse += "";
                }
                this.XMLResponse += "";
            }
        } else {
            Vector resultList = (Vector) query.getResultList();
            int deptSize = resultList.size();
            if (deptSize > 0) {
                this.XMLResponse = "";
                for (int i = 0; i < deptSize; i++) {
                    //  Object obj = resultList.elementAt(i);
                    this.XMLResponse += "";
                    Departments dept = (Departments) resultList.elementAt(i);
                    this.constructXML(dept.getClass(), dept, false);
                    if (this.childValue == true) {
                        List empList = dept.getEmployeesList1();
                        for (int j = 0; j < empList.size(); j++) {
                            Employees emp = empList.get(j);
                            this.XMLResponse += "";
                            this.constructXML(emp.getClass(), emp, true);
                            this.XMLResponse += "";
                        }
                    }
                    this.XMLResponse += "";
                }
                this.XMLResponse += "";
            }
        }
    }

    /**
     * @param clazz
     * @param obj
     * @param childValue
     */
    public void constructXML(Class clazz, Object obj, boolean childValue) {
        Class noparams[] = { };
        try {
            Field[] field = clazz.getDeclaredFields();
            for (int i = 0; i < field.length; i++) {
                String fieldName = field[i].getName();
                if (field[i].isAnnotationPresent(Column.class)) {
                    String methodName =
                        "get" + field[i].getName().toString().substring(0, 1).toUpperCase() +
                        field[i].getName().toString().substring(1);
                    Method method = clazz.getDeclaredMethod(methodName, noparams);
                    Object fieldValue = method.invoke(obj, null);

                    if (this.fields == null || childValue == true) {
                        this.XMLResponse += "<" + fieldName + ">" + fieldValue + "</" + fieldName + ">";
                    } else {
                        String columnNames[] = this.fields.split(",");
                        for (int j = 0; j < columnNames.length; j++) {
                            if (columnNames[j] != null) {
                                boolean colNamesExist = this.findFieldExists(Departments.class, columnNames[j]);
                                if (colNamesExist == true && columnNames[j].equals(fieldName))
                                    this.XMLResponse += "<" + fieldName + ">" + fieldValue + "</" + fieldName + ">";
                            }
                        }
                    }
                }
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     *
     * @param clazz
     * @param fieldName
     * @return boolean
     */
    public boolean findFieldExists(Class clazz, String fieldName) {
        try {
            Field field = clazz.getDeclaredField(fieldName);
            if (field != null)
                return true;
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return false;
    }
 
  /**
     * All changes that have been made to the managed entities in the
     * persistence context are applied to the database and committed.
     */
    private void commitTransaction() {
        final EntityTransaction entityTransaction = em.getTransaction();
        if (!entityTransaction.isActive()) {
            entityTransaction.begin();
        }
        entityTransaction.commit();
    }

    public Object queryByRange(String jpqlStmt, int firstResult, int maxResults) {
        Query query = em.createQuery(jpqlStmt);
        if (firstResult > 0) {
            query = query.setFirstResult(firstResult);
        }
        if (maxResults > 0) {
            query = query.setMaxResults(maxResults);
        }
        return query.getResultList();
    }

    public  T persistEntity(T entity) {
        em.persist(entity);
        commitTransaction();
        return entity;
    }

    public  T mergeEntity(T entity) {
        entity = em.merge(entity);
        commitTransaction();
        return entity;
    }
}