第2章
この章では、CM APIを使用したCM (コンテンツ管理)サブシステムのインフラストラクチャの設定方法および管理方法について説明します。この章は、次の節から構成されています。
注記: この章では、独自のCMアプリケーションを作成できるexteNd DirectorAPIについて説明します。exteNd Directorでは、exteNd Directorアプリケーションのすべてのコンテンツを作成、維持、管理、および保護するために使用できるCMS管理コンソールも提供されます。
CM APIを使用すると、ビジネスプロセスに適したシステムを作成できます。ポートレットを作成することによって、次のような機能が含まれる完全なインタフェースを作成できます。
CM APIによって、ドキュメントリポジトリへの完全なプログラム的アクセスが提供されます。
EbiContentMgmtDelegateインタフェースのメソッドでは、CMサブシステムにあるほとんどのオブジェクトへのアクセスが提供されます。
この章にあるすべての例では、ポートレットの任意の場所で次のコードを使用し、コンテンツマネージャ委任への参照を取得する必要があります。
EbiContentMgmtDelegate defaultCmgr =
com.sssw.cm.client.EboFactory.getDefaultContentMgmtDelegate();
if (cmgr != null)
... // do content-related processing
else
System.out.println("Failed to get Content Manager");
委任の使用 「委任」は、(コンテンツマネージャオブジェクトなどの)主なexteNd Directorマネージャオブジェクトの抽象化層を提供するオブジェクトです。委任を使用すると、exteNd Directorサービスへのローカルおよびリモートアクセスなどをコーディングする必要がなくなります。
最良実施の観点から、exteNd Directorマネージャオブジェクトに直接アクセスするのではなく、常に委任を使用することをお勧めします。
最も単純な場合では、リポジトリのオブジェクトを操作するための基本的な手順は次のとおりです。
EbiContentMgmtDelegateのupdateメソッドを使用して、変更したオブジェクトをリポジトリに戻すか、またはオブジェクト自体のupdateメソッドが使用可能な場合はそれを使用します。
オブジェクトによっては、さらに複雑な場合もあります。この章の残りの部分では、コード例を使用して、これらのオブジェクトの多くを操作する方法を説明します。
CMサブシステムでドキュメントを作成する前に、ドキュメントを整理する条件が含まれるコンテンツインフラストラクチャを設定する必要があります。インフラストラクチャには、次のようなフィールド、ドキュメントタイプ、レイアウトスタイル、フォルダ、およびカテゴリが含まれます。
すべてのドキュメントには、タイトル、作者、要約、発行されたバージョンなどのメタデータの基本的なセットがあります。また、カスタムのメタデータフィールドを定義して、各ドキュメントタイプについてアプリケーション固有のデータを保存します。フィールドは、すべてのドキュメントに値がある任意のデータに適しています。たとえば、ムービーレビューには、監督、出演者、公開日、および評価が含まれます。本には、著者、出版者、出版日、およびページ数が含まれます。旅行先のレビューには、国、料金カテゴリ、および質評価が含まれます。
フィールドは、ドキュメントを検索する場合にも便利です。フィールドのセットによって、各ドキュメントタイプの内容について、関連する検索可能な情報が識別されます。ドキュメントのコンテンツテキストの検索とは異なり、フィールドは、データベースルックアップを通じてすばやく検索できます。
たとえば、MovieReviewのドキュメントタイプには、次のような複数のフィールドを作成できます。
注記: この例では、ジャンルおよびランタイムには、複数の値を設定できます。
データタイプ EbiDocFieldで、フィールドに使用される複数のデータタイプが定義されます。使用可能なタイプの分類は、次の表のとおりです。
|
データのタイプ |
EbiDocFieldで定義された使用可能なデータタイプ |
|---|---|
|
文字データ |
FT_CHAR、FT_STRING |
|
数字 |
FT_BIGDECIMAL FT_DOUBLE、FT_FLOATFT_INT、FT_LONG、FT_SHORT |
|
ブール値 |
FT_BOOLEAN |
|
日付と時刻 |
FT_DATE、FT_TIME、FT_TIMESTAMP |
|
バイナリ |
FT_BYTE、FT_BYTEARRAY |
フィールドのメタデータ ドキュメントに関するメタデータをフィールドに保存できることは、すでに説明しました。さらに、フィールド自体に関するデータを保存することもできます。この「拡張メタデータ」を使用すると、適切な値のリスト、フォームで使用するプロンプト、フィールド用のイメージ、またはアプリケーションに適した他の情報を保存できます。このデータはバイトアレイです。
フィールドおよびドキュメントタイプ ドキュメントタイプを作成する場合、ドキュメントタイプによって使用されるフィールドのセットを指定します。フィールドは、複数のドキュメントタイプで使用できます。
フィールドおよび値 特定のドキュメントタイプの各ドキュメントで、関連付けられたすべてのフィールドには、EbiDocExtnMetaInfoオブジェクトを通じて指定された値が少なくとも1つ必要です。この値にはnullを使用できます。フィールド値は、EbiDocExtnMetaオブジェクトを通じて、セットとしてドキュメントに割り当てます。EbiDocExtnMetaには、ドキュメントタイプに関連付けられた各フィールドのEbiDocExtnMetaInfoオブジェクトが保持されます。getFieldValues()を呼び出して、フィールドの値のアレイを取得します。値がStringとして返される場合と、値にフィールドのデータタイプが含まれる場合があります。
EbiContentMgmtDelegateの次のメソッドを使用すると、フィールドを追加したり修正したりすることができます。
フィールドのドキュメントタイプとの使用の詳細については、ドキュメントタイプの管理を参照してください。
次の例では、拡張メタデータフィールドを追加するaddField()というメソッドを示します。
public void addField(EbiContentMgmtDelegate cmgr, EbiContext context)
throws EboUnrecoverableSystemException, EboSecurityException, EboItemExistenceException
{
String fieldName = "Rating";
String valueType = EbiDocField.FT_STRING;
String extnMeta = "This is a Rating field...";
cmgr.addDocumentField(
context, // Context
fieldName, // Field name
valueType, // Value data type
extnMeta.getBytes(), // Extension metadata
null); // ACL
}
次の例では、フィールドをポートレットのprocessAction(0メソッドに追加する方法を示します。ユーザがHTMLフォームに入力した名前およびデータタイプが取得され、フィールドが追加されます。成功または失敗についてのメッセージは、コンテキストオブジェクトに保存され、ポートレットコンテンツが生成されると表示されます。
public void processAction (ActionRequest request, ActionResponse response)\
String name = request.getParameter(FORM_NAME);
String datatype = request.getParameter(FORM_DATATYPE);
String valuelist = request.getParameter(FORM_LIST);
EbiContentMgmtDelegate cmgr = ...; // get content manager
try
{
cmgr.addDocumentField(context,name,datatype,valuelist,null);
context.setValue(
this.getPortletName() + KEY_STATUS,
"Field " + name + " successfully added.");
}
catch (Exception e)
{
context.setValue(
this.getPortletName() + KEY_STATUS,
"Field " + name + " not added.");
}
}
次の例では、異なる方法で結果をフィルタすることによって既存のドキュメントフィールドを取得するlistFields()というメソッドを示します。
listFields()メソッドでは、コンテンツマネージャ(EbiContentMgmtDelegate)およびコンテキストオブジェクト(EbiContext)にアクセスする必要があり、これらは引数として渡されます。コンテキストオブジェクトでは、ユーザのセキュリティ権限についての情報が提供されます。listFields()メソッドでは、コンテキストオブジェクトがgetFilteredDocumentFields()メソッドに渡され、ユーザに読み込みアクセスがあるフィールドのみが返されます。
public void listFields(EbiContentMgmtDelegate cmgr, EbiContext context)
throws EboUnrecoverableSystemException, EboSecurityException, EboItemExistenceException
{
// Get all the existing fields (note:no security checking is done here)
Collection allFields = cmgr.getDocumentFields(context);
Iterator iterAllFields = allFields.iterator();
while (iterAllFields.hasNext())
{
EbiDocField field = (EbiDocField)iterAllFields.next();
System.out.println(field + "\n\n");
}
// Get all the fields that belong to doctype \qMovieReview\q
EbiDocType docType = cmgr.getDocumentTypeByName(context,"MovieReview");
Collection docTypeFields = cmgr.getDocumentFields(context,docType.getDocTypeID());
// Get all the fields to which the user has Read access
Collection filteredFields = cmgr.getFilteredDocumentFields(context);
// Get all the Read-accessible fields that belong to doctype \qMovieReview\q
Collection filteredDtFields = cmgr.getFilteredDocumentFields(context, docType.getDocTypeID());
}
ドキュメントタイプにより、コンテンツの特定のタイプが識別されます。一般的には、類似したコンテンツが含まれるドキュメントのグループに対してドキュメントタイプを作成します。ドキュメントでは、そのコンテンツを説明するフィールドの同じセット、および(XMLコンテンツの場合は)コンテンツを表示する同じレイアウトスタイルが共有されます。
ドキュメントタイプを作成した後で、その名前および説明を修正できます。これを実行するには、EbiDocTypeオブジェクトを取得し、setDocTypeName()またはsetDescription()を呼び出してからupdateDocumentType()を呼び出して、変更されたタイプをコンテンツリポジトリに戻します。
ヒント: また、レイアウトスタイルをドキュメントタイプに関連させることもできます。詳細 については、レイアウトスタイルの管理を参照してください。
EbiContentMgmtDelegateの次のメソッドを使用すると、ドキュメントタイプを追加したり修正したりすることができます。
次のEbiContentMgmtDelegateのメソッドでは、ドキュメントタイプとフィールドの間の関連付けが管理されます。
次の例では、Movie Reviewというドキュメントタイプを追加して複数の既存のフィールドに関連付けるaddDocType()というメソッドを示します。addDocType()メソッドでは、コンテンツマネージャ(EbiContentMgmtDelegate)およびコンテキストオブジェクト(EbiContext)にアクセスする必要があり、これらは引数として渡されます。
public void addDocType(EbiContentMgmtDelegate cmgr, EbiContext context)
throws EboUnrecoverableSystemException, EboSecurityException, EboItemExistenceException
{
// Get several fields by name
EbiDocField fldDir = cmgr.getDocumentFieldByName(context, "Director");
EbiDocField fldGenre = cmgr.getDocumentFieldByName(context, "Genre");
EbiDocField fldYear = cmgr.getDocumentFieldByName(context, "Year");
EbiDocField fldCast = cmgr.getDocumentFieldByName(context, "Cast");
// Get the field IDs
String[] fieldIDs = {
fldDir.getFieldID(),
fldGenre.getFieldID(),
fldYear.getFieldID(),
fldCast.getFieldID() };
// Add the doctype
EbiDocType dt = cmgr.addDocumentType(
context, // Context
"Movie Review", // Doctype name
"Movie Review document type", // Description
fieldIDs, // Associated fields
null); // ACL for the doctype
System.out.println("The new doctype:" + dt);
}
レイアウトは、ドキュメントを表示するためのXSL仕様です。ドキュメントは、XMLである場合と、XSLで処理できる他の形式である場合があります。実際のレイアウト仕様は、ドキュメントのコンテンツとしてリポジトリに保存されています。CMサブシステムには、レイアウトドキュメント用にすでにインストールされた、Document Layoutというドキュメントタイプがあります。これを使用するか、またはレイアウトの独自のドキュメントタイプを追加できます。
実行できる操作 レイアウトドキュメントを追加した後で、チェックアウト、修正、およびチェックインすることができます。つまり、特定のレイアウトドキュメントに複数のバージョンを作成できるということです。それらのバージョンの1つを発行できます。
レイアウトスタイルの傘下で、複数のレイアウトをまとめてグループ化することができます。レイアウトスタイルのさまざまなレイアウトでは、ブラウザ、PDA、および他の表示デバイスなどの異なるクライアント(「ユーザエージェント」とも呼ばれます)に対するドキュメントの表示が処理されます。レイアウトドキュメントのユーザエージェントとの関連付けは、レイアウトドキュメント記述子によって処理されます。
レイアウトスタイルおよびドキュメントタイプ レイアウトスタイルは、ドキュメントタイプに関連付けられています。そのタイプのドキュメントを表示する場合、ポートレットのコンテキストオブジェクトで指定されるように、システムでスタイルのレイアウトドキュメント記述子が検索され、ユーザエージェント用のレイアウトドキュメント記述子が検出されます。
複数のレイアウトドキュメント記述子があるレイアウトスタイルでは、さまざまなクライアントのコンテンツを処理できます。特定のドキュメントタイプのドキュメントを表示する場合は、getDocumentLayout()を呼び出すと、システムでコンテキストオブジェクトから現在のユーザエージェントが取得され、適切なレイアウトが選択されます。
コンテンツドキュメントのXSL処理が提供されるオブジェクトのグループは、次の図のとおりです。

注記: ドキュメントタイプのレイアウトスタイルに加えて、特定のドキュメントに対して「レイアウトセット」を定義することもできます。レイアウトセットは、単一のコンテンツドキュメントに対するレイアウトドキュメントのカスタムの組み合わせです。この専用機能は、ドキュメントの特別なタイプに適しています。同じタイプのドキュメントを多数作成する場合、通常はドキュメントタイプのレイアウトスタイルを使用します。詳細については、ドキュメントのレイアウトセットの指定を参照してください。
予想されるコンテンツ用に設計されたXSLがあるレイアウトドキュメントを、1つまたは複数追加します。バージョンではコンテンツを異なる方法で配置したり、さまざまなクライアントに応じてコンテンツを調整したりすることができます。
EbiContentMgmtDelegateの次のメソッドを使用すると、レイアウトスタイルおよび関連付けられたオブジェクトを追加したり修正したりすることができます。
ユーザエージェントは、サーバに送信されるHTTPヘッダで識別されます。exteNd Directorでは、識別する文字列がコンテキストオブジェクトに保存されます。ブラウザで使用される文字列は、ブラウザのバージョンによって異なります。次に例を示します。
User Agent:Mozilla/4.0 (compatible; MSIE 4.01; Windows 98) User Agent:Mozilla/4.0 (compatible; MSIE 5.01; Windows NT) User Agent:Mozilla/4.5 (Macintosh; U; PPC) User Agent:Mozilla/4.7 [en] (WinNT; I) User Agent:Mozilla/3.0 (compatible; Opera/3.0; Windows 95/NT) 3.1
これらの文字列は、EbiLayoutDocumentDescriptorオブジェクトで使用する必要があります。
ユーザエージェントの詳細については、HTTP 1.1の仕様を参照してください。
次の例では、Movie Reviewというドキュメントタイプのレイアウトスタイルを追加するaddLayoutStyle()というメソッドを示します。addLayoutStyle()メソッドでは、コンテンツマネージャ(EbiContentMgmtDelegate)およびコンテキストオブジェクト(EbiContext)にアクセスする必要があり、これらは引数として渡されます。
public void addLayoutStyle(EbiContentMgmtDelegate cmgr, EbiContext context)
throws EboUnrecoverableSystemException, EboSecurityException, EboItemExistenceException
{
// Get the doctype for which the style is to be added
EbiDocType dtMovieReviews = cmgr.getDocumentTypeByName(context, "Movie Review");
// Add the new style
EbiDocLayoutStyle style = cmgr.addDocumentLayoutStyle(
context, // Context
dtMovieReviews.getDocTypeID(), // Doctype ID
"MovieReviewStyle-PicOnLeft", // Style name
"Layout style for movie reviews, with pic on left", // Style descr
true, // Is default style
null); // ACL for style
System.out.println("The new style:" + style);
}
次の例では、レイアウトドキュメントおよびレイアウト記述子を追加するaddLayoutDocAndDescriptor()というメソッドを示します。レイアウト記述子によって、レイアウトドキュメントが前の例のレイアウトスタイルに関連付けられます。addLayoutDocAndDescriptor()メソッドでは、コンテンツマネージャ(EbiContentMgmtDelegate)、コンテキストオブジェクト(EbiContext)、レイアウトファイル名、およびレイアウトスタイルにアクセスする必要があり、これらは引数として渡されます。
public void addLayoutDocAndDescriptor(
EbiContentMgmtDelegate cmgr, EbiContext context, String layoutFileName, String layoutStyleID)
throws
EboUnrecoverableSystemException, EboSecurityException, EboItemExistenceException, FileNotFoundException, IOException
{
// Read in the XSL for the layout
FileInputStream fis = new FileInputStream(layoutFileName);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] value = new byte[4096];
while (true)
{
int bytes = fis.read(value);
if (bytes < 1)
break;
baos.write(value, 0, bytes);
}
byte[] content = baos.toByteArray();
baos.close();
// Get the document layout doctype
EbiDocType dtLayout = cmgr.getDocumentTypeByName(context, "Document Layout");
// Get the Layouts folder
EbiDocFolder layoutFolder = (EbiDocFolder)cmgr.lookupDirectoryEntry(
context, "MyApp/Layouts", EbiDocFolder.EL_DOC_FOLDER);
// Add the layout document
EbiAddDocumentParams params = cmgr.createAddDocumentParams();
params.setName("ReviewLayout-POL");
params.setDocTypeID(dtLayout.getDocTypeID());
params.setFolderID(layoutFolder.getID());
params.setAuthor("JSmith");
params.setTitle("ReviewLayout-POL");
params.setSubtitle("This is the layout with picture on left");
params.setMimeType("text/xsl");
params.setContent(content);
params.setComment("Initial revision.");
// params.setAcl(...); specify an ACL, otherwise inherit ACL of parent folder
EbiDocument layoutDoc = cmgr.addDocument(context, params);
System.out.println("New layout doc:" + layoutDoc);
// Publish the new layout document
cmgr.publishDocumentContentVersion(context, layoutDoc.getID(), 1, true, true);
// Figure out what user agent this layout is intended for
String userAgent = "User Agent:Mozilla/4.0 (compatible; MSIE 5.01; Windows NT)";
// Associate the new layout document with the specified layout style
EbiLayoutDocDescriptor ldd = cmgr.addLayoutDocumentDescriptor(
context, // Context
layoutStyleID, // Layout style ID
layoutDoc.getID(), // Layout document ID
userAgent); // User agent
}
この例では、ドキュメントタイプのデフォルトのスタイルを取得して、デフォルトではなくなるように変更するchangeLayoutStyle() というメソッドを示します。changeLayoutStyle()メソッドでは、コンテンツマネージャ(EbiContentMgmtDelegate)およびコンテキストオブジェクト(EbiContext)にアクセスする必要があり、これらは引数として渡されます。
public void changeLayoutStyle(EbiContentMgmtDelegate cmgr, EbiContext context)
throws EboUnrecoverableSystemException, EboSecurityException, EboItemExistenceException
{
EbiDocType dtMovieReview = cmgr.getDocumentTypeByName(context, "MovieReview");
EbiDocLayoutStyle style = cmgr.getDefaultDocumentLayoutStyle(context, dtMovieReview.getDocTypeID());
style.setDefault(false);
cmgr.updateDocumentLayoutStyle(context, style);
}
ドキュメントを整理するには、フォルダおよびカテゴリを使用します。ドキュメントは1つのフォルダに属し、複数のカテゴリに属することができます。通常は、プロジェクトのすべてのドキュメント、またはアクセス制限があるドキュメントなど、フォルダを使用して管理上の目的でドキュメントをグループ化します。カテゴリを使用すると、エンドユーザの表示用に、通常内容別にドキュメントを整理できます。
システムには、Root Folderというルートフォルダ、およびRoot Categoryというルートカテゴリがすでに作成されています。コンテンツマネージャでは、Root Folderに対してEbiDocFolderオブジェクトを取得するgetRootFolder()メソッド、およびRoot Categoryに対してEbiDocCategoryオブジェクトを取得するgetRootCategory()メソッドが提供されます。
フォルダおよびカテゴリのデフォルトディレクトリタイプは、EbiDirectory.DIR_TYPE_DEFAULTです。ルートタイプはルートフォルダに適用され、システムタイプはルートカテゴリに適用されます。また、独自のフォルダタイプを定義することもできます。詳細については、『API参照』の「EbiDirectory」を参照してください。
次のEbiContentMgmtDelegateのメソッドを使用すると、フォルダおよびカテゴリを管理できます。
次の例では、新しいカテゴリの作成に必要な情報を取得してから、指定したペアレントのサブカテゴリとして新しいカテゴリを追加するaddCategory()というメソッドを示します。addCategory()メソッドでは、コンテンツマネージャ(EbiContentMgmtDelegate)およびコンテキストオブジェクト(EbiContext)にアクセスする必要があり、これらは引数として渡されます。
public void addCategory(EbiContentMgmtDelegate cmgr, EbiContext context)
throws EboUnrecoverableSystemException, EboSecurityException, EboItemExistenceException
{
// Locate the parent category
EbiDocCategory categParent = (EbiDocCategory)cmgr.lookupDirectoryEntry(
context, "MyApp/Shopping", EbiDocCategory.EL_DOC_CATEGORY);
EbiDocCategory categChild = cmgr.addCategory(
context, // Context
categParent, // Parent category
"Clothing", // Tew category name
EbiDirectory.DIR_TYPE_DEFAULT, // type of the new category
"This is the clothing-related category", // Description
null); // ACL for the new category
System.out.println("New category added:" + categChild);
ディレクトリの階層が確立されたら、ディレクトリのコンテンツのリストを取得して、個別エントリのプロパティを検査できます。
この節では、ディレクトリの階層を移動するメソッドおよびクラスの使用方法について説明します。カテゴリとフォルダの両方で、スーパインタフェースEbiDirectoryにあるディレクトリ操作の機能が実装されます。また、フォルダ、カテゴリ、ドキュメントでは、EbiDirectoryEntryも実装され、ディレクトリのコンテンツに関する情報を取得するメソッドが共有されます。
メソッド 次のメソッドは、カテゴリおよびフォルダを移動する際に便利です。
EbiContentMgmtDelegateのgetRootCategory()およびgetRootFolder()では、ディレクトリの構造のトップが取得されます。
EbiContentMgmtDelegateのgetDirectoryList()およびgetFilteredDirectoryList()では、EbiDirectoryEntryオブジェクトのコレクションが返されます。サブディレクトリまたはドキュメント、あるいはその両方をリストに含ませるかどうかを指定できます。
EbiDirectoryEntryのisDirectory()では、エントリがディレクトリであるかドキュメントであるかがレポートされます。
EbiContentMgmtDelegateのlookupDirectoryEntry()では、階層内のペアレントオブジェクトの名前から作成されたパスに基づいて、カテゴリ、フォルダ、またはドキュメントのEbiDirectoryEntryオブジェクトが取得されます。
EbiContentMgmtDelegateのgetEntry()では、指定されたディレクトリのエントリが名前で取得されます。
例 次の例では、ルートカテゴリから始まって、ネストされたカテゴリのXML DOMツリーが作成されます。ルートカテゴリは、カテゴリ内のカテゴリ要素であり、ルートのサブカテゴリおよびさらにネストされたレベルもカテゴリ要素です。各カテゴリの名前およびIDは属性です。
コードによってカテゴリのコンテナ要素が作成され、作成するツリーのルートカテゴリが取得されます。その後、addNode()が呼び出されて、サブカテゴリが検索および追加されます。変数domはDOMオブジェクトで、rootはDOMのルート要素です。
Element categories = dom.createElement("Categories");
root.appendChild(categories);
EbiDocCategory category = cmgr.getRootCategory(context);
if (category == null)
System.out.println("root category is null");
else
{
Element rootCategory = dom.createElement("category");
categories.appendChild(rootCategory);
rootCategory.setAttribute("id", category.getID());
rootCategory.setAttribute("name", category.getName());
addNode(rootCategory, category, dom, context,
cmgr, "category");
}
addNode()メソッドでは、特定のカテゴリのサブカテゴリが取得され、チャイルド要素として追加されます。追加レベルのネストされたサブカテゴリが存在する場合は、このメソッドが再帰的に呼び出され、追加されます。
public void addNode(org.w3c.dom.Element element,
EbiDirectoryEntry directoryEntry, org.w3c.dom.Document document,
EbiContext context, EbiContentMgmtDelegate cmgr, String elementName)
{
try
{
Collection collection = cmgr.getFilteredDirectoryList(
context, (EbiDirectory) directoryEntry, true, false);
Enumeration list = Collections.enumeration(collection);
if (list != null)
{
Element child;
while (list.hasMoreElements())
{
EbiDirectoryEntry subdirEntry =
(EbiDirectoryEntry) list.nextElement();
child = document.createElement(elementName);
child.setAttribute("id", subdirEntry.getID());
child.setAttribute("name", subdirEntry.getName());
element.appendChild(child);
addNode(child, subdirEntry, document,
context, cmgr, elementName);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
Copyright © 2004 Novell, Inc. All rights reserved. Copyright © 1997, 1998, 1999, 2000, 2001, 2002, 2003 SilverStream Software, LLC. All rights reserved. more ...