Tuesday, June 30, 2015

ODTUG KScope15: One week in a nutshell

Last week I visited ODTUG KScope15 in Hollywood Florida. In this post I will share my findings and hope that you can benefit from it.
My general impression of the conference is that it was to hot for me in Florida. At least to hot for a conference. If you can sit on the beach whole day long, it is a great place to be, but hey, I had to work.... From a content perspective, there was a whole lot of Mobile and Cloud, and just a tiny bit of ADF. We need to work on this for next year as per request of the attendees who really were asking for more ADF content.

Anyway, here is my week in a nutshell.
Saturday
Not much to do after an 18 hour trip. So when I arrived at the hotel the only thing to do was enjoy the view and get some Cuban food. What better place to do that then in Southern Florida. The food was good and I went there again during the week.


Sunday
The Sunday symposium was a very good one from an Oracle ADF Developer Perspective. The Sunday Symposium took a deep look at the impact of the cloud's platform on the role and capabilities of application developers. Oracle showed the latest and upcoming products for web and mobile application development and deployment in the cloud. There was also room for empowering business users with development capabilities through the cloud, with products such as MAX and ABCS (see later). There was a demo of MAX, aka Mobile App Accelerator. MAX is a part of Oracle Mobile Cloud Service in a demo. where a business user can actually create real mobile apps without coding.

The final session of the day was really worthwhile. In this session Brian Fry showed Oracle Application Builder Cloud (it is as easy as ABC) for the very first time publicly. Oracle ABCS enables you to drag-and-drop your way to enterprise-ready JavaScript and HTML web applications.

More details on the Sunday Symposium can be found here.

Monday
On Monday I attended an IoT hands-on lab. It used an APEX front end to show results, but for me the main content (IoT) was more important. I actually built my own thing and connected it to an Oracle APEX application. I used a circuit board, wires, capacitors, a wifi device, LEDs and a sensor. I wired them together into a real THING, connected to the internet and start communicating with an Oracle database. Because of the closed API for the REST Service they used I had to build an APEX application to control the thing and report on its data. Had it been an open API, I would have created an ADF or MAF app to do the exact same thing.

During Lunch I sat down at the MAF and ADF table discussing with my peers and also with Oracle Product Management. We also discussed the big announcements made by Oracle where from my perspective Mobile Cloud is the most interesting one. However there are many other clouds that we need to look into. Makes you wonder: Usually when I am that much in the clouds, I tend to call it foggy. I hope Oracle's Cloud Strategy is not foggy, but cristal clear. I know they have a plan, and I know there is a lot of work todo. We discussed the overlap we see in Mobile Cloud and Integration Cloud and also in Application Builder Cloud. Not sure if Oracle will take parts of those clouds and bundle them into a new one, or actually transfer stuff from MCS and ABCS to ICS, where it belongs. We will see.

In the after noon I had to work on a proposal for a Customer so no more sessions till 6.00 pm when the Oracle ACE reception started. It was outside near the pool but still it was very hot so we had to drink plenty of 'water'. After that I continued to the ADF Community night where we used the Wii to play games.

Tuesday
This was the day of my presentation. "Real Life MAF: The things you don't learn from Oracle's Developer Guide". I promised attendees to provide links to my demo's and also to my slides. So here they are:

1 and 2 are available here whereas you can find 4 here.
  1. Loading Images in the background
  2. Action Complete (Programatically showing popup) and fragment demo
    1. Files involved in action complete:
      1. Complaints.amx
      2. fha.js
      3. FlightAppBean.java
  3. Real Sliding Springboard (is provided as Sample App with Oracle MAF)
  4. Local Notifications
The whole presentation is available at slideshare.
After my session I had to run to the Lunch and Learn panel where I was in with co-ACE(D)s Lonneke Dikmans, Debra Lilley, Mia Urman and John Flack. As you can see, the women have the majority, which I think is not necessarily a bad thing. We discussed with the audience what they expect of the next ODTUG KScope, which by the way will be in Chicago! Min observation was that there was too much cloud and mobile and too little ADF. So here is something that we should think about for next conference. Next I joined Lonneke's session called "SOA made Simple". Using a waitress as an analogy for a service bus really makes ordering breakfast a whole other experience.

My afternoon ended with a hacking session with one of the attendees in my session. It was mainly about background threads and refreshing the UI in an MAF App. The only thing you need to do that is to call to flushDataChangeEvent() and it will refresh the UI with all changes from that background thread.
 AdfmfJavaUtilities.flushDataChangeEvent()  
I was even able to refresh the UI from two separate background threads. I will blog about that in a separate post.

My day ended with a great diner with a customer, or better a friend at GG's, a very nice place to have good seafood.

Wednesday
Today was actually a day where I followed several sessions. I started with some support (and coffee) for Lonneke who was talking about Choosing the Right Mobile Architecture. One bummer was the Oracle Mobile Cloud Service Hands On lab, which in the end was not a hands on, but a demo. Unfortunately even for Oracle Product Management there was no way to get access to MCS instances that can be used by the audience. I was really looking forward to get my hands on MCS but this ws not possible. After lunch I attended two more sessions. One of Joe Huang, outbound PM for Oracle MAF.He was talking about "New Core and Sync Services in the Oracle Mobile Application Framework: What Does It Mean for Developers". Nothing really new here for me. The other session of the afternoon was by Raghu Srinivasan. He is Director of Development in the Cloud and Mobile Development Tools, and responsible for Eclipse tooling (OEPE). His session was called "REST Easy with Oracle MAF: Building RESTful MAF Applications in Eclipse (OEPE)". It was a great session where I got to see the power of MAF in eclipse. I mainly (or should I say only) use JDeveloper but this was een eye opener. Amongst a lot of other things he showed how to use a RAML definition to generate all Java you need to work with REST/JSON services. I am convinced now. From now on I will use OEPE more for MAF Development. Great features!! A good tutorial if you just get started can be found here.

The day ended with the white party at Nikki Beach. Great food, great venue, but from the way I look at it, it was a pretty long trip (1 hour one way) for the party.


Thursday
It's a wrap. No time to attend the closing session. We had to be at the airport at 11:30. It was a great KScope again. I met some old friends and found some new ones. I missed several people who I hope to see soon. And I hope to be back next year.

Monday, June 29, 2015

IoT Hackathon Part II : Overview of Grove Pi sensors

This post gives you an Overview of Grove Pi sensors and the fun things that you can do with it.

I will add the real content later, but for now we need a placeholder.

IoT Hackathon Part I : Setting up your Raspberry Pi

This post describes how you can setup your Raspberry Pi in order to start working with it.

I will add the real content later, but for now we need a placeholder.

Sunday, June 21, 2015

ODTUG KScope15: Sunday Symposium

Sunday symposium took of at 8:30. Unfortunately i had to skip the first two session due to some work That had to be done first. The first session I really got to attend was Brian Fry's session on Oracles Developer Cloud Service.

JDeveloper, Eclipse or Netbeans was the way you would work with Oracle Developer Cloud. Now there is a new feature (not publicly available) that is a browser based IDE. It is a full development environment. It is a very flexible, responsive and quick UI. it has an integrated out of the box GIT repository. It can be used for both JavaScript, and Java EE and java SE. Oracle uses the DevCS to build the DevCS with over 145 developers using 27 GIT repositories. So oracle now is drinking their own champagne, which sounds much better then eating their own dog food by the way.  


The demo was nice and showed the power of the Javascript editor and the integrated support with GIT. also the Java editor looked very promising. We will have to sit and wait for this to be publicly available.

After lunch Lynn Munsinger, talked about Mobile Cloud Service aka MCS. She explained some of the concepts of mobile development and Mobile Backend as A Service. (MBaaS). Somewhere during this session there was a small hint about MCS being released maybe tomorrow during a live webcast. Next Lynn showed Oracle Mobile Cloud Service in a demo. finally as a bonus, there was a demo of MAX, aka Mobile App Accelerator. MAX is a part of Oracle MCS where a business user can actually create real mobile apps without coding. I have seen this before, but now it is going really close to being production. It might even be part of the first MCS release, that might be release tomorrow.

The final session of the day was really worthwhile. In this session Brian Fry showed Oracle Application Builder Cloud (it is as easy as ABC) for the very first time publicly.



ABCS is a tool for the Citizen Developer. It is targeting non technical business users to rapidly build web and mobile apps. The key features of ABCS are the following (amongst others):

- UI First Experience
- Desktop and Mobile
- Zero Install
- Codeless Drag and drop development
- No Deployment
- HTML5/Javascript UI supporting ALTA UI or Simplified UI



I certainly hope there is more to come soon.
For you the get a glimpse of what ABCS can do, you can look at the two video's I created.

Here is the first one:



Here is the second one:


Wednesday, April 22, 2015

ODTUG Mobile Day : "Going Mobile? Where is Oracle Taking You?"

Yesterday ODTUG organised the ODTUG Mobile Day in Utrecht the Netherlands. The event was supported by two local user groups, OGh and OBUG, and sponsored by eProseed NL.
This full-day event was dedicated to Oracle's next-generation mobile development, infrastructure, and security solutions: "Going Mobile? Where is Oracle Taking You?". All of this was presented by the world’s leading ACE Directors and ACEs,
  • Lonneke Dikmans – Oracle ACE Director
  • Dimitri Gielis – Oracle ACE Director
  • Roel Hartman – Oracle ACE Director
  • Mark Rittman – Oracle ACE Director
  • Mia Urman – Oracle ACE Director
  • Luc Bors – Oracle ACE
and several Oracle speakers. Session abstract are published at the ODTUG website.
These top experts in the field showed how to implement the latest mobile advancements from the newest technologies, such as Oracle Mobile Cloud infrastructure to building BI mobile dashboards to a deep dive into APEX security for mobile to the latest features in Oracle MAF.

With over 75 attendees, this event was fully booked. Sessions where well attended and with an enthusiastic audience it was fun to present at this event. All sessions focused on mobile technology applications.


In his opening keynote of Regis Louis, VP Product Management, Oracle Corporation, explained what "Going Mobile with Oracle" means and talked about Oracle's Platform and Application Strategy regarding mobile. Next, the audience split into two groups for a total of 10 paralel sessions in 2 separate tracks. One track was devoted to FMW whereas the other track was for BI and APEX.

After a great day packed with sessions, the closing keynote was for Steven Davelaar.


He showed a demonstration of an end-to-end mobile solution and discussed the overall architecture and key implementation techniques. He also shared some practical guidelines on getting started with "mobilizing" your enterprise and discuss on-premise versus cloud-based mobile back ends. Some very nice UI's and an excellent insight on how to use Oracle's cloud solution in your architecture.
At around 5:15 pm it was a wrap and people went home, happy and satisfied. We have had some really good feedback on this event and on the individual sessions as well.

Not sure where ODTUG will share the presentations, but in the meanwhile, mine can be found here:




Thanks to ODTUG, OGh and OBUG for organising this great event.




Thursday, April 02, 2015

MAF 2.1.1 : Using Local Notifications

One of the new features in version 2.1.1. of Oracle MAF are local notifications.
These notifications originate within the MAF application and are received by the same application. They are delivered to the end user through standard mechanisms supported by the mobile device platform (for example, banner, sound) and can work when the application is either in the foreground, background or not running at all.

I this post I show you an example of how to work with Local Notifications from Java. I use a simple MAF app. I will not explain how to build this app, but the source can be downloaded here. It is mainly derived from the "LocalNotificationDemo" public sample app.

Introducing Local Notifications
As with many framework features, MAF supports three ways to set Local Notifications. First you can use the device features datacontrol. To support declarative use of Local Notifications, the DeviceFeatures data control includes the addLocalNotification and cancelLocalNotification methods, which enable MAF applications to leverage a device's interface for managing notifications so end users can schedule or cancel local notifications.


Second you have the option to set Local Notifications from JavaScript. MAF allows you to manage local notifications using JavaScript APIs in the adf.mf.api.localnotification namespace. The methods add() and cancel() are available. More info on this is available from the developer guide (see resources at the end of this post).

Finally you can set Local Notifications from Java code, which is what I will explain in the remaining part of this post.

Set up the Listening Part
Because the Listening part is the same for all methods mentioned above I will start to explain this before going into detail for setting Local Notifications from Java code.
The concept of Local Notifications is from an MAF perspective not different from Push Notifications
First we need to create an eventListener that specifically listens for Local Notifications.
This class must implement oracle.adfmf.framework.event.EventListener.
In this class we must use the onMessage() method, which will fire when a notification is received.

public class MyLocalNotificationListener implements EventListener {
    public MyLocalNotificationListener() {
    }

    public void onMessage(Event event) {
    
       // work with the notification event
       // Here we can get the application state (for wich we can use a util method) and the payload
        String appState = stringifyAppState(event.getApplicationState());
        String payload = event.getPayload();

       // Now do whatever we want, for instance call a feature
        AdfmfContainerUtilities.gotoFeature("com.blogspot.lucbors.lnb.NotificationShower");
        
        }
    private String stringifyAppState(int appState) {
    switch(appState) {
        case Event.APPLICATION_STATE_FOREGROUND: return "FOREGROUND";
        case Event.APPLICATION_STATE_BACKGROUND: return "BACKGROUND";
        case Event.APPLICATION_STATE_NOT_RUNNING: return "NOT RUNNING";
    }
    return "UNKNOWN";
    }


After creating this Listener class it must be added as an eventSource in the start Method of the application Lifecycle Listener.
In this way, each time the app starts, we make sure the app is actually Listening to the local Notification event.
public void start()
  {
    // Listen for local notifications
    EventSource evtSource =  
            EventSourceFactory.getEventSource(EventSourceFactory.NATIVE_LOCAL_NOTIFICATION_EVENT_SOURCE_NAME);
    evtSource.addListener(new MyLocalNotificationListener());
  }

With all of this code in place, the app is ready to listen for and respond to local notification.

Creating local Notifications from Java
Creating a new Notification is pretty much straightforward. In this example we use a button to create a local Notification.

To work with Local Notifications using Java, the frameworks' utility class oracle.adfmf.framework.api.AdfmfContainerUtilities contains several methods to help you. The first one is addLocalNotification(). This method can be used to create a local notification. It needs an MafNativeLocalNotificationOptions Object.

The MafNativeLocalNotificationOptions Object contains several properties that can be used for notifications.

protected String title;
    protected String alert;
    protected LocalDateTime date;
    protected RepeatInterval repeat;
    protected int badge;
    protected String sound;
    protected String vibration;
    protected HashMap <String> payload;

So for creating a local Notification we first need to create a new MafNativeLocalNotificationOptions object, called options in the code below. Next we set the values for all, or many, of the properties such as alertTitle, alert, notifationDate, badge and maybe even sound and vibration. Also, if the notification needs a payload we need to set this payload. Once these values are properly set we can add a new notification to the app by calling addLocalNotification(). The call to this method returns a String containing the notificationId.

public void addNotificationForAction(ActionEvent actionEvent) {
        String notificationDate = "now";
        try
        {
            // Set the notification options
            MafNativeLocalNotificationOptions options = new MafNativeLocalNotificationOptions();
            options.setTitle("Just Some Reminder");
            options.setAlert("Did you Forget Something ?");
            if (date != null) {
                date.setSeconds(0); // Clear the seconds component to fire on the minute
                LocalDateTime l = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime())
                                                        , ZoneOffset.UTC);
                options.setDate(l);
                DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
                notificationDate = dateFormat.format(date);
            }
            options.setBadge(-1);
            if (sound) {
                options.setSound(MafNativeLocalNotificationOptions.SOUND_DEFAULT_SYSTEM);
            }
            if (vibration) {
                options.setVibration(MafNativeLocalNotificationOptions.VIBRATION_DEFAULT_SYSTEM);
            }
            // Set 3 values in the JSON payload
            HashMap<String Object> payload = new HashMap<String Object>();
            payload.put("key1", 1);
            payload.put("key2", "hello");
            payload.put("key3", true);
            options.setPayload(payload);
            
            // Add the notification
            String notificationID = AdfmfContainerUtilities.addLocalNotification(options);
            System.out.println("++++ Notification added successfully for " + notificationDate);
            System.out.println("++++ Notification ID is " + notificationID);
        }
        catch(Exception e)
        {
            System.out.println("++++ There was a problem adding notification: " + e.getMessage());
            e.printStackTrace();
        }    
    }

Once this notification is added, it will show up at the given moment; in this case the date that is set with options.setDate(). It will show the Notification with the corresponding message; in this case "Did you Forget Something ?" as set by setAlert();



Cancelling a Notification
Sometimes, a notification is set as a reminder for some action. As in the previous example, "Did you forget something", the user is reminded to do something. There are situations where the user already did what you reminded him to do. In that case we don't want to show the notification.

For that purpose we can use the cancelLocalNotification() method in the AdfmfContainerUtilities class. To cancel the notification we must provide the notification ID.

    public void cancelLocalNotification(ActionEvent actionEvent) {   
        try {
       // We use the notificationId that was set when the notification was added
       //setNotificationId( AdfmfContainerUtilities.addLocalNotification(options));
          String cancelledNotificationId = AdfmfContainerUtilities.
                 cancelLocalNotification(notificationId);
          System.out.println("Notification successfully canceled"); 
        }
        catch(AdfException e) {
          System.err.println("There was a problem cancelling notification");
        }
    }


Scheduling a Repeating Notification
If we want to schedule a notification to fire every time during a given interval we can use the setRepeat() on the MafNativeLocalNotificationOptions. An example of this could be that you want to remind the user to open his app every day to check for changes. So if we want a notification that is scheduled to fire every Day we simply need to call setRepeat() with interval Daily.

options.setRepeat(MafNativeLocalNotificationOptions.RepeatInterval.DAILY);

Just for testing and for the purpose of the demo I like to work with a Minutely interval. This makes not a lot of sense, but for testing, it is a good interval, at least compared to yearly.... The available options are displayed below:



Cancelling a Repeating Notification: A Solution for a Limitation
Now lets assume we have a clever user who by now knows that we what him to check his app every day. So here is the situations where the user already did what you reminded him to do. In that case we don't want to show the notification.

For that purpose we must also use the cancelLocalNotification() method in the AdfmfContainerUtilities class. Cancelling a notification means that the notification is COMPLETELY cancelled even if it was scheduled to fire every DAY. Once cancelled, it will no longer notify, not even the next day. So in order to make this really work, cancel today and notify tomorrow, we must create a new local notification, exactly like the one that was just cancelled with interval DAILY.
Just for testing purposes I added an application preference that I can use to indicate whether or not I really want to recreate the local notification.

    public void cancelLocalNotification(ActionEvent actionEvent) {   
        try {
       // We use the notificationId that was set when the notification was added
       //setNotificationId( AdfmfContainerUtilities.addLocalNotification(options));
          String cancelledNotificationId = AdfmfContainerUtilities.
                 cancelLocalNotification(notificationId);
          System.out.println("Notification successfully canceled"); 
          

            Boolean recreate = (Boolean)AdfmfJavaUtilities.evaluateELExpression(
            "#{preferenceScope.application.ReCreateNotificationAfterCancel.ReCreate}");
            // if recreate preference is true
            if (recreate.booleanValue()){
               addNotification();
            }
        }
        catch(AdfException e) {
          System.err.println("There was a problem cancelling notification");
        }
    }

Look at the logfile below and see what happens. First, the notification is created (1), then it is cancelled (2), and immediately recreated !


NOTE (1): The above limitation is not a limitation of Oracle MAF. This is simply how local notifications work on the Operating Systems. Although I am not 100% sure that the solution of re-creating the notification is the most optimal, for now it really seems to be the only way to make this work.

Resources
MAF Developer Guide Chapter 24
The sources of this app are available here.


Wednesday, March 18, 2015

MAF 2.1 : Mobile Front End for Live Mobile Hacking with an OFM 12c red stack - Part I

During the closing keynote at the Oracle Fusion Middleware Forum XX in Budapest my former colleague Lucas Jellema and I delivered a live development demo. It was great fun to do. During this demo I developed an Oracle MAF frontend on top of Oracle SOA Suite 12c REST-JSON services. All in all it was a good session that was exactly delivered on-time with no errors. I think the demo gods where with us.
In this post I describe some of the details regarding the Oracle MAF part of this demo. To be more specific, how the MAF application is able to work with the REST-JSON Services. In a later post I will go into the details of how to user interface was built. The details of the SOA suite 12c Backend are described by Lucas at the AMIS Technology blog in the following posts:

Preparing ,

Implementing part - 1  and

Implementing part – 2



Before I start with the details I best describe the case first. We decided to create a tablet app that can be used by flight attendants to browse flight details, change the flight status (from boarding to boarding complete for instance), and browse the passenger list. Also, in case a customer wants to file a complaint for some reason, a flight attendant can do this using the app. For all these purposes the SOA Suite 12c backend exposes the necessary RESTJ-SON Services.



To create a MAF application that is able to consume these services I first need to create Connections to these services, next I need to transform the JSON Object to Java objects and finally I need to

Setting up Connections

In order to “talk” to the backend REST-JSON Services I need to connect to them by means of a REST connection. This can be easily created in JDeveloper. Simply create a connection that points to the URL endpoint of the REST-JSON Service.



I used SOAP-UI to mock the services. This helps a lot, especially because Lucas and I are not really connected all the time. This way he could work on the backend and I on the mobile front end.

The JSON Object
When I call the Service it returns a JSON Object that contains all information regarding a specific Flight. The JSON Object looks like the one below. This is the format and structure that Lucas and I agreed on.

1:  {  
2:    "FlightCode":  {  
3:     "CarrierCode": "KL",  
4:     "FlightNumber": "34"  
5:    },  
6:    "FlightStatus": "boarding",  
7:    "FlightDate": "2015-03-07T+01:00",  
8:    "Departure":  {  
9:     "Time": "2015-03-07T09:50:00+01:00",  
10:     "AirportCode": "AMS",  
11:     "AirportName": "Schiphol Airport",  
12:     "City": "Amsterdam",  
13:     "Country": "NL"  
14:    },  
15:    "Destination":  {  
16:     "Time": "2015-03-07T11:55:00-08:00",  
17:     "AirportCode": "SFO",  
18:     "AirportName": "San Francisco International Airport",  
19:     "City": "San Francisco",  
20:     "Country": "US"  
21:    }  
22:  }  

Working with the JSON Object

To work with the JSON Object in the MAF app, when data is read to or from REST services, some additional processing is required to translate the JSON Object (which in fact is a String) to and from Java objects. This proces is called (de-) serialisation.

All of the classes that I created to work with JSON in this MAF app are displayed in the following class Hierarchy. I will discus them bottom up and for clarity in the remaining part of this post I will explain how to work with the FlightDetail Object. That is the one in the middle (top to bottom) of the class hierarchy diagram.


The RestCallerUtil Class

The RestCallerUtil class wraps the MAF calls to the REST service, thus further simplifying the REST interaction, avoiding you to write unnecessary boilerplate code over and over again.
The RestCallerUtil class contains some utility methods such as invokeREAD() (line 12). This methode takes the RequestURI of the service call as an argument and then calls the invokeRequest() method. The InvokeRequest() method finally calls the REST Service (line 54), returning the response of the service call. The MAF Framework has a RestServiceAdapter class that facilitates calling REST Services. to call a REST Service, simply create an instance of that class, set the connection name to the connection that was created earlier (Line 35), set the Request type (Line 37), set the RequestURI (Line 43) and finally call the service (Line 54). The request URI identifies the actual resource that we need. This will be explained later.

The way the RestCallerUtil class is setup enables you to simply add more and more service calls by adding more and more of these simple methods. The method that actually invokes the service call does not change.

1:  package com.fhacust.app.mobile.utils;  
2:  import java.util.logging.Level;  
3:  import oracle.adfmf.dc.ws.rest.RestServiceAdapter;  
4:  import oracle.adfmf.framework.api.Model;  
5:  import oracle.adfmf.util.Utility;  
6:  import oracle.adfmf.util.logging.Trace;  
7:  public class RestCallerUtil {  
8:    public RestCallerUtil() {  
9:      super();  
10:    }  
11:    //GET  
12:    public String invokeREAD(String requestURI){  
13:      return this.invokeRestRequest(RestServiceAdapter.REQUEST_TYPE_GET, requestURI, "");  
14:    }  
15:    //POST  
16:    public String invokeUPDATE(String requestURI, String payload){  
17:      return this.invokeRestRequest(RestServiceAdapter.REQUEST_TYPE_POST, requestURI, payload);  
18:    }  
19:    public String invokeUPDATEcust(String requestURI, String payload){  
20:      return this.invokeRestRequestCust(RestServiceAdapter.REQUEST_TYPE_POST, requestURI, payload);  
21:    }  
22:    /**  
23:     * Method that handles the boilerplate code for obtaining and configuring a service   
24:     * adapter instance.   
25:     *   
26:     * @param httpMethod GET, POST, PUT, DELETE  
27:     * @param requestURI the URI to appends to the base REST URL. URIs are expected to start with "/"  
28:     * @return  
29:     */  
30:    private String invokeRestRequest(String httpMethod, String requestURI, String payload){  
31:      String restPayload = "";  
32:      RestServiceAdapter restServiceAdapter = Model.createRestServiceAdapter();  
33:      restServiceAdapter.clearRequestProperties();  
34:      //set URL connection defined for this sample.   
35:      restServiceAdapter.setConnectionName("FlightsREST");  
36:      //set GET, POST, DELETE, PUT  
37:      restServiceAdapter.setRequestType(httpMethod);  
38:      //this sample uses JSON only. Thus the media type can be hard-coded in this class  
39:      //the content-type tells the server what format the incoming payload has  
40:      restServiceAdapter.addRequestProperty("Content-Type", "application/json");  
41:      //the accept header indicates the expected payload fromat to the server  
42:      restServiceAdapter.addRequestProperty("Accept", "application/json; charset=UTF-8");  
43:      restServiceAdapter.setRequestURI(requestURI);      
44:      restServiceAdapter.setRetryLimit(0);    
45:      Trace.log(Utility.ApplicationLogger,Level.INFO, this.getClass(),"invokeRestRequest", requestURI);  
46:      //variable holding the response  
47:      String response = "";  
48:      //set payload if there is payload passed with the request  
49:      if(payload != null){    
50:         //send with empty payload  
51:         restPayload = payload;  
52:      }  
53:      try {  
54:        response = (String) restServiceAdapter.send(restPayload);  
55:      } catch (Exception e) {  
56:        //log error  
57:        Trace.log("REST_JSON",Level.SEVERE, this.getClass(),"invokeRestRequest", e.getLocalizedMessage());  
58:      }  
59:      Trace.log(Utility.ApplicationLogger,Level.INFO, this.getClass(),"response= ", response);  
60:      return response;  
61:    }  


The FlightCustURI class contains methods that return the request URIs for a specific REST request like GetFlightsByNumber, PostFlightStatusUpdate and PostCustomerComplaint.
For each and every resource that we want to use, this class defines a static String holding the exact URI. In order to write this class it is necessary to know the URI's. Let's look at the REST Service call for FlightDetails, or more specific, the details of a given flight. That call looks like this :

http:///FlyingHigh/FlightService/FlightService/flights/KL34

The part in bold is the endpoint that was defined in the REST connection that was setup earlier. The part /flights/KL34  is the URI that we are looking for. The flightnumber, in this case KL34, is dynamic and changes if we need info for a different flight. Now based on this information we can create the FlightCustURI class and add the getFlightsByNumberURI method. This method takes the flightNumber as an argument and returns the exact URI that we need to call the service (Line 7).

1:  package com.fhacust.app.mobile.uri;  
2:  public class FlightCustURIs {  
3:    private static final String FLIGHTS_URI = "/flights";  
4:    private static final String CUSTOMER_URI = "/complaints";    
5:    //Flights URI  
6:    public static String GetAllFlightsURI(){ return FLIGHTS_URI; }  
7:    public static String GetFlightsByNumberURI(String flightNumber){ return FLIGHTS_URI+"/"+flightNumber;}  
8:    public static String GetPassengersinFlight(String flightNumber){ return FLIGHTS_URI+"/"+flightNumber+"/passengerlist";}  
9:    public static String PostFlightStatusUpdateURI(){ return FLIGHTS_URI;}  
10:    //customer URI  
11:    public static String PostCustomerComplaintURI(){return CUSTOMER_URI;}  
12:  }  

From JSON to Java

Now that the infrastructure for calling the REST-JSON Service is setup, I must make sure that I can work with the JSON Object in my MAF app. I use a couple of classes that help me to convert the JSON Object to my Java Objects. In this section I will explain how I get from the JSON Object for FlightDetails to my Java Object that represents the Flight Details. In this scenario I use the following 3 classes:
  • JsonObjectToFlightDetailsObject (Translates JSON String to Java Object)
  • FlightDetailsObject (Mimics the JSON payload for FlightDetail)
  • FlightDetailsEntity (Definition of FlightDetails Object)

All of these team up to take care of the JSON conversion.

1:  package com.fhacust.app.mobile.json.helper;  
2:  import com.fhacust.app.mobile.entities.FlightDetailsEntity;  
3:  import java.util.logging.Level;  
4:  import oracle.adfmf.framework.api.JSONBeanSerializationHelper;  
5:  import oracle.adfmf.util.logging.Trace;  
6:  public class JsonObjectToFlightDetailsObject {  
7:    public JsonObjectToFlightDetailsObject() {  
8:      super();  
9:    }  
10:    public static FlightDetailsEntity getFlightsObject(String jsonObjectAsString) {  
11:      FlightDetailsEntity flightsResult = null;  
12:      //object that serializes the JSON payload into the Java object  
13:      JSONBeanSerializationHelper jbsh = new JSONBeanSerializationHelper();  
14:      try {  
15:        flightsResult = (FlightDetailsEntity) jbsh.fromJSON(FlightDetailsEntity.class, jsonObjectAsString);  
16:      } catch (Exception e) {  
17:        Trace.log("JSONArray_to_JavaArray", Level.SEVERE, JsonObjectToFlightDetailsObject.class, "getFlightsObject",  
18:             "Parsing of REST response failed: " + e.getLocalizedMessage());  
19:      }  
20:      return flightsResult;  
21:    }  
22:  }  

The FlightDetailsObject is used to translate the JSON Object to the Java FlightDetailsEntity Object in one go. Note that this FlightDetailsObject  class is not really necessary in this specific case because the JSON Object is not an array of objects. However if the JSON object that is returned by the service call is an actual Array of objects, the FlightDetailsObject can be used to work with the array. For our current example it now has only setters and getters for the flightDetails object of type FlightDetailsEntity. In case of an array I would change that to FlightDetailsEntity[].

1:  package com.fhacust.app.mobile.json.dao;  
2:  import com.fhacust.app.mobile.entities.FlightDetailsEntity;  
3:  public class FlightDetailsObject {  
4:    /**  
5:     * class that mimics the structure of the JSON payload for the flights request  
6:     *  
7:     */  
8:    private FlightDetailsEntity flightDetails = null;  
9:    public FlightDetailsObject() {  
10:      super();  
11:    }  
12:    public void setFlightDetails(FlightDetailsEntity flightDetails) {  
13:      this.flightDetails = flightDetails;  
14:    }  
15:    public FlightDetailsEntity getFlightDetails() {  
16:      return flightDetails;  
17:    }  
18:  }  


Finally I use class called FlightDetailsEntity which is the detailed implementation of the FlightDetails object with all the necessary getters and setters.

1:  package com.fhacust.app.mobile.entities;  
2:  import oracle.adfmf.java.beans.PropertyChangeListener;  
3:  import oracle.adfmf.java.beans.PropertyChangeSupport;  
4:  public class FlightDetailsEntity {  
5:    private FlightCodeEntity flightcode;  
6:    private String flightDate;  
7:    private String flightStatus;  
8:    private SlotEntity departure;  
9:    private SlotEntity destination;  
10:    public FlightDetailsEntity() {  
11:      super();  
12:    }  
13:    here go getters and setters  

And the FlightCodeEntity class that is used to hold FlightNumber and CarrierCode.

1:  package com.fhacust.app.mobile.entities;  
2:  import oracle.adfmf.java.beans.PropertyChangeListener;  
3:  import oracle.adfmf.java.beans.PropertyChangeSupport;  
4:  public class FlightCodeEntity {  
5:    private String CarrierCode;  
6:    private String FlightNumber;  
7:    public FlightCodeEntity() {  
8:      super();  
9:    }  
10:   here go getters and setters  

Note that I created two other groups of these classes (FlightPassengerList and the CustomerComplaint) that enable me to work with these REST-JSON services as well.

Calling the REST Services from the MAF app

So far I created everything that I need in order to call the REST-JSON Service and to deserialise the result of that service call. While preparing this demo Lucas and I decided to actually have 4 services that the MAF UI needs. These are the following services.
  • findFlightByNumber
  • getFlightPassengers
  • updateFlightStatus
  • createCustomerComplaint
In order to actually call these services, using all the infrastructure code that was created in the first part of this post, I now create a JavaClass that I call FlightPassengersDC. This class holds all the methods for all of the service calls that I need. Because I only discuss the flightDetails part in this post, I will not post all the code here. It can be downloaded from GIT (link is provided at the bottom of this post) but I will show the part up till findFlightByNumber().

All that I need to call the service is a simple method ( getFlightDetails() ) uses the RequestURI (Line 34) to get the correct URI,  the RestCallerUtil (Line 36) to call the service and finally the JsonObjectToFlightDetailsObject.getFlightsObject() (Line 37) to deserialise the result to Java Objects.


1:  ackage com.fhacust.app.mobile.datacontrol;  
2:  import com.fhacust.app.mobile.entities.CustomerComplaintEntity;  
3:  import com.fhacust.app.mobile.entities.DepartureDestinationEntity;  
4:  import com.fhacust.app.mobile.entities.FlightCodeEntity;  
5:  import com.fhacust.app.mobile.entities.FlightDetailsEntity;  
6:  import com.fhacust.app.mobile.entities.FlightPassengerListEntity;  
7:  import com.fhacust.app.mobile.entities.PassengerEntity;  
8:  import com.fhacust.app.mobile.json.helper.CustomerComplaintToJson;  
9:  import com.fhacust.app.mobile.json.helper.FlightDetailsEntityToJson;  
10:  import com.fhacust.app.mobile.json.helper.JsonObjectToFlightDetailsObject;  
11:  import com.fhacust.app.mobile.json.helper.JsonObjectToFlightPassengerListObject;  
12:  import com.fhacust.app.mobile.uri.FlightCustURIs;  
13:  import com.fhacust.app.mobile.utils.RestCallerUtil;  
14:  import oracle.adfmf.java.beans.ProviderChangeListener;  
15:  import oracle.adfmf.java.beans.ProviderChangeSupport;  
16:  public class FlightPassengersDC {  
17:    String flightNumber ="KL34";  
18:    //Details for a selected flight.   
19:    private FlightDetailsEntity flightDetails = null;  
20:    //all Passengerrs. We fecth the passnegers only once as this is not expected to  
21:    //change in the context of the flight  
22:    private FlightPassengerListEntity flightPassengers = null;  
23:    public FlightPassengersDC() {  
24:      super();  
25:    }   
26:    public void findFlightByNumber(String flightNr){  
27:      if (flightNr!=null&&flightNr!=""){  
28:        this.flightNumber=flightNr;  
29:      }  
30:      getFlightDetails();  
31:    }  
32:    public FlightDetailsEntity getFlightDetails() {  
33:      if(flightDetails == null){   
34:        String restURI = FlightCustURIs.GetFlightsByNumberURI(this.flightNumber);  
35:        RestCallerUtil rcu = new RestCallerUtil();  
36:        String jsonObjectAsString = rcu.invokeREAD(restURI);  
37:        FlightDetailsEntity flights = JsonObjectToFlightDetailsObject.getFlightsObject(jsonObjectAsString);  
38:        flightDetails = flights;  
39:      }  
40:      return flightDetails;  
41:    }  
42:  more here below .........  


That is all. Now I can call the services and translate the payload to and from Java Objects. It is time to expose the data to the UI. This is done by using a Data control.

From Rest Service to MAF UI: The datacontrol

The datacontrol can be created from the FlightPassengersDC Class. Data controls in MAF expose the service data model to the mobile client developer. I can create a datacontrol based on this class, and the datacontrol will expose all the methods from the class as operations, and all .

  


From here on it is easy to create AMX pages that display flightDetails and Passengers. The easiest way is to simply drag and drop from the datacontrol on to an AMX page. The code in GIT shows the end result of you would do it like this. For the live demo that was all that time permitted.

In a next post I will describe how I created a more fancy User Interface.

Resources


The code for the MAF app can be downloaded from GIT.

Wednesday, December 03, 2014

MAF 2.0 : Custom Toggle Springboard Functionality (or how I discovered AdfmfSlidingWindowUtilities)

Mobile apps usually have the possibility to toggle the springboard by using an icon that is displayed in the header of the app. The Oracle MAF reference app, Work Better, also tries to implement this behavior. The showing of the springboard works fine, however, hiding it does not really work as expected. In this post I show you how to implement a working custom toggle springboard functionality.

Default Toggle Springboard Implementation

First let's take a look at how the toggle springboard functionality works out of the box. In your application configuration file you need to set the "Show Springboard Toggle Button" to true in order to enable toggle functionality.

This image is described in the surrounding text
All the rest is taken care of by the framework at runtime and these setting results in the default toggle springboard icons to show up on both iOS and Android. Note that this of course also works with a custom springboard.


This image is described in the surrounding text

An obsolete way to implement Custom Springboard Toggle (you might want to skip reading this)

Now lets see what we need to implement the custom functionality. First we need to show the springboard. This can be done by calling gotoSpringboard() on the containerUtilities class, or by invoking it from the applicationFeatures Datacontrol.

 AdfmfContainerUtilities.gotoSpringboard();  

This all pretty straightforward and provided by the framework.

Second we need to be able to hide the springboard. This can be done by no particular builtin. However, when you call gotoFeature(), the springboard is hidden and the requested feature is displayed.

 AdfmfContainerUtilities.gotoFeature("feature.id");  

It works ok, but what if you don't select a feature to go to, and simply want to stay on the already active feature ? In that case we could really use a hideSpringboard() method, or something similar.
If we create a hideSpringboard() and combine it under one button with the gotoSpringboard() we can use this one button to show and hide the springboard. In order to implement all this we also need to know whether or not the springboard is visible. For that we can use a simple custom property in a bean, lets call it springboardToggleFlag.

Whenever the springboard is shown, we invert the state of the springboardToggleFlag:

 springboardToggleFlag=!springboardToggleFlag;  

and the app knows the state of the springboard. All that we need to do from here is find a way to nicely show and hide the springboard.

While figuring out how to implement the rest of this example I surprisingly found that the solution is already provided by the Framework and also somewhat documented and also available in the PublicSamples provided by Oracle. Because I never had any previous requirement to implement this functionality I totally missed that Oracle added this to the framework. Also I am not sure in what specific version it was added. I know now that the in the MAF 2.0.0 docs it is mentioned very briefly, and in the MAF 2.0.1 docs it is described in a more elaborate way, including a sample app. The API documentation was already available in MAF 2.0.0.  Below you can read the details on where to find this samples and docs.

The (Somewhat) Out of the Box Implementation

By implementing the oracle.adfmf.framework.api.AdfmfSlidingWindowUtilities interface in the application lifecycle listener (ALCL), you can use an application feature as a sliding window, which displays concurrently with the other application features that display within the navigation bar or springboard. You can use a sliding window to display content that always present within the application, such as a Springboard.

An example of the implementation can be found in a workspace called "slidingWindows", and is aprt of the Public Samples. This application demonstrates the use of the AdfmfSlidingWindowUtilities API, which can be used to display multiple features on the screen at the same time. This sample shows how you can create a custom springboard using the AdfmfSlidingWindowUtilities API.


Note that the sliding window plugin API can only be used for features defined within the application that do not appear in the navigation bar and is not the springboard feature .

So in order to make a custom springboard that nicely slides in and out of view we need to instruct the app that it has NO springboard, and create a custom feature that functions as a springboard.



All the other details of this implementation can be found in the sample app.

Resources

https://docs.oracle.com/middleware/mobile201/mobile/ADFMF.pdf
https://docs.oracle.com/middleware/mobile201/mobile/OEPMF.pdf (is missing the description of the Sample app)
https://docs.oracle.com/middleware/mobile201/mobile/api-ref/oracle/adfmf/framework/api/AdfmfSlidingWindowUtilities.html
http://docs.oracle.com/middleware/mobile201/mobile/api-ref/oracle/adfmf/framework/api/AdfmfSlidingWindowOptions.html