Mobile
 

Mapping Well-Known Patterns onto Symbian OS : Adapter

2/4/2012 4:13:04 PM
1. Problem
1.1. Context

There is a pre-existing component, the adaptee, which exposes an interface that you wish to use via an alternative incompatible interface, the target.

1.2. Summary

You need to resolve one or more of the following:

  • You wish to reduce your development costs by porting a pre-existing component, the adaptee, to a new environment.

  • You have re-engineered a component, the adaptee, but you wish to support the old interface, the target, to preserve compatibility for legacy clients.

  • You wish to increase the functionality and flexibility of a component by re-using existing components, the adaptees, at run time.

1.3. Description

This pattern was originally described in [Gamma et al., 1994]. Hence in this discussion rather than examine the pattern itself in too much detail, we place emphasis on how it can be used on Symbian OS, covering a range of diverse circumstances.

The context of this pattern of having to match incompatible interfaces can arise in numerous different situations. Software based on Symbian OS is subject to many pressures all of which can give rise to problems of mismatching interfaces: it is an evolving system with a large user base, which means that maintaining interface compatibility from release to release is important not just for the operating system itself but also for any services and frameworks built on top of it. It targets a domain which covers a wide and diverse set of technologies, many of which are standards-driven and must interoperate, and it is a specialized operating system, carefully evolved to work in a demanding environment.

In addition, you may not be able to change existing components to make compatible what were incompatible interfaces. The reasons for this range from pragmatic ones of engineering cost or risk, to business reasons (for example, licensing terms), to quality reasons (avoiding tight coupling between logically independent components).

Each of the following kinds of problem demonstrates the same common factor: the need, for whatever reason, to make incompatible interfaces work together, without changing existing components:

  • Preserving compatibility

    When the legacy functionality provided by one component is made obsolete by changes that, as your system evolves, introduce new, alternative functionality which meet at least the same requirements, clients relying on the legacy behavior must still be supported, if only for a transitional period. For reasons of maintainability and code size, it may not be feasible to leave the old component in place alongside the newer version.

  • Wrapping components to ease porting

    For existing components created for a different environment from the one you would like to use it in, it may be impossible or undesirable to make changes to the component. This commonly arises when integrating an industry-standard implementation of a specific technology.

    The interfaces presented to a component in the Symbian OS environment may be incompatible with what the component expects and, vice versa, the interfaces it presents may be incompatible with what potential Symbian OS clients of the component expect.

    For Symbian OS, the problem is complicated by the fact that native C++ interfaces are often not directly usable by ported components written in standard C or C++; and standard C or C++ client interfaces of ported components are typically not directly usable by native Symbian OS clients.

  • Run-time adaptation

    Different components that all store state may present very different settings interfaces, but updating their settings may be the responsibility of a single agent or service. Hardwiring knowledge of each different settings interface into the updating service may be a poor design choice, because it breaks modularity and flexibility by forcing a tight coupling between many different components, possibly in quite different parts of the system.

1.4. Example

Here we give an example for each of the three main uses of this pattern identified in the Description above.

Preserving Compatibility

Symbian OS provides a communications database which is a persistent store of the data required to configure communications components or to make connections with remote servers. Until Symbian OS v9.1, the interface to this communications database was via the CCommsDatabase provided by the CommDB component that used the existing OS database service DBMS.

However, in Symbian OS v9.1, Symbian introduced a new component called CommsDat that provides the same interface as the old CommDB, CCommsDatabase, but uses the Symbian OS Central Repository instead. This move makes use of the extra functionality provided by the Central Repository compared to DBMS, such as standard tools to set up and configure the Central Repository during development as well as performance enhancements such as caching repositories used by multiple clients.

Since the underlying data is stored in a different place, the old CommDB component cannot simply be maintained alongside the new CommsDat component. Hence an alternative solution is needed which maintains compatibility to allow the legacy clients of CommDB to continue to work but which uses the new storage location.

Wrapping Components to Ease Porting

Before Symbian OS v9.2, a native database component known as DBMS provided all clients with a database service supporting the SQL specification. However, for v9.2, Symbian decided to port the open source SQLite component[] to Symbian OS. One of the main reasons for this was that there was a requirement to provide more SQL features, such as triggers, that aren't provided by DBMS.

[] www.sqlite.org.

One decision taken early on was not to attempt to attempt to preserve compatibility with the old DBMS interface but instead to provide an interface to SQLite alongside it. This was because, whilst SQLite provided more SQL functionality than DBMS, it didn't, at the time, provide a superset, i.e. there is some SQL functionality that DBMS supported that SQLite didn't.

This left the following remaining challenges to be overcome to integrate SQLite into Symbian OS:

  • SQLite exposes a C-based API to clients but most Symbian OS programmers would expect a Symbian OS C++ API.

  • The databases accessed through SQLite need to comply with features of the operating system such as platform security as well as backup and restore.

  • SQLite expects a POSIX API to support it from underneath.

Run-time Adaptation

Consider the example of the remote management of a device, for example, a network operator configuring an email account on a subscriber's device 'over the air'. In this case, the provisioning agent or service needs to provide a service to the network that is logically identical to a service provided locally on the device. However, it's often the case that the standards-driven network interface is different to the interface provided locally. Hence a means is needed to join the two interfaces together.

That's not the end of the problem though because clearly it would be beneficial to deploy the same device management system on as many devices as possible and they will vary in their support for features such as email. This means that the central device management system needs to provide an extension point which discovers and loads plug-ins representing the components being remotely managed. This extension point defines a target interface into which we need to adapt existing components.

2. Solution

This pattern decouples interfaces by creating an intermediary class, called the adapter, whose purpose is to adapt the interface presented by one class, the adapter, and present the interface, known as the target, expected by another class, the client, that wants to use the adapter. This pattern can therefore be used to solve the problem of incompatibility between two existing interfaces, without requiring changes to either interface.

2.1. Structure

From the client's point of view, only the adapter need be visible or known; the implementation of the adapter and any details of the conversion or translation it performs using the adaptee can remain hidden from the client. In short, the client never doesn't directly depend on the adaptee. This is achieved by the structure shown in Figure 1.

2.2. Dynamics

At its simplest, an adapter is a wrapper. For example, methods are provided that convert the API of a ported application and make it appear to be a native API to clients. Wrappers can also be used to convert outbound system calls from the ported component to match native Symbian OS calls. The adapter simply redirects a call made through the target's interface to a matching adaptee function (see Figure 2).

In more complex cases, rather than simply wrapping each method, an adapter may be a complete component that intercepts and converts calls made to one API into calls to a quite different API where there doesn't exist a close match for the target method called. The adapter may even need to use multiple adaptees to satisfy the client's request on the target interface (see Figure 3).

Figure 1. Structure of the Adapter pattern

Figure 2. Dynamics of the Adapter pattern (simple)

In the most complex case, at run time the adapter may need to select and load the adaptee that satisfies the client's request on the target interface. In these more complex cases, it is common to implement the adapter in its own DLL separately from the adaptees so that clients have even fewer dependencies on the adaptees. This allows the adapter to use the ECom service to dynamically load adapters as plug-ins at run time if needed.

Figure 3. Structure of the Adapter pattern (complex)

2.3. Implementation

There's little to say about the implementation of this pattern over and above what's already been said in the Structure and Dynamics sections. The one exception to this is if you wish to load adaptees at run time. If so, you should consider using Buckle (see page 252) to do this securely based upon the ECom plug-in service provided by Symbian OS.

2.4. Consequences

Positives

  • This pattern is simple to implement and understand.

  • Development time is reduced by enabling the re-use of existing components.

  • By preserving any pre-existing interfaces, you reduce your testing costs because any existing tests written to use the target interface also work for the adaptees if the tests use the adapter.

  • It preserves decoupling between the target and adaptee interfaces. By keeping interfaces decoupled, your solution remains flexible which makes future maintenance and evolution easier. Typically, it is much easier to update the adapter in the case of future changes than to try to evolve the clients or the adaptee if that would even be possible.

  • In the case of a run-time adapter design, new adaptees can be integrated with potentially no change to the client or even the adapter. This makes your design more open and easily extensible.

Negatives

  • It imposes additional overheads on the use of an adaptee interface because an additional layer is interposed between the client and the functionality it wants to use in the adaptee. This increases both the code size and the execution time for the solution although they are unlikely to be significant in all but the most extreme of cases.

  • If you are using this pattern to preserve compatibility by maintaining a legacy API in addition to a new API then this necessarily increases the complexity of the system since there are now two APIs for clients to choose from during development as well as two APIs for the provider of the adapter to support.

  • Using a run-time adapter based on the ECom plug-in service has no impact on execution time once the plug-in has been loaded. However, searching for and loading a plug-in is an additional overhead compared to statically loading a DLL which is why, if your intention is just to wrap a single legacy component, a regular DLL could be a better choice.

2.5. Example Resolved

Preserving Compatibility

In the example described above there was a need to maintain a target interface, CCommsDatabase, originally implemented by the CommDB component. However this interface needed to work with the new location for the communications database within the Symbian OS Central Repository instead of the old DBMS location. Figure 4 shows the old structure of CommDB.

Figure 4. Legacy structure of CommDB

This problem was solved by removing the old CommDB component and providing a replacement, the adapter, called the CommDB shim which implements the CCommsDatabase interface, the target, in terms of the new communications database component CommsDat, the adaptee. This is possible because CommsDat provides all the functionality of CommDB and more. Figure 5 shows the new structure.

This solution clearly provides source compatibility between the legacy and the new shim implementations of the CCommsDatabase interface since the same class is used in both. Binary compatibility is also preserved by providing the new adapter in a binary with the same name as used in the legacy solution, commdb.dll. However, this doesn't guarantee that the new implementation of the old interface will behave exactly as it used to. The only way to guarantee that this behavioral compatibility has been maintained is to ensure the tests written for the legacy CommDB component work for the new shim version.

Figure 5. Structure of the CommDB Adapter

The result of this is that Symbian OS now has two APIs to one communications database: CCommsDatabase and CMDBSession.

Wrapping Components to Ease Porting

The example described above for this use of the pattern was the porting of SQLite to Symbian OS. The first problem was that, as SQLite is implemented in standard C, it expects a POSIX API to support it from underneath to provide interfaces into the memory model, the file system, and so on. This was solved by using the PIPS[] to provide near-perfect support for the SQLite C calls, requiring little or no tuning of the SQLite code and working 'straight out of the box'.

[] PIPS is POSIX on Symbian (PIPS) is itself a large wrapper for a number of native Symbian OS C++ APIs.

More challenging was providing an interface for use by clients of SQLite. The problem was not that the interface needed to be compatible with the legacy database service, DBMS. Instead the existing interface is written in C and most Symbian OS programmers would expect a Symbian OS C++ API that supports the system-wide functionality, such as platform security as well as backup and restore. So a new interface for Symbian OS, based on C++ was needed.

The key to understanding the final design is to remember that the new interface to SQLite would have to check the security credentials of any clients attempting to access a database. Since this can only be done securely across a process boundary,[] this immediately rules out the use of Client-Thread Service (see page 171) to provide the service interface. Instead, Client–Server (see page 182) was selected to be able to provide a secure service interface to the underlying SQLite component. Accordingly the new component is called the SQL Server. This server provides all the support specific to Symbian OS needed by clients accessing the SQLite library underneath. It resulted in the component structure shown in Figure 6.


Figure 6. Structure of the SQLite Adapter

Run-time Adaptation

Another example of this pattern is provided by the Symbian OS Device Management (DM) subsystem, which supports all aspects of remote provisioning of devices (for example by network operators) to manage device settings. In this case, multiple adapters are required which implement a common provisioning interface CSmlDmAdapter, the target, which is used by the DM client on the handset acting as an agent for a DM server hosted remotely off the device. The adapters map the common target DM interface into various settings interfaces provided by diverse Symbian OS components, for example phone, messaging, and mail.

This gives rise to the structure shown in Figure 7.

Figure 7. Structure of the Device Management Adapter

However, this only partly solves the design problem. Some issues still remain:

  1. Shielding the client from changes in the adaptee with a binary firewall (not just a compilation firewall)[] to reduce any dependencies during development.

    [] This can be achieved to a certain extent with a regular DLL. However, with DLLs one has to supply the module definition (DEF) file to be used by the 'client' at link time. In this case, clearly such a link-time dependency is not suitable.

  2. Keeping track of the number of adapters available on the device.

  3. Loading the appropriate adapter based on the type of settings it supports.

  4. Supporting the addition or removal of adapter implementations to or from the device at run time to allow third-party developers to supply adapters for the DM client.

Here ECom comes to the rescue. By implementing the adapters as ECom plug-ins, the adapters have to be provided in a separate DLL from the DM client, thereby making it completely agnostic of the implementation details or locations of adapters.

Issues 2 and 3 are resolved by ECom's REComSession::List-ImplementationsL() and REComSession::CreateImplementationL() methods, which allow the DM client to specify a 'tag'[] that identifies the plug-ins it is looking for.

[] This 'tag' can be anything, ranging from MIME types to number codes. It is specified in the resource (RSS) file of the ECom Plug-in. Custom ECom resolvers can be designed easily to handle the 'tag'. Alternatively, the implementation UID of the plug-in can be used directly.

Furthermore, the registry of ECom plug-ins is updated whenever a plug-in is added or removed. Clients of the ECom service can register for notification of when this happens, through REComSession::NotifyOnChange(). This is used by the DM Client so that it can maintain an up-to-date list of adapter implementations available on the device, which addresses issue 4.

This example also illustrates how adapters can potentially handle more than one adaptee. Going back to the definition of DM, provisioning a device includes operations such as adding, deleting or modifying various device-level settings. Though there are different types of such settings, each one must support these three main operations. The OMA[] DM provisioning standard defines these as ADD, DELETE and REPLACE 'commands' respectively.

[] www.openmobilealliance.org.

Consider the case of remotely managing the MMS accounts on a device. The MMS framework in Symbian uses two classes to store the details of the MMS accounts:

  • CMmsAccounts is the container for MMS accounts. Each MMS Account item holds the ID and name of the account along with a reference to its MMS Settings object.

  • CMmsSettings holds all the configuration information that is associated with an MMS account. It includes settings such as proxy and IAP settings, delivery notification flags, image specifications, and so on.

To 'ADD' an MMS account, the remote server sends the details via a SyncML message. The DM framework passes the received details and uses ECom to decide that it should be passed on to the MMS adapter (CDmMmsAdapter). CDmMmsAdapter in turn has to deal with both CMmsAccounts and CMmsSettings.

Figure 8 shows a simplified version of how the DM MMS adapter distributes work to its multiple adaptees.

When instructed to create a new MMS account, the CDmMmsAdapter does the following:

// Create a new MMS Settings object
mmsSettings = CMmsSettings::NewL(); // Adaptee 1
CleanupStack::PushL(mmsSettings);

// Add this to Settings array
iMmsSettings.AppendL(mmsSettings);

// Ownership transferred
CleanupStack::Pop(mmsSettings);

// Device-management house-keeping code

// Create a new MMS account
iMmsAccounts.CreateMMSAccountL(aName, *mmsSettings); // Adaptee 2

This is followed by calls to set (or reset) various MMS Settings parameters (as and when the DM commands arrive), e.g.

// Set an MMS Proxy
iMmsSettings[index].AddProxyL(aProxyUid);

or

// Set delivery notification
iMmsSettings[index].SetAllowDeliveryNotification(aAllow);

Figure 8. Dynamics of the Device Management Adapter pattern

3. Other Known Uses

  • Preserving Compatibility

    A number of features of Symbian OS may be excluded from a device by a device manufacturer, usually because it doesn't have the hardware to support them. For example, this is true of infrared and Bluetooth. However, if the feature is excluded completely then any existing software that uses the feature would fail to compile as the APIs it depends on aren't present. Instead a mechanism is provided to allow existing software to compile and instead check at run time if the feature is present.

    This pattern is used to allow such clients to compile by adapting the target interface of a feature to a non-existent adaptee. The adapter simply provides stubs for the target interface that return KErr-NotSupported. This provides a binary- and source-compatible component for clients to use though, of course, it's not possible to provide behavior compatibility!

  • Porting

    Java, Freetype, and OpenGL have all been ported to Symbian OS using this pattern.

  • Run-time Adapters

    SyncML uses ECom-based adapters to implement an extensible framework for handling multiple transport mechanisms. A number of Transport Connection Adapters (TCAs) exist for the various transport types (HTTP, OBEX over USB, OBEX over Bluetooth, WSP, etc.) and are implemented as ECom plug-ins. This allows device manufacturers to create new adapter plug-ins to add support for new transport mechanisms.

 
Others
 
- Mobile Web Apps : Loading Pages (part 3) - Going Backwards
- Mobile Web Apps : Loading Pages (part 2) - Sliding
- Mobile Web Apps : Loading Pages (part 1) - Swapping Pages & Fading with WebKit Animations
- Personalize & Secure Your iPad : Adjusting Sounds on your iPad & Personalize Your Picture Frame
- Personalize & Secure Your iPad : Changing your Lock Screen and Home Screen Wallpapers
- Using Media in XNA Game Studio : Media Enumeration
- Using Media in XNA Game Studio : What Is Media?
- Android Application Development : Layouts (part 2) - AbsoluteLayout & RelativeLayout
- Android Application Development : Layouts (part 1) - inearLayout
- Building an Advanced Java Game on Symbian OS (part 4) - Using the Bluetooth API
- Building an Advanced Java Game on Symbian OS (part 3) - Using the Mobile 3D Graphics API
- Building an Advanced Java Game on Symbian OS (part 2) - Using the Mobile Media API & Using the Scalable 2D Vector Graphics API
- Building an Advanced Java Game on Symbian OS (part 1)
- jQuery 1.3 : Simultaneous versus queued effects (part 2) - Working with multiple sets of elements & Callbacks
- jQuery 1.3 : Simultaneous versus queued effects (part 1) - Working with a single set of elements
- iPhone 3D Programming : Crisper Text with Distance Fields (part 3) - Implementing Outline, Glow, and Shadow Effects
- iPhone 3D Programming : Crisper Text with Distance Fields (part 2) - Smoothing and Derivatives
- iPhone 3D Programming : Crisper Text with Distance Fields (part 1) - Generating Distance Fields with Python
- Mapping Well-Known Patterns onto Symbian OS : Singleton
- Mapping Well-Known Patterns onto Symbian OS : Model–View–Controller
 
 
Most View
 
- Windows 7 : BitLocker (part 3) - How to Manage BitLocker Keys on a Local Computer, How to Recover Data Protected by BitLocker
- System Center Configuration Manager 2007 : Distributing Packages - Creating Advertisements (part 5)
- Microsoft Lync Server 2013 : Mediation Server Administration
- : Windows Server 2008 and Windows Vista : Administrative (.adm) Templates (part 4) - Managing .adm Templates
- Microsoft Excel 2010 : Enabling and Examining Macros (part 1) - Macro Security in Excel 2010
- Exchange Server 2013 : Types of Exchange Client (part 1) - Messaging Application Programming Interface, Exchange Web Services
- Developing, Integrating, and Building Applications in Sharepoint 2013 (part 2) - User Interface Integration - App Parts and Pages
- Exchange Server 2013 administration overview : Using the graphical administration tools
- Microsoft Visio 2010 : Aligning and Arranging Shapes (part 4) - Aligning and Distributing Shapes Using Position Functions - Practice Aligning Shapes
- Windows Server 2012 : A complete virtualization platform (part 8) - Resource metering
 
 
Top 10
 
- Microsoft Project 2010 : Setting Up Project for Your Use - Defining Custom Fields
- Microsoft Project 2010 : Setting Up Project for Your Use - Defining Calendars (part 2) - Setting Project and Resources Calendar
- Microsoft Project 2010 : Setting Up Project for Your Use - Defining Calendars (part 1) - Calendar Hierarchy , Modifying and Defining Base Calendars
- Windows Phone 8 : Receiving Input with Buttons - Check Box
- Windows Phone 8 : Receiving Input with Buttons - Radio Button
- Windows Phone 8 : Receiving Input with Buttons - Repeat and Toggle Buttons
- Windows Phone 8 : Receiving Input with Buttons - Tap and Click Events, Button Click Mode, Hyperlink Button
- Windows Phone 8 : Receiving Input with Buttons
- Windows Phone 8 : Content Controls - Defining the Default Content Property
- Windows Phone 8 : Control Type Taxonomy - Identifying Controls Not Supported or Absent in Windows Phone