Skip to main content

Manage Apache Pluto within Geronimo

J. Jeffrey Hanson (jeff@jeffhanson.com), CTO, Max Software, Inc.
Jeff Hanson
Jeff Hanson has more than 20 years of experience in the software industry, including working as senior engineer for the Microsoft Windows port of the OpenDoc project, lead architect for the Route 66 framework at Novell, and chief architect for eReinsure.com, Inc., where he directed design and implementation of frameworks and platforms for J2EE-based reinsurance systems. Jeff is currently the CTO for Max Software, Inc., where he directs efforts to provide desktop and enterprise applications and platforms for Internet safety and parental controls. Jeff is the author of numerous articles and books, including .NET versus J2EE Web Services: A Comparison of Approaches, Pro JMX: Java Management Extensions, and Web Services Business Strategies and Architectures.

Summary:  Portal and portlet technology development is a hot skill to have, especially when developing Web 2.0-enabled applications. The Apache Pluto project is the reference implementation of the Java™ Portlet Specification, which was originally created through the Java Community Process and Java Specification Request (JSR) 168. The specification defines guidelines for portals and portlet components developed with the Java programming language. This specification is being recognized as the universal standard for traditional portals and as a framework for building pluggable Web applications. Find out how combining the Pluto project with Apache Geronimo's Java Platform, Enterprise Edition (Java EE) platform creates a highly flexible and powerful environment for building customizable and manageable systems using portals and portlets.

Date:  17 Jul 2007
Level:  Intermediate
Activity:  1099 views

Introduction to Apache Pluto

Apache Pluto, a subproject of the Apache Portals project, is an open source implementation of the Java Portlet Specification. The Pluto project provides a portlet container runtime environment, as mandated by the specification, in which portlets can be instantiated and managed. In this article, I discuss the high-level features of Pluto and how Pluto can be integrated with Apache Geronimo. There's also a sample application (see the Downloads section) containing Geronimo GBeans, which abstract the public interfaces of the primary components of the Pluto framework. These GBeans can then be deployed to a running Geronimo instance, where they can be used to monitor and manage Pluto's portal features.

The Pluto project provides a portlet container for managing portlets and a special-purpose portal application known as the Portal Driver, which provides configuration services, a template framework, and a framework for invoking individual portlets embedded within the container. The Pluto portlet container manages portlet invocations, portlet context handling, portlet deployment descriptor registry functionality, and portlet deployment descriptor services. The container also provides a plug-in framework for extending itself with additional functionality.

The Pluto portlet container exposes a set of mandatory and optional services for facilitating, among other things, communication between portlets and calling parties. These services are described later in this article.

The diagram in Figure 1 illustrates the primary components of Apache Pluto.


Figure 1. Architectural components of Apache Pluto
Apache Pluto architecture

Introduction to Apache Geronimo

Apache Geronimo is a fully compliant Java EE platform that you can use to build enterprise-grade applications and services. Geronimo is based on an architecture that uses Inversion of Control (IoC) techniques to decouple components and services. This decoupling makes an extremely configurable and modular runtime environment. Geronimo also takes advantage of Java Management Extensions (JMX) and a similar proprietary managed-bean framework that makes Geronimo an easy platform to monitor, configure, and manage.

Geronimo is bootstrapped using a given aggregation of assembled modules that are connected and managed internally by a lightweight kernel component. A Geronimo module is an arbitrary component made up of a set of classes: dependencies, other modules, and a serialized configured state. The Geronimo kernel loads and assembles modules when a Geronimo instance is started. Modules determine functionality for a runtime Geronimo subsystem and the dependencies that are required for each Geronimo subsystem. All core services in a Geronimo run time are deployed as modules.

Modules are described using an XML document that's referred to as a deployment plan or plan. A final deployment plan in Geronimo is made up of a combination of an initial deployment plan, a Maven Project Object Model (POM) file, and a Maven project.properties file. Figure 2 shows how these files are processed to create a final deployment plan.


Figure 2. Geronimo deployment plans
Geronimo plans

The contents of a plan are constrained by an XML Schema Document (XSD). Among other things, a plan defines a module ID, a module's dependencies, environment properties for a module, services provided by the module, and GBeans for the module.

The example in Listing 1 illustrates a simple Geronimo deployment plan.


Listing 1. A simple Geronimo deployment plan
                            
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="http://geronimo.apache.org/xml/ns/deployment-1.1">
  <environment>
    <moduleId>
      <groupId>geronimo</groupId>
      <artifactId>simple</artifactId>
      <version>1.0.0</version>
      <type>car</type>
    </moduleId>  
    <dependencies>
      <dependency>
        <groupId>geronimo</groupId>
        <artifactId>j2ee-server</artifactId>
        <type>car</type>
      </dependency>
    </dependencies>
    <hidden-classes/>
    <non-overridable-classes/>
  </environment>
  
  <gbean name="SimpleService" class="com.example.myservices.MyServiceGBean">
    <attribute name="prop1">12345</attribute>
    <attribute name="prop2">This is the value for property 2</attribute>
  </gbean>
</module>      
      

When a deployment plan similar to the plan in Listing 1 is transformed by the build process, a Configuration Archive (CAR) file is created with a unique name. The unique name generated for the configuration in Listing 1 is geronimo/simple-1.0.0/car.

Geronimo CAR files

A CAR file is an automatically generated Java Archive (JAR) file containing the serialized state of a deployment plan and any other ancillary resources. A serialized deployment plan is embodied in a file called config.ser in the META-INF directory of the CAR. CAR files are created by the build process via the Geronimo packaging plug-in for Maven.

Geronimo repositories

A Geronimo repository is a data store and registry of artifacts, typically structured as a directory hierarchy on the file system. The binary distribution of Geronimo provides a directory named repository that contains all dependencies for the modules that make up the core Geronimo platform.

A Geronimo artifact is an arbitrary entity, such as a JAR file, Web Archive (WAR) file, CAR file, and so on, that's added to a Geronimo repository using either the Geronimo Web console or the command-line deployment and build utilities provided with the Geronimo distribution.

Download and install Apache Geronimo

Download the Geronimo platform from the site listed in the Resources section, and extract the files to a directory to be referred to as {GERONIMO_HOME}. After you've downloaded and extracted the Geronimo distribution, execute the startup script found in the {GERONIMO_HOME}\bin\ directory of your Geronimo installation. You should see a console window similar to Figure 3.


Figure 3. Geronimo startup
Geronimo startup

The startup console shows a number of modules, connectors, and applications that are loaded and started and from which the Geronimo runtime environment is created. You can stop the Geronimo run time using the shutdown script, also found in the {GERONIMO_HOME}\bin\ directory of your Geronimo installation.


Pluto services used by Geronimo

Geronimo 1.1.1 uses Pluto 1.0.1 as the portal environment for its management console. In this portal environment, services required by the portal container and services used by the portal driver are defined and loaded when the Web console initializes.

The following is a list of the standard services defined for Geronimo's Pluto-enabled Web console:

  • Config service — The ConfigService interface represents information about Geronimo's Pluto portal configuration.
  • Factory manager service — The FactoryManagerService interface represents functionality for managing the lifetime of factories registered during the Pluto container startup.
  • Log service — The LogManagerService interface represents functionality defining the Pluto portal driver's logging implementation.
  • Portlet definition registry service — The PortletDefinitionRegistryService interface represents all portlets and portlet applications available in the Web console portal. This service presents and manages information about the Web console portlets and portlet applications.
  • Portlet entity registry service — The PortletEntityRegistryService interface represents a registry of all portlet and portlet application instances available in the Web console portal.
  • Page registry service — The PageRegistryService interface represents a registry of pages, windows, and portlet entries that make up the Geronimo Web console.

In this article, you create GBeans and utilities that expose these Pluto services to an administration framework operating in the Geronimo runtime environment.


Managing the Pluto portal environment

Before you begin work to manage the Pluto environment, you need to download the source for the Pluto framework (see the Resources section for a link). Download and extract the files to a directory to be referred to as {PLUTO_HOME}.

Object instances representing the Pluto services mentioned previously can be retrieved by a component running within the same servlet context as the Web console Web application. This allows access to configuration information that can be wrapped by Geronimo GBeans.

Configuration information for the Pluto portal environment is referenced by the object stored in the org.apache.pluto.portalImpl.services.ServiceManager class and retrieved using the getService method of the ServiceManager class. A reference to the org.apache.pluto.portalImpl.services.config.ConfigService class must be passed as a parameter to the getService method of the ServiceManager class to retrieve an instance of the ConfigService component.

With an instance of the ConfigService component available, you can wrap this component as a standard Geronimo GBean and make it available to the Pluto admin environment. Listing 2 illustrates how the ConfigService information is presented by a Geronimo GBean.


Listing 2. ConfigService as a Geronimo GBean
                            
public class ConfigServiceGBean
  implements GBeanLifecycle
{
  public static final GBeanInfo GBEAN_INFO;
  
  static
  {
    GBeanInfoBuilder infoBuilder =
      new GBeanInfoBuilder("ConfigServiceGBean", ConfigServiceGBean.class);
  
    infoBuilder.addAttribute("parameters",
                       org.apache.pluto.portalImpl.util.Parameters.class,
                       true);

    infoBuilder.addOperation("getParameters");
    infoBuilder.addOperation("setString", new Class[] { String.class,
                                                        String.class });
    infoBuilder.addOperation("getString", new Class[] { String.class });
    infoBuilder.addOperation("getInteger", new Class[] { String.class });
    infoBuilder.addOperation("getBoolean", new Class[] { String.class });
    infoBuilder.addOperation("paramKeys");

    GBEAN_INFO = infoBuilder.getBeanInfo();
  }
  
  public static GBeanInfo getGBeanInfo() 
  {
    return GBEAN_INFO;
  }
  
  private org.apache.pluto.portalImpl.services.config.ConfigServiceImpl
            configServiceImpl = null;
  
  public ConfigServiceGBean()
  {
    try
    {
      Class configServiceCls =
      Class.forName("org.apache.pluto.portalImpl.services.config.ConfigService");
      org.apache.pluto.portalImpl.services.Service configService =
org.apache.pluto.portalImpl.services.ServiceManager.getService(configServiceCls);
      if (configService != null)
      {
        configServiceImpl =
          (org.apache.pluto.portalImpl.services.config.ConfigServiceImpl)
            configService;
      }
    }
    catch(Exception e)
    {
      System.err.println("Exception in ConfigServiceGBean: " + e);
    }
  }
  
  public org.apache.pluto.portalImpl.util.Parameters getParameters()
  {
    return configServiceImpl.getParameters();
  }

  public java.util.Iterator paramKeys()
  {
    return configServiceImpl.getParameters().keys();
  }

  public String getString(String name)
  {
    return configServiceImpl.getString(name);
  }

  public void setString(String name, String value)
  {
    configServiceImpl.getParameters().setString(name, value);
  }

  public Integer getInteger(String name)
  {
    return configServiceImpl.getInteger(name);
  }

  public Boolean getBoolean(String name)
  {
    return configServiceImpl.getBoolean(name);
  }

  public void doFail()
  {
    System.err.println(getClass().getName() + " failed");
  }
  
  public void doStart()
    throws Exception
  {
    System.out.println("Starting " + getClass().getName());
  }
  
  public void doStop()
    throws Exception
  {
    System.out.println("Stopping " + getClass().getName());
  }
}
      

After you've created a standard Geronimo GBean for the ConfigService component, it must be loaded and started by the Geronimo kernel. You retrieve the Geronimo kernel using the getSingleKernel method of the org.apache.geronimo.kernel.KernelRegistry class.

With a reference to the kernel, you can describe a GBean using a GBeanData instance. The GBean is then loaded and started by the kernel, as shown in Listing 3.


Listing 3. Loading and starting a GBean using the GBeanManager
                            
public class GBeanManager
{
  public static void loadAndStartGBean(Class gBeanCls,
                                       GBeanInfo gBeanInfo,
                                       String serviceType)
  {
    java.net.URI gBeanURI =
      java.net.URI.create("devworks/" + gBeanCls.getName() + "/1.0.0/?service="
                          + serviceType);
    org.apache.geronimo.gbean.AbstractName abstractName =
      new org.apache.geronimo.gbean.AbstractName(gBeanURI);

    org.apache.geronimo.gbean.GBeanData gBeanData =
      new org.apache.geronimo.gbean.GBeanData(abstractName, gBeanInfo);
    System.out.println("Loaded gBeanData for class: " + gBeanCls.getName());

    org.apache.geronimo.kernel.Kernel geronimoKernel =
      org.apache.geronimo.kernel.KernelRegistry.getSingleKernel();
    System.out.println("Found kernel: " + geronimoKernel.getKernelName());

    ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();

    try
    {
      geronimoKernel.loadGBean(gBeanData, clsLoader);
      System.out.println("Loaded gBean: " + gBeanCls.getName());
    }
    catch (GBeanAlreadyExistsException e)
    {
      System.out.println("GBean [" + gBeanCls.getName() + "] already loaded");
    }

    try
    {
      geronimoKernel.startGBean(gBeanCls);
      System.out.println("Started gBean: " + gBeanCls.getName());
    }
    catch (GBeanNotFoundException e)
    {
      System.err.println("GBean [" + gBeanCls.getName() + "] not found");
    }
  }

  public static void stopAndUnloadGBean(Class gBeanCls)
  {
    org.apache.geronimo.kernel.Kernel geronimoKernel =
      org.apache.geronimo.kernel.KernelRegistry.getSingleKernel();
    System.out.println("Found kernel: " + geronimoKernel.getKernelName());

    try
    {
      geronimoKernel.stopGBean(gBeanCls);
      System.out.println("Stopped gBean: " + gBeanCls.getName());
    }
    catch (GBeanNotFoundException e)
    {
      System.err.println("GBean [" + gBeanCls.getName() + "] not found");
      return;
    }

    try
    {
      geronimoKernel.unloadGBean(gBeanCls);
      System.out.println("Unloaded gBean: " + gBeanCls.getName());
    }
    catch (GBeanNotFoundException e)
    {
      System.err.println("GBean [" + gBeanCls.getName() + "] not found");
      return;
    }
  }

  public static Object getGBean(Class gBeanCls)
    throws GBeanNotFoundException
  {
    org.apache.geronimo.kernel.Kernel geronimoKernel =
      org.apache.geronimo.kernel.KernelRegistry.getSingleKernel();
    System.out.println("Found kernel: " + geronimoKernel.getKernelName());

    return geronimoKernel.getGBean(gBeanCls);
  }
}
      

After the ConfigService component has been wrapped in a Geronimo GBean and loaded and started by the kernel, a JavaServer Pages (JSP) component or servlet can use the GBean to access information managed by the ConfigService component.

The JSP example in Listing 4 illustrates retrieving the ConfigService GBean and using it to get and set information managed by the ConfigService component.


Listing 4. Management page for the ConfigService GBean
                            
<h3>Pluto Admin on Geronimo: Config Service</h3>
<%
try
{
  com.devworks.pluto.gbeans.GBeanManager.loadAndStartGBean(
       com.devworks.pluto.gbeans.ConfigServiceGBean.class,
       com.devworks.pluto.gbeans.ConfigServiceGBean.GBEAN_INFO,
       "config");

  Object gbeanObj =
    com.devworks.pluto.gbeans.GBeanManager.getGBean(
      com.devworks.pluto.gbeans.ConfigServiceGBean.class);
  if (gbeanObj != null)
  {
    com.devworks.pluto.gbeans.ConfigServiceGBean configServiceGBean =
      (com.devworks.pluto.gbeans.ConfigServiceGBean)gbeanObj;

    java.util.Enumeration reqParams = request.getParameterNames();
    while (reqParams.hasMoreElements())
    {
      String paramName = (String)reqParams.nextElement();
      String newParamValue = request.getParameter(paramName);
      String oldParamValue = configServiceGBean.getString(paramName);
      if (newParamValue.equalsIgnoreCase(oldParamValue) == false)
      {
        System.out.println("Setting param: " + paramName + " to value: "
                           + newParamValue);
        configServiceGBean.setString(paramName, newParamValue);
      }
    }

    out.println("<form name='configForm' method='post' "
                + "action='ConfigServiceAdmin.jsp'><table>");
    java.util.Iterator keys = configServiceGBean.paramKeys();
    while (keys.hasNext())
    {
      String paramKey = keys.next().toString();
      String paramValue = configServiceGBean.getString(paramKey);
      out.println("<tr><td>Param " + paramKey + "</td>"
                  + "<td><input type='text' name='" + paramKey
                  + "' value='" + paramValue + "'/></td>"
                  + "<td><input type='submit' value='submit'/></td></tr>");
    }
    out.println("</table>");

    com.devworks.pluto.gbeans.GBeanManager.stopAndUnloadGBean(
      com.devworks.pluto.gbeans.ConfigServiceGBean.class);
  }
  else
  {
    out.println("Unable to find ConfigServiceGBean<br/>");
  }
}
catch(Exception e)
{
  e.printStackTrace(System.out);
  out.println("<br/>Exception: " + e);
}
%>
      

You can access information managed by other services, such as the portlet definition registry service or portlet entity registry service, using the same techniques to wrap each component as a GBean. These GBeans can then be loaded and started by the kernel and referenced by administration components, such as JSPs or servlets. (See the Resources section for the source code demonstrating these concepts.)


Configure users and roles

Security role references (represented by org.apache.pluto.om.common.SecurityRoleRef instances) determine whether a user is mapped to a specified role for each portlet request. Standard Java 2 Platform, Enterprise Edition (J2EE) and Java EE security measures are taken to determine the rights given to an authenticated user in the context of a particular portlet by applying the security information configured within the set of security role references stored within each portlet definition.

The portlet entity registry service GBean is used to retrieve and modify security role references for a given portlet definition. As with the ConfigService GBean, a static GBeanInfo object is created and made available in a static getGBeanInfo method, as shown in Listing 5.


Listing 5. GBeanInfo for the portlet entity registry service GBean
                            
public class PortletEntityRegistryServiceGBean
  implements GBeanLifecycle
{
  public static final GBeanInfo GBEAN_INFO;

  static
  {
    GBeanInfoBuilder infoBuilder =
      new GBeanInfoBuilder("PortletEntityRegistryServiceGBean",
                           PortletEntityRegistryServiceGBean.class);

    infoBuilder.addOperation("getPortletEntityKeys");
    infoBuilder.addOperation("getPortletEntityID",
                             new Class[] { String.class });
    infoBuilder.addOperation("getPortletEntityWindowList",
                             new Class[] { String.class });
    infoBuilder.addOperation("getPortletEntityDefName",
                             new Class[] { String.class });
    infoBuilder.addOperation("getPortletEntityDefAppID",
                             new Class[] { String.class });
    infoBuilder.addOperation("getPortletEntityDefAppContext",
                             new Class[] { String.class });
    infoBuilder.addOperation("getSecurityRoleRefs",
                             new Class[] { String.class });
    infoBuilder.addOperation("setSecurityRoleRefs",
                      new Class[] { String.class,
                      org.apache.pluto.om.common.SecurityRoleRefSet.class });

    GBEAN_INFO = infoBuilder.getBeanInfo();
  }

  public static GBeanInfo getGBeanInfo()
  {
    return GBEAN_INFO;
  }
      

Each operation defined by the GBeanInfo object is implemented using an instance of the object stored in the org.apache.pluto.portalImpl.services.ServiceManager class and retrieved using the getService method of the ServiceManager class. In a manner similar to the ConfigService GBean, a reference to the org.apache.pluto.portalImpl.services.portletentityregistry.PortletEntityRegistryService class must be passed as a parameter to the getService method of the ServiceManager class to retrieve an instance of the PortletEntityRegistryService component.

With an instance of the PortletEntityRegistryService component available and exposed as a standard Geronimo GBean, security role information for a portlet can be made available to the Pluto admin environment, as illustrated by the GBean methods in Listing 6.


Listing 6. Security role information presented by the portlet entity registry service GBean
                            
public org.apache.pluto.om.common.SecurityRoleRefSet
  getSecurityRoleRefs(String entityKey)
{
  org.apache.pluto.portalImpl.om.portlet.impl.PortletDefinitionImpl
    portletDefinitionImpl = getPortletDefImpl(entityKey);
  if (portletDefinitionImpl == null)
  {
    return null;
  }

  return portletDefinitionImpl.getCastorInitSecurityRoleRefs();
}

public void setSecurityRoleRefs(String entityKey,
             org.apache.pluto.om.common.SecurityRoleRefSet securityRoleRefSet)
{
  org.apache.pluto.portalImpl.om.portlet.impl.PortletDefinitionImpl
    portletDefinitionImpl = getPortletDefImpl(entityKey);
  if (portletDefinitionImpl == null)
  {
    return;
  }

  portletDefinitionImpl.setCastorInitSecurityRoleRefs(securityRoleRefSet);
}
      

When you wrap the desired services as GBeans and make them available to the kernel, you can use them to monitor and manage portal applications, as covered in the next section.


Monitor and manage portal applications

To use the GBeans described previously, you must use them in the same context as the Web console application. You do so by adding JSPs, servlets, and/or Java classes to the Web console CAR file. For the Jetty Web console, you can find the CAR file in the Geronimo repository at Geronimo/webconsole-jetty/1.1.1/car directory. The Web application for the Web console is in a .war file named framework.war, which is a child of the CAR directory.

To add Pluto administration functionality to Geronimo using the GBeans defined earlier, create a new subdirectory of framework.war, called plutoadmin. In the plutoadmin directory, place the JSPs found in the sample code (see the Downloads section). These JSPs provide the GBeanManager class to access the GBeans.

After you've populated the plutoadmin directory with the desired JSPs and other Web resources (and each time a modification is made to the plutoadmin resources), you must restart the Web console CAR file using the Geronimo deployer utility, as shown in Listing 7.


Listing 7. Command line for restarting the Web console CAR file
                            
C:\<GERONIMO_HOME>\bin>deploy --user system --password manager restart \
geronimo/webconsole-jetty/1.1.1/car
      

The result of this command line is shown in Listing 8.


Listing 8. Results of restarting the Web console CAR file
                            
    Restarted geronimo/webconsole-jetty/1.1.1/car
      `-> standard.war
      `-> framework.war
      

Now point a Web browser to http://localhost:8080/console/plutoadmin/ to access the Pluto management pages. Links are provided for each of the primary services described in this article. You can investigate each link and the GBean associated with each.


Conclusion

Apache Geronimo uses Apache Pluto as the portal environment for its management console. Using Geronimo's managed-bean framework, Pluto services can be wrapped and presented to components and services to allow a flexible and powerful environment for managing Pluto components and services.



Download

DescriptionNameSizeDownload method
Sample code for this articleos-ag-geronpluto.zip104KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

Discuss

About the author

Jeff Hanson

Jeff Hanson has more than 20 years of experience in the software industry, including working as senior engineer for the Microsoft Windows port of the OpenDoc project, lead architect for the Route 66 framework at Novell, and chief architect for eReinsure.com, Inc., where he directed design and implementation of frameworks and platforms for J2EE-based reinsurance systems. Jeff is currently the CTO for Max Software, Inc., where he directs efforts to provide desktop and enterprise applications and platforms for Internet safety and parental controls. Jeff is the author of numerous articles and books, including .NET versus J2EE Web Services: A Comparison of Approaches, Pro JMX: Java Management Extensions, and Web Services Business Strategies and Architectures.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

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

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

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

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

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Java technology, WebSphere
ArticleID=240707
ArticleTitle=Manage Apache Pluto within Geronimo
publish-date=07172007
author1-email=jeff@jeffhanson.com
author1-email-cc=ruterbo@us.ibm.com

My developerWorks community

Tags

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

Use the slider bar to see more or fewer tags.

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

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

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

Rate a product. Write a review.

Special offers