“Enterprise Applications are just the same as Consumer Applications.”
This is a point of view that I often hear expressed, more often than not, by developers who have primarily focused on the consumer space. I don’t share that point of view. Enterprise Applications and Consumer Applications exist in different ecosystems, have different constraints, and often different requirements.
My colleague Martin Woolley covered many aspects of Enterprise Mobile Applications in A Flexible Architecture for Enterprise Mobile Applications Parts One and Two, where he outlined many of the considerations that need to be weighed when working in this area. He also identified a potential re-usable pattern for such applications that share many common features and requirements.
Like Martin this activity has catapulted me back into the world of Enterprise Applications, which is where I spent most of my time before joining BlackBerry about 5 years ago. I’m no stranger to Enterprise Architecture, Oracle Enterprise systems or even SAP Basis Component, though I’ve been focusing on technologies like NFC and Bluetooth more recently.
In this article I’d like to build on what Martin has described and discuss one possible way of accommodating this application architecture within Momentics when working with Cascades and native applications. There are other potential implementations of this same pattern using, for example, a web-based framework. (Watch out for these in the near future.)
The Target Application Structure in Momentics
The starting point is the overall application architecture, in Figure 1, that Martin has already described.
Figure 1: Overall Application Architecture
If we pare away the details, there are really four major elements as shown in Figure 2.
- The GUI component of the Application
- The Background Service component of the application
- The Common Functions and Data component
- The Adapter component of the application
Figure 2: Major Application Elements
Figure 3: Project Structure
I’ve chosen to name the complete application “AnnualLeave” letting me name the main components as shown in Figure 3 as:The components in green, the GUI and Background service, map nicely to the GUI and Headless service components of a Headless application. The components in orange, Common Functions and Adapter, map nicely to a pair of shared libraries.
I use Git as a source code control tool and as a vehicle to share the development. In fact this is a significant point that’s worth dwelling on. The four projects live outside the Momentics workspace in a folder called “AnnualLeave;” this directory has a Git Repository associated with it – take a look at Figure 4.
This allows me to manage all four projects as a single unit in Git, but it also has other consequences. All four of these projects have dependencies on one another.
Figure 4: Folder Structure
This is much easier and much more transportable if all these projects live outside the Momentics workspace and are just imported into Momentics without copying. I’ve been there and tried it various other ways and it’s just much easier this way without any side effects.For example, the GUI project (AnnualLeave) needs to have access to the header files that the Common library project (AnnualLeaveCommon) has exposed to allow its APIs to be used at build time and to its shared library – once built – to allow external references to be resolved at link time.
Let’s look at each of these projects with a bit more detail, starting with the two shared library projects first since they are dependants of the GUI and Service projects and are almost identical in terms of structure.
Figure 5: Library Project Structure
The “AnnualLeaveCommon” and “AnnualLeaveAdapter” projects
These projects are both shared library projects, so they have three important aspects.
The header files of many of the classes need to be exposed to other projects since they embody the API exposed by the library. This is achieved by placing these header files in a folder called “/public” in the project, in contrast to the implementation of the APIs in the “/src” folder.
The shared library itself that is built is called “libAnnualLeaveCommon.so”.
The Common project is identical in layout and has its shared library under the name “libAnnualLeaveAdapter.so”
You can see this in the case of the “AnnualLeaveCommon” project in Figure 5.
There is a slight wrinkle with both of these shared library projects. I needed these projects to understand Qt constructs and for their classes to inherit from QObject in many cases.
The wrinkle is that the current version of Momentics only contains a shared library template that allows you to create a shared library project that uses the built-in tool-chain rather than qmake. A library that you can create using this template does not understand Qt since it’s unable to run the MOC pre-processor against the project’s header files.
To create a shared library that understands Qt and inherits from QObject you need to be a bit inventive. I can’t take credit for this; check out this great project on GitHub by Isaac Gordezky.
The instructions are very clear and it works very nicely.
The “AnnualLeave” and “AnnualLeaveService” projects
These two projects just represent the GUI and Headless Service components of a “Headless Application.” In principle, they are quite simple to create since there is already a Headless Application Template in the Momentics tools. Just use: “New -> BlackBerry Project – > Cascades -> Headless App”
I did find one wrinkle with this process where the destination of the pair of projects was outside the Momentics workspace. I found it more convenient and less error-prone to create the project in the Momentics workspace, export the two projects to a new location, delete the original projects from the workspace and re-import them without copying from the new location.
Now that we have established four projects supporting the application, the next thing is to ensure that they know about one another and that the application is packaged correctly into a .bar file with all the assets in the correct locations for installation on the BlackBerry 10 device.
Let’s take a look at the “AnnualLeave” project first; the one that is the GUI part of the application. This project really needs to know how to access the APIs in the “AnnualLeaveCommon” project; this would include things like the Operations API that Martin has already described.
There are three aspects of this:
- The header files needed at compile time in /public in “AnnualLeaveCommon”
- The shared library itself, needed at link time – “libAnnualLeaveCommon.so”
- Where to find the shared library “libAnnualLeaveCommon.so” at run time on the device. (I’ll address this later in this article.)
The compile-time and link-time dependencies are expressed in the “AnnualLeave.pro” file. This file is used by qmake to construct the Makefiles that orchestrate the overall build process.
Figure 6 shows the additions I’ve made to the .pro file for the AnnualLeave project. I’ve added the /public path in the “AnnualLeaveCommon” project to the INCLUDEPATH for the build of the project. This is the same whether you’re building a Debug-Build or a Release-Build.
We have to be a little bit more careful when adding the information used to locate the shared library “libAnnualLeaveCommon.so” at link-time. Take a look at Figure 6.
Figure 6: AnnualLeave.pro file changes
You can see that I’ve had to deal with each of the possible target build cases, Debug, Release, Profile and Simulator, separately. Each of these build-permutations builds the library itself in a different location.
The “AnnualLeaveService” project is handled in exactly the same way, except that it needs to be aware of the details of the location of both the “AnnualLeaveCommon” and “AnnualLeaveAdapter” projects, and its .pro file needs to be updated appropriately. Similarly, the “AnnualLeaveAdapter” needs to be aware of the “AnnualLeaveCommon” and “AnnualLeaveService” projects.
You may also find it useful to identify the references that these projects have with respect to one another. It helps make building and cleaning projects a bit simpler in that Momentics knows what projects need to be re-built if another project changes.
You can see how to do this in Figure 5.
Figure 7: Project References
Now that all the projects build consistently individually, the next task is to ensure that all the necessary assets are collected together into the .bar file when the application is packaged.
Packaging the Complete Application with all Four Components
The specification of the packaging of an application is the responsibility of the bar-descriptor.xml file. Since there is only a single .bar file to be created for this application, even though it consists of four constituent projects, only a single bar-descriptor.xml file is needed and this lives in the GUI project.
Figure 8: bar-descriptor.xml file for the AnnualLeave Project
Figure 8 shows a section of that file. This happens to be the section that describes how the assets are assembled for a Debug-Build, but the principle works for other build types as well.
There are two points to draw your attention to. Each “<asset …/>” stanza identifies where to find a particular asset and where to place it in the target .bar file. In this case:
- “libAnnualLeaveCommon.so” is found in the directory: “../AnnualLeaveCommon/arm/so.le-v7-g/”
- And, placed into the .bar file under the directory: “lib/”, under the name: “libAnnualLeaveCommon.so.1”
Did you notice the “.1” at the end of the shared library name? This is a technique for associating version numbers to shared libraries. I had discovered that when the BlackBerry 10 OS tried to locate the shared library for “libAnnualLeaveCommon.so,” it was actually searching for “libAnnualLeaveCommon.so.1,” so I had to add it to the .bar file under this name. If you look at Figure 5 you can see that a variety of different version annotations have been created by the build process itself.
You can see that both of the shared libraries are placed under the “lib/” directory in the .bar file. But how do we let BlackBerry 10 know where to find these files at run-time on the device? Take a look at Figure 9.
Figure 9: Where to find the shared libraries at run-time
The highlighted directive makes sure the application is launched with the environment variable LD_LIBRARY_PATH set to include the path “app/native/lib,” which is where they end up on the BlackBerry 10 device as a result of the instructions in Figure 8.
How the Adapter Shared Library is able to Leverage Qt Signals and Slots
You may wonder why I wanted to have the AnnualLeaveAdapter implemented as a shared library that understood about Qt and have its contained classes inherit from QObject.
I wanted the ability to be able to interact with the objects defined in the library through Qt Signals and Slots. I also wanted to be able to communicate with the adapter through ordinary methods and have events returned from the adapter as Qt Signals. Lastly, I wanted to define the interface to the adapter in terms of C++ methods and Qt Signals.
So, how should we define and implement an interface to achieve this? If we were working only in C++, then we’d define an abstract class, and the developer who was implementing the “Adapter” library would create an implementation class that implements this abstract parent class.
This works nicely for methods that call into the implementation; however, it takes a bit of Qt magic to get it working for Signals. Here’s how I did this.
Figure 10: How to define a Qt aware interface
This class “IAdapter” in Figure 10 is a standard abstract class with added “Qt Magic.” It has a pure virtual function “annualEntitlementRequ()“ which will request, for example, leave entitlement for a specific year, and a Qt signal “annualEntitlementResp()” representing the response – also defined as a pure virtual function.
The key to getting this to work is the Qt declaration “Q_DECLARE_INTERFACE()“. This identifies the class “IAdapter” as being a Qt Interface, in addition to just being an abstract class, and allows the MOC compiler to get behind the scenes of the class and weave all the Qt meta-data together.
The implementation side of a class, “IAdapterImpl,” that implements this interface, looks something like Figure 10.
Figure 11: How to implement a Qt aware interface
This class is dependent on both QObject, to give me the Signal/Slot functionality, and IAdapter, since I’m implementing its interface. Q_DECL_EXPORT just ensures that this class is visible externally from the shared library.
The Qt magic here is the declaration Q_INTERFACES(IAdapter). This is what tells Qt that we’re implementing an interface declared previously by Q_DECLARE_INTERFACE , and allows the MOC compiler to weave Qt goodness into the implementation.
Figure 12: Using the interfaces
In Figure 12 you can see how I might actually use the interface. In the first instance I can use this to call a method in the adapter, and in the second I can connect a “Signal” in the adapter implementation with a “Slot” in my application.
Notice the subtle part where you need to cast the pointer to the implementation instance to a QObject* to have it as the correct type for QObject::connect().
Implementing the adapter in this way supports extensibility in that it makes it easy to substitute other adapters that support other Enterprise interfaces while retaining a well-defined interface to the rest of the application.
I hope you found this post useful. We suspect that, because of its generality, this project structure will be useful in many other enterprise mobile applications.
What do you think? Let us know in the comments below.