Saturday, January 13, 2018

Oracle JET: Caching data

Problem Description: Oracle JET is a client side technology and mostly it uses REST services to fetch data from server. Some of data is used on multiple pages and it does not change very frequently so its a good approach to query such data once and then cache it. Next time when you need it refer from cache.


Solution:

In Oracle JET we have index.html, which serves as main (or base) page. All other pages (module) appears on it. Some modules might be static and some are attached dynamically using router. To some up at run time, things are like below image.


Based on URL different modules keep on changing but index.html (base page) is as it is. It implies view model associated with it (also called root viewmodel) is also available all the time.

Another thing that we should know is 'LifeCycle of ViewModel instances'. In general code for viewmodels are like below

define(['ojs/ojcore', 'knockout', 'jquery', 'appController'],
 function(oj, ko, $, app) {
    
    function PageXViewModel() {
      console.log("Instantiating PageXViewModel");
      var self = this;
 
    }

    return new PageXViewModel();
  }
);

If we see ViewModels are created using RequireJS define function. RequireJS is an AMD framework. It helps creating JS objects only when needed and once object is available, it associate it with a key. Object remains in memory always and you can access it with that key. All ViewModels are created like that. Which means ViewModel instace would get created when first time you access module associated with that ViewModel. Next time when you try to access ViewModel, you will not get a fresh instance, you will get same old instance instead.
In above example  PageXViewModel instance would get created only once, when we access PageX first time. After that if we go to PageY and come back to PageX, same instance of PageXViewModel will be reused. To confirm that you can see browser console log and find how many times "Instantiating PageXViewModel" is getting printed. You will find its only once, when PageX is loaded first time.

Effectively we can say if  page is showing a module (PageX), there are two instances available PageXViewModel and RootViewModel. We can use them to cache our data. 

With that in mind, in this blog we can see various strategy to cache our data.

There are different kinds of data that we show on pages
1. Transactional data: which keeps on changing and user expects it to be latest always. For example table showing employee data. We should not try to cache such data as user expectation is to see latest data always. As we know our ViewModel instances are getting reused so how should we make sure that every time, we go to a page data is re-fetched from server. ViewModel has a function called handleActivated. This is called by framework always when a view is loaded. We can use this function to fetch data from server. For example

 self.handleActivated = function(info) {

         var promise = $.ajax({
                 type: 'GET',
                 url: 'http://myservice-url',
                 contentType: "application/json; charset=utf-8",
                 crossDomain: true,
                 dataType: "json"
              })
 
  promise.then(function(data) {
            self.data = ko.observableArray(data);

          });
 
         return promise;

        
        
     };
In above code we are setting data property of ViewModel using a REST output and this code runs every time we load page, so data will always show new values from server whenever page is loaded.

2. Setup data: which rarely changes. For example data appearing in various drop-downs (lookup).  This kind of data is ideal for caching. In this example let say we have a lookup PRIORITY and it shows values as High (H), Medium (M), Low (L). We want to show it as drop-down on customer and profile pages.

  To cache that I have two step code
  a. Add below method in appcontroller.js

function ControllerViewModel() {
        var self = this;

        //initiate global cache with empty objects
        self.globals = {lookups: []};


        //core private method responsible for fetching lookup data using rest-service
        var fetchLookupData = function fetchLookupData(lookupType){
            
            var encodedString = 'Basic ' + btoa('username:password');
            var promise = $.ajax({
                 type: 'GET',
                 url: 'my-rest-url,
                 contentType: "application/json; charset=utf-8",
                 headers: {"Authorization": encodedString},
                 crossDomain: true,
                 dataType: "json"
              });

            promise.then(function(response) {
                  self.globals.lookups.push ( {"lookupType": lookupType, 
                                              "lookupValues": response.lookupValues});
                 

              }, function(error) {
                  //alertFactory.handleServiceErrors(error);
                  console.log(error); //Handle errors in a better way
              });

            return promise;
        }

        //method to put a logic to get lookup data from cache (global) first and if not present then from rest-service
        self.initLookpCache = function initLookpCache(lookupType){
          
            var lookups = self.globals.lookups.filter(function (lookup){
              return lookup.lookupType === lookupType;
            })
            if(lookups.length > 0){
              console.log("Lookup present in cache");
              return lookups[0];
              
            }
            else{
              console.log("Reading lookup from service");
              return fetchLookupData(lookupType);
            }
          
        }
      
         //Remaining standard code of appcontroller.js is not shown.

Now on page, which requires cached data we can add below lines. Add it in customer.js and profile.js both

self.handleActivated = function(info) {
        // Implement if needed
        console.log('running handleActivated from CustomerViewModel');
        
        var promise = app.initLookpCache('MY_LOOKUP_TYPE');
        Promise.resolve(promise).then(function(lookupTypeValues) {
            self.linkTypes = ko.observableArray(lookupTypeValues.lookupValues);
        });


        return promise;

      };


We have actually put a call to Root-ViewModel's initLookupCache
It can return either Promise or lookupvalue. Promise is returned if values are getting fetched from REST service call. LookupValues are returned directly if values are coming from cache.

Promise.resolve make sure that
        if input is promise object then wait for it to get completed and once completed then only run function specified in then.
        if input is not promise but direct values then directly runs the function and pass values as input.

If you run customer and profile pages, you will find below log
running handleActivated from CustomerViewModel
Reading lookup from service

Instantiating ProfileViewModel
running handleActivated from ProfileViewModel
Lookup present in cache

You can see lookup values are coming from cache second time. Good part is its incremental cache, as and when you need lookup data first time, it will get cached. You are not loading all lookup data upfront.

Few other tips

  a. If you want to cache a data which is required very frequently (almost on all pages) you can cache such data from appcontroller.js itself.


  c. If you want to cache a data, which is required only in very very few pages you can cache them in their respective page's ViewModel itself. For that instead of calling appcontroller's method you can have local method and populate ViewModel (self.data) variable. NOTE: Even these page's viewmodel are not getting destroyed so anything cached there is also present throughout. For local caching your code of handleActivated could be be something like
self.data = ko.observable();

self.handleActivated = function(info) {
   if(!self.data()){
      //Make reset service call and populate self.data
   }
}




Thats all.

Friday, November 24, 2017

Removing a corrupted weblogic deployment.

Problem Description: What if you have deployed an application on weblogic and somehow it has got corrupted and you want to undeploy it. Most of the time you can go to weblogic console and undeploy but at times it gets corrupted so badly that you can't even start your weblogic server. I faced this issue recently while experimenting with filters. My filter code has some problem. Application got deployed successfully but when I try to use it, due to code bug in filter, it was going in infinite loop. So I stopped weblogic server. Now when I try to start my weblogic server, it by default tries to deploy previous application and this time because of problem in deployment weblogic was not starting. To sum up I need to delete my weblogic deployment offline.

Here are the changes I did

1. Find your config.xml and see app-deployment node. For integrated server config.xml is located in <System-Directory>/DefaultDomain/config/config.xml

 Your entry will be something like below

<app-deployment>
    <name>MyApplication</name>
    <target>DefaultServer</target>
    <module-type>ear</module-type>
    <source-path>D:\Oracle\Middlewares\JDEV_SystemDir\system12.2.1.0.42.151011.0031\o.j2ee\drs\MyApplication</source-path>
    <security-dd-model>DDOnly</security-dd-model>
    <staging-mode xsi:nil="true"></staging-mode>
    <plan-staging-mode xsi:nil="true"></plan-staging-mode>
    <cache-in-app-directory>false</cache-in-app-directory>
  </app-deployment>

a. Note source-path location. Remove your deployment from that location.
b. Remove entry of app-deployment from config.xml file as well.
c. Also remove your applicaton from <System-Directory>/servers\DefaultServer\tmp\_WL_user directory.
d. To my surprise even after all these changes, weblogic server was not starting. I thought of deleting DefaultDomain directory but then I decided to close jdev once and try. That worked.

To sumup you need to close jdev and then perform a,b and c task mentioned above.


Now you can start your application.

Similarly I believe you can delete invalid jndi entries also from this file if you find that your weblogic is taking long time to start because of invalid jndi entries. Actually weblogic tries to look in this file and connect with those database while starting to reserve connections in pool but if database is down or no longer valid, weblogic waits for timeout and it makes weblogic startup slow.

Thats all.

Monday, July 31, 2017

JCS-SX: Deploy your first application

Problem description: If you create a minimum ADF application and try deploy it directly on JCS-SX instance your deployment may fail with below error

2017-07-31 19:02:13 CDT: weblogic.application.ModuleException: weblogic.application.ModuleException:
at weblogic.servlet.internal.WebAppModule.startContexts(WebAppModule.java:1531)
at weblogic.servlet.internal.WebAppModule.start(WebAppModule.java:488)
at weblogic.application.internal.flow.ModuleStateDriver$3.next(ModuleStateDriver.java:425)
at weblogic.application.utils.StateMachineDriver.nextState(StateMachineDriver.java:52)
at weblogic.application.internal.flow.ModuleStateDriver.start(ModuleStateDriver.java:119)
at weblogic.application.internal.flow.ScopedModuleDriver.start(ScopedModuleDriver.java:200)
at weblogic.application.internal.flow.ModuleListenerInvoker.start(ModuleListenerInvoker.java:247)
at weblogic.application.internal.flow.ModuleStateDriver$3.next(ModuleStateDriver.java:425)
at weblogic.application.utils.StateMachineDriver.nextState(StateMachineDriver.java:52)
at weblogic.application.internal.flow.ModuleStateDriver.start(ModuleStateDriver.java:119)
at weblogic.application.internal.flow.StartModulesFlow.activate(StartModulesFlow.java:27)
at weblogic.application.internal.BaseDeployment$2.next(BaseDeployment.java:671)
at weblogic.application.utils.StateMachineDriver.nextState(StateMachineDriver.java:52)
at weblogic.application.internal.BaseDeployment.activate(BaseDeployment.java:212)
at weblogic.application.internal.EarDeployment.activate(EarDeployment.java:59)
at


Solution:
You need to create weblogic.xml file inside WEB-INF and make following entry in it

<?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">
   

    <library-ref>
        <library-name>adf.oracle.domain.webapp</library-name>
    </library-ref>
    <library-ref>
        <library-name>jstl</library-name>
        <specification-version>1.2</specification-version>
    </library-ref>
    <library-ref>
        <library-name>jsf</library-name>
        <specification-version>1.2</specification-version>
        <exact-match>true</exact-match>
    </library-ref>


</weblogic-web-app>


Once done you can deploy your application.


Thats all.

Making ADF page Readonly

Problem description: Sometime we found that we want to make ADF page readonly based on certain condition. One option is apply EL expression on all input fields of ADF page. For example you can write EL as readonly="#{pageFlowScope.pPageMode=='View'}. By this expression we are saying make input field read only if page mode is view. If there are multiple input fields, we need to apply it on all those fields. I would say if number of fields are less, its manageable but at times fields are more and it becomes very difficult to maintain this condition. In future also some developer may add new field but then forget to add this condition. So at times it becomes error pron and we need a centralized solution to make a page readonly. In this blog I am showing a generic method, which makes page input/action items disabled/readonly.

Solution

1. Have a bean for page and write below method

private void makeComponentHierarchyReadOnly(UIComponent component) {

        try {
            Method method1 =
                component.getClass().getMethod("setDisabled", boolean.class);
            if (method1 != null) {
                method1.invoke(component, true);
            }
         

        } catch (NoSuchMethodException e) {
            try {
                Method method =
                    component.getClass().getMethod("setReadOnly", boolean.class);
                if (method != null) {
                    method.invoke(component, true);
                }
            } catch (Exception e1) {
                // e.printStackTrace();//silently eat this exception.
            }


        } catch (Exception e) {
            // e.printStackTrace();//silently eat this exception.
        }
        List<UIComponent> childComponents = component.getChildren();
        for (UIComponent comp : childComponents) {
            makeComponentHierarchyReadOnly(comp);
        }

    }

Above method is taking UI component as input. It iterates over each child upto n level and try to disable them or if disable property is not there then it tries to make it readonly.

NOTE: Disable will also disable links/button and all action items as well.

2. You can call above code at appropriate time: Mostly we want to call above code at page load but at times you may have View/Edit button and on click of that you want to call it. If you want to call it at page load you can take approach 3 as described in blog http://sanjeev-technology.blogspot.in/2017/02/adf-execute-code-before-page-load.html

If you want to call on button click then you can simply have actionListener and call above method from there. For example
public void makePageReadOnly(ActionEvent actionEvent) {
        // Add event code here...
        makeComponentHierarchyReadOnly(this.getMyPageRootComponent());

    }

getMyPageRootComponent is a getter for a UI component under which you want everything to be readonly. To get this component, you just need to bind it with bean.

Thats all. Thank you for your time.



Monday, May 8, 2017

Oracle JET: Hybrid android app using NetBeans

This blog is continuation of my previous blog http://sanjeev-technology.blogspot.in/2017/05/oracle-jet-first-hybrid-android-mobile.html

In that blog I showed how we can use npm, yo and grunt commands to develop a new web/mobile application in JET.
In this blog my idea is simply open the project in NetBeans and start development in NetBeans. NetBeans provide auto-completion + templates for new project etc for Oracle JET. As we have already created project using commands so we will not be creating a new project but directly open it in NetBeans and start using it.

1. Open project: To open project in NetBeans you can navigate as File > Open Project
      Good News: NetBeans automatically identifies our project created using yoman (yo) as a project.
     

We get various source directories (src, src-hybrid, src-web). We also have a separate web directory.


2. Make changes: Let us make some changes in source and see its
Few things to notice:

  • We can directly run index.html present in web directory.
  • Once its running Chrome browser shows a bar " NetBeans Connector" is debugging this browser. Don't cancel it. It enables browser log to appear in NetBeans console. 
  • Any changes done in web directory (Site Root) are picked immediately (without restart). 
  • Any changes done in src directories are also picked immediately (without restart). Actually these changes are pushed automatically to web directory immediately and application picks from web after that.
  • Ideally we should be changing src directory and should not code in web directory in this kind of setup. web directory is kind of classes directory and NetBeans will use it to run application. 

  

3. Run and Quick test: For this demo I just make changes in index.html inside src directory. I will change quickly change <h2 class="demo-profile-name">James</h2> to <h2 class="demo-profile-name">Sanjeev</h2>. I just need to save it. I find that changes are automatically pushed to web directory and even my browser is refreshed automatically to show new changes.




4. Release using Grunt from NetBeans

To run grunt commands for project, I can right click on project and select Grunt Tasks. There are few tasks already listed. To release I need to use serve:release task, which is not listed, so I can go to advance and create my own task.



Under grunt tab log you will see location of apk file.


Thats all. Now you can move and install this apk in android phone.

Monday, May 1, 2017

Oracle JET: First Hybrid android Mobile App

Problem Description: In this blog I am just putting all steps those I followed to create a mobile app using Oracle JET

Solution: Here are the steps that I followed to create my first application. As there are multiple ways to start JET development so I would not say this is best way but this is what works for me.



1.      Install Node.js: https://nodejs.org/en/download. Once node is installed you should be able to run npm commands.

2.      Run command prompt in Admin mode and then run below commands to install various npm modules
a.       npm install -g cordova
b.      npm install -g yo
c.       npm install -g grunt-cli
d.      npm install -g bower
e.       npm install -g generator-oraclejet
f.       npm install -g cordova

3.      Install android sdk: I have installed android studio, which comes with android sdk as well. https://developer.android.com/studio/index.html

4.      Use SDK Manager and install api required api: To launch SDK manager from android studio you need not to create any project. You can simply start android studio and select configure option


      
                 Below image shows how my SDK manager looks like





5.   Create web project and run it.
a.       Navigate to directory where you want to create project (say: D:\practice-jet)
b.      Before creating porject verify that you have all npm modules installed:  


npm list -g --depth=0

        
       
  
                 c.    Run command: I prefer creating a web project first and then add hybrid mobile
                        application feature to it.
 yo oraclejet MyFirstApp --template=navdrawer:hybrid

 
  
                  d.     cd MyFirstApp
                  e.      grunt build

                    f.     grunt serve
                  grunt serve command will start a process at port 8000 port. Your page will be visible on
                  browser at http://localhost:8000
                    we can kill process using ctrl+c.  Now our application is running fine on browser. We
                    have following directory structure till now.
 6.     Let us add android feature to our project
         yo oraclejet:add-hybrid --platforms=android
           If we see directory structure, it has got two additional directory src-web and src-hybrid. We already have one src directory. Main (or common code ) will reside in src folder. Code which is only applicable to web application will go in src-web project. Code which is only applicable to hybrid application will be in src-hybrid folder.

   7.      Build project
          grunt build --platform=android

            This step has created an android-debug.apk file in D:\practice-jet\MyFirstApp\hybri\platforms\android\build\outputs\apk folder. 

8. Create release ready apk file
Keystore with key creation: Now we want to create a release ready apk file. For that we need to first use keytool to create keystore and alias. We can run below command for that 
keytool -genkey -v -keystore MyFirstApp.keystore -alias MyFirstApp -keyalg RSA -keysize 2048 -validity 10000
Command should ask for password for keystore and key. It will also ask for additional details like lastname, first name etc. You can hit enter if you don't want to provide. 

We can keep keystore file outside project so that you can use same keystore with multiple application. My KeyStore name is MyFirstApp, password is 'manager' and Key name is agains MyFirstApp. Keystore and key passwords are same. Definitely you will have different keystore/key/passwords.

    
Now we will create a buildconfig file, which has entry of keystore. I placed file in d:\practice-jet folder. Content of file looks like


Finally run below command to generate release ready apk file

grunt serve:release --platform=android --buildConfig=D:\practice-jet\buildConfig.json
                       
  

   I have moved file to my android phone and installed apk. Here is the image of running application


9. Edit something in code and verify changes.
     To edit code you should make changes in src folder and then use grunt serve command to run it.
     NOTE: to edit web specific code use src-web and to edit hybrid specific code use src-hybrid.
    NOTE: As we have multiple platform now grunt serve will not be enough, we need to specify  which platform. Use command grunt serve --platform=web to run its web version in browser.

To sum up, we have run following important commands



1.      One time setup
a.       Node install
b.      Node module install
                                                              i.      npm install -g cordova
                                                            ii.      npm install -g yo
                                                          iii.      npm install -g grunt-cli
                                                          iv.      npm install -g bower
                                                            v.      npm install -g generator-oraclejet
                                                          vi.      npm install -g cordova
c.       Android SDK installation

2.      Project Specific setup
a.       Create project: yo oraclejet MyFirstApp --template=navdrawer:hybrid
b.      cd MyFirstApp
c.       grunt build
d.      grunt serve
e.       Add hybrid features: yo oraclejet:add-hybrid --platforms=android
f.       grunt build --platform=android
g.      Create keystore: keytool -genkey -v -keystore MyFirstApp.keystore -alias MyFirstApp -keyalg RSA -keysize 2048 -validity 10000
h.      Create buildConfig file
i.        Create release ready apk file: grunt serve:release --platform=android --buildConfig=D:\practice-jet\buildConfig.json

From here you can use NetBeans for development: To do so, you can follow
http://sanjeev-technology.blogspot.in/2017/05/oracle-jet-hybrid-android-app-using.html