Skip to main content

Using a Connection's Application ID

Knut Stolze, Information Integration Development, IBM Germany
Photo: Knut Stolze

Knut Stolze started his work with DB2 when he joined IBM as a visiting scientist at the Silicon Valley Lab where he worked on the DB2 Image Extender. He moved on to the DB2 Spatial Extender Version 8 and was responsible for several enhancements to improve the usability, the performance, and the standard-conformance of the Extender for more than two years.

Currently, he works as a teaching assistant at the University of Jena, Germany, and continues his work for IBM in the area of federated databases. He can be reached through the newsgroups comp.databases.ibm-db2 and ibm.software.db2.udb.spatial or at stolze@de.ibm.com.

Paul Yip, DB2 Partner Enablement, IBM Toronto Lab
Paul Yip is a consultant from the IBM Toronto Lab's Partner Enablement Team. Much of his work revolves around helping business partners migrate from other relational database management systems (RDBMS) platforms to DB2 with a specialization in database application development. He has written many articles for DB2 Developer domain and recently co-authored the book, DB2 SQL Procedural Language for Linux, UNIX™, and Windows® . Paul can be reached at ypaul@ca.ibm.com.

Summary:  All is revealed! It's easier than you think to get and use an application ID for such purposes as testing connections and for auditing in connection pooling architectures. Sample binaries are included.

Date:  13 Feb 2003
Level:  Introductory
Activity:  951 views

Introduction

Customers often ask us if there is any notion of a session or application identifier in IBM® DB2® Universal Database™ (UDB), and if so, how to access it. Applications can use this information to determine the state of connections, or for auditing purposes when users modify sensitive data.

Well, there is good news and bad news. The good news is that there is indeed an application identifier with every connection to a database. Starting with DB2 UDB Version 8.2 (DB2 V8.2) you also have the SQL function APPLICATION_ID to retrieve that application ID programmatically. The bad news is that DB2 releases prior to DB2 V8.2 do not provide a built-in SQL function to retrieve this easily -- you'll need to write one yourself. The function is not difficult to write or build, and Part 1 of this article will show you how to do this quickly. Part 2 of this article discusses some sample scenarios on how to use application ID.


Part 1. Building a function to get a connection's application ID

Please note that you can entirely skip ahead to part 2 if you are using DB2 V8.2 (or later) because the function described here is provided as a built-in function in the SYSFUN schema in this version. The sample scenarios explained in the next part are equally applicable for the built-in function.

To build this function, you will need to have the DB2 Application Development Client and a C or Java™ compiler installed. (If you do not have a C compiler, see Installing the Binaries). Issue all commands locally at the server as the database instance owner (for example, db2inst1).

To begin, the information that the function retrieves from DB2 is called an application ID. The application ID that is retrieved is the same value that you see when you issue LIST APPLICATIONS from the DB2 Command Line Processor (CLP):

Listing 1. Output of LIST APPLICATIONS from the CLP

 
Auth Id  Application  Appl.  Application Id           DB       # of 
         Name         Handle                          Name    Agents 
-------- ------------ ------ ------------------------ -------- ----- 
DB2ADMIN db2bp.exe    5      *LOCAL.DB2.00BE85034416  SAMPLE   1 

In Listing 1, the fourth column indicates the application ID for the connection is *LOCAL.DB2.00BE85034416. For remote connections, *LOCAL is replaced with a hexadecimal representation of the IP address of the client machine.

The C function

The following C function (in Listing 2) will allow us to retrieve a connection's application ID.

Listing 2. A C function to retrieve a connection's application ID

 
#include <string.h> 
#include <sqludf.h> 
 
void SQL_API_FN getApplicationId( 
        SQLUDF_CHAR *applId,  SQLUDF_NULLIND *applId_ind, 
        SQLUDF_TRAIL_ARGS,  SQLUDF_DBINFO *dbinfo) 
{ 
    strncpy(applId, dbinfo->appl_id, 128); 
    *applId_ind = 0; 
} 

Building the C function

To build this function, you'll need an export file that defines the entry points for the library. The export file will be different depending on your platform:

  • For Windows®, name the file application_id.def. It is shown in Listing 3a.

    Listing 3a: The export file for Windows (application_id.def)

     
    LIBRARY application_id 
    EXPORTS 
    	getApplicationId 
    

  • For UNIX™ , the file should be called application_id.exp. It consists of the single line shown in Listing 3b.

    Listing 3b: The export file for UNIX (application_id.exp)

     
    getApplicationId

After you've created one of these two files (depending on your platform) in the same directory where you put the C source code file (application_id.c), compile and link the code. DB2 provides a build routine script (bldrtn) to simplify the process. The script is located in the sqllib/samples/c directory.

DB2 V7: For Version DB2 UDB 7.x, use the script bldudf on UNIX and bldmudf on Windows. The syntax for its invocation is slightly different from what it is in Version 8. View the files for details on how to run those build scripts.

To build the user-defined function (UDF), execute:

				
					INSTHOME
				/sqllib/samples/c/bldrtn application_id 

where INSTHOME is the path to the instance home directory. For example:

 
	c:\program files\ibm\sqllib\samples\c\bldrtn application_id 
	or 
	/home/db2inst1/sqllib/samples/c/bldrtn application_id 

This script will do two things:

  1. Compile and link the UDF into a shared library.
  2. Copy the resulting shared library (named application_id on UNIX systems and application_id.dll on Windows systems) to the sqllib/function directory

The Java function

The same functionality provided above with a C function can be implemented using the Java programming language as shown in Listing 4. The code for the function is equally short.

Listing 4. A Java function to retrieve a connection's application ID

 
import java.sql.*; 
import COM.ibm.db2.app.*; 
 
public class appl_id extends UDF 
{ 
    public void getApplicationId(String result) throws Exception 
    { 
	   try { 
		   // set the output parameter based on DBINFO 
		   set(1, getDBapplid()); 
	   } 
	   catch (Exception e) { 
		   setSQLstate("38XXX"); 
		   if (e.getMessage().length() > 0) { 
			  setSQLmessage("Exception '" + e.getMessage() + 
			               "' encountered."); 
           	   } 
		   else { 
			    setSQLmessage("Exception '" + e.toString() + 
			               "' encountered."); 
		   } 
	   } 
     } 
} 

Building the Java function

Compiling such a short Java function is easily done. You use the Java compiler javac to convert the source file to Java byte code in a class file and copy the resulting class file to the sqllib/function directory as illustrated in Listing 5. Please note that "cp" is usually the UNIX command to copy files, and you should use "copy" on Windows systems.

Listing 5. Compiling and installing the Java function

 
javac appl_id.java 
cp appl_id.class INSTHOME/sqllib/function 

Installing the binaries

To simplify this process even further, you can use the libraries prebuilt from the C code or class file generated from the Java code.

The libraries built from the C code are all named application_id (or application_id.dll on Windows systems). Depending on the platform of your database server, you should copy the library from the respective directory from the downloadable zip file to the sqllib/function directory of the DB2 instance. Please note that different binaries exist for 32-bit and 64-bit instances.

The class file constructed from the Java code is named appl_id.class and it can be found in the "Java" subdirectory of the zip file.

You can find the zip file with the prebuilt libraries here.

Registering the function

The final step is to issue the CREATE FUNCTION statement to register the external function in your database. You have to use different CREATE FUNCTION statements, depending on whether you used the C code or the Java code. Listing 6a shows the statements for the UDF implemented in C, and listing 6b for the Java function. Substitute your database name for DBNAME :

Listing 6a. Registering the C function

 
db2 -td$ 
 
CONNECT TO 
					DBNAME
				  $ 
CREATE FUNCTION application_id() 
   RETURNS VARCHAR(128) 
   SPECIFIC applId  EXTERNAL NAME 'application_id!getApplicationId' 
   NOT FENCED  LANGUAGE C  PARAMETER STYLE SQL  DETERMINISTIC 
   NO SQL  NO EXTERNAL ACTION  ALLOW PARALLEL  DBINFO $ 

Listing 6b. Registering the Java function

 
db2 -td$ 
 
CONNECT TO 
					DBNAME
				  $ 
CREATE FUNCTION application_id() 
   RETURNS VARCHAR(128) 
   SPECIFIC applId  EXTERNAL NAME 'appl_id.getApplicationId' 
   NOT FENCED  LANGUAGE JAVA  PARAMETER STYLE DB2GENERAL 
 DETERMINISTIC 
   NO SQL  NO EXTERNAL ACTION  ALLOW PARALLEL  DBINFO $ 

In the above examples, we've changed the default delimiter on the command line to $ (using db2 -td$) which lets us conveniently enter multi-line commands. To indicate the end of the command, enter $ and then press the Enter key. If you execute the CREATE FUNCTION statement from the DB2 Control Center, you can change the statement terminator in the Tools Settings menu.

And while you are connected, test your function:

 
SELECT application_id() AS appid FROM SYSIBM.SYSDUMMY1 $ 
 
APPID 
---------------------------------- 
*LOCAL.DB2.00B045155621 
 
  1 record(s) selected. 

If you see the same output as in Listing 1, you're done!


Part 2. Application ID usage scenarios

In Part 1, we showed you how to build a function to retrieve a connection's application ID. Now we describe how you can use application ID to solve real problems. We present two scenarios:

Determining if a connection exists

Starting with Version 8.1, DB2 Universal Database provides a set of table functions that let you access DB2 snapshot monitor data via SQL.

The function SNAPSHOT_APPL_INFO() returns information for all currently connected applications, including their application ID. Using that information, we can determine whether a given application ID belongs to an existing connection or not. The following SELECT statement uses the snapshot function to return the application IDs for all existing connections to the database. Note that the table function returns more information, but we don't discuss that in this article. Please refer to the documentation of the function in the SQL Reference for more details.

 
SELECT appl_id 
FROM   TABLE (SNAPSHOT_APPL_INFO(CURRENT SERVER, -2)) AS t 

If two connections exist, the output might look like this:

 
APPL_ID 
-------------------------------- 
*LOCAL.stolze.0AFDD4122938 
*LOCAL.stolze.0AD7F4121810 
 
  2 record(s) selected. 

Accessing all the application-specific information from the DB2 monitor imposes some restrictions. For security reasons, only users with the SYSADM, SYSMAINT, or SYSCTRL privileges are by default allowed to access the monitor. This is too restrictive for a general way to access application IDs, in which each user might need to verify if a certain connection exists. To allow the unrestricted access to the snapshot functions, the DB2_SNAPSHOT_NOAUTH registry variable can be used. Executing the following command causes DB2 to not verify the privileges of the current user when one of the snapshot functions is accessed:

 
db2set DB2_SNAPSHOT_NOAUTH=on

Please be aware that any user can now use the SNAPSHOT_APPL_INFO (and related) functions and process its results.

Let us verify this feature in a practical example. Here is the case where DB2_SNAPSHOT_NOAUTH registry variable has not been set. USER3 is an unprivileged user.

 
$ db2set DB2_SNAPSHOT_NOAUTH 
DBI1303W Variable not set. 
$ db2 "connect to test user USER3 using somepwd" 
$ db2 "SELECT appl_id FROM TABLE ( SNAPSHOT_APPL_INFO( CURRENT 
 SERVER, -2 ) ) AS t" 
APPL_ID 
-------------------------------- 
SQL0443N  Routine "*PPL_INFO" (specific name "") has returned an 
 error 
SQLSTATE with diagnostic text "SQL1092  Reason code or token: USER3 
   , ".  SQLSTATE=3855 

As can be derived from the error message SQL1092, USER3 does not have the necessary privileges to access the monitor functions and, thus, cannot retrieve the list of the current connections and their application IDs.

Now, the instance SYSADM user turns on the less restrictive authorization requirements for the snapshot functions and restarts DB2. After that, USER3 is able to use the function SNAPSHOT_APPL_INFO without any further restrictions.

A SYSADM user performs:

 
$ db2set DB2_SNAPSHOT_NOAUTH=on 
$ db2stop force 
SQL1064N  DB2STOP processing was successful. 
$ db2start 
SQL1063N  DB2START processing was successful. 

USER3 executes the following after the DB2_SNAPSHOT_NOAUTH has been properly set:

 
$ db2 connect to test 
   Database Connection Information 
 Database server        = DB2/6000 8.1.0 
 SQL authorization ID   = USER3 
 Local database alias   = TEST 
$ db2 "SELECT appl_id FROM TABLE ( SNAPSHOT_APPL_INFO 
  (CURRENT SERVER, -2 ) ) AS t" 
APPL_ID 
-------------------------------- 
*LOCAL.stolze.095A34142226 
 
  1 record(s) selected.

Now, any user can use regular SQL to work with the results from the functions SNAPSHOT_APPL_INFO and our user-defined function application_id() to validate if a connection still exists.

Consider a scenario where your application must maintain its own locks on objects, for whatever reason. Let's implement the following logic:

  • Whenever a user connects to the database, that user acquires a shared lock on the current data. (This is an application-specific lock and not a DB2 lock.) The lock is held until the user disconnects from the database.
  • A batch process executes routinely, but must first ensure that no other shared locks (besides its own) exist on the object to be processed.

The logic is very straightforward and does not impose any difficulties, except for one situation:

What happens if a user connected to the database, acquired a shared lock and then connection abended? Perhaps the network broke down, or maybe there was a power outage, or maybe the user simply killed the client application without properly shutting it down. In such a case we would still have the shared lock stored somewhere in the database, but it would not be valid any longer, leaving garbage data.

Using the application IDs here lets us easily implement a way to check whether a given shared lock is still valid. When a user requests a shared lock upon connect, we also retrieve the related application ID using our UDF application_id(). Later on when the batch process executes, we verify that all of the application IDs that are associated with the shared locks are still valid. If not, then the invalid lock can be safely ignored because the connection does not exist any more.

The following table illustrates a schema of a table that can be used to store the shared locks in such a way that it is accessible for all users.

 
CREATE TABLE shared_locks ( 
   USER_NAME  VARCHAR(18)  NOT NULL, 
   APPL_ID    VARCHAR(128) NOT NULL, 
   OBJECT     INTEGER )

Each row in that table shows which user is currently accessing the data. When a new connection is opened, the first operation of the client application is the following SQL statement:

 
INSERT 
INTO   shared_locks(user_name, appl_id, object) 
VALUES ( USER, application_id(), ... )

When object X is to be processed by the batch process, we first check if there are any locks on it, and then ensure that each application ID for the lock is verified. The following combined SQL statement returns any remaining valid locks on object X data. All invalid locks are ignored.

 
SELECT appl_id 
FROM   shared_locks AS l, 
       TABLE (SNAPSHOT_APPL_INFO(CURRENT SERVER, -2)) AS t 
WHERE  t.appl_id = l.appl_id AND 
       l.object = objectX;

Alternatively, a DELETE statement can be used to delete all the locks that are assigned to connections that no longer exist.

 
DELETE 
FROM   shared_locks 
WHERE  appl_id NOT IN 
          ( SELECT t.appl_id 
            FROM  TABLE ( SNAPSHOT_APPL_INFO( 
                             CURRENT SERVER, -2) ) AS t )

After this delete, you know that all locks in the shared_locks table are valid, until the next connection gets dropped unexpectedly.

Creating audit trails in connection pool architectures

An easy way to enforce an audit trail of changes to data is by defining INSERT, UPDATE, and DELETE triggers on a table that keeps sensitive information. The information you'd likely include in the trail is:

  • Old values, if applicable
  • New values, if applicable
  • The user ID of the connection associated with the INSERT, UPDATE, or DELETE operation

In modern connection pool architectures, however, all applications access data using a common middle-tier user ID; the application (commonly) maintains its own information about application users in database tables. In other words, users "Sally" and "John" may be application user IDs, but behind the scenes, those users connect to the database using the user ID AppUser defined at middle tier. To clarify the discussion, let's define two terms, which are illustrated in Figure 1:

  • Application users. Users defined and maintained by the application.
  • Database users. Users defined at the application server for connecting to the database on behalf of application users.

Figure 1. Users in a 3-tier architecture
Users in a 3-tier architecture

Because connections in a pool don't truly disconnect from the database, connections using a particular database user will likely be shared by many application users over time. Therefore, the application ID alone (or the value from the DB2 special register USER) is no longer sufficient to maintain a proper audit trail.

To solve this problem, we can make use of application_id() in conjunction with the application user ID. Let's define the following table, LOGIN, to temporarily associate a connection's application ID and application user ID.

 
CREATE TABLE LOGIN ( 
    appl_id VARCHAR(128) not null, 
    user_id VARCHAR(128) not null, 
    PRIMARY KEY (appl_id, user_id)) 

The method works as follows:

  1. When the application user logs into the application, the application automatically INSERTs a row of data into the LOGIN table, which associates the application ID of the database connection (using the UDF application_id()) and the application user ID This pair of values now uniquely identifies the user connection until he/she logs out of the application. For example:
     
    INSERT INTO LOGIN VALUE ('*LOCAL.DB2.00BE85034416', 'SALLY')

    or

     
    INSERT INTO LOGIN VALUE (application_id(), 'JOHN')

  2. If the user ever performs INSERT, UPDATE, or DELETE to sensitive data, a trigger on that table activates and logs in the audit trail. The audit trail becomes a two-step process:
    1. The trigger first calls the application_id() function to get the application ID of the connection performing the SQL operation.
    2. Then, the trigger performs a lookup in the LOGIN table to match the application ID with the actual application user and writes the required audit information.
  3. When the user logs out of the application, the associated entry in the LOGIN table is deleted by the application.

The following code illustrates this method. Note that only an INSERT trigger is illustrated, but UPDATE and DELETE triggers would be similar. For more information on triggers, see the article How to Temporarily Disable Triggers in DB2 Universal Database.

 
	db2 -td$ 
 
	CONNECT TO 
					DBNAME
				 $ 
 
CREATE TABLE LOGIN ( 
    appl_id VARCHAR(128) not null, 
    user_id VARCHAR(128) not null, 
    PRIMARY KEY (appl_id, user_id)) $ 
 
CREATE TABLE t1 (c1 INT) $ 
 
CREATE TABLE audit (c1 varchar(100)) $ 
 
CREATE TRIGGER auditT1 
    AFTER INSERT ON t1 
    REFERENCING NEW AS newrow 
    FOR EACH ROW 
    MODE DB2SQL 
    BEGIN ATOMIC 
      DECLARE v_user VARCHAR(128); 
      SET v_user = ( 
           SELECT user_id FROM login L 
           WHERE L.appl_id=application_id()); 
 
      -- note: v_user will be null if application ID not found. 
 
      INSERT INTO audit VALUES ( 
          'The value: ' || char(newrow.c1) || 
          ' was inserted by ' || COALESCE(v_user, 'unknown!')); 
      END$ 

To test the trigger, you can issue:

 
-- case where insert performed as non-registered user 
insert into t1 values (0) $ 
 
-- case where insert performed by application user 
insert into login values (application_id(), 'John') $ 
insert into t1 values (1),(2),(3) $ 
select * from audit $ 

Result:

 
----------------------------------------------- 
The value: 0           was inserted by unknown! 
The value: 1           was inserted by John 
The value: 2           was inserted by John 
The value: 3           was inserted by John 


Summary

In this article, we discussed how every connection to a database has a unique application ID. We then showed you how to create and test a function called application_id() to retrieve this information easily. We also presented two common scenarios where the application ID can be used to solve real database application problems. Please note that the function described here is available as a built-in function starting with DB2 V8.2 so that you don't have to build the function yourself anymore. Prior to that version, you can follow the steps explained here to achieve the same functionality.

Top of page


Disclaimer

This article contains sample code. IBM grants you ("Licensee") a non-exclusive, royalty free, license to use this sample code. However, the sample code is provided as-is and without any warranties, whether EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. IBM AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE THAT RESULT FROM YOUR USE OF THE SOFTWARE. IN NO EVENT WILL IBM OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.



Downloads

NameSizeDownload method
application_id.zip94 KB HTTP
application_id.tgz89 KB HTTP

Information about download methods


About the authors

Photo: Knut Stolze

Knut Stolze started his work with DB2 when he joined IBM as a visiting scientist at the Silicon Valley Lab where he worked on the DB2 Image Extender. He moved on to the DB2 Spatial Extender Version 8 and was responsible for several enhancements to improve the usability, the performance, and the standard-conformance of the Extender for more than two years.

Currently, he works as a teaching assistant at the University of Jena, Germany, and continues his work for IBM in the area of federated databases. He can be reached through the newsgroups comp.databases.ibm-db2 and ibm.software.db2.udb.spatial or at stolze@de.ibm.com.

Paul Yip is a consultant from the IBM Toronto Lab's Partner Enablement Team. Much of his work revolves around helping business partners migrate from other relational database management systems (RDBMS) platforms to DB2 with a specialization in database application development. He has written many articles for DB2 Developer domain and recently co-authored the book, DB2 SQL Procedural Language for Linux, UNIX™, and Windows® . Paul can be reached at ypaul@ca.ibm.com.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Information Management
ArticleID=13715
ArticleTitle=Using a Connection's Application ID
publish-date=02132003
author1-email=
author1-email-cc=
author2-email=
author2-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers