Skip Headers

Agile Product Lifecycle Management SDK Developer Guide - Using APIs Release 9.3.3 E39307-01 |
| Contents

Previous | Next |

# 2 Getting Started with Agile API

This section provides an overview of the functionality provided by the Agile API. Topics covered include:

- Types of Agile API classes and interfaces

- Loading Agile API classes

- How the Agile API is thread-safe

- Packaging your Agile API applications

- Finding the sample programs

## 2.1 Types of Agile API Classes and Interfaces

The Agile API contains several different classes and interfaces in the AgileAPI.jar library. These files are further classified into the following groups according to functions that they support:

- Aggregate interfaces - These interfaces aggregate the applicable functional interfaces for a particular object type. For example, the IItem interface extends IDataObject, IRevisioned, IManufacturingSiteSelectable, IAttachmentContainer, IHistoryManager, and IReferenced. Most SDK functionalities fall within these interfaces. The Agile API's underlying implementation classes, which are not exposed, implement these interfaces.

- Functional Unit Interfaces - These interfaces hold units of functionality that are extended to other interfaces. For example, IAttachmentContainer provides a convenient way to access the attachments table for any object. Other interfaces in this group such as IChange and IItem extend the IAttachmentContainer interface. IRoutable is another class that serves as a functional unit; it provides methods for any object that you can route to another Agile PLM user; IChange, IPackage, and ITransferOrder all extend IRoutable.

- Metadata interfaces - This group of classes defines the metadata (and meta-metadata) for the Agile Application Server. Metadata is simply data that describes other data. The metadata interfaces include classes such as IAgileClass, INode, IRoutableDesc, ITableDesc, and IWorkflow.

- Factory classes - AgileSessionFactory is a factory class that is used to create a session (IAgileSession) and access transaction management. IAgileSession is also a factory object allowing you to instantiate other objects. Many Agile API objects, in turn, are factory objects for tables or other referenced objects. Tables, in turn, are factories for rows.

- Exception classes - There's only one Exception class, APIException.

- Constants - These classes contain IDs for attributes, tables, classes, and so on. All classes containing only constants have class names that end with "Constants," for example, ChangeConstants, ItemConstants, UserConstants, and so on.

## 2.2 Network Class Loading

The Agile API has two main software components:

- Client-side library, AgileAPI.jar

- Server-side implementation classes

The server-side implementation classes are installed automatically with every instance of the Agile Application Server.

The Agile API Client-side library is composed almost entirely of interfaces; it's essentially a class loader. When you run an Agile API program, it connects to the Agile Application Server and automatically downloads whatever implementation classes it needs. For example, if your program uses methods of IItem, it downloads an implementation of IItem at run time.

Figure 2-1 Agile API architecture

Network class loading provides many benefits, including the ability to update client implementation classes by automatically downloading them from a server. Any Agile API classes that are downloaded from the server are automatically cached to a local disk. When an Agile API program needs to load a particular class, it retrieves it from the cache rather than downloading it again from the network. The cache results in faster loading of classes and reduced network load.

If the network class loader detects that its cache is stale, that is, its classes are older than the classes on the server, it invalidates the cache and reloads the necessary classes from the server. This allows you to update Agile SDK Clients to use the latest implementation classes without redeploying applications throughout the enterprise.

## 2.3 Single-Threaded versus Multi-Threaded Applications

The Agile API has been certified thread-compatible. It can be used for both Single-Threaded and Multi-Threaded application development. You can safely use Agile API calls concurrently, by surrounding each method invocation (or sequence of method invocations) with external synchronization.

## 2.4 Packaging an Agile API Program

After you develop a program that makes calls to the Agile API, you'll need to package its files so that you or other users can install it. Many development environments include tools for packaging and deploying applications.

You can also choose to package your program manually. If you choose to do this, you'll need to know the dependencies your project has. Again, many development environments include tools for generating dependency files. A dependency file lists the runtime components that must be distributed with your program's project files.

## 2.5 Distributing Agile API Files

You can freely distribute any Java applications or applets that you create that make calls to the Agile API. You can include the Agile API library, AgileAPI.jar, when you package your application's files.

Your development environment may require you to distribute other class files or libraries with your program. Check the documentation for your development environment to see which runtime files you must distribute with your program. Consult the applicable license agreement of the manufacturer for each of the files you plan to distribute to determine whether you are authorized to distribute the files with your application.

## 2.6 Sample Programs

The Agile SDK provides several sample programs that demonstrate how to use its APIs. These sample programs are in the api, dx, px, and wsx folders. You can find them in the SDK_samples (ZIP file). To access this file, see the Note in Client-Side Components on page 2.

Each sample program has its own Readme.txt file. Be sure to review the Readme.txt document before trying to run a sample program.

## 2.7 Starting an Agile API Program

When you create a program using the Agile API, follow this general approach for structuring your program:

1. At the top of each class file, add an import statement to import Agile API classes:

   import com.agile.api.*;

2. Get an instance of the Agile Application Server.

3. Create an Agile session.

4. Complete one or more business processes. This is where most of your program code goes.

5. Close the Agile session.

### 2.7.1 Setting the Class Path for the Agile API Library

When Java looks for a class referenced in your source, it checks the directories specified in the CLASSPATH variable. To create Agile API programs, you must include AgileAPI.jar in the class path.

If you are using a Java development environment, you usually can modify the class path for each project. If you don't let your development environment know where the Agile API library is located, it is not able to build the application.

### 2.7.2 Importing Agile API Classes

The only Java package your program has access to automatically is java.lang. To refer to Agile API classes, you should import the com.agile.api package at the beginning of each class file:

    import com.agile.api.*;
    //Create the params variable to hold login parameters
    com.agile.api.IItem source = (com.agile.api.IItem)m_session.getObject(com.agile.api.IItem.OBJECT_TYPE, "1000-02");

Rather than importing the com.agile.api package, you can also refer to Agile API classes by their full package name, for example:

As you can see, if you don't import the com.agile.api package, it's cumbersome to type the full package name whenever you refer to one of its classes. Also, when you don't import the com.agile.api package, or reference the Agile API classes by full package name, the Java compiler will return an error when you try to build your program.

### 2.7.3 Creating a Session and Logging In

Note Use the JVM parameter called disable.agile.sessionID.generation=true to create a session when the login user is a LDAP user. This is applicable only when Agile runs on Weblogic server. If the Client is a standalone SDK Client, add the JVM option java -Ddisable.agile.sessionID.generation=true pk.sample. Alternatively you can set the parameter in the code as follows: System.setProperty("disable.agile.sessionID.generation", "true"); HashMap params = new HashMap(); params.put(AgileSessionFactory.USERNAME, USERNAME); params.put(AgileSessionFactory.PASSWORD, PASSWORD); AgileSessionFactory factory = AgileSessionFactory.getInstance(URL); IagileSession session = factory.createSession(params);

To start an Agile API program, you must complete the following two tasks:

1. Get an instance of the Agile Application Server.

   Use the AgileSessionFactory.getInstance() method to get an instance of the Agile server. You must specify a connection URL for the server. The URL you specify depends on whether you connect directly to the Agile server or through a proxy Web server.

   · To connect directly to the Agile server, type this URL: `http://appserver:port/virtualPath`

   · To connect to the Agile server through a proxy Web server, type this URL: protocol://webserver:port/virtualPath

   where

   - appserver is the name of the Agile server computer.

   - webserver is the name of the Web server computer.

   - virtualPath is the virtual path for your Agile PLM server. The default value is Agile. The virtual path is specified when the Agile PLM system is installed. For more information, refer to the Agile PLM Installation Guide.

   - protocol is either HTTP or HTTPS.

   - port is the port number used for the specified protocol. The port is needed only if a nonstandard port number is being used. Otherwise, you can omit it.

2. Create a session for the Agile PLM server instance.

Use the AgileSessionFactory.createSession() method to create a session. For the params parameter of createSession(), specify a Map object containing the login parameters (username and password).

The following example shows how an Agile API program creates a session and logs into the Agile PLM server.

Example: Creating a session and logging in

    private IAgileSession login(String username, String password) throws APIException {
      HashMap params = new HashMap();
    //Put username and password values into params

params.put(AgileSessionFactory.USERNAME, username);

params.put(AgileSessionFactory.PASSWORD, password);

    //Get an Agile server instance. ("agileserver" is the name of the Agile proxy server,
    and "virtualPath" is the name of the virtual path used for the Agile system.)

AgileSessionFactory instance =

    AgileSessionFactory.getInstance
    //Create the params variable to hold login parameters
    //Create the Agile PLM session and log in
         return instance.createSession(params);
    }

(">");http://<agileserver>/<virtualPath

Your Oracle Agile PLM agreement determines the maximum number of concurrent open sessions to the Agile Application Server per user account. If you exceed this maximum number, the server prevents you from logging in. Therefore, it is important to use the IAgileSession.close() method to properly log out and close a session when your program is finished running. If the Agile PLM system is hosted on Oracle Application Servers, you are limited to only one session per thread.

### 2.7.4 Creating a Session by Accessing a Password Protected URL

To provide additional security for users accessing Agile PLM across a firewall, the proxy server may have a password-protected URL. If so, the normal method of obtaining a server instance and then creating a session will not work. Instead, you must use the AgileSessionFactory.createSessionEx() method to specify the username, password, and URL parameters needed to log in. The login code is simpler if you use createSessionEx() because you don't need to call the method AgileSessionFactory.getInstance() first to obtain a server instance. The createSessionEx() method obtains the server instance and creates the session in one call as shown in the following example.

Example: Creating a session by accessing a password-controlled URL

    private IAgileSession securelogin(String username, String password) throws APIException {
      HashMap params = new HashMap();
    //Put username, password, and URL values into params
         params.put(AgileSessionFactory.USERNAME, username);
         params.put(AgileSessionFactory.PASSWORD, password);
         params.put(AgileSessionFactory.URL, "http://agileserver.agilesoft.com/Agile");
    //Create the Agile PLM session and log in

return AgileSessionFactory.createSessionEx(params);

}

The createSessionEx()method also works for URLs that are not password-protected, so you can use it instead of createSession() if you prefer.

### 2.7.5 Creating a Session from an Agile Web Service

If you developed a web service using web service extensions and deployed it in the same container as Agile PLM, you can take advantage of the Agile API to access Agile PLM server functionality from within the web service. To get an Agile PLM server instance for your web service, use the AgileSessionFactory.getInstance() method, but pass a null value for the url parameter.

Once you have retrieved an AgileSessionFactory object, you can also create a session. The web service request provides user authentication, so you don't need to specify a username or password when you create an Agile API session. Therefore, make sure you specify a null value for the params parameter of AgileSessionFactory.createSession().

    AgileSessionFactory factory = AgileSessionFactory.getInstance(null);
    IAgileSession session = factory.createSession(null);

If you pass a null value for the params parameter of createSession(), the user authentication that took place when the Agile PLM server intercepted the web service request is reused for the Agile API session. You don't need to log in again. Do not attempt to close the session using IAgileSession.close(); the authorization handler will automatically close the session.

Specifying a null parameter for the createSession() method creates an IAgileSession corresponding to the session created by the authorization handler. If your web service doesn't use the authorization handler, or if you want to create a session for a different user than the one used for the authorization handler, you can still use createSession(params) to create a session. For the params parameter, specify a Map object containing the login parameters (username and password). If you don't use the authorization handler to create a session, you are responsible for closing it. Call the IAgileSession.close() method to close the session. For more information about web service extensions, see Developing Web Service Extensions.

### 2.7.6 Creating a Session in a Cluster Environment

The AgileSessionFactory.getInstance() and AgileSessionFactory.createSession()that you use to create an instance of the AgileSessionFactory, cache the Agile server properties to get the instance. Because of this caching, the getInstance() method retrieves the same instance of AgileSessionFactory anytime it is invoked.

While retrieving the same instance of AgileSessionFactory is not an issue in single server environments, it can be problematic in Agile cluster environments when the cached server is down. This is due to the following facts:

1. When AgileSessionFactory is initiated with a proxy URL, a specific server in the cluster is cached and is used to create the session.

2. When the cached server is down, AgileSessionFactory.createSession() will try to establish a connection with the server, but will fail because the server is down.

To overcome this issue, the Agile SDK exposes the following APIs to refresh the AgileSessionFactory instance for Agile cluster environments. These new APIs clear the cached server details and create a new instance of the AgileSessionFactory.

- AgileSessionFactory.refreshInstance(String url)

- AgileSessionFactory.refreshInstanceEx(Map params)

- AgileSessionFactory.refreshSessionEx(Map params)

The following examples use these APIs to create Agile sessions in cluster environments

Example: Creating a session with public static AgileSessionFactory refreshInstance(String url)

AgileSessionFactory factory = AgileSessionFactory.refreshInstance(URL);

HashMap params = new HashMap();

params.put(AgileSessionFactory.USERNAME, USERNAME);

params.put(AgileSessionFactory.PASSWORD, PASSWORD);

IAgileSession lsession = factory.createSession(params);

Example: Creating a session with public static AgileSessionFactory refreshInstanceEx (Map map)

HashMap params = new HashMap();

params.put(AgileSessionFactory.URL, URL);

params.put(AgileSessionFactory.USERNAME, USERNAME);

params.put(AgileSessionFactory.PASSWORD, PASSWORD);

AgileSessionFactory factory = AgileSessionFactory.refreshInstanceEx(params);

IAgileSession lsession = factory.createSession(params);

Example: Creating a session with public static IAgileSession refreshSessionEx(Map params)

HashMap params = new HashMap();

params.put(AgileSessionFactory.URL, URL);

params.put(AgileSessionFactory.USERNAME, USERNAME);

params.put(AgileSessionFactory.PASSWORD, PASSWORD);

IAgileSession lsession = AgileSessionFactory.refreshSessionEx(params);

## 2.8 Loading and Creating Agile PLM Objects

With every Agile API program, a basic requirement is the ability to get and create objects. The following interfaces map to objects that you can work with in the Agile API:

IChange | IManufacturer | IRequestForQuote
--- | --- | ---
ICommodity | IManufacturerPart | IServiceRequest
ICustomer | IManufacturingSite | ISpecification
IDeclaration | IPackage | ISubstance
IDesign | IPrice | ISupplier
IDiscussion | IProgram | ISupplierResponse
IFileFolder | IProject | ITransferOrder
IFolder | IQualityChangeRequest | IUser
IItem | IQuery | IUserGroup

To load and create these Agile PLM objects, you must first get an instance of the AgileSessionFactory object and then create an Agile PLM session. Then use IAgileSession.getObject() to load Agile PLM objects andIAgileSession.createObject() to create objects.

For more information about creating queries and folders, see Creating and Loading Queries on page 39 and Working with Folders.

### 2.8.1 Loading Objects

To load an Agile PLM object, use one of the IAgileSession.getObject()methods.

- IAgileObject getObject(Object objectType, Object params)

- IAgileObject getObject(int objectType, Object params)

Note If not specified by the user, objects will always load according to their base class which are derived from the subclass or class. Objects will also load correctly when the object's derived base class is correct. However, the SDK will load an object even if an invalid subclass is passed for that object when the derived base class of the invalid class and that of the object are both the same.

#### 2.8.1.1 Specifying Object Types

The two getObject() methods let you specify the objectType parameter using these values:

- An IAgileClass instance that represents one of the Agile PLM classes.

- A class ID (for example, ItemConstants.CLASS_PART corresponds to the Part class). Predefined class IDs are available in the various *Constants files provided with the Agile API.

- An OBJECT_TYPE constant, such asIItem.OBJECT_TYPE or IChange.OBJECT_TYPE

- A class name (for example, "Part"). However, Oracle does not recommend using class names to instantiate objects because the class names can be modified and are not guaranteed to be unique.

Note: When you use the getObject() method to load an object, you can specify abstract or concrete Agile PLM classes. For more information, see Concrete and Abstract Classes.

#### 2.8.1.2 Specifying Object Parameters

The params parameter for the getObject() method can be a Map or String. If you specify a Map object for the params parameter, it must contain attributes (either attribute IDs or IAttribute objects) and their corresponding values. The Map must contain all identification related information. For example, when you load an IManufacturerPart, both the Manufacturer Name and Manufacturer Part Number must be specified.

If the Map object you specify for the params parameter contains additional attributes other than the identifying information, those attributes are ignored. The server uses only identifying information to retrieve an object. For a complete list of attributes used to uniquely identify Agile PLM objects, see "Identifying Attributes for Agile PLM Classes" in SDK Developer Guide - Developing PLM Extensions.

This example shows how to load part 1000-02 using a Map parameter that specifies the attribute (ItemConstants.ATT_TITLE_BLOCK_NUMBER) and a value.

Example: Loading a part using a Map

    try {
    Map params = new HashMap();
     params.put(ItemConstants.ATT_TITLE_BLOCK_NUMBER,   "1000-02");
         IItem item = (IItem)m_session.getObject(ItemConstants.CLASS_PART, params);
    } catch (APIException ex) {
      System.out.println(ex);
    }

If the object you're loading has a single attribute that serves as a unique identifier, you can enter the String value for that attribute as the params parameter. For example, the unique identifier for a part is a part number. Therefore, you can enter the part number as the parameter to load the object.

Note: Not all objects have one attribute that serves as a unique identifier. For example, a manufacturer part is identified by both its manufacturer name and manufacturer part number. Therefore, to load a manufacturer part you must specify values for at least those two attributes.

This example shows how to load part 1000-02 by specifying a unique String identifier.

Example: Loading a part using a String

    try {
    IItem item = (IItem)m_session.getObject(ItemConstants.CLASS_PART, "1000-02");

} catch (APIException ex) {

    System.out.println(ex);
    }

#### 2.8.1.3 Loading Different Types of Objects

The following example shows how to load different types of Agile PLM objects.

    try {
    //Load a change
         IChange change = (IChange)m_session.getObject(IChange.OBJECT_TYPE, "C00002");
         System.out.println("Change : " + change.getName());
    //Load a commodity
         ICommodity comm = (ICommodity)m_session.getObject(ICommodity.OBJECT_TYPE, "Res");
         System.out.println("Commodity : " + comm.getName());
    //Load a customer

    ICustomer cust = (ICustomer)m_session.getObject(ICustomer.OBJECT_TYPE, "CUST00006");

    System.out.println("Customer : " + cust.getName());

    //Load a declaration
    params.clear();

    IDeclaration dec = (IDeclaration)m_session.getObject(IDeclaration.OBJECT_TYPE, "MD00001");
    System.out.println("Declaration : " + dec.getName());

    //Load a discussion

    IDiscussion discussion = (IDiscussion)m_session.getObject(IDiscussion.OBJECT_TYPE, "D00002");
    System.out.println("Discussion : " + discussion.getName());

    //Load a file folder
    IFileFolder ff = (IFileFolder)m_session.getObject(IFileFolder.OBJECT_TYPE, "FOLDER00133");
    System.out.println("File Folder : " + ff.getName());

    //Load a folder
    IFolder folder = (IFolder)m_session.getObject(IFolder.OBJECT_TYPE, "/Personal Searches/MyTemporaryQueries");System.out.println("Folder : " + folder.getName());

    //Load an item
    IItem item = (IItem)m_session.getObject(IItem.OBJECT_TYPE, "1000-02");System.out.println("Item : " + item.getName());

    //Load a manufacturer
    Map params = new HashMap();params.put(ManufacturerConstants.ATT_GENERAL_INFO_NAME, "World Enterprises");IManufacturer mfr = (IManufacturer)m_session.getObject(IManufacturer.OBJECT_TYPE, params);
    System.out.println("Manufacturer : " + mfr.getName());

    //Load a manufacturer part

params.put(ManufacturerPartConstants.ATT_GENERAL_INFO_MANUFACTURER_NAME, "World Enterprises");

params.put(ManufacturerPartConstants.ATT_GENERAL_INFO_MANUFACTURER_PART_NUMBER, "WE10023-45");

IManufacturerPart mfrPart =

(IManufacturerPart)m_session.getObject(IManufacturerPart.OBJECT_TYPE, params); System.out.println("ManufacturerPart : " + mfrPart.getName());

    //Load a manufacturing site
    IManufacturingSite siteHK =

(IManufacturingSite)m_session.getObject(ManufacturingSiteConstants.CLASS_SITE, "Hong Kong");

    System.out.println("ManufacturingSite : " + siteHK.getName());
    //Load a package
    IPackage pkg = (IPackage)m_session.getObject(PackageConstants.CLASS_PACKAGE, "PKG00010");
    System.out.println("Package : " + pkg.getName());
    //Load a price
    IPrice price =

(IPrice)m_session.getObject(IPrice.OBJECT_TYPE, "PRICE10008");

    System.out.println("Price : " + price.getName());
    //Load a program
    IProgram program =

(IProgram)m_session.getObject(IProgram.OBJECT_TYPE, "PGM10008");

    System.out.println("Program : " + program.getName());
    //Load a PSR
    IServiceRequest psr = (IServiceRequest)m_session.getObject(IServiceRequest.OBJECT_TYPE, "NCR01562");
    System.out.println("PSR : " + psr.getName());
    //Load a QCR
    IQualityChangeRequest qcr =

(IQualityChangeRequest)m_session.getObject(IQualityChangeRequest.OBJECT_TYPE, "CAPA02021");

    System.out.println("QCR : " + qcr.getName());
    //Load a query
    IQuery query =

(IQuery)m_session.getObject(IQuery.OBJECT_TYPE,

"/Personal Searches/Part Numbers Starting with P");

    System.out.println("Query : " + query.getName());
    //Load an RFQ
    IRequestForQuote rfq =

(IRequestForQuote)m_session.getObject(IRequestForQuote.OBJECT_TYPE, "RFQ01048");

    System.out.println("RFQ : " + rfq.getName());
    //Load an RFQ response
    params.clear();
     params.put(SupplierResponseConstants.ATT_COVERPAGE_RFQ_NUMBER, "RFQ01048");
     params.put(SupplierResponseConstants.ATT_COVERPAGE_SUPPLIER, "SUP20013");
     ISupplierResponse rfqResp =

(ISupplierResponse)m_session.getObject(ISupplierResponse.OBJECT_TYPE, params);

    System.out.println("RFQ Response : " + rfqResp.getName());
    //Load Sourcing Projects
    IProject prj =

(IProject)m_session.getObject(IProject.OBJECT_TYPE, "PRJACME_110");

    System.out.println("Project : " + prj.getName());
    //Load a specification
    ISpecification spec =

(ISpecification)m_session.getObject(ISpecification.OBJECT_TYPE,"WEEE");

    System.out.println("Specification : " + spec.getName());
    //Load a substance
    ISubstance sub =

(ISubstance)m_session.getObject(ISubstance.OBJECT_TYPE, "Cadmium");

    System.out.println("Substance : " + sub.getName());
    //Load a supplier
    ISupplier supplier =

(ISupplier)m_session.getObject(ISupplier.OBJECT_TYPE, "SUP20013");

    System.out.println("Supplier : " + supplier.getName());
    //Load a transfer order
    ITransferOrder to =

(ITransferOrder)m_session.getObject(TransferOrderConstants.CLASS_CTO,"456602");

    System.out.println("TransferOrder : " + to.getName());
    //Load a user
    params.clear();
    params.put(UserConstants.ATT_GENERAL_INFO_USER_ID, "OWELLES");
    IUser user =

(IUser)m_session.getObject(IUser.OBJECT_TYPE, params);

    System.out.println("User : " + user.getName());
    //Load a user group
    params.clear();
    params.put(UserGroupConstants.ATT_GENERAL_INFO_NAME, "Designers");
    IUserGroup group =

(IUserGroup)m_session.getObject(IUserGroup.OBJECT_TYPE, params);

    System.out.println("UserGroup : " + group.getName());
    } catch (APIException ex) {
      System.out.println(ex);

    }

### 2.8.2 Creating Objects

To create an Agile PLM object, use one of theIAgileSession.createObject() methods:

- IAgileObject createObject(Object objectType, Object params)

- IAgileObject createObject(int objectType, Object params)

Note: The SDK does not support setting the Lifecycle Phase (LCP)/Workflow status attribute of an object while you are creating that object. The reason is that the necessary settings for LCP are not available until after the object is created. The same is also applicable in the UI. For example, IChange will not get Workflow status values until a Workflow is selected. However, you can use the SDK to create objects, and then set and modify the LCP/Workflow status attribute. Also, you cannot get a list of values for this field, until the object is created, and the relevant actions are performed on the object.

The objectType and params parameters are identical to those used in the IAgileSession.getObject() methods; for more information, see Loading Objects on page 18. Except for IFolder and IQuery objects, you must specify a concrete class for the objectType parameter. For example, if you are creating a part, you can't specify ItemConstants.CLASS_PARTS_CLASS because that class is an abstract class that can't be instantiated. However, you can specify the class ID of any predefined or user-defined concrete class, such as ItemConstants.CLASS_PART.

If you are creating an object of a user-defined subclass, the objectType parameter of createObject()should be an Integer object corresponding to the subclass ID.

In addition to a Map or String type, the params parameter for IAgileSession.createObject() can also be an INode object representing an autonumber source for the particular object class. The Agile Application Server queries the autonumber source for the next number in its sequence, and that number is used as the unique identifier.

Note: You cannot specify an INode object for the params parameter for objects that don't have their autonumber sources available.

The following example shows how to create part 1000-02 using a Map parameter that specifies an attribute (ItemConstants.ATT_TITLE_BLOCK_NUMBER) and a value.

Example: Creating a part using a Map

    try {
    Map params = new HashMap();

params.put(ItemConstants.ATT_TITLE_BLOCK_NUMBER, "1000-02");

IItem item = (IItem)m_session.createObject(ItemConstants.CLASS_PART, params);

    } catch (APIException ex) {
    System.out.println(ex);
    }

The following example shows how to create part 1000-02 by specifying a unique String identifier.

Example: Creating a part using a String

    try {
    // Get the IAdmin interface for this session
    } catch (APIException ex) {
        System.out.println(ex);

IItem item = (IItem)m_session.createObject(ItemConstants.CLASS_PART, "1000-02");

}

#### 2.8.2.1 Working with Agile PLM Classes

Because classes are customized for each Agile Application Server, you should avoid hard-coding references to class names, particularly if your program is going to be used on multiple Agile Application Servers or in different locales. Instead, you can retrieve the classes for each object type at run time. Your program can then provide a user interface to allow the user to select a class from the list.

The following example shows how to retrieve the list of classes for a particular object type at run time.

Example: Getting classes

    try {
          IAdmin m_admin = session.getAdminInstance();
    // Get the Item base class
          IAgileClass itemClass =
          m_admin.getAgileClass(ItemConstants.CLASS_ITEM_BASE_CLASS);
    // Get the Item subclass names
          IAgileClass[] subclasses = itemClass.getSubclasses();
          for (int i = 0; i < subclasses.length; ++i) {
          String listSubclasses = subclasses[i].getName();
          System.out.println(listSubclasses);
      }
    } catch (APIException ex) {
        System.out.println(ex);
    }

#### 2.8.2.2 Creating Objects of User-Defined Subclasses

User-defined subclasses are classes created specifically for your Agile PLM system. Consequently, the Agile API doesn't provide predefined class ID constants for them. To specify a user-defined subclass for the objectType parameter of createObject(), pass an Integer corresponding to the class ID. To get the class ID for a user-defined class, use the IAgileClass.getId() method.

The following example shows how to create a Resistor object. In this example, Resistor is a user-defined subclass of the Parts class.

Example: Creating an object of a user-defined subclass

    try {
    //Define a variable for the Resistor subclass
      Integer classResistor = null;
    //Get the Resistor subclass ID
     IAgileClass[] classes =

m_admin.getAgileClasses(IAdmin.CONCRETE);

    for (int i = 0; i < classes.length; i++) {
    if (classes[i].getName().equals("Resistor")) {
          classResistor = (Integer)classes[i].getId();
          break;
        }
      }
    //Create a Resistor object
      if (classResistor != null) {
     IItem resistor =

(IItem)m_session.createObject(classResistor, "R10245");

    }
    } catch (APIException ex) {
        System.out.println(ex);
    }

Of course, you can also reference a user-defined subclass by name, as in the following example. However, class names are not necessarily unique. If there are two subclasses with the same name, the Agile API matches the first one found, which may not be the one you intended.

    Creating an object by referencing the subclass name //Creating an object by referencing the subclass name try { IItem resistor = (IItem)m_session.createObject("Resistor", "R10245");
    } catch (APIException ex) {
    System.out.println(ex);
    }

#### 2.8.2.3 Using AutoNumbers

An Agile PLM class can have one or more AutoNumber sources. An AutoNumber source is a predefined sequence of numbers that automatically number an object. AutoNumber sources are defined in the administrative functionality of Agile Java Client.

Note: The Manufacturers and Manufacturer Parts classes, and their user-defined subclasses, do not support automatic numbering.

You must configure your Agile Application to use AutoNumber when you create an object of a particular class. However, the Agile API does not enforce automatic numbering of objects, even when it is required for a particular class. If your environment requires this capability, you must develop the necessary routine. Thus, if you develop a GUI program that allows users to create Agile PLM objects, make sure the user interface enforces automatic numbering when it is required. For an example of how a client program enforces automatic numbering, create a few objects using Agile Web Client and note how the user interface works.

To get the next available AutoNumber in the sequence:

Use the IAutoNumber.getNextNumber(IAgileClass) method to assign the next available AutoNumber in the sequence. This method will check to ensure the number is not used by another object. It will continue this process until it finds and returns the first available AutoNumber for the specified Agile subclass. This method will throw an exception if it fails to get the next available AutoNumber. The IAutoNumber.getNextNumber() method will not check and skip if the number is already used by another object.

The following example shows how to create a part using the next AutoNumber.

Example: Getting the next available AutoNumber

    private void createPart(String partNumber) throw APIException {
    IAdmin admin;
      IAgileClass cls;
      IItem part;
      IAutoNumber[] numSources;
      String nextAvailableAutoNumber;

    //Get the Admin instance
      admin = session.getAdminInstance();
    //Get the Part class
      cls = admin.getAgileClass(ItemConstants.CLASS_PART);
    //Check if AutoNumber is required
      if (isAutoNumberRequired(cls)) {
    // Get AutoNumber sources for the Part class
        numSources = cls.getAutoNumberSources();
    // Get the next available AutoNumber using the first autonumber source
        nextAvailableAutoNumber = numSources[0].getNextNumber(cls);
      // Create the part using the available AutoNumber
      part =

(IItem)session.createObject(ItemConstants.CLASS_PART, nextAvailableAutoNumber);

    } else {
    // Create the part using the specified number
      // (if AutoNumber is not required)

part = (IItem)session.createObject(ItemConstants.CLASS_PART, partNumber);

    }
    public boolean isAutoNumberRequired(IAgileClass cls) throws APIException {
      if (cls.isAbstract()) {
        return false;
      }
      IProperty p = ((INode)cls).getProperty(PropertyConstants.PROP_AUTONUMBER_REQUIRED);
      if (p != null) {
        IAgileList value = (IAgileList)p.getValue();
        return ((Integer)(value.getSelection()[0]).getId()).intValue() == 1;
      }
      return false;
    }

#### 2.8.2.4 Setting the Required Fields

A class can be defined with several required attributes. To make a particular attribute mandatory, the Agile PLM administrator sets the Visible and Required properties for the attribute to Yes. If you try to create an object in Agile Java Client or Agile Web Client without completing the required fields, the Client does not allow you to save the object until you set the values for all required fields.

Although the Agile PLM administrator can define whether an attribute is required for a class, the Agile API doesn't automatically enforce required fields when you set values. Consequently, you can use the API to create and save an object even if values aren't set for all required fields. If you want to enforce required fields in your Client program and make them behave the way they do in Agile Web and Java Clients, you have to write that code.

To check for required fields:

1. Call ITable.getAttributes() or ITableDesc.getAttributes() to get the list of attributes for a table.

2. For each attribute, call IAttribute.getProperty(PropertyConstants.PROP_REQUIRED).getValue() to get the value for the Required property.

The following example shows how to get the array of required attributes for Page One, Page Two, and Page Three for a class.

Example: Getting required attributes for a class

    /**
    * Returns true if the specified attribute is required and visible.
     */
    public boolean isRequired(IAttribute attr) throws APIException {
      boolean result = false;
      IProperty required = attr.getProperty(PropertyConstants.PROP_REQUIRED);
      if (required != null) {
        Object value = required.getValue();
        if (value != null) {
          result = value.toString().equals("Yes");
        }
      }
      IProperty visible = attr.getProperty(PropertyConstants.PROP_VISIBLE);
      if (visible != null) {
        Object value = visible.getValue();
        if (value != null) {
          result &= value.toString().equals("Yes");
        }
      }
      return result;
    }
    /**
     * Returns an array containing the required attributes for the specified class.
     */
    public IAttribute[] getRequiredAttributes(IAgileClass cls) throws APIException {
      //Create an array list for the results
      ArrayList result = new ArrayList();

      //Check if the class is abstract or concrete
      if (!cls.isAbstract()) {
        IAttribute[] attrs = null;
        //Get required attributes for Page One
        ITableDesc page1 = cls.getTableDescriptor(TableTypeConstants.TYPE_PAGE_ONE);
        if (page1 != null) {
          attrs = page1.getAttributes();
          for (int i = 0; i < attrs.length; i++) {
            IAttribute attr = attrs[i];
            if (isRequired(attr)) {
              result.add(attr);
            }
          }
        }
        //Get required attributes for Page Two
        ITableDesc page2 = cls.getTableDescriptor(TableTypeConstants.TYPE_PAGE_TWO);
        if (page2 != null) {
          attrs = page1.getAttributes();
          for (int i = 0; i < attrs.length; i++) {
            IAttribute attr = attrs[i];
            if (isRequired(attr)) {
              result.add(attr);
            }
          }
        }
        //Get required attributes for Page Three
        ITableDesc page3 = cls.getTableDescriptor(TableTypeConstants.TYPE_PAGE_THREE);
        if (page3 != null) {
          attrs = page3.getAttributes();
          for (int i = 0; i < attrs.length; i++) {
            IAttribute attr = attrs[i];
            if (isRequired(attr)) {
              result.add(attr);
            }
          }
        }
      }
      return (IAttribute[])result.toArray(new IAttribute[0]);
    }

Note Primary key fields that are used to create an object are required regardless of the setting for the Required property. For example, for items the [Title Block.Number] field must be specified to create a new item regardless whether the field is required.

#### 2.8.2.5 Creating Different Types of Objects

The following example shows several different ways to create various types of Agile PLM objects. To simplify the code, AutoNumbers are not used.

Example: Creating different types of objects

    try {
    //Create a Map object to store parameters

Map params = new HashMap();

    //Create a change
    IChange eco =

(IChange)m_session.createObject(ChangeConstants.CLASS_ECO, "C00002");

System.out.println("Change : " + eco.getName());

    //Create a commodity
    ICommodity comm =

(ICommodity)m_session.createObject(CommodityConstants.CLASS_COMMODITY,"RES");

    System.out.println("Commodity : " + comm.getName());
    //Create a customer
    params.clear();
    params.put(CustomerConstants.ATT_GENERAL_INFO_CUSTOMER_NUMBER, "CUST00006");
    params.put(CustomerConstants.ATT_GENERAL_INFO_CUSTOMER_NAME, "Western Widgets");
    ICustomer customer =

(ICustomer)m_session.createObject(CustomerConstants.CLASS_CUSTOMER, params);

    System.out.println("Customer : " + customer.getName());
    //Create a declaration
    params.clear();
    ISupplier supplier =

(ISupplier)m_session.getObject(ISupplier.OBJECT_TYPE, "SUP20013");

    params.put(DeclarationConstants.ATT_COVER_PAGE_NAME, "MD00001");
    params.put(DeclarationConstants.ATT_COVER_PAGE_SUPPLIER, supplier);
    IDeclaration dec = (IDeclaration)

m_session.createObject(DeclarationConstants.CLASS_SUBSTANCE_DECLARATION, params);

    System.out.println("Declaration : " + dec.getName());
    //Create a discussion
    params.clear();
    params.put(DiscussionConstants.ATT_COVER_PAGE_NUMBER, "D000201");
    params.put(DiscussionConstants.ATT_COVER_PAGE_SUBJECT, "Packaging issues");
    IDiscussion discussion =

(IDiscussion)m_session.createObject(DiscussionConstants.CLASS_DISCUSSION, params);

System.out.println("Discussion : " + discussion.getName());

    //Create a file folder
    IFileFolder ff =

(IFileFolder)m_session.createObject(FileFolderConstants.CLASS_FILE_FOLDER, "FOLDER00133");

    System.out.println("File Folder : " + ff.getName());
    //Create a folder
    params.clear();
    IFolder parentFolder =

(IFolder)m_session.getObject(IFolder.OBJECT_TYPE, "/Personal Searches");

    params.put(FolderConstants.ATT_FOLDER_NAME, "MyTemporaryQueries"); params.put(FolderConstants.ATT_PARENT_FOLDER, parentFolder);
    IFolder folder = (IFolder)m_session.createObject(IFolder.OBJECT_TYPE, params);
    System.out.println("Folder : " + folder.getName());
    //Create an item
    IItem part =

(IItem)m_session.createObject(ItemConstants.CLASS_PART, "1000-02");

    System.out.println("Item : " + part.getName());
    //Create a manufacturer
    params.put(ManufacturerConstants.ATT_GENERAL_INFO_NAME, "World Enterprises");
    IManufacturer mfr =

(IManufacturer)m_session.createObject(ManufacturerConstants.CLASS_MANUFACTURER, params);

    System.out.println("Manufacturer : " + mfr.getName());
    //Create a manufacturer part
    params.clear();
    params.put(ManufacturerPartConstants.ATT_GENERAL_INFO_MANUFACTURER_NAME, "World Enterprises");
    params.put(ManufacturerPartConstants.ATT_GENERAL_INFO_MANUFACTURER_PART_NUMBER, "WE10023-45");
    IManufacturerPart mfrPart =

(IManufacturerPart)m_session.createObject

    (ManufacturerPartConstants.CLASS_MANUFACTURER_PART, params);
    System.out.println("ManufacturerPart : " + mfrPart.getName());
    //Create a manufacturing site
    IManufacturingSite siteHK =

(IManufacturingSite)m_session.createObject(ManufacturingSiteConstants.CLASS_SITE, "Hong Kong");

    System.out.println("ManufacturingSite : " + siteHK.getName());
    //Create a package
    IPackage pkg =

(IPackage)m_session.createObject(PackageConstants.CLASS_PACKAGE, "PKG00010");

    System.out.println("Package : " + pkg.getName());
    //Create a price
    params.clear();
    params.put(PriceConstants.ATT_GENERAL_INFORMATION_NUMBER, "PRICE10008");
    params.put(PriceConstants.ATT_GENERAL_INFORMATION_CUSTOMER, "CUST00006");
    params.put(PriceConstants.ATT_GENERAL_INFORMATION_ITEM_NUMBER, "1000-02");
    params.put(PriceConstants.ATT_GENERAL_INFORMATION_ITEM_REV, "B");
    params.put(PriceConstants.ATT_GENERAL_INFORMATION_PROGRAM, "PROGRAM0023");
    params.put(PriceConstants.ATT_GENERAL_INFORMATION_MANUFACTURING_SITE, "San Jose");
    params.put(PriceConstants.ATT_GENERAL_INFORMATION_SUPPLIER, "SUP20013");
    IPrice price =

(IPrice)m_session.createObject(PriceConstants.CLASS_PUBLISHED_PRICE, params);

    System.out.println("Price : " + price.getName());
    //Create a program
    DateFormat df =

new SimpleDateFormat("MM/dd/yy");

    IAttribute attr =
    //Create a QCR
    IAgileList list = attr.getAvailableValues();
    list.setSelection(new Object[] {"Fixed"});
    params.clear();
    params.put(ProgramConstants.ATT_GENERAL_INFO_NAME, "Wingspan Program");
    params.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_START_DATE, df.parse("06/01/05"));
    params.put(ProgramConstants.ATT_GENERAL_INFO_SCHEDULE_END_DATE, df.parse("06/30/05"));
    params.put(ProgramConstants.ATT_GENERAL_INFO_DURATION_TYPE, list);
    IProgram program = (IProgram)m_session.createObject(ProgramConstants.CLASS_PROGRAM, params);
    System.out.println("Program : " + program.getName());
    //Create a PSR
    IServiceRequest psr =

m_admin.getAgileClass(ProgramConstants.CLASS_PROGRAM).getAttribute(ProgramConstants.ATT_GENERAL_INFO_DURATION_TYPE);

(IServiceRequest)m_session.createObject(ServiceRequestConstants.CLASS_NCR, "NCR01562");

    System.out.println("PSR : " + psr.getName());
    IQualityChangeRequest qcr = (IQualityChangeRequest)m_session.createObject(
        QualityChangeRequestConstants.CLASS_CAPA, "CAPA02021");

System.out.println("QCR : " + qcr.getName());

    //Create a query
    params.clear();
    IFolder parent =

(IFolder)m_session.getObject(IFolder.OBJECT_TYPE, "/Personal Searches");

    String condition =
    //Create a specification
    params.put(QueryConstants.ATT_CRITERIA_CLASS, ItemConstants.CLASS_ITEM_BASE_CLASS);
    params.put(QueryConstants.ATT_CRITERIA_STRING, condition);
    params.put(QueryConstants.ATT_PARENT_FOLDER, parent);
    params.put(QueryConstants.ATT_QUERY_NAME, "Part Numbers Starting with P");
    IQuery query =

"[Title Block.Number] starts with 'P'";

(IQuery)m_session.createObject(IQuery.OBJECT_TYPE, params);

    System.out.println("Query : " + query.getName());
    ISpecification spec = (ISpecification)

m_session.createObject(SpecificationConstants.CLASS_SPECIFICATION, "WEEE");

    System.out.println("Specification : " + spec.getName());
    //Create a substance
    ISubstance sub =

(ISubstance)m_session.createObject(SubstanceConstants.CLASS_SUBSTANCE, "Cadmium");

    System.out.println("Substance : " + spec.getName());
    //Create a transfer order
    ITransferOrder to =

(ITransferOrder)m_session.createObject(TransferOrderConstants.CLASS_CTO, "456602");

    System.out.println("TransferOrder : " + to.getName());
    //Create a user
    params.clear();
    params.put(UserConstants.ATT_GENERAL_INFO_USER_ID, "OWELLES");
    params.put(UserConstants.ATT_LOGIN_PASSWORD, "agile");
    IUser user =

(IUser)m_session.createObject(UserConstants.CLASS_USER, params);

    System.out.println("User : " + user.getName());
    //Create a user group
    params.clear();
    params.put(UserGroupConstants.ATT_GENERAL_INFO_NAME, "Designers");
    IUserGroup group =

(IUserGroup)m_session.createObject(UserGroupConstants.CLASS_USER_GROUP, params);

    System.out.println("UserGroup : " + group.getName());
    } catch (APIException ex) {
        System.out.println(ex);
    }

Note You cannot use the Agile API to create a SupplierResponse.

### 2.8.3 Checking the State of Agile PLM Objects

The IStateful interface supports Agile objects that have either Agile Workflow status or Agile lifecycle phases. Objects that support this interface are Item and routable objects.

Routable objects are:

- IChange

- IDeclaration

- IFileFolder

- IPackage

- IProgram

- IQualityChangeRequest

- IServiceRequest

- ITransferOrder

The following example returns an array that shows all states of the object, or null when they are not defined.

Example: Getting the array that defines the different states of an object

    public interface IStateful {
    public IStatus[] getStates()
    throws APIException;
    }

The following example returns the current state of the object, or null if it is not defined.

Example: Getting the current state of the object

    public interface IStateful {
    public IStatus getStatus()
    throws APIException;

}

### 2.8.4 Propagating Values to Related Objects

Several objects in Agile PLM have related objects. For example, problem reports and nonconformance reports have a Related PSR table. On the Related PSR table, you can specify that a Workflow event should trigger a particular result in a related object, such as another problem report or noncomformance report. The triggered result does not occur instantaneously. In fact, there may be a noticeable delay-perhaps several seconds-in the time it takes Agile PLM to propagate values to related objects.

### 2.8.5 Saving an Object to a New Object

The Agile API lets you save an existing object as a new object. For example, in addition to a Save button, a dialog box in your program may have a Save As button, which saves the data to a new object. When you use the IDataObject.saveAs() method, you must specify the subclass that you are using to save the object and the object number. If the subclass supports it, you can use an AutoNumber.

This example shows how to save the current object to a new object using the next AutoNumber for the specified subclass.

Example: Saving an object as a new object

    private void saveAsObject(IDataObject obj, IAgileClass sub) {
    String nextNum;
      try {
        // Get the next autonumber for the sublass
        IAutoNumber[] numSources = sub.getAutoNumberSources();
        nextNum = numSources[0].getNextNumber();
        // Save the object
        obj.saveAs(sub, nextNum);
      } catch (APIException ex) {
        System.out.println(ex);
      }
    }

### 2.8.6 Sharing an Object

The IShareable interface is implemented by every Agile PLM business object that the Agile API exposes. Therefore, every business object can be shared. Sharing lets you grant one or more of your roles to another Agile PLM user or user group for a specific object. The roles you can assign when you share an object include your assigned or permanent roles and any roles assigned to you from membership in a user group.

Users that have been shared an object can perform actions permitted by the roles for that object only. They don't acquire the roles in a global fashion.

The IShareable interface has only two methods, getUsersAndRoles() and setUsersAndRoles(). The getUsersAndRoles() method returns a Map object. Each user in the Map has an associated array of roles. The setUsersAndRoles() method has one parameter, a Map object, which, like the Map returned by getUsersAndRoles(), maps each user to an array of roles. Each user can be assigned a different selection of roles.

Example: Sharing an object

    private void getDataForSharing() throws Exception {
    //Get item
      IItem item = (IItem)m_session.getObject(ItemConstants.CLASS_ITEM_BASE_CLASS, "P10011");
      //Get users
      IUser user1 = (IUser)m_session.getObject(UserConstants.CLASS_USER, "albertl");
      IUser user2 = (IUser)m_session.getObject(UserConstants.CLASS_USER, "peterl");
      IUser[] users = new IUser[]{user1, user2};
      //Get roles
      INode nodeRoles = (INode)m_session.getAdminInstance().getNode(NodeConstants.NODE_ROLES);
      IRole role1 = (IRole)nodeRoles.getChildNode("Component Engineer");
      IRole role2 = (IRole)nodeRoles.getChildNode("Incorporator");
      IRole[] roles = new IRole[]{role1, role2};
      //Share the item
      shareItem(item, users, roles);
    }
    private void shareItem(IItem item, IUser[] users, IRole[] roles) throws Exception {
      Map map = new HashMap();
      for (int i = 0; i < users.length; i++) {
        map.put(users[i], roles);
      }
      IShareable shareObj = (IShareable)item;
      shareObj.setUsersAndRoles(map);
    }

Note: Each user and user group has a Share table that lists objects that have been shared and which roles have been granted for those objects.

### 2.8.7 Deleting and Undeleting Objects

The Agile API, like Agile Web Client, lets you delete and undelete objects. To delete and undelete an object, you must have Delete and Undelete privileges, respectively, for the particular object type.

The Agile API supports "soft" and "hard" deletes. The first time you delete an object, it is "soft-deleted." Though it is marked "Deleted" in the database, it is not permanently removed. You can still retrieve a soft-deleted object; for example, you could use the IAgileSession.getObject() method to get a deleted object. When you run a query, soft-deleted objects are not included in the query results. However, Agile provides predefined queries (such as the Deleted Items query in the Change Analyst Searches folder) that let you find deleted objects.

To remove an object permanently, you delete it a second time, which is a "hard" delete. Once you hard-delete an object, you cannot restore it using the IDataObject.undelete() method.

Not all Agile PLM objects can be deleted. For example, the following objects cannot be deleted. If you attempt to delete one of these objects, the delete() method throws an exception.

- An item with a pending change

- An item with a revision history

- An item with a canceled change

- An item with an AML

- A released change

- A manufacturer part currently used on the Manufacturers tab of another object

- A manufacturer with one or more manufacturer parts

If you try to delete an Item that is used on the BOM tab of another item, the Agile PLM server throws an exception whose ID is ExceptionConstants.APDM_DELETECOMPINUSE_WARNING. The following example shows how to disable this warning and delete the item.

Example: Deleting an Item

    private void deleteItem(IDataObject obj) {
    try {
        // Delete the Item
        obj.delete();
      } catch (APIException ex) {
        // Check for "Item is Used" warning
        if (ex.getErrorCode() == ExceptionConstants.APDM_DELETECOMPINUSE_WARNING) {
          int i = JOptionPane.showConfirmDialog(null, "This Item is used by another Item. " +
          "Would you still like to delete it?", "Item is Used Warning", JOptionPane.YES_NO_OPTION);
        }
        if (i == 0) {
          try {
            // Disable "Item is Used" warning
            m_session.disableWarning(ExceptionConstants.APDM_DELETECOMPINUSE_WARNING);
            // Delete the object
            obj.delete();
            // Enable "Item is Used" warning
            m_session.enableWarning(ExceptionConstants.APDM_DELETECOMPINUSE_WARNING);
          } catch (APIException exc) {
            System.out.println(exc);
          }
        } else {
          System.out.println(ex);
        }
      }
    }

To restore an object that has been soft-deleted, use the IDataObject.undelete() method. Once again, to undelete an object, the user must have Undelete privileges for that object type. However, soft-deleted changes that have items on the Affected Items tab cannot be restored, regardless of the user's privileges. The following example shows how to undelete an object that has been deleted.

Example: Undeleting an object

    private void undeleteObject(Object obj) throws APIException {
    // Make sure the object is deleted before undeleting it
      if (obj.isDeleted()) {
          // Restore the object
          obj.undelete();
      }
    }

### 2.8.8 Closing a Session

Each Agile PLM user can open up to three concurrent sessions. Therefore, each session that you open using the Agile API should be closed properly. If you fail to close a session properly, you may not be able to log in with a new session until one of the concurrent sessions time out.

Example: Closing a session

    public void disconnect(IAgileSession m_session) {
    m_session.close();
    }