整合以内容为基础的Portlet

类别:Java 点击:0 评论:0 推荐:
                  Context-based Portlet Integration

Download PDF

Portals have become the de facto standard for Web application delivery. In particular, enterprise portals make an important contribution to enabling enterprise knowledge management by providing users with a consolidated, personalized user interface that allows efficient access to various types of (structured and unstructured) information. Today's portal systems allow combining so-called portlets with access to different information sources and applications side by side on a single portal webpage. However, there is only little interaction between those portlets. If inter-portlet communication capabilities are offered, they require extensive individual programming. This paper presents a generic approach for communicating the user context (revealing the user's information need) among portlets, utilizing Semantic Web technologies. For example, the context of an OLAP portlet, which provides access to structured data stored in a data warehouse, can be used by a search portlet in order to automatically provide the user with related intranet articles or documents found in the organization's document management system. In order to describe the approach independently of a specific portal server product, we base our proposal on extensions to the Java Portlet Specification and WSRP. 1 Introduction

Modern software is complex and expensive, which has motivated many companies to invest in enterprise portals as a mechanism by which they can manage their information in a cohesive and structured fashion. Portals offer many advantages over other software applications. They provide a single point of access for employees, partners, and customers to various types of (structured and unstructured) information, making an important contribution to enabling enterprise knowledge management. Portals can support B2E (business-to-employee), B2B (business-to-business), or B2C (business-to-consumer) relationships. B2E portals are also called enterprise or enterprise information, enterprise knowledge portals. The goal is to provide the user with a consolidated, personalized interface to all information he needs for his daily tasks.

Portals have become the de facto standard for Web application delivery. In fact, analysts have predicted that portals will become the next generation desktop environment. Portals distinguish themselves from other software systems because they provide the ability to integrate disparate systems and leverage the functionality provided by those systems. They provide a unique opportunity to combine nascent technologies with mature, well-established software applications.

Today's portal systems allow combining so-called portlets with access to different information sources and applications side by side on a single portal webpage. More and more vendors of standard software (such as content management, reporting and business intelligence, ERP, CRM) provide portlet implementations of their user interfaces. With emerging standards like the Java Portlet Specification it can be expected that these will become more and more interoperable. However, in current portal systems there is only little interaction between those portlets. If inter-portlet communication capabilities are offered, they require extensive individual programming. As a consequence, when a user navigates within one portlet, the others usually remain unchanged, which means that each source has to be searched individually for relevant information. This paper presents an approach for communicating the user context (revealing the his information need) among portlets, utilizing Semantic Web technologies. For example, the context of an OLAP portlet, which provides access to structured data stored in a data warehouse, can be used by a search portlet in order to automatically provide the user with related intranet articles or documents found in the organization's document management system. The presented approach is generic, i.e. the portlets can be developed without taking into account with which other portlets they will need to communicate. The context communication is configured declaratively and managed by the portal platform.

The remainder of this paper is organized as follows: Section 2 gives an overview of the state-of-the-art of existing portal platforms with an emphasis on emerging standards and discusses inter-portlet communication techniques they provide. Some commercially available portal servers provide additional capabilities that are discussed in section 3 as related work. Currently, extensive custom programming is necessary to allow portlets to communicate with each other. To overcome this, section 4 develops a generic integration approach based on communicating the portlets' user context and identifies possible integration scenarios. Necessary extensions to portal platforms and APIs are sketched in section 5. We propose to use Semantic Web technologies like the Resource Description Framework (RDF) [W3C04c] and an ontology to capture the context. Section 6 discusses how this can be achieved. Finally, section 7 describes our prototypical implementation which is based on the findings in sections 4 through 6. Section 8 concludes the paper and discusses possible future work.

2 Portal Platforms and Standards

Meanwhile many standard software platforms, so called portal servers, have appeared on the market. The main features of such portal servers are [SW02]:

Personalization, i.e. it is possible to create user profiles in order to offer frequently used functions and save user specific parameters. Integration of various information sources and applications into a single web-based user interface, i.e. the system provides predefined portlets for standard data sources and a development environment to build custom portlets. Content and document management functionality Flexible search functionality Collaboration features The focus is, however, clearly on the personalization and integration features. In particular, portal servers act as a container for portlets which deliver HTML fragments that together with the portal frame and portlet decorations are combined to the portal page. Figure 1 shows a schematic view of such a portal page with four portlets. Users can personalize the system by choosing their portlets and placing them on their portal page. They can usually also adjust the portal appearance by means of so-called "skins'' (sets of colors and decorations). The content/document management, search, and collaboration features do not necessarily have to be provided by the portal server itself, they can also come in as third-party portlets.

Figure 1: Elements of a portal page [JCP03]

Nowadays, almost every vendor of application server software (Sun, IBM, Oracle, BEA, etc.) also has a portal server product in its portfolio. In the open source community the Apache Software Foundation offers the Jetspeed portal platform [http://portals.apache.org/jetspeed-1/] which we also use for our research prototype. Meanwhile, standards are appearing allowing for interoperability and the development of portlets that run on different server platforms. Thus, the following subsections give an overview of those standards.

2.1 Java Portlet Specification (JSR 168)

The Java Portlet Specification [JCP03], also know as Java Specification Request (JSR) 168, makes a first attempt to standardize the way how portlets are implemented and how they interact with a portal server. Actually, the specification distinguishes between a portlet container and the portal (resp. portal server) itself. A portlet container runs portlets and provides them with the required runtime environment. It contains portlets and manages their lifecycle. However, it is not responsible for aggregating the content produced by the portlets. This is the responsibility of the portal, which is however, out of the scope of the specification. The following is a typical sequence of events, initiated when a user requests a portal page [JCP03]:

A client (usually a web browser) makes an HTTP request to the portal. The request is received by the portal. The portal determines if the request contains an action targeted to any of the portlets on the portlet page. If there is an action targeted to a portlet, the portal requests the portlet container to invoke the portlet to process the action. The portal obtains, through the portlet container, content fragments from the portlets that can be included in the resulting portal page. The portal aggregates the output of the portlets, combines it with portlet titles, decorations, etc., and sends the rendered portal page back to the client. The above term action refers to what is also known as action events in the Model-View-Controller (MVC) paradigm [BMR+96], i.e. events triggered by user interactions. The Portlet API is based on the Java 2 Platform, Enterprise Edition (J2EE). Portlets have many similarities to Java servlets, however, the JSR 168 expert group decided to define portlets separate from the servlet specification, mainly because the notion of actions should be inherent in the API and the portlet lifecycle. As we will see in section 7, this is very important. Mixing portlet action handling and rendering poses many problems, especially when dealing with inter-portlet communication, which is also a major drawback of the current Apache Jetspeed portal that we utilize for our our prototype implementation.

Portlets are Java classes that implement the Portlet interface. The interface defines a processAction() method for action processing and a render() method that is called to retrieve the HTML fragment of the portlet. Alltogether, the life cycle of a portlet consists of four phases:

Init: At the time of portlet creation (i.e. when the portlet application is started, or delayed, when a user first accesses a portlet) a portlet's init() method is called. Process Action: In response to an action request (usually triggered through a certain query string in the portal page URL), the processAction() method is of the portlet is called, taking ActionRequest and ActionResponse objects as parameters. It is assumed that the portlet updates its state based on the information sent in the action request parameters. Render: During a render request, portlets generate content based on their current state. The portal container calls a portlet's render() method for this purpose. Here RenderRequest and RenderResponse objects are used as parameters. Destroy: When a portlet is taken out of service, e.g. when the user logs out from the portal application, the portlet's destroy() method is called. The portlet container is not required to keep a portlet loaded for any particular period of time. It might take it out of service, and recreate it at its convenience.

Even though the specification does not dictate how a portal performs the portal page rendering itself, it does strictly specify the above life cycle and the fact that the rendering is not supposed to start before all actions are processed (see figure 2). One important reason is that the specification allows portlets to change their window state (e.g. maximized) during action processing. In addition portlets may decide that the portal should send a redirect to a different URL back to the client. An HTTP redirect would no longer be possible if rendering had already begun.

Figure 2: Request handling process accoriding to the Java Portlet Specification [JCP03]

The Java Portlet Specification is quite new, so it is not yet supported by all portal systems. However, all major vendors, including the Apache Software Foundation, have announced that future releases of their portal server products will conform to the specification. As mentioned before, we use the Apache Jetspeed Portal for our prototype, which in its current versions does not conform to the specification. However, currently a new release Jetspeed 2 is under development, which builds upon of the portlet container Pluto [http://portals.apache.org/pluto/], the reference implementation for the Java Portlet Specification.

The current version of the Java Portlet Specification does not define any particular means for inter-portlet communication. However, the separation of action processing and rendering is an important requirement to allow for portlet messaging. Portlets can invoke other portlets' actions, possibly changing their state as well. If the rendering process would already have started, the target portlet of such a message might already be rendered, and the state change would not become visible before the next portal page reload.

More sophisticated inter-portlet communication capabilities are planned for future versions of the specification. As IBM is one of the main contributors in the JSR 168 expert group, the standard turns out to have many similarities to the API of the IBM WebSphere Portal [Hep03]. In section 3 we will shortly discuss some of the inter-portlet capabilities of WebSphere. Similar techniques might also appear in future versions of the Java Portlet Specification. Also, we will present our context-based integration approach as extensions to the Java Portlet API in section 5.

2.2 Web Services for Remote Portlets (WSRP)

Another yet similar standard is Web Services for Remote Portlets (WSRP) [OAS03]. While the Java Portlet Specification definines Java interfaces for implementing portlets that run (locally) in a portlet container, WSRP defines web service interfaces that allow portlets to run remotely outside the portlet container. Also, the standard pays special attention to the fact that portlets (WSRP producers) and containers (WSRP consumers) may be based on very different platforms like J2EE or Microsoft's .NET.

The WSRP interfaces are very similar to the ones of the Java Portlet Specification. The standard defines a Markup interface with a getMarkup() operation which the consumer uses to retrieve the HTML fragment of a portlet. A performBlockingInteraction() operation provides action handling which is as such decoupled from the rendering process like in the Java Portlet Specification. WSRP portlets can be stateless or stateful. If they decide to be stateful, i.e. store some kind of session state locally, they send a session ID back to the consumer. The life cycle of a WSRP portlet session is as follows:

Init: There is no explicit initialization of a portlet session. When a portlet web service receives a getMarkup() request without an existing session ID it is required to create a new session. Process Action: Before rendering the performBlockingInteraction() is called by the consumer to invoke portlet actions. As for the Java Portlet Specification, a portlet can request a window mode change or a redirection as a response to a performBlockingInteraction() request. Render: When the actions have been processed, the consumer calls getMarkup() to acquire the portlet content. Destroy: The consumer may inform the producer that it will no longer be using a set of sessions by invoking releaseSessions(), however this is not required. Like the Java Portlet Specification WSRP is not yet supported by all portal servers, however, is very likely to be introduced in future releases. The WSRP reference implementation is WSRP4J [http://ws.apache.org/wsrp4j/] by Apache and might eventually also find its way into Jetspeed.

Also for WSRP, the standard so far defines no particular inter-portlet communication means; these might be included in future versions. However, like for the Java Portlet Specification, as action processing and rendering is separated, portlets may invoke actions on other portlets when handling an action. The extensions we propose in section 5 conform well to the standard and could be incorporated into WSRP as well.

3 Related Work

The next subsections give a short overview on the inter-portlet communication capabilities of portal server software. We focus on the SAP Enterprise Portal and the IBM WebSphere Portal as these two offer the most promising approaches.

3.1 SAP Enterprise Portal

The SAP Enterprise Portal framework consists of three components: the Portal Server itself for the user interface and global services, the Unification Server for back-end integration (see below), and the Knowledge Management platform for document and collaboration management. Within the Portal Server the iView Server builds the runtime environment for the portlets, which SAP calls iViews. When a client request occurs, the iView Server collects the content from the iViews which are then combined to the portal page and transmitted to the client.

The SAP Enterprise Portal provides a Client Framework for client-side communication between iViews [SAP02]. It allows for low-level messaging based on JavaScript and (if desired) Java applets. This way iViews can send messages to each other on the client, hence not requiring HTTP request/response cycles. For this purpose SAP uses a Client Eventing mechanism and a Data Bag . The eventing is based on a publish/subscribe multicast mechanism. The Client Framework provides an elegant solution to syncronize iViews on the portal page. Although the communication is configurable (i.e. message paths do not need to be coded in the portlet code), the semantics of the messages need to be defined and interpreted by the portlets. There is no underlying semantic integration like our context-based one. However such an approach could be built on top of the Client Framework messaging mechanism.

Second, the Unification technology together with Drag&Relate, provides a drag and drop functionality to enable a user-oriented integration [SAP02]. Drag&Relate does not really provide an inter-portlet communication mechanism, but allows draging objects from a portlet onto the navigation iPanel invoking certain navigation actions. For example, you can drag a stock item number from the order information of a Siebel CRM iView onto a link that opens the corresponding mySAP PLM iView [FK02]. As Drag&Relate relies on Unification it only works for Unifier iViews (i.e. not for custom programmed Java iViews). Unifier iViews can be used to access (and combine) structured information from various data sources such as relational databases or legacy systems, provided a Unifier exists for this data source. The query processing is done by Microsoft's Distributed Query Processor (DQP) which is built into SQL Server.

The core of the Unification (and Drag&Relate) technology is an integrated metadata model, the so-called Unified Object Model (UOM) and the so-called Correlation Matrix, which maps it to specific Application Object Models (AOM). This approach is similar to our ontology-based approach presented in section 7. However, while we propose extensions to the portlet API that allow custom-programmed portlets to participate in the integration, SAP provides this technology only for special (Unifier) iViews, without, on the other hand, requiring any extra programming effort.

3.2 IBM WebSphere Portal

As we already discussed in section 2, if the action processing and rendering cycles are separated, it is possible to use action events for inter-portlet communication by having an action handler of one portlet invoke actions of others. Unlike SAP's Client Framework IBM relies on this approach for (server-side) portlet messaging [LR02].

As mentioned before, the API of the IBM WebSphere Portal is very similar to the Java Portlet Specification, however provides some additional features for portlet messaging [Hep03]. IBM provides a concept called Cooperative Portlets which builds upon portlet messaging with action events and adds advanced capabilities for managing the communication paths [RC03]. They no longer have to be explicitly coded. A Property Broker receives data and distributes it to the targets. Hence, Cooperative Portlets portlets allow dynamic binding for portlet communication. The communication targets do not need to be known when the portlets are developed, only the data (consisting of properties and values) that is transmitted and its semantics. The communication paths are defined by configuring the Property Broker. The message transmissions can be triggered by the user clicking special symbols, which is why the technology is also called Click-to-Action. When the user clicks a Click-to-Action symbol, a context menu is shown which allows him to select the desired target. Click-to-Action also supports broadcasts and multicasts using predefined target configurations, so-called Wires. The Click-to-Action symbols can be included in the JSP code of portlets by using a special tag library. For the receiving portlet, a special configurable action event is invoked.

Like SAP's Client Framework, Cooperative Portlets and Click-to-Action provide an elegant messaging mechanism. The interpretation of the messages and the back-end integration is not addressed, however, we borrow IBM's idea of special JSP tags for selective context push scenarios.

4 Context Integration

The portlet integration approach presented in this paper develops a framework for communicating the context between portlets to provide a generic integration mechanism. Usually portlets only provide their portlet content for rendering the user interface. In addition, we introduce a Context Bus, i.e. a communication platform where portlets can publish their current context (i.e. a semantic representation of what user sees). Other portlets can pick that context up and use it to also display related information. Figure 3 shows the overall architecture of our context-based portlet integration.

Figure 3: Architecture for context integration

Henrich and Morgenroth [HM02,HM03] define a unified user model which considers user, working, and interaction context for context-supported information retrieval. While the user context comprises the physical and organizational (i.e. the static) context of a user, the working context characterizes the current activity he performs, e.g. his current task in a workflow management system. Finally, the interaction context reflects the current interactions with the application systems supporting his present activities.

For our context-based portlet integration approach, we concentrate on user and, in particular, interaction context elements. The static user context can be queried together with the portlet (i.e. interaction) context in context pull scenarios (see next subsection). We do not explicitly consider a working context as we cannot assume the existence of a structured representation of the user tasks, however, a workflow management portlet could publish a description of the current user task as its portlet context. In our terminology, a user's portal context consists of his static user context together with a number portlet contexts.

The main idea is to use RDF metadata to represent the context. For example, if a user displays an OLAP report like the one shown in the OLAP portlet in figure 12 in section 7, the portlet context can be represented as the set of elements (e.g. product categories) shown on the report. Or, a portlet representing a CRM system displaying information about a certain customer can point to a customer element to represent its context. We propose the use of an enterprise ontology for this purpose. Details on capturing the portlet context by means of metadata will be covered in section 6.

4.1 Context Integration Scenarios

We have identified different context integration scenarios utilizing inter-portlet communication techniques. Section 5 will discuss how these scenarios can be implemented by extending portal platforms and APIs.

Figure 4: Morphological box of context integration scenarios

The morphological box in figure 4 shows the dimensions that distinguish the different context integration scenarios:

The first dimension is the communication paradigm. Context integration can follow a push or a pull principle. An example for a context pull is a search engine that uses the context of the other portlets to enhance the precision of the search results. In a context pull a portlet should be able to query only the context of a specific portlet, or the overall portal context (i.e. the combination of all portlet contexts). The overall portal context might also include static user context elements (e.g. based on a user's identity or location, or user-defined). To address context push scenarios we introduce a publish/subscribe mechanism, i.e. a portlet can trigger a context event that can be handled by other portlets. For context pushes, the triggering event can be explicit or implicit. Explicit context pushes require the user to explicitly invoke the event, e.g. by clicking a "find related'' button. Implicit context pushes are triggered implicitly by other (e.g. navigating) user events. An example for an implicit context push would be a topic navigation portlet that publishes the selected topic after every browsing event. Context event messages can be transmitted as unicast (with a single portlet as the destination), as multicast (with a set of target portlets), or as broadcast (to all other portlets). Finally, the context that is published with a context push can be full (i.e. cover all information that is shown within the portlet) or selective. A selective context push can only be explicit, as it requires the user to select the part of the context that should be published. An example for a selective context push would be a "find related'' button next to a customer name in a customer list (similar to IBM's Click-To-Action symbols) that triggers a CRM portlet to display related customer information. Note that not all combinations of all characteristics are possible. Detailed examples for some context integration scenarios that we implemented in our prototype will be givien in section 7. 5 Proposed Extensions to Portal Platforms and APIs

In order to describe the approach independently of a specific portal server product, we base our proposal on extensions to the interfaces of the Java Portlet Specification. In addition, we will discuss a possible adaption for WSRP.

5.1 Context-aware Portlet Development

We propose that the Portlet interface is extended by a getContext() method that, similarly to render(), is called by the portal container. While render() provides the content of the portlet as an HTML fragment, getContext() is required to provide the portlet context (i.e. its current semantic state) in RDF/XML. The corresponding request and response objects are assumed to be of type ContextRequest and ContextResponse, similar to RenderRequest and RenderResponse for render(). Section 6 will discuss capturing the portlet context in RDF in more detail. For now, let's assume that a portlet conetxt can be represented as an anonymous RDF description [W3C04c].

The portal server is required to provide some kind of context repository to store the different portlet contexts. The PortletContext interface that a portlet container provides to give portlets access to the portal runtime environment is expected to be extended by a getPortletContext() method that takes a portlet session ID as a paramater. This routine can be used for context pulls. If the portlet session ID is omitted (or null) the getPortletContext() will return the combined context of all active portlets. In addition, getUserContext() will return the static context of the user, getPortalContext() a combination of all portlet contexts and the user context.

In oder to handle context push events, the regular processAction() could be used. However, as it is intended for user-triggered (i.e. HTTP-based) requests, we instead propose an additional acceptContext() method that the portlet needs to implement. The request and response objects will be of type AcceptContextRequest and AcceptContextResponse. The request object will provide the source portlet session ID of the push event and the pushed context in RDF/XML. The response object is identical to the one for regular action processing.

These two getContext() and acceptContext() methods, together with their request and response objects, are actually all the portlet developer has to deal with for context pushes. The actual publish/subscribe mechanism is expected to be configured declaratively, in deployment descriptors or init parameters (see next subsection).

The same approach can be taken for WSRP, i.e. the Markup interface can be extended by getContext() and acceptContext() operations. Only context pull scenarios are difficult to realize in a remote portlet environment, as there is no mechanism for the portlet web service to query the consumer. If a context pull is required, one way would be to include the complete portal context (i.e. of all active portlets) in every getMarkup() and performBlockingInteraction() request.

5.2 Context Publish/Subscribe Mechanism

For the context push scenarios we propose a publish/subscribe mechanism similar to the Wire concept of IBM's Click-to-Action (see section 3.2). Explicit full context pushes are expected to be triggered by a specific control in the portlet title bar. The targets can be configured with init parameters, using either single portlet IDs (unicast), a set of portlets (multicast), or "all'' (broadcast). Also, named wires (preconfigured portlet sets) can be used, or a specific setting "select'' to enable a context menu as known from Click-to-Action, allowing the user to select the target at runtime. For implicit context pushes, a set of action events that the portlet uses for regular user interactions can be configured as context triggers, thus requiring no explicit user interaction for the context push. Besides implementing the above generic getContext() and acceptContext() no additional programming effort is required for invoking or accepting full context push events.

Selective context pushes can only be explicit. By the definition above they require the user to select the part of the context that is supposed to be published and require some extension to the portlet user interface to leverage this selection. We suggest a special tag library, again similar to IBM's approach for Click-to-Action, to include selective context push controls within the portlet JSP. For example, a findRelated tag from the inwiss namespace could be used for this purpose as shown in figure 5. The example shows the JSP code for a customer list, including find related controls after each customer name to push the customer object to a CRM portlet. We assume that the ontology includes customer objects with the URI http://www.inwiss.org/ontology#Cusomer_<CustNo> where <CustNo> represents an enteprise-wide customer number. Again, the use of an ontology to capture the portlet context is discussed in more detail in the next section.

<table>
  <tr>
    <th>Customer No</td>
    <th>Name</td>
    <th>Address</td>
    <th>City</td>
  </tr>

<%
// some Java code to iterate through customer objects
// ...
%>

  <tr>
    <td><%=customer.getCustNo()%></td>
    <td><%=customer.getName()%>
      <inwiss:findRelated target="crmportlet">
        <dc:coverage rdf:resource=
          "http://www.inwiss.org/ontology#Customer_<%=customer.getCustNo()%>"/>
      </inwiss:findRelated>
    </td>
    <td><%=customer.getAddress()%></td>
    <td><%=customer.getCity()%></td>
  </tr>

<%
// ...
%>

<table>

Figure 5: Sample JSP code with tags for selective context push controls

6 Capturing the Portlet Context

As already mentioned, we propose to represent the context by means of the Resource description Framerwork (RDF) [W3C04c]. RDF definies a standardized, XML-based from to represent metadata. Hence, it extends the only syntactically defined XML with formal semantics. RDF uses a simple triple-based model, i.e. resources are identified by a URI and have a set of properties with specific values associated to them. The values can be literals or other resources (again identified by a URI). RDF Schema [W3C04b] extends RDF with modeling constructs like XML Schema for XML.

While RDF itself only defines the base model, i.e. the fact that resources have properties with values, it is possible to more precisely specify the metadata elements to be used by means of RDF Schema. However, this has to be done individually for every application. In order to enable interoperability, especially on the Internet, it is desirable to define a set of universal metadata elements as a base vocabular. This is what the Dublin Core initiative [http://www.dublincore.org] tries to accomplish. The aim and motivation of Dublin Core is to define a standardized set of metadata elements for annotating text documents and other resources.

In its base model the Dublin Core standard [DCM03] defines fifteen elements whose semantics have been specified by an international and interdisciplinary expert group. Among them the elements Title, Creator, Subject, Description, Type, Format, Language are of particular interest. Within our prototype we use these also for knowledge and document management purposes. As Dublin Core aims at interoprability it seems natural to apply it also for describing the context in our Context Bus approach. However, there only the semantic elements Subject and Coverare are applicable. Descriptive elements like Title cannot be used to semantically capture the context.

In general the values of semantic metadata elements should not contain free text, but rather use a controlled vocabulary. For the element Subject, usually topics from a taxonomy are used. Coverage semantically describes the content of a resource. According to [DCM03] geographic locations or time periods can be used as values, in an enterprise context, however, also other possible objects can be found, e.g. products or divisions. We propose the use of an enterprise ontology as a controlled vocabulary for Coverage.

While in principle Dublin Core is independent of the metadata model, the most common form of represention is RDF. The mentioned Dublin Core elements are then identified by the namespace http://purl.org/dc/elements/1.1/, e.g. the element Subject is defined as http://purl.org/dc/elements/1.1/subject (or shortly dc:subject).

6.1 Taxonomies and Ontologies

A well established approach for organizing documents and other resources is the use of a taxonoy. A taxonomy consists of a hierarchical set of categories or topics. A resource can be linked to one or more categories, e.g. by means of the Dublin Core metadata element Subject. A category can thus be seen as something like a virtual folder that holds all resources dealing with the corresponding topic. This approach is also known from Web directories like Yahoo [http://www.yahoo.com] or DMOZ [http://www.dmoz.org].

An excerpt of a sample taxonomy is shown in figure 6. It represents a mail-order retail company that sells various consumer goods via calling centers, which is also the sample scenario of our prototype (see section 7). The first category level devides into "Sales'', "Distribution'', and "Marketing''. While the "Sales'' category is subdevided geographically, the refinement within Marketing is based on the products of the company.

Figure 6: Resource categorization with a hierarchical taxonomy

For the description of a taxonomy RDF can be used as well. This approach is also pursued by DMOZ [DMOZ RDF dumps are available at http://rdf.dmoz.org.]. The individual categories are identified by a URI and described by RDF properties. The hierarchical composition of the URIs is used to encode the hierarchy of the taxonomy. The category Audio in figure 6, for example, could be represented by the URI http://www.inwiss.org/topics/Marketing/Electronics/Audio.

Taxonomies are mostly used for browsing purposes, i.e. as a navigational structure. Therefore it is of particular importance, that a taxonomy is simple and easy to understand. In order to fully describe the semantic content of a resource (e.g. with the Dublin Core metadata element Coverage) more complex structures are required. As already mentioned, we suggest the use of an ontology for this purpose.

Ontologies are used to formally describe a part of reality, the ontology's domain. Here, the Web Ontology Language (OWL) [W3C04a], which is largely based on DAML+OIL [http://www.daml.org], has evolved as a standard formalism. OWL builds upon RDF Schema and extends it by additional constructs. An OWL ontology consists of classes and instances (also called individuals). Classes follow an inheritance hierarchy, e.g. Person with Customer and Employee as subclasses. The instances of an ontology together build the objects that play a role in the considered domain (in our case an enterprise).

Compared to a taxonomy an ontology is usually larger and more complex. While a resource will usually be linked to one or only few categories from a taxonomy, its coverage can consist of a much larger number of ontology instances. For the use by a search engine this is a good thing, as it will increase the recall of the search results. However, due to its complexity an ontology is not suited for user navigation. Hence, a dual approach, using a taxonomy together with an ontology, is desirable.

When using metadata for portlet integration, an obvious problem is the heterogeneity of the portlets and the underlying application systems. An OLAP portlet will use its OLAP data model to formulate the context, while a portlet responsible for accessing a legacy application component will rather use an underlying operational data model or an application object model. Finally, a (metadata-based) search portlet will use the document metamodel for this purpose. To solve this problem, with an OWL ontology in place, we propose to use ontological concept mapping for both the schema and instance level.

If a portlet decides not to use Dublin Core to represent the context, OWL elements for synonyms (owl:sameClassAs) and subclassing can be used to map the proprietary properties to dc:subject and dc:coverage. Also, if different applications use different URIs for the same entitity (e.g. http://www.inwiss.org/ontology#Customer_1 and http://www.siebel.com/customers/Maxwell_Aaronson for the same customer), they can be mapped in the ontology (by using owl:sameIndividualAs). The same can be achived for mapping taxonomy topics to folder names. OWL also supports much more complex mapping (i.e. inference) rules, for details refer to [W3C04a]. Figure 7 shows how a global ontology can help with mapping constructs of different metadata models.

Figure 7: Ontology for model construct mapping

6.2 Sample Context Descriptions

We will now give some examples for possible portlet contexts, based on our scenario of a mail-order company. For example, an intranet article dealing with the opening of a new calling center can be represented by the metadata shown in figure 8 a). When a user views this article in a content portlet, the portlet will publish the semantic dc:subject and dc:coverage elements as its context as shown in figure 8 b), i.e. pointing to the "Sales'' taxonomy topic and the ontology object for the Atlanta calling center.

a)

<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/">

<rdf:Description rdf:about="http://www.inwiss.org/content/CC_Atlanta.html">
  <dc:title>New Calling Center Opened in Atlanta, GA</dc:title>
  <dc:creator rdf:resource="ldap://cn=Calvin Rosie,ou=Sales,o=MyCompany"/>
  <dc:date>2000-05-01</dc:date>
  <dc:type rdf:resource="http://www.inwiss.org/schema#Article"/>
  <dc:format>text/html</dc:format>
  <dc:language>en-US</dc:language>
  <dc:description>Today a new calling center was opened in
    Atlanta, GA...</dc:description>
  <dc:subject rdf:resource="http://www.inwiss.org/topics/Sales"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#CallingCenter_1"/>
</rdf:Description>

</rdf:RDF>

b)

<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/">

<rdf:Description>
  <dc:subject rdf:resource="http://www.inwiss.org/topics/Sales"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#CallingCenter_1"/>
</rdf:Description>

</rdf:RDF>

Figure 8: Sample metadata for an intranet article and the corresponding context representation

Similarly, an OLAP report can be described by the elements shown on its rows and columns. For example, the OLAP report shown in the OLAP portlet in figure 12 shows the sales of all product categories within the four quarters of 1998. The context for the OLAP portlet can thus be represented by the RDF description in figure 9.

<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/">

<rdf:Description>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Quarter_199801"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Quarter_199802"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Quarter_199803"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Quarter_199804"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Category_1"/> <
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Category_2"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Category_3"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Category_4"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Category_5"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Category_6"/>
  <dc:coverage rdf:resource="http://www.inwiss.org/ontology#Category_7"/>
</rdf:Description>

</rdf:RDF>

Figure 9: Context representation for sample OLAP report

Finally, The above JSP code in section 5 creates selective context descriptions using dc:coverage elements to point to a certain customer.

7 Prototype

In order to evaluate our context-based integration approach we implemented INWISS, an Integrative Enterprise Knoweldge Portal. Figure 10 shows the overall architecture of the prototype. The Context Bus is implemented as an extension to the Apache Jetspeed Portal platform [http://portals.apache.org/jetspeed-1/]. At this point we provide four portlets: One is responsible for displaying content (i.e. intranet articles) and a second one provides access to a MicroStrategy 7i OLAP system [http://www.microstrategy.com]. The navigation portlet represents a taxonomy-based topic browser. Finally, a fourth portlet is responsible for metadata-based searches. For details on our fuzzy information retrieval approach for metadata see [PSP04].

We use the open source Sesame RDF Framework [http://www.openrdf.org] [BKvH02] as a repository for resource metadata, taxonomy, and ontology. The search portlet communicates with the search engine via HTTP. The search engine uses the Sesame API to retrieve candidate search results from the RDF repository. The CSAP security module [PMDP04] deals with authentication and access control issues.

Figure 10: Architecture of the INWISS prototype

7.1 Apache Jetspeed Portal

Jetspeed is a Java-based open source implementation of a portal server, providing support for XML/XSL and templating and content publication frameworks such as Java Server Pages (JSP), Velocity, and Cocoon. Besides regular browsers Jetspeed also supports WAP devices. The goal is to make Jetspeed a tool for both portal developers as well as user interface designers. Currently the focus is, however, on providing developers with a set of tools that facilitates building the base for the portal and syndicating content.

The current release of Apache Jetspeed is 1.5, which is also the version used for our prototype. Currently, Jetspeed 2, the next-generation enterprise portal at Apache, is under development. Jetspeed 2 offers several architectural enhancements and improvements over Jetspeed 1. First, Jetspeed 2 is conformant to the Java Portlet Specification (see section 2.1) and will provide a standard mechanism for the deployment of portlets. Second, Jetspeed 2 has matured to a more scalable multi-threaded component architecture and is decoupled from several legacy open source projects. In this paper we focus on Jetspeed 1, as so far no stable release of Jetspeed 2 is available. So, when we use the name Jetspeed we talk about Jetspeed 1.

Jetspeed builds upon the Apache Turbine web application framework and uses a servlet container such as Apache Tomcat. User and personalization data is stored in a relational database (accssed through the Apache Torque persistence layer) and XML registry files. Jetspeed focusses on personalization and integration features, its main goal ist to provide a runtime environment for portlets. Jetspeed also provides a set of predefined portlets for syndicating HTML and XML data, as well as RSS feeds [Win03]. However, there is no built-in content/document management or collaboration functionality. In our prototype we use our own content management service based on RDF metadata [W3C04c].

7.1.1 Portlet Development

Jetspeed portlets are Java classes that implement the Jetspeed Portlet interface which among others defines the method getContent() which is called by the portal to retrieve the HTML fragment for a portlet. A portlet can store its state in a Turbine RunData object. Jetspeed provides helper functions for getting and setting values in the PortletSessionState class. The life cycle of a portlet consists of three phases [Apa04]:

Init: At the time of portlet creation, a portlet's init() method is called. It is only called once, during the portlet creation phase. The lifetime of a portlet is controlled by how long a portlet stays in the cache. When a portlet is removed from the cache, the portlet is effectively removed from memory (left to the garbage collector) and is no longer accessible. If a portlet is removed from the cache, and then requested again, the portlet will have its init() method called again, since a new instance is created of the portlet. Render: The render phase is called per request. Every time a portlet's content is requested, the getContent() method is called. Destroy: The portlet object is destroyed when a portlet is removed from the cache. However, unfortunately the portlet is no notified when this occurs. When the portal page is requested by the user's browser, Jetspeed iterates through all visible portlets from the top left-most down to the bottom right-most one, calls their getContent() method and sends the results back.

Like we already discussed in section 2, the usual way of developing portlets is to use the MVC paradigm [BMR+96], separating the handling of action events from the rendering. This paradigm is also used by Jetspeed, however, unfortunately, does not manifest itself directly in the Jetspeed portlet interface. Instead, actions must be handled in separate action classes together with a template engine like JSP or Apache Velocity. This also leads to restrictions for inter-portlet communication (see below).

For a JSP portlet you define a JSP template (as the MVC view) and an action class (as the MVC controller) that handles action events triggered by the user. The action class has to be derived from JspPortletAction which defines an abstract buildNormalContext() method which has to be implemented by the concrete action class to set portlet session state to be used by the template. Both, the action class and the template can use further business classes whose instances can be stored in the portlet session state. These are then considered as the MVC model. Action events are triggered by a user event (i.e. a link or a form submit with certain parameters in the query string). For every portal page reload the framework checks the query string for the parameters js_peid (identifying the target portlet) and eventSubmit_do<Event> (where <Event> is the name of a user event), and calls the corresponding do<Event>() method of the corresponding action class. Consequently, the life cycle of a JSP portlet consists of the following four phases [Apa04]:

Init: There is no explicit initialization for JSP portlets. The action class has to check the current portlet session state within the event handlers or buildNormalContext() for the necessity of an initialization. Process Action: The corresponding do<Event>() method of the JSP portlet action class is called. Render: First buildNormalContext() of the portlet action class is called, then the JSP template is rendered. Destroy: Like for regular portlets, no notification occurs when the portlet is destroyed.

Note that, as the MVC paradigm is not inherent in the Jetspeed portlet API, but is only used by certain MVC portlets, the action processing and rendering phases always co-occur portlet by portlet. On a portal level, there is no separate action handling phase before the rendering starts. In other words, when the action event of a certain portlet is handled, other portlets have already been rendered. The process of rendering the portal page is depicted in figure 11.

Figure 11: Portal page rendering process in Jetspeed

7.1.2 Inter-portlet Communication

Jetspeed does not provide any explicit techniques for inter-portlet communication. However, the portlets share a single Turbine RunData object. Usually, every portlet only accesses its own session state through the mentioned PortletSessionState class. However, it is also possible to read and modify the state of other portlets. The second technical means that can be used to have portlets talk to each other is the action events. Theoretically, it would be possible to communicate something to another portlet by writing information into its session state in the RunData object and then call an event handler in the target portlet's action class to trigger an action event. This is what you would do for a portlet container that conforms to Java Portlet Specification.

Unfortunately the missing separate event handling phase in Jetspeed defeats this approach. As stated above, portlets are rendered directly after their action events have been processed. If a portlet action handler decides to communicate with (and as a consequence change the state of) another portlet which has already been rendered, the state change will not be considered before the next rendering cycle. A second problem is posed by the caching mechnism of Jetspeed. Portlets are usually only rerendered, if there the query string contains an action event to be handled by them, thus direct calls to another portlet's action class will not invalidate the cache. Action events thus need to be invoked by page reloads.

Usually, a portlet will only trigger its own events by building a query string with a js_peid parameter pointing to its own portlet ID. However, if a portlet knows the ID of another target portlet it can also trigger events of another portlet. By omitting the js_peid parameter, a broadcast can be realized, as all portlets will handle the triggered event. Encoding the inter-portlet communication in query strings within the template is, however, not desirable. A portlet template should usually trigger its own events and the action handler should then decide whether it wants to trigger another event for a target portlet. The only way to achieve this is by means of a redirect, that restarts the rendering cycle.

Both, the Java Portlet API and WSRP standards include the possibility of redirects as a response to handling a action. Theoretically, this would also be possible in Jetspeed with the setRedirectURI() method of the RunData object. However, as Jetspeed has already started sending HTML back to the browser when the actions are handled, a regular HTTP redirect can no longer be sent to the browser. To overcome this issue we have modified the JspPortletAction behaviour in our prototype to send JavaScript redirects if requested by a portlet action handler.

With the help of the common RunData object and by triggering each others (or broadcast) action events, also from action handlers through redirects, various inter-portlet communication scenarios become possible. We have used these in our prototype to implement some of the context integration scenarios discussed in section 4. The implementation is discussed in more detail in section 7.

7.2 Context Bus Implementation

We have used the above means (common RunData object and action events) to implement our Context Bus approach for the Apache Jetspeed platform. Like we proposed in section 5 as an extension to the Java Portlet API and WSRP, we introduced a getContext() method to a modification of the JspPortletActionClass, called ContextPortletActionClass. The method is called by the event handler after the usual buildNormalContext() and the retrieved context RDF description is put into a context variable of the portlet's session state. In other words, the context variables in the RunData object together represent the context repository.

The ContextBus class provides a service function getPortletContext() for portlets to query the context of others, i.e. for a context pull. If a portlet ID is given as an argument, the RDF context only of this portlet is returned. If the portlet ID is null or omitted, the combined context of all portlets is returned. So far we do not consider static user context elements.

For context push events we introduced (as proposed in section 5) an acceptContext() method to the ContextPortletActionClass. Portlets that wish to receive context push events need to implement this method. The context RDF is given as a parameter together with the source portlet ID. In order to trigger a context push, we added an extra "find related'' control to the portlet title bar. The user can click this control to invoke a context push. Internally the MVC action event acceptcontext is used for this purpose, however this is transparent for the portlet developer.

All that remains to be done for the portlet developer is to implement the getContext() and acceptContext() methods. The user can then configure the publish/subscribe mechanism by means of the acceptcontext, contexttarget, and contexttrigger portlet parameters. The paramenter acceptcontext holds a set of source portlet IDs (or "all'') the portlet should receive context events from, contexttarget respectively holds a set of target portlet IDs the portlet wishes to send context notificatios to. Finally, the contexttrigger parameter can be used for implicit context pushes, i.e. without requiring the user to click the context control. It holds the names of action events that should (besides their regular action) also trigger a context push.

7.3 Context Integration Scenarios

Figure 12 shows a screenshot of our prototype. As mentioned above, the portal configuration consists of four portlets: a navigation portlet used to browse topics from a taxonomy, a search portlet to search for content and documents, a content portlet to view intranet articles, and finally an OLAP (or reporting) portlet that can be used to access OLAP reports from a data warehouse.

Figure 12: Screenshot of the running prototype

The navigation portlet publishes the selected topic as a Dublin Core Subject. In browsing mode the content portlet also publishes the currently accessed taxonomy topic, in view mode (i.e. when displaying an article) it in addition publishes the ontology elements from the article metadata as Dublin Core Coverage. The same is true for the OLAP portlet, it publishes the currently open folder as Subject and the elements shown on a report as Coverage. Note that these elements are dynamically retrieved from the report content, i.e. this also works for ad-hoc reports created by drilling and slicing/dicing.

The content and OLAP portlets accept Subject elements when receiving context action events, reacting by browsing the corresponfing topic resp. folder. The search portlet accepts arbitrary context elements, using these as a search query.

Navigation Portlet Search Portlet Content Portlet OLAP Portlet Accept Context - <Content PID>,
<OLAP PID> <Navigation PID> <Search PID> Context Target all - <Search PID> <Search PID> Context Trigger browse - - -

PID = Portlet ID

Table 1: Context bus configuration

The Context Bus (i.e. publish/subscribe) configuration is shown in table 1. The navigation portlet publishes its topic to all other portlets (i.e. using a broadcast). The context event is triggered implicitly by browse events. Hence this scenario represents a full implicit broadcast context push as shown in the morphological box in figure 13.

Figure 13: Topic navigation scenario

The search portlet accepts context events from the content and the OLAP portlet. These respectively publish their context only to the search portlet. The context event is only triggered when the user clicks the corresponding find related control in the potlet title bar. Hence this second scenario represents a full explicit unicast context push as shown in figure 14.

Figure 14: Find related in content and OLAP portlet

Finally, checking to use the portal context in the search portlet demonstrates a context pull, as the search engine will add the context of the other portlets to the user query. This is depicted in figure 15.

Figure 15: Search engine using a context pull

8 Conclusions

We have presented an approach for utilizing the user context for inter-portlet communication. The advantage of this approach is that portlets can be developped without taking into account with which other portlets they might be integrated. As opposed to other existing inter-portlet communication techniques, which only address the communication paths, we also present a way to handle the exchanged information, including its semantics, in a generic way. We use Semantic Web technologies to capture the context and propose a Context Bus that is capable of supporting different (push and pull) communication scenarios.

In order to be independent of a specific portal server product, the approach has been presented as proposed exensions to the Java Portlet Specification (JSR 168) and WSRP. In addition, a prototype portal called INWISS has been developed using the open source Apache Jetspeed portal platform as a basis. Unfortunately the current version of Jetspeed does not conform to the Java Portlet Specification. Hence, there is a certain (however, not essential) gap between our proposal and its implementation. Future work will deal with moving the implementation closer to the specification when the new JSR 168 compliant Jetspeed 2 becomes available.

Currently, we are finalizing the implementation by providing support for static user context elements and selective context push scenarios. In addition we are improving the search engine and the content/document management capabilities of the portal. In another project we work on utilizing user attributes and resource metadata for access control purposes. A security module with an attribute-based access control facility [PMDP04] has already been integrated into the INWISS prototype. An interesting next step will be to use the user attributes (e.g. his location) also as static user context elements.

For the dynamic portlet context we currently only consider the current state, i.e. we do not keep a context history. However, especially for context pull scenarios, also older context information might be of interest, e.g. for context-supported information retrieval as proposed by [HM02,HM03]. Technically, this can be achieved by adding the context descriptions to a persistent RDF repository, like Sesame [BKvH02], which we also use for the ontology and resource metadata in our porototype.

References [Apa04] The Jetspeed Portal Tutorial 1.5. Apache Portals project website, 2004. http://portals.apache.org/jetspeed-1/tutorial/. Visited August 2004. [BKvH02] J. Broekstra, A. Kampman, and F. van Harmelen. Sesame: A Generic Architecture for Storing and Querying RDF and RDF Schema. In Proc. of the First International Semantic Web Conference (ISWC 2002), Sardinia, Italy, June 2002. [BMR+96] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, and M. Stal. Pattern-Oriented Software Architecture: A System of Patterns. John Wiley & Sons Ltd., 1996. [DCM03] Dublin Core Metadata Initiative. DCMI Metadata Terms. DCMI recommendation, November 2003. http://dublincore.org/documents/2003/11/19/dcmi-terms/. [FK02] G. Färber and J. Kirchner. mySAP Technology. Galileo Press, Bonn, 2002. [Hep03] S. Hepper. Comparing the JSR 168 Java Portlet Specification with the IBM Portlet API. IBM developerWorks article, December 2003. http://www-106.ibm.com/developerworks/websphere/library/techarticles/0312_hepper/hepper.html. Visited August 2004. [HM02] A. Henrich and K. Morgenroth. Integration von kontextunterstütztem Information Retrieval in Portalsysteme. In Teilkonferenz Management der Mitarbeiter-Expertise in IT-Beratungsunternehmen, MKWI 2002, Nürnberg, Germany, 2002. [HM03] A. Henrich and K. Morgenroth. Supporting Collaborative Software Development by Context-Aware Information Retrieval Facilities. In Proc. of the DEXA 2003 Workshop on Web Based Collaboration (WBC 2003), Prague, Czech Republic, September 2003. [JCP03] Java Portlet Specification (JSR 168). JCP specification, October 2003. http://jcp.org/aboutJava/communityprocess/final/jsr168/index.html. [LR02] D. Lection and V. Ramamoorthy. Websphere Portal V4 Programming, Part 2: Portlet Application Programming. IBM developerWorks article, August 2002. http://www-106.ibm.com/developerworks/ibm/library/i-portal2v4/. Visited August 2004. [OAS03] Web Services for Remote Portlets Specification. OASIS standard, August 2003. http://www.oasis-open.org/committees/wsrp. [PMDP04] T. Priebe, B. Muschall, W. Dobmeier, and G. Pernul. A Flexible Security System for Enterprise and e-Government Portals. In Proc. of the 15th International Conference on Database and Expert Systems Applications (DEXA 2004), Zaragoza, Spanien, September 2004. [PSP04] T. Priebe, C. Schläger, and G. Pernul. A Search Engine for RDF Metadata. In Proc. of the DEXA 2004 Workshop on Web Semantics (WebS 2004), Zaragoza, Spain, September 2004 2004. An extended version is available at http://www.inwiss.org/whitepapers/MetadataRetrieval.pdf. [RC03] A. Roy-Chowdhury. Using Cooperative Portlets in WebSphere Portal V5. IBM developerWorks article, October 2003. http://www-106.ibm.com/developerworks/websphere/library/techarticles/0310_roy/roy.html. Visited August 2004. [SAP02] Administration Guide Portal Platform - Enterprise Portal 5.0. SAP AG, 2002. http://help.sap.com/saphelp_ep50sp6/helpdata/en/start.htm. Visited August 2004. [SW02] J. Schelp and R. Winter. Enterprise Portals und Enterprise Application Integration - Begriffsbestimmung und Integrationskonzeption. In S. Meinhardt and K. Popp (Eds.), Enterprise-Portale & Enterprise Application Integration, number 225 in HMD - Praxis der Wirtschaftsinformatik, pp. 6-20. dpunkt.verlag, Heidelberg, Germany, June 2002. [W3C04a] OWL Web Ontology Language Overview. W3C recommendation, February 2004. http://www.w3.org/TR/2004/REC-owl-features-20040210/. [W3C04b] RDF Vocabulary Description Language 1.0: RDF Schema. W3C recommendation, February 2004. http://www.w3.org/TR/2004/REC-rdf-schema-20040210/. [W3C04c] Resource Description Framework (RDF): Concepts and Abstract Syntax. W3C recommendation, February 2004. http://www.w3.org/TR/2004/REC-rdf-concepts-20040210/. [Win03] D. Winer. RSS 2.0 Specification. Berkman Center for Internet & Society at Harvard Law School, 2003. http://blogs.law.harvard.edu/tech/rss.

本文地址:http://com.8s8s.com/it/it12018.htm