Overview

The priint:comet InDesign Desktop Plug-Ins support custom find statements for the product panel. In the priint:publishing server environment, find statements are implemented as priint:publishing server plug-ins or plug-in methods, each method corresponding one find statement.

This documentation shows how to implement find statement methods.

Requirements

To implement custom find statements, you need

If you installed a full devstack including Eclipse workspace and plug-in examples, a demo project for find statements should already exist in your workspace called DemoFindStatement. The DataMapping and hierarchy features require

HelloWorld

Step by step

To demonstrate the principle of implementing custom find statements, we start with a simple step by step example.

  1. open an existing priint:publishing server plug-in library project or create a new plug-in library project
    If unsure, how to create or use a plug-in library project, please refer to the How to create a plug-in library guide.
  2. add an priint:publishing server plug-in class to this project, e.g.
    package com.priint.pubserver.comet.findstatement;
    
    @Stateless(mappedName="com.priint.pubserver.comet.findstatement.HelloWorld")
    @LocalBean
    @PubServerPlugin
    public class HelloWorld extends PluginControlDefault {
    
    }        
  3. create a new priint:publishing server method like shown below:
    @PubServerMethod(
        type=PluginMethod.MethodType.FIND_STATEMENT
        )
    public List<Bucket> findRootBucketsByLabel(
         @PubServerMethodParameter String modelId,
         @PubServerMethodParameter Context context,
         @PubServerMethodParameter String searchLabel) throws CometException {
      try {
        String sessionId = PluginControlDefault.getSessionId();
        EntityManagerLocal em = 
                       PluginUtils.getPlugin(Constants.MANAGER_ENTITY, 
                                             sessionId,
                                             EntityManagerLocal.class);
        List buckets = em.getEntityRootBuckets(sessionId, modelId, "", context, "");
        List result  = new ArrayList();
        for (Bucket bucket : buckets) {
          if (bucket.getLabel().contains(searchLabel)) {
            result.add(bucket);
          }
         }
    
        return result;
      }
      catch (CometException e) {
        throw e;
      }    
      catch (Exception e) {
        throw new CometException(this, "Unable to find buckets: " + e.getMessage(), e);
      }
    }          
              
  4. export the plug-in library to an EJB jar file and deploy it on your priint:publishing server
  5. login with InDesign Desktop and open the product panel. You should now see a new find statement like shown in this screenshot:
  6. By selecting the entry "findRootBucketsByLabel", the method implemented above will be invoked immediately with searchLabel set to an emtpy string.

Conclusion

Find statements / Java method details

Java find statement methods must meet the following requirements:

Parameters

Find statement methods must accept two parameters and up to four optional / additional parameters:

The modelId parameter is set to the identifier of the entity model associated to the Comet project, the current session is connected to.

The context parameter is initialized according to the product panel configuration.

Additional parameters (up to 4 Strings) are set to the value entered by the user in text input fields 1 to 4. Empty fields will result in an empty String, the value will never be null.

Examples

// valid: mandatory 1st and 2nd parameter plus one search criteria
@PubServerMethod(
    type=PluginMethod.MethodType.FIND_STATEMENT,
    label="Search by label"
    )
public List<Bucket> findRootBucketsByLabel(
     @PubServerMethodParameter String modelId,
     @PubServerMethodParameter Context context,
     @PubServerMethodParameter(
       name="Product label") String searchLabel) 
    throws CometException
// invalid: missing some @PubServerMethodParameter annotation
@PubServerMethod(
    type=PluginMethod.MethodType.FIND_STATEMENT,
    label="Search by label"
    )
public List<Bucket> findRootBucketsByLabel(
     String modelId,
     @PubServerMethodParameter Context context,
     String searchLabel) 
    throws CometException
// invalid: wrong type of second parameter
@PubServerMethod(
    type=PluginMethod.MethodType.FIND_STATEMENT,
    label="Search by label"
    )
public List<Bucket> findRootBucketsByLabel(
     @PubServerMethodParameter String modelId,
     @PubServerMethodParameter String context,
     @PubServerMethodParameter(
       name="Product label") String searchLabel) 
    throws CometException

Return Type

The return type of a find statement method must be java.util.List, element types must be either

In the first case, the standard data mapping (or the data mapping configured for this project) for products is used to map the result to the appropriate type for the priint:comet renderer (InDesign resp. pdf renderer), in the second case, you can control the mapping in the find statement itself.

Examples

// EntityData return type
List<EntityData> result = new ArrayList<>();
result.add (new Bucket());
// etc.

// for all entries, the default product data mapping or the data mapping
// configured for this project will be used:
return result;
// Product result type
List<Product> result = new ArrayList<>();

com.priint.pubserver.comet.bridge.entitydata.Product product = new Product();
product.setId(1);
product.setPageTemplateId(123);
// ... set more particular values

result.add (product);

return result;

Hierarchy

Starting with PublishingServer version 4.1.5 Build #1199, developers can control the hierarchy for the find statment result, i.e:

Of course, this requires, that the result products use proper identifiers, for Buckets, this must be the IDs (entityId, recordId, groupId, class) of the Bucket, for planning data the IDs of the planning record.

To define the type of hierarchy, the annotation @CometFindStatement was introduced, which can be applied to find statement methods in addition to the @PubServerMethod annotation.

Example for Bucket hierarchy

@PubServerMethod(
     type=PluginMethod.MethodType.FIND_STATEMENT,
     label="Show planning content",
     description="Find planning records to given document in Planning & Briefing")
@CometFindStatement(hierarchy=Hierarchy.BUCKET)
public List<Product> showPlanningContent (
    @PubServerMethodParameter String modelId,
    @PubServerMethodParameter Context context) throws CometException {
  
  List<Product> result = new ArrayList<>();
  String sessionId  = getSessionId();

  if (context.getDocumentId() != null && !context.getDocumentId().isEmpty()) {
    try {
      EntityManagerLocal em = getEntityManager();
      List<Planning> plannings = em.getEntityPlanningsOfDocument(
                                         sessionId, 
                                         modelId, 
                                         "", "", context.copy(), "");

      if (plannings != null) {
        // get the project default data mapping
        ProductDataMapping dataMapping = 
                            ProductDataMappingUtils.getProductDataMapping();
        
        // use the project default product data mapping:
        result = dataMapping.entitiesToProducts(plannings, 
                            null, // no parent
                            ProductDataMappingUtils.getEntityModel(modelId), 
                            context.copy(),
                            // use Id Builder for Buckets
                            CometIdBuilderFactory.useBucketId());  
      }
    } 
    catch (Exception e) {
      throw new CometException(this, 
                   "An error occured when requesting planning data for document.",
                   e);
    }
  }
  else {
    throw new CometException(this, "Document Id is null or empty.");
  }
  return result;
}

With only very slight changes, the method will show the Planning hierarchy

@PubServerMethod(
     type=PluginMethod.MethodType.FIND_STATEMENT,
     label="Show planning hierarchy",
     description="Find planning records to given document in Planning & Briefing")
@CometFindStatement(hierarchy=Hierarchy.PLANNING)
public List<Product> showPlanningHierarchy (
    @PubServerMethodParameter String modelId,
    @PubServerMethodParameter Context context) throws CometException {
  
  List<Product> result = new ArrayList<>();
  String sessionId  = getSessionId();

  if (context.getDocumentId() != null && !context.getDocumentId().isEmpty()) {
    try {
      EntityManagerLocal em = getEntityManager();
      List<Planning> plannings = em.getEntityPlanningsOfDocument(
                                         sessionId, 
                                         modelId, 
                                         "", "", context.copy(), "");

      if (plannings != null) {
        // get the project default data mapping
        ProductDataMapping dataMapping = 
                            ProductDataMappingUtils.getProductDataMapping();
        
        // use the project default product data mapping:
        result = dataMapping.entitiesToProducts(plannings, 
                            null, // no parent
                            ProductDataMappingUtils.getEntityModel(modelId), 
                            context.copy(),
                            // use Id Builder for Planning
                            CometIdBuilderFactory.usePlanningId());  
      }
    } 
    catch (Exception e) {
      throw new CometException(this, 
                   "An error occured when requesting planning data for document.",
                   e);
    }
  }
  else {
    throw new CometException(this, "Document Id is null or empty.");
  }
  return result;
}

Find statement label

You may want to have a user readable label for your find statement instead of the Java method name. This can easily be achieved with the @PubServerMethod label attribute:

@PubServerMethod(
    type=PluginMethod.MethodType.FIND_STATEMENT,
    label="Search by label"
    )
public List<Bucket> findRootBucketsByLabel(  // etc. ... 
See screenshot below for the effect.

Search field label

By using the @PubServerMethodParameter name attribute, the label of search input fields can be defined:

@PubServerMethod(
    type=PluginMethod.MethodType.FIND_STATEMENT,
    label="Search by label"
    )
public List<Bucket> findRootBucketsByLabel(
     @PubServerMethodParameter String modelId,
     @PubServerMethodParameter Context context,
     @PubServerMethodParameter(
       name="Product label") String searchLabel) 
    throws CometException { 
  // etc. ... 

Note: the space for labels is very limited in the InDesign product panel, only about 7 to 10 characters can be shown. The full label is always shown in the tooltip, when the mouse pointer is moved over the label text field.

Default value

Default values for the input text fields can be set with the @PubServerMethodParameter defaultValue attribute:

@PubServerMethod(
    type=PluginMethod.MethodType.FIND_STATEMENT,
    label="Search by label"
    )
public List<Bucket> findRootBucketsByLabel( 
     @PubServerMethodParameter String modelId,
     @PubServerMethodParameter Context context,
     @PubServerMethodParameter(
           name="Product label",
           defaultValue=Please enter search criteria") String searchLabel) 
    throws CometException { 
  // etc. ... 

More search criteria

Find statement methods support up to 4 search parameters. In addition to the (mandatory) modelId and context parameter, you may define zero to four String parameters for search criteria.

@PubServerMethod(
    type=PluginMethod.MethodType.FIND_STATEMENT,
    label="Detail search")
public List<Bucket> productDetailSearch(
  @PubServerMethodParameter String modelId,
  @PubServerMethodParameter Context context,
  @PubServerMethodParameter(name="ID") String id,
  @PubServerMethodParameter(name="Label") String label,
  @PubServerMethodParameter(name="Label") String alternativeLabel,
  @PubServerMethodParameter(name="Market") String market
  ) throws CometException {
  // etc. ...
Note: you can use the same label for several search parameters (like shown in this example), but of course not the same Java parameter name.

ProductDataMapping

Starting with PublishingServer version 4.1.5 Build #1199, you can implement custom data mapping classes for products.

These classes can be configured as the project default data mapping, and will be used in all situations, when EntityData is mapped to Product (unless implemented inline in find statements as shown above in some examples).

Basically, a ProductDataMapping implementation is

Interface Overview

The ProductDataMapping interface defines

public interface ProductDataMapping {
  public <T extends EntityData> List<Product> entitiesToProducts(
    List<T> entities, 
    EntityData parent, 
    EntityModel model, 
    Context context, 
    CometIdBuilder idBuilder) 
        throws CometException;
  
  public Product createProductFromBucket(
    Bucket bucket, 
    EntityModel model, 
    EntityData parent, 
    Context context, 
    CometIdBuilder idBuilder) 
        throws CometException;
  
  public Product createProductFromCord(
      Cord cord, 
      EntityModel model, 
      EntityData parent,
      Context context, 
      CometIdBuilder idBuilder) 
        throws CometException;
  // etc.
}

ProductDataMapping Implementation

Usually, developers only want to override certain aspects of data mapping. To ease the implementation of custom data mapping classes, a default (abstract) implementation of the ProductDataMapping interface is provided in the SDK, which is also base for the standard data mapping delivered with the PublishingServer.

The minimal implementation of a custom data mapping would be:

@Stateless(mappedName=CustomDataMappingPlugin.MAPPED_NAME)
@PubServerPlugin
@LocalBean
public class CustomDataMappingPlugin extends ProductDataMappingDefault implements
    ProductDataMapping, PluginControl {

  public static final String MAPPED_NAME = 
             "com.priint.pubserver.custom.datamapping.CustomDataMappingPlugin";

// custom plugins must override the entititesToProduct method, even if this (as shown) 
// just delegates to the default / super class implementation, otherwise the Plugin
// wont be registered properly as ProductDataMapping plugin.
  @Override
  @PubServerMethod(
      type=PluginMethod.MethodType.PRODUCT_DATA_MAPPING, 
      label="Custom Data Mapping", 
      description="Custom Description")
  public <T extends EntityData> List<Product> entitiesToProducts(
      @PubServerMethodParameter(name="entites") List<T> entities, 
      @PubServerMethodParameter(name="parent") EntityData parent, 
      @PubServerMethodParameter(name="model") EntityModel model,
      @PubServerMethodParameter(name="context") Context context, 
      @PubServerMethodParameter(name="idBuilder") CometIdBuilder idBuilder) throws CometException {
    return super.entitiesToProducts(entities, parent, model, context, idBuilder);
  }
}

Id Builder

Because setting up proper IDs is a crucial part of data mapping, an interface and some default implementations are provided in the PubServerSDK for this.

Usually

Products have 4 ID properties, though in PublishingServer environment, only the last ("StringId", alphanumerical Id) has semantic meaning.

The general pattern to set product IDs should be

CometIdBuilder idBuilder; // provided to method call
EntityData     entity;    // from request result, e.g. Bucket, Text, ...

Product product = new Product();
product.setId(1);   // must be non-zero, no further semantic
product.setId2(0);  // usually ignored
product.setId3(0);  // usually ignored
product.setStringId(idBuilder.createEntityId(entity));

// or, to include parent IDs:
EntityData parent;
product.setStringId(idBuilder.createEntityId(entity, parent));

The following IdBuilders are included in the standard distribution:

// use the Bucket resp. Content entity IDs (for planning data also):
CometIdBuilder idBuilder = CometIdBuilderFactory.useBucketId();

// use Planning IDs for planning records:
CometIdBuilder idBuilder = CometIdBuilderFactory.usePlanningId();

// use Bucket IDs for planning records and the Planning IDs as 
// parent IDs:
CometIdBuilder idBuilder = CometIdBuilderFactory.useBucketAndPlanningId();

Configuration

Configuration using Panel Config Editor

Starting with

findstatement visibility and order can be controlled using the ISON product panel configuration (after updating PubServer, ISON clients also must be updated).
In the Comet Explorer, navigate to your project (e.g. demo), open InDesign Desktop Panels and right-click on Product Pool. Choosing the Edit Panel Context Criteria entry opens the Panel Config Editor:

In the Eitor, choose the Find Statements tab.
Find statements can be disabled or enabled via drag & drop, also the order of enabled find statements can be controlled via drag & drop.
Since this editor show find statements currently registered as PubServer PlugIns only, you can also decide, whether unknown statements (not configured or deployed in the future) should be shown or not:

Configuration using XML Files

The XML configuration file responsible for storing product panel configuration can be found in the repository in

com.priint.pubserver.comet.config.CometConfiguration.CLIENT.PROJECT.panel.productpool
Name of the file is config.xml. Using the Repository Explorer, the configuration can be changed by directly editing the XML file:

The panel configuration file contains a custom object of type CometProductPanelConfig:

<con:PluginConfig ...>
  <con:name>config.xml</con:name>
  <con:type>CometProductPanelConfig</con:type>
  <con:description>0 [CometProductPanelConfig]</con:description>
  <con:custom>
    <comet:productPanelConfig>
      <comet:id>0</comet:id>
      <!-- etc ... -->
    
In this section, several aspects of the panel can be configured.

findStatementsVisibleByDefault

To control the default visibility of statements, use the findStatementsVisibleByDefault property:

<!-- don't show statements not explicitly set to enabled: -->
<comet:findStatementsVisibleByDefault>false</comet:findStatementsVisibleByDefault>
<!-- do show statements not explicitly set to disabled: -->
<comet:findStatementsVisibleByDefault>true</comet:findStatementsVisibleByDefault>

activeFindStatements

To enable certain find statements, use the activeFindStatements property:

<comet:activeFindStatements>
  <comet:findStatement>
    <pl:pluginMappedName>
      com.priint.pubserver.plugins.pubplanner.findstatements.FindStatement
    </pl:pluginMappedName>
    <pl:methodName>findRootBucketsByLabel</pl:methodName>
    <pl:label>Find bucket by label</pl:label>
    <pl:parameters name="modelId" 
                      description="" 
                      defaultValue="" 
                      type="java.lang.String"/>
    <pl:parameters name="context" 
                      description="" 
                      defaultValue="" 
                      type="com.priint.pubserver.plugin.entitydata.Context"/>
    <pl:parameters name="searchLabel" 
                      description="" 
                      defaultValue="" 
                      type="java.lang.String"/>
  </comet:findStatement>
  <comet:findStatement>
    <!-- more find statements ... -->
</comet:activeFindStatements>
The activeFindStatements element contains a list of elements of type PluginMethod. Each PluginMethod refers to one method implemented in a PubServer Plugin with PluginMethod.MethodType.FIND_STATEMENT.

To identifiy find statements, the following information is required:

The following information should be set to make it possible to continue editing this file in the panel config editor:

inactiveFindStatements

To explicitly disable find statements (regardless of the findStatementsVisibleByDefault setting), use the inactiveFindStatements property.

Type and structure of this element is exactly the same like activeFindStatements (in this example, we use the short notation for method parameters):

<comet:inactiveFindStatements>
  <comet:findStatement>
    <pl:pluginMappedName>
      com.priint.pubserver.plugins.pubplanner.findstatements.FindStatement
    </pl:pluginMappedName>
    <pl:methodName>findRootBucketsByLabel</pl:methodName>
    <pl:label>Find bucket by label</pl:label>
    <pl:parameters type="String"/>
    <pl:parameters type="Context"/>
    <pl:parameters type="String"/>
  </comet:findStatement>
  <comet:findStatement>
    <!-- more find statements ... -->
</comet:inactiveFindStatements>