Monday, November 22, 2010

UCM 11g: Sourcing of metadata fields and/or How to create metadata during component installation





Just wanted to note an interesting and useful new feature of 11g: metadata can now be sourced.

It appears that this functionality is specifically designed to be leveraged by components, but perhaps this will be expanded to metadata created via the Config Manager applet in the future (or perhaps someone will create an enhancement will allow admin access to these values).

This ability is useful for any system where multiple custom, or component,  attributes are in use. The ability to track an attribute back to the component that’s leveraging it allows for better understanding of its intended purpose. This can prevent illogical hijacking of attributes which could potentially confuse programmers and users alike. On the other hand, if it is decided to share an attribute across components, this also can be detailed.

Anything that helps answer the question, “why is this here?”, is useful in my opinion.


As previously mentioned, it appears that there is no admin-level UI to modify this field. To leverage this, I’ll show how the ‘dComponentName’ property can be appended to the standard set of attribute metadata in order to use this feature. 

I’ve created a simple component to demonstrate this functionality. I’ll start by briefly reviewing the functional aspects, then I’ll go into the different features of the component.


The SenaSampleManageMetadata component is an example of a starting template one could use to create components. Some people prefer to work out of the ComponentWizard for editing, modifying, and generally managing their components. I prefer to use Eclipse and my svn to keep a history of each component I create. 

That being the case, I have a few different starting templates I use to jump-start my component building. This component has two useful maintenance features: managing of metadata & tracing information. Side note: I’m working on building an ant build file that will automate the cloning and renaming of the template files into the new template. I also would like it to auto increment the version information (<today’s date> <previous build # + 1).

The first feature will allow for the simultaneous deployment of metadata with their associated components instead of creating a component bundle or manual creation via an IQ/OQ document or implementation notes. Anything that simplifies and streamlines deployment and implementation is typically going to be a step in the right direction.

The second feature is an important step in keeping log files tidy and useful. Developers can easily enable or disable debugging output from their custom components by leveraging custom trace sections. Using custom trace sections will not only allow for easy isolation (e.g., by grepping a tail) of output, but it will ensure that I/O is limited when the trace is disabled. Think about the last project you were on where all code utilized the system trace… it’s not pretty.


Now that the reasons for the maintenance features have been defined, we can discuss how we approach these via component additions. 

The first feature is handled by leveraging a component installation filter. The ComponentWizard creates a starting class that includes a conditional structure for handling multiple filter events within the same class. I’m using the extraAfterServicesLoadInit filter as well as the SenaSampleManageMetadataComponentUninstallFilter filter.

As defined in the template, the extraAfterServicesLoadInit filter is called after the last standard activity of a server side application initialization. These events are triggered as the content server starts during the post-installation restart. This is a good place to manipulate cached data or override standard configuration data. The SenaSampleManageMetadataComponentUninstallFilter filter is called for custom un-installation steps. NOTE: Change the uninstall filter name to have your component name prefix. For example: <ComponentName>CompnentUninstallFilter. This event is triggered after confirmation of component uninstallation via the UCM admin server. I’ve modified the descriptions a bit for added clarity, but they’re basically lifted from the filter template comments.

Code added for the extraAfterServicesLoadInit filter condition:

 else if (param.equals("extraAfterServicesLoadInit"))
    {
      SystemUtils.trace("senasamplemeta", "\n\n\nSenaSampleManageMetadata: Found install filter\n\n\n");
      processMeta(loader, ws, binder, cxt, true);
    }

Code added to the SenaSampleManageMetadataComponentUninstallFilter filter condition:

else if (param.equals("SenaSampleManageMetadataComponentUninstallFilter"))
    {
      SystemUtils.trace("senasamplemeta", "\n\n\nSenaSampleManageMetadata: Found uninstall filter\n\n\n");
      processMeta(loader, ws, binder, cxt, false);
    }

The function created to handle the create/delete of metadata:

protected int processMeta(IdcExtendedLoader idcextendedloader,
      Workspace ws, DataBinder binder,
      ExecutionContext cxt, boolean isInstall)
  {
    SystemUtils.trace("senasamplemeta", "Starting to process custom meta..");

    try
    {
      1: DataResultSet dataresultset = SharedObjects
          .getTable("SenaSampleManageMetadata_Metadata");
      SystemUtils.trace("senasamplemeta", "after get table");
      if (dataresultset != null)
      {
        SystemUtils.trace("senasamplemeta", "table not null");
        dataresultset.first();
        for (; dataresultset.isRowPresent(); dataresultset.next())
        {
          2: Properties properties = dataresultset.getCurrentRowProps();
          String metaDefName = properties.getProperty("dName");
          SystemUtils.trace("senasamplemeta", "retrieved metadata name: " + metaDefName);
          SystemUtils.trace("senasamplemeta", "hasDocMetaDef: " + MetaFieldUtils.hasDocMetaDef(metaDefName));
          3: if (metaDefName == null || metaDefName.length() == 0
              || ( isInstall && MetaFieldUtils.hasDocMetaDef(metaDefName))
              || ( !isInstall && !MetaFieldUtils.hasDocMetaDef(metaDefName)))
            continue;
          try
          {
            SystemUtils.trace("senasamplemeta", "is install: " + isInstall);
            4: if (isInstall)
              MetaFieldUtils.updateMetaDataFromProps(ws, null, properties,metaDefName, true);
            else
              MetaFieldUtils.deleteMetaData(ws, cxt, metaDefName);
            SystemUtils.trace("senasamplemeta", "after Meta call");
          } catch (ServiceException serviceexception)
          {
            SystemUtils.error(serviceexception, (new StringBuilder()).append(
                "Error:").append(metaDefName).append(
                " senasamplemeta -- metadata field was not installed.").toString());
          }
        }

      }
    } catch (Exception exception)
    {
      SystemUtils.error(exception,
          "senasamplemeta: Unable to execute install filter.");
    }
    return 0;
  }

There are four main points of interest:

  1. Instead of hard-coding metadata values, they can be externalized into an UCM static table (see below)
  2. While looping over the rows in the table, this retrieves the property keysets for each row
  3. Verification check: ensure that the current metaField exists; if this is an install request, verify that the metaField doesn’t already exist; if this is an uninstall, verify that the metaField exists 
  4. Choose function based upon install/uninstall

The MetaFieldUtils helper class supplies the heavy lifting by encapsulating the COLLETION_UPDATE_META_TABLE and DEL_METADEF services.
Below is the detail of my static table:
<@table SenaSampleManageMetadata_Metadata@>
<table border=1><caption><strong>

<tr>
      <td>dsdComponentName</td>
      <td>dComponentName</td>
      <td>dsdVersion</td>
      <td>dName</td>
      <td>dCaption</td>
      <td>dType</td>
      <td>dIsRequired</td>
      <td>dIsEnabled</td>
      <td>dIsSearchable</td>
      <td>dIsOptionList</td>
      <td>dOptionListKey</td>
      <td>dOptionListType</td>
      <td>dDefaultValue</td>
      <td>dOrder</td>
      <td>dIsPlaceholderField</td>
      <td>dsdCheckFlag</td>
      <td>dsdDisableOnUninstall</td>
</tr>
<tr>
      <td>SenaSampleManageMetadata</td>
      <td>SenaSampleManageMetadata</td>
      <td>1.0.0</td>
      <td>xSampleMetaField1</td>
      <td>Sample Meta Field 1</td>
      <td>BigText</td>
      <td>0</td>
      <td>1</td>
      <td>1</td>
      <td>0</td>
      <td></td>
      <td></td>
      <td></td>
      <td>25000</td>
      <td>0</td>
      <td></td>
      <td></td>
</tr>
<tr>
      <td>SenaSampleManageMetadata</td>
      <td>SenaSampleManageMetadata</td>
      <td>1.0.0</td>
      <td>xSampleMetaField2</td>
      <td>Sample Meta Field 2</td>
      <td>BigText</td>
      <td>0</td>
      <td>1</td>
      <td>1</td>
      <td>0</td>
      <td></td>
      <td></td>
      <td></td>
      <td>25100</td>
      <td>0</td>
      <td></td>
      <td></td>
</tr>

</table>
<@end@>
After installing this component, you can verify that the metadata has been added:

Uninstalling the component will remove the attributes. Again, this is a POC, whether this is a feature you would want to implement is up to your specific requirements.

Going forward:

This code can be enhanced to include much more functionality. Different datatypes can be introduced and managed. The creation and population of views, drop-down lists (ddl), user attributes, etc can all be handled within this filter. Modification of these variables can also occur. Other possibilities include sharing an existing component attribute. You could check if the attribute exists, then modify the dComponentName attribute by appending your component name to the end of the existing string.

Values could also be maintained via component install strings and/or component configurations which would allow users to review and potentially modify during install. Component configurations can also be configured to be modifiable while the component is installed.



For the second feature, I’ve added a custom trace to the standard ddl so that it’s easily remembered and accessible by developers and admins alike. Remember, just because the initial developer that added the custom trace remembers the exact string doesn’t preclude that the next developer will remember when it comes time to debug … or, the even the initial developer just a few months down the road for that matter…



The table where the custom trace section is defined:
<@table SenaSampleManageMetadataTraceSections@>
<table border=1><caption><strong>SenaSampleManageMetadata Tracing Sections</strong></caption>
<tr>
  <td>itsSection</td>
  <td>itsDescription</td>
  <td>itsDefaultEnabled</td>
</tr>
<tr>
  <td>senasamplemeta</td>
  <td>Sena Sample Manage Metadata Tracing</td>
  <td></td>
</tr>
</table>
<@end@>
This table is merged into IdcTracingSections.


You can find the component here.


I hope you’ve found some part of this useful and perhaps even reusable.

Thanks,
-ryan

edits: 
12/17/2012: updated download link


Disclaimer: The code, opinions, and other content on this page are my own and are not necessarily representative of my employer. There is no expressed support for any code contained herein. Most code supplied within these pages has been created to proof out a specific concept or to show a specific case. No code should be considered production-ready without your own testing and validation.