Thursday Apr 03, 2014

Going to Vegas? Don't gamble with your learning and attend my sessions!

Next week, I will be in Las Vegas for the Collaborate 2014 conference. I have the privilege to deliver two sessions there. Are you intrigued by ADF Essentials? Would you like to know how to use APEX applications as a back-end for mobile applications? Here is your chance to learn more!

178: A Free Toolkit for Modern Web Applications: A Look at ADF Essentials
Date: 04/08/2014 (Tuesday)
Time: 4:15 PM
Location: Level 3, Murano 3205

179: Bring Your APEX Application to iOS and Android with ADF Mobile
Date: 04/10/2014 (Thursday)
Time: 4:15 PM
Location: Level 3, Murano 3203

I will probably spend a lot of time at our Mobility booth as well. Drop by and say hello if you're there!

Friday Mar 28, 2014

ADF Mobile: the Access Control Service

ADF Mobile applications use standard HTTP mechanisms for authentication.  The HTTP protocol, however, does not handle authorization. Thus, to enable applications to obtain the roles and privileges of a specific user,  you need to implement a REST web service called the Access Control Service. In this post, I will show you how to implement the foundations for that service. 

The product documentation states that the Access Control Service consumes and produces JSON data. The snippet below illustrates how parameters are passed to the service. 

{
        "userId": "johnsmith",
        "filterMask": ["role", "privilege"],
        "roleFilter": [ "role1", "role2" ],
        "privilegeFilter": ["priv1", "priv2", "priv3"] 
}

All parameters other than userId are optional. FilterMask is used to specify which filters should be applied to the request (role, privilege or both). RoleFilter and privilegeFilter simply enumerate the filter values. If no filters are specified, the web service should return the list of all roles and privileges for the user. Otherwise, the service should only verify if the user belongs to the roles listed in roleFilter and if he has been granted the privileges listed in privilegeFilter.

The ADF Mobile documentation gives the JSON snippet below as an example of a service response.

{
        "userId": "johnsmith",
        "roles": [ "role1" ],
        "privileges": ["priv1", "priv3"] 
}

As you can see, the service returns only the roles and privileges that actually apply to the user. 

The easiest way to implement the service is to take advantage of the POJO mapping feature offered by some JAX-RS implementations, such as Jersey. The first step is to build POJO class definitions for the service request and response. JDeveloper made this very easy. I only had to type a few lines of code for the class attributes and generated the accessors and constructors. 

This is the code for the request class. 

package oracle.sample.maf.accesscontrol.bo;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class ACSRequest {
    
    private String userId;
    private String[] filterMask;
    private String[] roleFilter;
    private String[] privilegeFilter;

    public ACSRequest() {
        super();
    }

    public ACSRequest(String userId, String[] filterMask, String[] roleFilter, String[] privilegeFilter) {
        super();
        this.userId = userId;
        this.filterMask = filterMask;
        this.roleFilter = roleFilter;
        this.privilegeFilter = privilegeFilter;
    }


    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserId() {
        return userId;
    }

    public void setFilterMask(String[] filterMask) {
        this.filterMask = filterMask;
    }

    public String[] getFilterMask() {
        return filterMask;
    }

    public void setRoleFilter(String[] roleFilter) {
        this.roleFilter = roleFilter;
    }

    public String[] getRoleFilter() {
        return roleFilter;
    }

    public void setPrivilegeFilter(String[] privilegeFilter) {
        this.privilegeFilter = privilegeFilter;
    }

    public String[] getPrivilegeFilter() {
        return privilegeFilter;
    }
}

The @XmlRootElement annotation makes it very easy to generate both JSON and XML from a single set of objects; I added it to the class even though it wasn't necessary in this specific use case.

The code for the response class is straightforward.  

package oracle.sample.maf.accesscontrol.bo;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class ACSResponse {
    
    private String userId;
    private String[] roles;
    private String[] privileges;
    
    public ACSResponse() {
        super();
        roles = new String[0];
        privileges = new String[0];
    }
    
    public ACSResponse(String p_userId, String[] p_roles, String[] p_privileges) {
        super();
        userId = p_userId;
        roles = p_roles;
        privileges = p_privileges;
    }

    
    public String getUserId(){
        return userId;
    }
    
    public void setUserId(String p_id){
        userId = p_id;
    }
    
    public String[] getRoles(){
        return roles;
    }
    
    public void setRoles(String[] p_roles){
        roles = p_roles;
    }

    public String[] getPrivileges(){
        return privileges;
    }
    
    public void setPrivileges(String[] p_privileges){
        roles = p_privileges;
    }
}
  

By default, POJO mapping is not enabled in Jersey. It is thus essential to add the following init parameter to the Jersey servlet declaration in the web.xml for the application.

        <init-param>
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
        </init-param>

The service itself is implemented as yet another POJO. I generated a skeleton for the class using the REST web service wizard in JDeveloper. The method that will process the request must be configured to process requests made using the POST HTTP verb. Hence, the single method in the class is annotated with @POST.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package oracle.sample.maf.accesscontrol.service;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

import oracle.sample.maf.accesscontrol.bo.ACSRequest;
import oracle.sample.maf.accesscontrol.bo.ACSResponse;

@Path("user")
@Consumes("application/json")
@Produces("application/json")
public class UserRessource {
    public UserRessource() {
    }

    @POST
    public Response postData(ACSRequest request) {
        
        // Replace this with actual logic.
        ACSResponse rolesAndPrivileges = new ACSResponse(request.getUserId(), 
                                                         new String[] { "user" }, 
                                                         new String[] { "user" });
        Response.ResponseBuilder builder = Response.ok(rolesAndPrivileges);
        return builder.build();
    }
}

The @Path annotation on line 12 determines the path to the REST resource, whereas @Consumes and @Produces specify the expected data formats for the method's input and output. 

The sample class above doesn't contain error handling code and returns a hard coded response. A production implementation should use the various static methods in javax.ws.rs.core.Response to build responses. The ok(Object) method will typically be used; other methods such as noContent(), notAcceptable(List) and serverError() will be called instead when specific conditions are met or if exceptions have been raised. 

There are many ways to obtain the data needed to populate the response. You could query a database, for example, or use the OPSS APIs to query an LDAP server. Whatever option you choose, your service implementation should ensure that users cannot query another user's roles and privileges unless they have administrative privileges themselves. In other words: exposing on the internet a web service which enables anybody to identify privileged user accounts for your back-end is a bad idea. Ideally, your service should:

  • Accept connections over HTTPS only
  • Check that the credentials used to establish the SSL / TLS connection match the userId in the service input - unless the user can manipulate the roles and privileges of other users
The Access Control service is an essential component of the ADF Mobile architecture. Since Oracle only specifies the service signature, you can implement it in the way that best fits your infrastructure and the level of authorization granularity you expect.  

Thursday Feb 27, 2014

Sometimes a new skin is not enough

I often have the opportunity to present about Oracle's mobile technologies. In those presentations, I usually explain to the audience that building and maintaining a mobile application will force them to make their software development processes more agile. The reason for this is simple. Mobile technologies evolve at consumer speed. Mobile operating systems are updated frequently, and applications must follow suit. The best proof I can give is the evolution of ADF Mobile itself. The initial version of the framework has been released in October 2012. Between that date and January 2014, we published one new version and five distinct patch sets for it; that's one update every 11 weeks on average. Such frequent releases are unusual for Oracle. In the mobile space, they're the norm

Recently, I installed ADF Mobile Patch 5. This version includes a new skin which brings an iOS 7 look and feel for Apple devices as well as a native look and feel for Android devices. In addition, JDeveloper now supports version 5 of XCode to package and deploy iOS applications. After the update, my applications didn't look right when I deployed and ran them. It turns out you must update the skin-family in adfmf-config.xml from this

<skin-family>mobileFusionFX</skin-family>

to this

<skin-family>mobileAlta</skin-family>

In addition, I suggest you perform a clean all (Build > Clean All) before deploying your applications. Mine would not pick up the new skin otherwise. 

Monday Jan 27, 2014

Picking up the threads in ADF Mobile

There is a huge difference between the actual performance of an application and the user's perception of that performance. Typically, developers will try to improve the latter by delegating time-consuming tasks to background threads; in other words: asynchronous processing makes it possible to keep the user interface responsive at all times. This is especially important in mobile applications, where network bandwidth and latency can fluctuate wildly in a short time frame. Users are not necessarily aware of changes in network conditions, and thus will readily ascribe any slowdown to the application itself. Consequently, multithreaded programming is an essential part of the mobile developer's tool set.

ADF Mobile applications run on a Java virtual machine. Therefore, they can start threads that will exist in the context of the JVM process. In the current release, the ADF Mobile JVM follows the JavaME CDC specification, which is based on Java 1.4. This means that, unfortunately, the improvements brought by JSR 166 (java.util.concurrent)  are not available. On the other hand, threads are well integrated in the ADF Mobile framework. They can invoke AdfmfJavaUtilities.invokeDataControlMethod or AdfmfJavaUtilities.setELValue, for example. This makes it possible for you to update the user interface or refresh a bound collection in memory from a thread among other things.

The Apple iOS and Google Android operating systems manage application-related resources themselves. In iOS, when you switch to another application, the current application is suspended. On the other hand, Android's behavior in the same scenario will vary depending on the free memory available on the device. Typically, the processes belonging to an application will continue to run in the background after the switch; when memory is scarce, the operating system may force-kill the process. What happens when you switch away from an ADF Mobile application is thus dependent on the underlying OS. Any threads started by the application process will behave in the same way as the process itself.  By default, threads will suspend and resume by themselves on iOS; they will still run in the background on Android. 

If you want to implement multithreading in your application, my recommendation is to always manage the state of your threads explicitly and to interrupt them when the application is deactivated or suspended. This will ensure the integrity of your data and will make the application behave the same way independently of the operating system. Interrupting a thread is done by calling the interrupt() method of the Thread class and by checking the return values of the interrupted() or isInterrupted() methods inside the run() method of the thread or of the runnable.  The proper location for the call to interrupt() is a listener class implementing the oracle.adfmf.application.LifeCycleListener interface; such listeners must be registered in  adfmf-application.xml. The activate() and deactivate() methods it specifies will be invoked even if the application is killed through the Android task manager. Typically, in addition to interrupt the threads, the application will need to do the following in order to ensure a proper deactivation:

  • Write any restorable state to an appropriate store
  • Close database cursors and connections
  • Defer pending web service requests
  • Release resources such as files

These tasks can be performed by the threads themselves, by their associated java.lang.Runnable instances, or somewhere else. Be careful, though, since activate() and deactivate()will not be called if the application is terminated. It is also possible to implement listeners at the feature level if more granularity is needed. Such listeners implement the oracle.adfmf.feature.LifeCycleListener interface instead. Please note calls to activate() and deactivate() are blocking; you will need to be careful to ensure the application doesn't look unresponsive to the user.

Resource contention is without a doubt one of the greatest challenges any multithreaded application must solve. In ADF Mobile, each local database corresponds to a single file; the SQLite database engine thus implements a complex but reliable locking system.  Fortunately, ADF Mobile encapsulates all the complexity. If two threads - each possessing its own JDBC connection to the database - try to write at the same time, no exception will be thrown. One of the threads will own the write lock and will be able to proceed, while the other will wait. In other words: there can be only one database connection in write mode at any given time. All other connections will be in read-only mode until they can acquire the write lock. This will influence the design of your application. For example: if you have to insert a sizable number of records in a background thread, you will perform the operation in smaller batches in order to yield the lock to other threads of higher priority. 

Writing a good application is not easy, nor is writing a good performing one.  Multithreading can help with the latter, but you must be careful not to waste resources when the application is not in the foreground. After all, performance is not the only component in the user's perception of your application; battery life counts as well... 

Monday Dec 09, 2013

Make the Oracle Service Bus IDE feel at home on Linux

I played a lot with Oracle Service Bus (OSB) in the last few weeks. You will soon know why; no, I can't say right now. I installed it inside an Oracle Enterprise Linux virtual machine, and I must say the installation process is straightforward: install OEPE, install WebLogic, run RCU, run the installer, create a domain... It was nothing special, until I started the Service Bus IDE (OEPE with the OSB plugins). I got the following error messages:

  • Failed to parse the output of ‘adb version’
  • adb: error while loading shared libraries: libncurses.so.5: cannot open shared object file: No such file or directory

My VM had all the prerequisites listed in the documentation installed. What was wrong, then? Turns out I had used the 64-bit version of Oracle Linux, and the IDE wanted some 32-bit dependencies. To fix the problem, I issued the following commands as root: 

yum install ncurses-libs.i686 libstdc++.i686 libgcc.i686
yum install zlib.i686 zlib-devel.i686

After that, the IDE started like a charm. Expect a few OSB-related posts from me in the future! 

Monday Dec 02, 2013

Put your VirtualBox VMs on a diet

One of the nice things with Oracle VM VirtualBox is that you don't need to allocate all the disk space in advance. It is possible, say, to give a 60 Gb hard drive to a virtual machine but have an actual footprint much lower than that by creating the hard drive in dynamically allocated storage mode. Basically, this means the actual size for the hard drive will match the size of the files it contains. The footprint will grow over time, as you add files. Unfortunately, this is a one way process. The virtual hard drive will take more space on disk as it grows, but will not release the space if it shrinks. This can be a problem, for example, if you copied several Fusion Middleware installers on the VM and want to recuperate that space after installation. 

I am currently playing with VMs a lot, and found a way to get the space back. This is a two step process. The first step is to fill the empty space with zeroes. There are utilities for that on Windows and OS X, but you can do it very easily on the command line on Oracle Enterprise Linux. Simply execute the following commands inside the virtual machine as root (or prefix with sudo):

dd if=/dev/zero of=/hugeemptyfile bs=4096k
rm -rf /hugeemptyfile

 Obviously, you should close all other programs before doing this, and ensure no background process will require disk space while dd is running.

Once this is done, shut down the VM and open a command line on the host machine. Go to the folder where VirtualBox is installed, and type something like this, substituting the correct path and file name for the virtual hard drive:

VBoxManage modifyhd "V:\VirtualBox VMs\Oracle\Oracle.vdi" -compact

 After a few minutes, you will get the wasted space back.

VirtualBox, by the way, has an extensive collection of command line utilities. You can even run a VM without opening the VirtualBox GUI using the VBoxHeadless command, for example. Also: did you know it is possible to start a VM in headless mode from the GUI? Simply press shift at the same time you click on the start button. 

Friday Oct 25, 2013

Juggling with JDKs on Apple OS X

I recently got a shiny new MacBook Pro to help me support our ADF Mobile customers. It is really a wonderful piece of hardware, although I am still adjusting to Apple's peculiar keyboard layout. Did you know, for example, that the « delete » key actually performs a « backspace »? But I disgress... As you may know, ADF Mobile development still requires JDeveloper 11gR2, which in turn runs on Java 6. On the other hand, JDeveloper 12c needs JDK 7. I wanted to install both versions, and wasn't sure how to do it.  

If you remember, I explained in a previous blog entry how to install JDeveloper 11gR2 on Apple's OS X. The trick was to use the /usr/libexec/java_home command in order to invoke the proper JDK. In this case, I could have done the same thing; the two JDKs can coexist without any problems, since they install in completely different locations. But I wanted more than just installing JDeveloper. I wanted to be able to select my JDK when using the command line as well. On Windows, this is easy, since I keep all my JDKs in a central location. I simply have to move to the appropriate folder or type the folder name in the command I want to execute. Problem is, on OS X, the paths to the JDKs are... let's say convoluted. 

Here is the one for Java 6.

/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home

The Java 7 path is not better, just different.

/Library/Java/JavaVirtualMachines/jdk1.7.0_45.jdk/Contents/Home

Intuitive, isn't it? Clearly, I needed something better...

On OS X, the default command shell is bash. It is possible to configure the shell environment by creating a file named « .profile » in a user's home folder. Thus, I created such a file and put the following inside:

export JAVA_7_HOME=$(/usr/libexec/java_home -v1.7)
export JAVA_6_HOME=$(/usr/libexec/java_home -v1.6)

export JAVA_HOME=$JAVA_7_HOME

alias java6='export JAVA_HOME=$JAVA_6_HOME'
alias java7='export JAVA_HOME=$JAVA_7_HOME'

 The first two lines retrieve the current paths for Java 7 and Java 6 and store them in two environment variables. The third line marks Java 7 as the default. The last two lines create command aliases. Thus, when I type java6, the value for JAVA_HOME is set to JAVA_6_HOME, for example. 

I now have an environment which works even better than the one I have on Windows, since I can change my active JDK on a whim. Here a sample, fresh from my terminal window.

fdesbien-mac:~ fdesbien$ java6
fdesbien-mac:~ fdesbien$ java -version
java version "1.6.0_65"
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-462-11M4609)
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-462, mixed mode)
fdesbien-mac:~ fdesbien$ 
fdesbien-mac:~ fdesbien$ java7
fdesbien-mac:~ fdesbien$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
fdesbien-mac:~ fdesbien$ 

Et voilà! Maximum flexibility without downsides, just I like it. 

Monday Oct 07, 2013

ADF at 12c: time to get the facts straight

The 12c release is a major milestone both for ADF and JDeveloper. Most of you already know how strategic ADF is to Oracle; day after day, thousands of our own developers use it to build Fusion Applications, Enterprise Manager, SOA Suite and WebCenter among others. What is not readily apparent is how much maturity there is in the framework. The roots of ADF can be traced back to 1999, when the first release of Java Business Objects (JBO) was made available. The ancestor of today's ADF Faces components, User Interface XML (UIX), has been introduced in 2002. More than ten years later, ADF is still going strong and the best is yet to come.

I have been developing with ADF since 2007. Since then, I often had the opportunity to introduce new developers to it. While I was often greeted with skepticism, the natural qualities of the framework and the productivity brought by JDeveloper usually won minds if not hearts. I saw this once again at OpenWorld 2013. Obviously, ADF is not perfect and there are several worthy alternatives in the market. But what surprises me is that many of the objections made against ADF stem from misconceptions - even after all those years. Here are five of the most common ones:

  • ADF is not open.
  • ADF is just for huge enterprise applications.
  • ADF is proprietary.
  • ADF is tied to JDeveloper,  Weblogic and Oracle Database.
  • ADF is expensive.

My aim in this post is to get the facts straight. Let's discuss each of them.

ADF is not open

This is something I heard frequently. But what does « open » mean? Is it about access to the source code? About technical interoperability? Maybe about stewardship? Customers covered by a valid support contract can request access to the ADF source code. Not only that, but some of its components have been released under open-source licenses, the most significant being Apache MyFaces Trinidad. And since ADF is built on the top of Java Enterprise Edition, it integrates with other solutions running on the platform. True, Oracle keeps full control over strategic orientations and new features. But our company is making significant efforts to better address the concerns of the community. The ADF Enterprise Methodology Group, for example, is a great forum to propose and discuss new features. We follow closely what is posted there and will never hesitate to open enhancements requests if needed.

ADF is just for huge enterprise applications

This is a funny one, and probably comes from the fact that ADF is based on Java. Yet, small and simple applications are a cinch to implement with the framework; it focuses on productivity first. Lots of people forget that ADF favors a code last approach. In other words: most ADF artifacts can be implemented declaratively rather than through code. In addition, most of the time, developers will build the user interface simply by drag and dropping attributes from the data controls palette. Moreover, ADF puts great emphasis on reuse. Entity Objects, View Objects, Task Flows and Page Fragments are inherently reusable. You can push this even further by using page fragments, JSF templates and declarative components. Thus, you can reduce the actual size of your applications by sharing code extensively between applications. It is also essential to remember that ADF implements several common software patterns, such as Model-View-Controller, for you. This results in a little more complexity, but ensures that even the smallest of your applications adhere to industry best practices. 

ADF is proprietary

ADF is certainly unique to Oracle. In fact, it represents one of our biggest competitive advantages in the marketplace. Yet, some people conveniently forget it is a superset of Java Enterprise Edition first and foremost. ADF Faces, for example, is probably the most comprehensive set of JSF components available right now; the Data Visualization components now render to HTML 5 instead of Flash. On the other hand, ADF offers extensive support for the SOAP protocol and the WS-* extensions, which are industry standards. Yes, ADF deviates from JEE in some cases - but typically this is because it was ahead of the curve. ADF BC is rooted in JBO, a technology introduced in 1999. EJBs didn't deliver the performance and features required by developers at the time. In 2008, ADF Controller and Task Flows brought more flexibility than the standard JSF controller - which finally caught up in 2013 in JEE 7. We even make it possible to use EJB or JPA to implement business logic if you prefer them to ADF BC. 

Moving forward, you can expect ADF to integrate many more standards, but not at the cost of innovation.

ADF is tied to JDeveloper,  Weblogic and Oracle Database

This one was true a few years ago. Nowadays, you can build ADF applications in Eclipse by installing the Oracle Enterprise Pack for Eclipse plugins. You can use almost any SQL92-compliant database with ADF, and we even offer optimizations specific to IBM DB2 and Microsoft SQL Server. Best of all, we offer integration to various Application Lifecycle Management platforms in JDeveloper and OEPE, but are not offering one ourselves. You get to choose the tools you prefer to support your development process. And with the free ADF Essentials, you can deploy your ADF applications to nearly all containers implementing the Java Enterprise Edition web profile. GlassFish server is an obvious choice here, but old favorites like Apache Tomcat and JBoss can be used too. 

ADF is expensive

No, it's not. For a long time, ADF has been merely inexpensive as it was bundled with WebLogic Server. With ADF Essentials, the core features are free; the features cut from from it are essentially hooks to other Fusion Middleware products. The developer tools, by the way, are completely free. You cannot really appreciate that fact unless you had to pay for multiple IDE licenses for your team, something I had to do earlier in my carrier when I was building software for IBM and Microsoft platforms. 

Conclusion

ADF 12c is there. And it's here to stay. Maybe you should consider it...

Wednesday Sep 04, 2013

Out of the matrix: the ADF EMG goes to OpenWorld!

The ADF Enterprise Methodology Group is an invaluable resource for ADF architects and experienced developers. Where else on the web can you discuss best practices and methodologies to deliver enterprise-level ADF applications? If you like it as much as I do, you will be glad to know there is an ADF EMG day this year at Oracle OpenWorld. Yes, the EMG leaves the matrix and boldly comes to the real world for a full day of presentations by well-known experts from the ADF community. And there is no better way to prepare for the conference than to share stories from the trenches over a few beers ar the ADF EMG social night. I hope to meet you there!

Sunday Sep 01, 2013

Think TV is boring? Think again!

Since July, I have been busy contributing to an exciting new initiative from Oracle. Our aim is simple: to revolutionize TV! While Apple's infamous iTV is still little more than a rumor, we are delivering! Ok. Maybe I got a little carried away... Anyway, if you are an ADF architect or developer, you really should  check our ADF Architecture TV channel on YouTube. There, you will find a series of weekly episodes which will help you learn ADF design and best practices from key Oracle ADF specialists. This is real, deep technical content; not marketing fluff. Make sure you subscribe as we have over 100 episodes planned covering security, web services, PL/SQL integration, UI design and much more! This week's episode was recorded by Frank Nimphius and is about Task Flows. 

The episodes I recorded are about internationalization. There are five of them and cover resource bundles, character encoding and time zone management, among other things. I will let you know when they are released. 

Who would have thought TV could get this interesting? Here is the official trailer for the channel:


Monday Jul 29, 2013

JDeveloper 12c and Maven: Using relative paths in your POM files

One of the greatest things about JDeveloper 12c is the significantly improved support for Apache Maven. By default, the POM files produced by my favorite IDE contain some absolute paths, such as the folder for the ojmake/ojdeploy executables and references to project (.jpr) and application (.jws) files. Here is a sample of such paths extracted from an application I created on a Windows machine.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
      <plugin>
        <groupId>com.oracle.adf.plugin</groupId>
        <artifactId>ojmake</artifactId>
        <version>12.1.2-0-0</version>
        <configuration>
          <ojmake>
            C:\Oracle\Middleware1212\jdeveloper\jdev\bin\ojmake.exe
          </ojmake>
          <files>
            C:\OracleData\JDEV_USER_DIR\mywork\MavenTest\Model\Model.jpr
          </files>
          <usemaven>
            true
          </usemaven>
        </configuration>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

This is perfectly fine if your development team uses workstations that are configured in a consistent way and run under the same operating system. It is often not the case in the real world. Fortunately, Maven provides features that make it very easy to use relative paths instead.

The first thing you should know is that Maven supports the use of variables in POM files. Those variables are always referenced using the following syntax:

${variable_name}

There are several built-in variables provided by Maven. One of them is ${basedir}, which points to the folder where the current POM is located. In addition, Maven can access any environment variable defined by the operating system. This is achieved though the following syntax:

${env.variable_name}

 Thus, it is possible to remove all absolute paths from the sample above by using ${basedir} and referencing an environment variable. Suppose I created such a variable named OJ_HOME, which points to  C:\Oracle\Middleware1212\jdeveloper\jdev\bin. Then, the POM would look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
      <plugin>
        <groupId>com.oracle.adf.plugin</groupId>
        <artifactId>ojmake</artifactId>
        <version>12.1.2-0-0</version>
        <configuration>
          <ojmake>
            ${env.OJ_HOME}\ojmake.exe
          </ojmake>
          <files>
            ${basedir}\ViewController.jpr
          </files>
          <usemaven>
            true
          </usemaven>
        </configuration>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

This POM will run on any workstation, granted the OJ_HOME variable is set to a suitable value.

Thursday Jun 20, 2013

Saved by the local database!

In my last post, I told you about my latest ADF Insider Essentials recording on the local database, and pointed you to the companion code sample. I had lots of feedback about both. I am glad to see I have so many viewers and readers!

Among all the questions I got, one was asked very frequently: « How can I transparently retrieve data from the database when a web service call fails? » In other words: how can the local database save my life if the web service doesn't respond? This is a bit different from what I had implemented initially. Thus, I built a new version of the sample application which does exactly that. In the original sample, the code simply detected the presence or absence of a network connection; an available connection meant the web service call was assumed to succeed. Otherwise, an exception was raised and displayed to the user. Thus, the key change to obtain the desired behavior is simply to catch the exception. Then, it is easy to invoke the method that retrieves data from the database instead.

Here is the relevant method in the sample application.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    private CountryBO[] getCountriesFromWS() {
        try {
            GenericType genericReturnValue =
                (GenericType)AdfmfJavaUtilities.invokeDataControlMethod("HR_WS", null, "findCountry", new ArrayList(),
                                                                        new ArrayList(), new ArrayList());
            CountryBO[] returnValue =
                (CountryBO[])GenericTypeBeanSerializationHelper.fromGenericType(CountryBO[].class, genericReturnValue,
                                                                                "result");

            Arrays.sort(returnValue);
            return returnValue;

        } catch (AdfInvocationException aie) {
            if (AdfInvocationException.CATEGORY_WEBSERVICE.compareTo(aie.getErrorCategory()) == 0) {
                AdfmfContainerUtilities.invokeContainerJavaScriptFunction("oracle.adfinsider.localdb.countries",
                                                                          "navigator.notification.alert",
                                                                          new Object[] { "The web service is unavailable. \n\n Data has been retrieved from the local cache.",
                                                                                         "null", "Warning", "Ok" });

                return getCountriesFromDB();
            } else {
                throw new RuntimeException(aie);
            }
        } catch (Exception ex) {
            Utility.ApplicationLogger.severe(ex.getMessage());
            throw new RuntimeException(ex);
        }
    }

Another nice thing this code snippet demonstrates is how to call JavaScript code from Java business logic. At line 15, I invoke a method which is part of the Apache Cordova library to display a warning message to the user. Cordova is an integral component of ADF Mobile, but your AMX pages must be properly configured in order to use it. I added the proper references to the countriesList.amx page like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  <amx:panelPage id="pp1">
    <amx:verbatim id="v1">
        <script type="text/javascript">if (!window.adf) window.adf = {}; adf.wwwPath = "../../../../www/";</script> 
        <script type="text/javascript" src="../../../../www/js/base.js"></script>
        <script type="text/javascript" src="../../../../www/js/cordova-2.2.0.js"></script>
    </amx:verbatim>
    <amx:facet name="header">
      <amx:outputText value="#{viewcontrollerBundle.COUNTRIES}" id="ot1"/>
    </amx:facet>
...
  </amx:panelPage>

I placed the script references (lines 3 to 5) inside a verbatim tag, which ensures that they will be rendered as is in the page.

While I was at it, I fixed a few other issues with the sample. In the original version, the database connection was closed inside the stop() method of the LifeCycleListenerImpl class. The stop() method is usually called when the use exits the application; there is no guarantee, however. Thus, the connection wouldn't be closed properly in some corner cases. To fix this, I moved the code to the deactivate() method, which doesn't suffer from the same drawback and will be called each time the user switches to another application. This is much better, as the connection will be properly closed even if the device crashes while the application is inactive.

You can download the refreshed sample application here

Friday May 17, 2013

See the ADF Mobile local database in action

ADF Insider recordings are probable one of the best parts of my job. They are a lot of work, sure. But they are lots of fun to do and join so many members of the ADF Community... 

My latest recording is on the ADF Mobile local database. In it, I explore the various aspect of the feature and devote a healthy chunk of time to the management of the database file. The slides contain a few selected code snippets, but I thought it would be better to build a sample application to fully illustrate the concepts. In particular, I wanted to show how it is possible to retrieve data from either a web service or the local database while binding the UI to a POJO Data Control. 

My sample application  is made is made of two distinct components: 

   - A simple SOAP web service (SDO view object) built on the top of the HR database schema. 

   - The ADF Mobile application itself, that demonstrates local database techniques and calls the web service when a network is available. Data is fetched from the local database when their is no connectivity.

I contributed the application to the ADF Enterprise Methodology Group samples repository. It is not listed on the web pages right now, but you can download it from the following location:

https://svn.java.net/svn/smuenchadf~samples/ADFMobileLocalDatabase.zip

My recording is available on our YouTube channel here: http://www.youtube.com/watch?v=-XzE1n_j5Nc

Monday May 06, 2013

From Russia with Java: answering the most frequently asked questions

Last week, I had the wonderful opportunity to visit Moscow and meet fellow Java developers. I was one of the presenters at the JavaOne Russia 2013 conference, on April 23 and 24. There, I talked about ADF, ADF Mobile and even forms. I was even able to spare a few hours for sightseeing...

When not presenting, I passed most of my time at the ADF Demo booth, where I fielded lots of interesting questions from attendees. Since some of these questions were asked quite frequently, I thought it would be interesting to answer them here.

  1. How do I get a license for ADF Mobile?
    Maybe you don't even need to buy anything! Isn't this great?

    ADF Mobile is bundled with ADF. Currently, there are two ways to license ADF: on its own or through a WebLogic Server license. If you buy named user licenses, you can distribute your ADF Mobile applications to those same users on the devices they own. On the other hand, you can distribute your applications to an unlimited number of users and devices if you buy a processor license. Thus, a processor license is your best bet if you plan to distribute your applications through the Apple and Google Play stores.

    If you hesitate between named user licenses and the processor license, the latter will be more advantageous if you have around 50 users or more. I am not in sales, and prices in your regions could affect the value equation so please check with a sales representative. 


  2. What is the difference between ADF, ADF Mobile and ADF Mobile Browser?
    Oracle Application Development Framework (ADF) is the toolkit at the core of Fusion Architecture. Through the ADF Faces set of components, it enables you to build rich and scalable web applications for desktop and high-end mobile devices (such as Apple's iPad), and can be used to build SOA backbones that will be accessed by Swing and JavaFX applications, among others. You can even integrate ADF back-ends in Microsoft Office through ADF Desktop Integration. ADF is built from the ground up for easy integration with the other components of Fusion Middleware, such as Oracle WebCenter and Oracle SOA Suite.

    Oracle ADF Mobile enables you to build Java applications that will run directly on most current iOS and Android devices. Its model and controller layers provide a developer experience very close to the ADF one. ADF Mobile applications can consume standards-based web services and work very well with ADF SOA back-ends.

    Oracle ADF Mobile Browser is a set of JSF components that you can use instead of the regular ADF Faces components to create web applications that target a wide variety of mobile devices. The pages produced will gracefully adapt to the device's capabilities in order to deliver the richest user experience possible.


  3. Oracle has got JDeveloper, Netbeans and Oracle Enterprise Pack for Eclipse. Why do you have so many IDEs?
    Exactly for the same reason your favorite car brand offers several models: because not everyone has got the same needs.

    Netbeans is the perfect complement to the GlassFish Application Server, which is the reference implementation for Java Enterprise Edition. Thus, it will be your IDE of choice if you want to benefit from the latest advances in that space. Moreover, Netbeans provides unparalleled tools for JavaFX applications.

    Oracle Enterprise Pack for Eclipse (OEPE), on the other hand, shows Oracle's commitment to the current market share leader in Java IDEs. Oracle has been a member of the Eclipse Foundation for a long time and contributes to at least ten distinct Eclipse subprojects. In the last few years, OEPE has added support for a number for Fusion Middleware technologies such as Oracle ADF. Thus, you can build Fusion Architecture applications using an IDE you already know and love.

    Finally, Oracle JDeveloper is the best development environment for building Fusion Architecture application. With deep support for both other Fusion Middleware products and Java Enterprise Edition technologies, it focuses on developer productivity. JDeveloper is heaviliy used internally at Oracle, especially by the Fusion Applications team.

  4. When will JDeveloper 12c be out?
    I wish I could tell you! Unfortunately, revenue recognition rules in the US prevent us to provide a better answer than « sometime this year » The product team is working very hard on it as I write this. 12c will be a very nice release, and the internal builds I used recently show great promise. 

I hope you found my answers useful. During my flight back home, I got a fortune cookie with my meal. I got two prophecies and really hope they will come true...

Friday Apr 19, 2013

ADF Mobile and SDO: getting results

In my previous post, I explained how to make web service calls to SDO view objects exposed through a service-enabled application module. So far, so good. What I didn't explain is how you can access the return value of the service call if there is one. The answer is straightforward... once you figured it out.

 Our starting point, once again, is JDeveloper's Data Control palette.

As you can see,  the name of the return value is result. Consequently, one could be tempted to use GenericTypeBeanSerializationHelper to convert the value. And one would be right. Even better: you don't have to worry about the type name exposed by the data control as with input parameters. Here, I simply retrieve the results of the call in VisitResultBO, which is a simple POJO. 

GenericType genericReturnValue =
    (GenericType)AdfmfJavaUtilities.invokeDataControlMethod("VisitsWS", null, "getVisit", 
                                                            namesList, paramsList, 
                                                            typesList);
Object returnValue =
    GenericTypeBeanSerializationHelper.fromGenericType(VisitResultBO.class, 
                                                       genericReturnValue,
                                                       "visit");

What makes it not obvious is that the fromGenericType method has got two signatures: 

fromGenericType(java.lang.Class beanClass, GenericType gt)
fromGenericType(java.lang.Class beanClass, GenericType gt, java.lang.String attributeName)

For SDO web services, you absolutely need to use the second form and pass "result" as the value for attributeName. I must admit it took me some time to realize this. 

About

Frédéric Desbiens

The musings of a member of the ADF Product Management team.

I focus here on my favorite development framework but also have a strong interest in Mobile Development, Oracle WebCenter and Oracle SOA Suite.

Attentive readers will even find posts about IT Strategy from time to time, an interest of mine since I completed my MBA in 2006.

The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today