Wednesday, October 26, 2011

The Lifecycle API

1. Bundle Activator
The main entry point for a bundle to the lifecycle API is the Activator. To use one, a bundle must declare it in its manifest, and provide the activator code. The manifest entry looks like this :

Bundle-Activator: org.phoenix.mybundle.MyActivator 

Note that the activator may be imported from another bundle, or provided in the bundle classpath.

The bundle activator, as defined in the manifest of a bundle (Bundle-Activator), is the bundle's hook into the lifecycle layer. The bundle activator must implement the interface :

org.osgi.framework.BundleActivator

which defines two methods : start(BundleContext ctx) and stop(BundleContext ctx).

When a bundle is installed and started :
  • OSGi framework creates an instance of the Activator;
  • invokes the activator's start method.
When a bundle is stopped :
  • OSGi framework invokes the stop method (on the same instance of the activator, but may be in a different thread);
  • releases the activator instance.
Warnings :
  • On a restart, a new instance of the activator is created;
  • the activator stop method should undo everything that was done in the start method.
2. Bundle Context
The bundle context is provided to the bundle by the lifecycle layer. It allows the bundle to interact with the framework, access its context, modify it.... A bundle context is unique for a bundle, and is valid between the calls to the start and stop methods. A bundle's activator should not be shared with another bundle.

As exposed in the BundleContext interface listed above, the bundle context provides access to more than its owning bundle. It also allows to access other bundles, and through them to their context. Here is the Bundle interface :

The bundle ID is a runtime ID, generated by the framework. It's different from the bundle's static identifier, declared in its manifest.
The bundle location is usually an url, from which the bundle can be downloaded. But a bundle can also be loaded from an inputstream.

3. Bundle lifecycle
The schema below shows the famous bundle life cycle diagram.


Bundle and lifecycle interactions

The module layer defines bundles and uses OSGi modularity features. The lifecycle layer uses OSGi framework to manage and execute bundles. It provides bundles with a management API and a lifecycle at runtime.
Outside the application, the lifecycle layer provides operations for the bundles through the lifecycle API.
Inside, it defines how bundles accesss their execution context.

Through the lifecycle API, a management application or an administrator can install/stop/start/update/refresh bundles to modify the application configuration. Bundles themselves can use the lifecyle API to modify their own configuration.

1. Bundle cache
OSGi framework provides a persistent bundle cache, which allows to reinstall and start the bundles that were installed at shutdown on the next framework startup. It can be seen as the deployed configuration of the application.

When started, cached bundles are marked as so. When the framework is restarted, cached bundles are reinstalled, and those marked as started are automatically started.

2. Bundle properties
Through its context, a bundle can access some properties. Theses properties can be defined either in the bundle context, or in system properties.
A call to context.getProperty("foo") searches first in the bundle context properties, then in the system properties. It eventually returns null if the property could not be found.

3. Threading
The OSGi framework heavily relies on java threads. It does not manage concurrency, but offers some guarantees. For example, it guarantees that the stop() method of a bundle activator will not be called before its start() method returns.

4. Persisting bundle state
There is only one instance of a bundle's activator between the calls of its start() and stop() methods. When the bundle is restarted, a new instance is created. This means there is no way to persist a bundle state between two starts of a bundle.

To persist a state, one has the following options :
  1. store the state in a database or a file;

Tuesday, October 11, 2011

Bundle dependencies resolution

Bundle dependencies resolution is the automation of dependency management. It precedes the Resolved status of a bundle. In other words, it's the process of matching a bundle's imported packages to exported packages from another bundle, so that a bundle only has access to a single version of any type. It uses classloader delegation between bundles. Failure to resolve a bundle's dependencies leaves the bundle in the Installed status.

1. Classloader delegation
The classloader delegation is the process of delegating class loading to different classloader, dependending on the package. The classloading delegation is ruled by the following algorithm :
  1. if package starts with java., ask parent classloader;
  2. if package is imported, ask the exporting bundle's classloader;
  3. search in Bundle-ClassPath.
2. Dependencies resolution

The resolution of a bundle's dependencies follow this algorithm :

resolve(bundle)
 bundle has no  imports
    Resolution is successful;
 bundle has imports :
    find a matching candidate for the imported package :
       if the candidate bundle is Resolved : 
          Resolution is successful;
       if the candidate bundle is not Resolved :
          resolve(candidate)
 wire bundles

2.1. Attributes and matching
Exported attributes (arbitrary name/value pairs) are important for matching only if the importing bundle specifies the same attribute. Otherwise, the framework just ignores them.
Imported packages with attributes specified must find a matching exported package with the same attribute and the same value. Otherwise, resolution fails.
If multiple attributes are declared, a logical and is used to resolve the dependency with attributes.


2.2. Multiple exporting bundles and version choosing
Dependency resolution may become tricky when it comes to packages exported  either with differents versions (by different bundles), or with the same version by multiple bundles. The OSGi framework uses the following algorithm to resolve such packages :

resolve(package)
 package is exported with different versions
    framework chooses the highest matching version;
 package is exported with the same version by multiple bundles :
    - priority is given to bundles installed first
    - priority then goes to maximizing collaboration
       already resolved candidates bundle have priority against not resolved ones,


"Maximizing collaboration" rule, giving priority to already resolved bundles over not resolved ones, may break the "highest version" rule. This is done because bundles can only collaborate if they are using the same version of a shared package. Furthermore, it minimizes the number of different packages versions.


So, this algorithm can be summarized like this :
 1. highest priority : already resolved candidates
    1.1. multiple matches are sorted against : 
       1.1.1. version (highest first) 
       1.1.2. installation order (lower first) 
 2. then for unresolved candidates, matches against : 
    2.1. version (highest first)  
    2.2. installation order (lower first)

3. The Uses constraint
Please note that this chapter is based on a Neil Bartlett's blog post rewritten and extended by me : Neil Bartlett's post.
Before explaining the uses constraint's goal, let's introduce the concept of bundle "class space". The bundle class space is the union of imported packages and bundle classpath. In some situations, dependencies resolution can lead to class spaces inconsistencies.


Let's illustrate this. First, we set up a simple situation. Bundle A export a package foo with version 1.0.0. A bundle B imports the package foo, witg the same version. The notation used in the schema below is taken from OSGi specifications : a black rectangle is an export, a white rectangle is an import, and the yellow "blob" container is a bundle. The line connecting an export to an import means that the OSGi framework has chosen this export to resolve the import.

Everything's fine. Now let's imagine bundle B exports a package bar. We now add a bundle C, which imports the package bar. This leads us to this situation :

Once again, no problem here. But we are going to complicate the situation by adding a bundle D, exporting the package foo (same as A's package), but with version 2.0. And then, we add in bundle C an import of package foo in version 2.0. The following schema describe the situation.

Still no problem here. But this is because package bar does not expose package foo, either by subclassing a class from package foo or by using a class of package foo as a method return type or as a method parameter. In this case, bundle class space C is consistent, which means it only has one version of each class. Bundle C's class space is shown in the following schema by the shaded blue area.

But what happens if package bar exposes package foo, by (as an example) subclassing a class from package foo, like in the following code snipet ?


In this situation, the framework is still able to resolve all dependencies, but a ClassCastException will be thrown, because the Foo class used in bundle B is located in B's class space (ie. coming from foo;1.0 import), but the Foo class in bundle C is located in C's class space. Since each OSGi bundle has its own class loader and a class is uniquely identified at runtime by the combination of its fully qualified class name and the class loader instance that defined the class, there can be two versions of each exported class present in the JVM at the same time. But even if the class is the same, they are not in the same classloader, so cannot be cast to one another.

The solution here is to use the uses constraint. We specify a uses constraint on bundle B's export of package bar, declaring that package bar uses package foo. The metadata for this would be :

Export-Package: bar; version="1.0"; uses:="foo"

This adds a constraint on the framework dependency resolution for bundle C importing package bar. It must have in its class space the same foo package as bundle B. But wait...Bundle B import package foo with version 1.0, and bundle C explicitely imports package foo with version 2.0. So how does the framework resolves bundle C dependencies? Well...it can't. Bundle C cannot be resolved.

To solve this problem, one could just remove version attribute in bundle C import of package foo. Without the uses constraint, and if bundle B package bar does not expose package foo, then the dependency resolution algorithm will give foo in version 2.0 from bundle D to bundle C import (higher version first). But if B's bar package exposes package foo and declares a uses constraint on it, then the framework will resolve bundle C's import of package foo to the same one imported by bundle B : the package exported with version 1.0 by bundle A. The resulting situation is depicted in the schema below.
The uses contraint is shown by the little white rectangle linking the export and import of bundle B. We can see there that the foo package import has been resolved using bundle A's export, and that bundle C's class space now includes the foo package of bundle A. Resolution is now possible, and no ClassCastException is to be feared.

WARNING : Please note that too much uses contraints may restrict the framework choice of dependency resolution candidates and versions. You should be aware of this restriction. By the way, a uses constraint is not mandatory when two versions of the same class can co-exist (by not using methods exposing the class)...but i agree this is a little dangerous.

Module metadata

OSGi modules (aka bundles), are described by metadata, located in a file called
MANIFEST.MF, in the META-INF directory.


1. Metadata syntax

OSGi bundles metadata is a list of header/values informations, following the following syntax :
  name: value(s)
 
More extensively this syntax allow 4 different elements :
  • Header name : Property-Name:
  • Property (header) value
  • Attributes (arbitrary name/value pairs) : attr1=value
  • Directives (alters the framework handling of the header information) : dir1:=value
Here is an example of a full header line :

  Property-Name: propValue; attr1=foo; dir1:=bar


1.1. Human readable headers
Some of the bundle's metadata headers are human readable informations, ignored by the framework, but useful for the developper :
  • Bundle-Name
  • Bundle-Description
  • Bundle-DocURL
  • Bundle-Category
  • Bundle-Vendor
  • Bundle-ContactAddress
  • Bundle-Copyright

1.2. Bundle identification headers
Some other headers are used by the framework to identify a bundle :
  • Bundle-SymbolicName (mandatory since OSGi R4)
  • Bundle-Version (following the pattern major.minor.micro.qualifier, as in 1.1.0.alpha, the qualifier being optional) (not mandatory, defaults to 0.0.0)
  • Bundle-ManifestVersion (2 means that the bundle manifest follows OSGI R4 specs, defaults to 1)
Bundle version order follows the rule described below :
1.3. Code visibility headers
Finally, some headers are used to describe a bundle's code visibility. They are :
  • Bundle-ClassPath : defaults to . (root based) : an ordered, comma separated list of relative bundle jar files and class/resource lookup locations. If this header is used, the . must be mentionned, because it will not be included automatically. Allowed values are :
    • . (the root classpath)
    • path/subpath : another root to look into (useful for resources lookup like images, without having to specify the directory holding them, or even better to find resources like css, images, xml files, located in different directories, without having to use the directory in code.
    • embedded.jar : the name of a jar embedded in the bundle
  • Export-Package : a comma separated list of packages to expose for sharing with other bundles. It exposes all public classes of the specified packages.
    • attribute discrimination : adding arbitrary attributes to an exported package allow packages importers (see Import-Package below) to discriminate bundles from which they get packages by adding the same attribute (more on that later).
    • versionning : defines a version for the exported package. This attribute is not arbitrary, and can take a value following the pattern major.minor.micro. This attribute value defaults to 0.0.0 if not specified.
  • Import-Package : used to import required packages (except java.*, provided by the framework).
    • uses the same attributes discrimination.
    • The version attribute can also be used but has a range meaning, as depicted in the following table:
 

1.4. Jar/Bundle comparison
A bundle is a jar, and can be used as such in a non OSGi framework (but only if in OSGi, it's bundle classpath was : .
A standard jar can be used as a bundle, but obviously will need at least an Export-Package declaration to be useful.

OSGi Framework

1. A modularity framework

The OSGi Framework is an execution environment in which functional modules are run. Theses modules are called bundles. Even though these two words are linked, there is an important difference between the concept they represent. Both deal with modularity, but modules are logical modularity elements, and bundles are physical logical elements.

Modules : A set of encapsulated implementation classes, offering a public API based on a subset of classes (or interfaces), and a set of dependencies on external code.
Bundles : A physical unit (jar) of modularity containing code, resources and metadata.

2. Framework architecture

The OSGi Framework contains three layers (plus one for security, not exposed here).

 Here they are :

The module layer manages packaging and code export/import.
The lifecycle layer manages bundles lifecycle, and provides bundles an execution context.
The services layer allows interaction and communication between bundles.

Saturday, April 2, 2011

Establishing a link between oracle databases


This article show how to establish a link between two Oracle databases. Here we'll establish such a link between two 'virtual' databases, called phoenix and zeus.

1. SSH Tunnel

The first step is to create a ssh tunnel between the two machines, hosting the databases. To perform that, we create a tunnel, redirecting phoenix 1522 port to zeus 1521 port (Oracle listening port).

phoenix% ssh oracle@zeus -L 1522:zeus:1521
From now, all connections on port 1522 of phoenix are redirected to zeus, on port 1521.

2. Virtual SID

Second step now, inform Oracle on phoenix of a new SID it can connect to. Of course this SID will be a redirection on zeus. For this, we need to get some informations about the target database, on zeus. Let's open the tnsnames.ora file, located usually in the folder $ORACLE_HOME/network/admin, on zeus machine. Now find the informations about your target database (here i want to connect to REMOTE_DB) :

...
REMOTE_DB =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = zeus)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = remote_db_DGMGRL)

    )
  )
...

We find the remote_db sid definition, so let's write down the associated service name : remote_db_DGMGRL.
We are now able to create a SID on phoenix to connect to this database. So let's open phoenix's tnsnames.ora  (same location, $ORACLE_HOME/network/admin), and add this :

REMOTE_DB =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = phoenix)(PORT = 1522))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = remote_db_DGMGRL)

    )
  )

We just created an alias towards host phoenix on port 1522. But remember, we established a SSH tunnel redirecting every connection on phoenix:1522 to zeus:1521. So actually our sid will connect to zeus machine, on port 1521. Smells good :).  The service name we specified is remote_db_DGMGRL, and it's the service name on zeus associated to our target database. Smells even better :D.
Save and test with a simple tns ping :

phoenix% tnsping remote_db
TNS Ping Utility for Linux: Version 10.2.0.5.0 - Production on 24-NOV-2010 12:48:58


Copyright (c) 1997,  2010, Oracle.  All rights reserved.

Used parameter files:


Used TNSNAMES adapter to resolve the alias
Attempting to contact (DESCRIPTION = (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = phoenix)(PORT = 1522))) (CONNECT_DATA = (SERVICE_NAME = remote_db_DGMGRL)))

OK (40 msec)

Connexion time of 40 ms shows this is not a localhost connection.
What is tested here is not the service connection, this will come later. Try to specify a wrong service name, you'll still be ok with tnsping. We just test connexion to the host/port we gave to our new sid.
If you change the sid, the hostname, the port or if you stop the ssh tunnel, tnsping will fail.
Now, on with the show, let's create our database link.

3. Database link

On phoenix, let's connect to the database, with sql*plus.
The following command creates the link :

SQL> create public database link my_far_away_db connect to db_schema identified by db_password using 'remote_db';

Database link created.
Let's check our data now :


SQL> select count(*) from my_table@my_far_away_db;
COUNT(*)
----------
     30266
To compare, assuming phoenix hosts the same database as zeus with different data (for instance a development database on phoenix, and a production database on zeus), you can check the difference without using the link :

SQL> select count(*) from db_schema.my_table;
COUNT(*)
----------
     27137
Voilà !

4. Link deletion

To delete a database link, its a one liner :

SQL> drop public database link my_far_away_db;

Database link dropped.
If you don't need this link anymore, you can remove the sid definition in tnsnames.ora, and stop your ssh tunnel.

Oracle table data comparison through time

Another flashback tips. This one allows you to compare data from your tables through time. Keep in mind the flashback setting of your oracle instance defines how far back in time you can go.

The key here is to perform an inner join between the table and its flashback counterpart. Here's how :

select current.value, past.valuefrom my_table current

inner join my_table as of timestamp sysdate-1 past
on current.id=past.id
where current.blah > 0

This query compares table my_table between its current state and its state one day ago (sysdate-1). Of course, one can use as of timestamp to_timestamp(....) to set a more specific time. Inner join is performed on the id field (if there's one :) ), and i added a filter (the where part), but this is for the show. The core of the functionality is the inner join w/ as of timestamp part.  

Finding blocked sessions

Ever had an oracle command blocked by a hanging session? Here's how to find them...and kill them if you want to.


select 
  c.owner,
  c.object_name,
  c.object_type,
  b.sid,
  b.serial#, 
  b.status,
  b.osuser,
  b.machine 
from 
  v$locked_object a,
  v$session b,
  dba_objects c 
where 
  b.sid = a.session_id 
  and a.object_id = c.object_id;

This query will display blocked session, along with their owner, and other useful info.

If you wish to kill one of the session displayed, grab the session sid and serial, and execute the following :

alter system kill session sid,serial;

Done.

Show table/schema size difference in time

Here's how to display the size evolution of a table or schema between its current state, and its state some time before. The distance you can go back in time depends of course on the flashback setting of your oracle instance.

When executed, those SQL commands will ask you for a date (past time) and a schema name. For the table version, it will output the list of tables of the given schema. DBA rights are required (access to dba_segments table).

List of table sizes for a schema, compared between now and the given past date :

select ds.tablespace_name,
   ds.owner,
   ds.segment_name table_name,   
   trunc(sum(ods.bytes)/(1024*1024)) previous_size_Mo,
   trunc(sum(ds.bytes)/(1024*1024)) current_size_Mo
from dba_segments ds
inner join dba_segments as of timestamp to_timestamp('&date','YYYY-MM-DD HH:MI:SS') ods
 on ods.owner=ds.owner
 and ods.segment_type=ds.segment_type
 and ods.segment_name=ds.segment_name
where ds.segment_type='TABLE'
and ds.owner=uper('&owner')
group by ds.tablespace_name, ds.owner, ds.segment_name
having sum(ds.bytes)/(1024*1024) > 5
order by current_size_Mo desc;



Schema size difference for the given owner between now and the given past date :

select ds.tablespace_name,
    sum(os.bytes)/1024/1024 AS previous_total_size_Mo,
     sum(ds.bytes)/1024/1024 AS current_total_size_Mo
from dba_segments ds
inner join dba_segments as of timestamp to_timestamp('&date','YYYY-MM-DD HH:MI:SS') os
 on os.owner=ds.owner
 and os.segment_type=ds.segment_type
 and os.segment_name=ds.Segment_name
where ds.owner = upper('&owner')
group by ds.tablespace_name