Overview

Java methods of a priint:publishing server plug-in can be invoked in cscript. Some prerequisites must be fulfilled:

In return you gain smooth integration of Java functionality in cscript - at almost no cost.

Requirements

Hello World

We expect basic knowledge of priint:publishing server plug-in development and cscript programming
To try out one of the following examples

  1. create a new priint:publishing server PluginLibrary project in Eclipse
  2. put the Java sample code in a new priint:publishing server plug-in class
  3. deploy the plug-in
  4. in Ison connect to a Comet project and create a new cscript. I would suggest to create a panel action, which can then be run from one of the priint:comet InDesign Desktop panels.
  5. in InDesign connect to this project (or clear script cache, if you are already connected to this project) and select the test cscript from the panel menu

Example 1 - Hello

The basic concept is best explained with this example:

@Stateless(mappedName=HelloWorld.MAPPED_NAME)
@LocalBean
@PubServerPlugin
public class HelloWorld extends PluginControlDefault {
  
  public static final String MAPPED_NAME = "com.priint.pubserver.comet.cscript.HelloWorld";
  
  /**
   * Hello World
   * 
   * @return Hello World
   */
  @PubServerMethod(type=PluginMethod.MethodType.GENERIC)
  @CometCScriptFunction
  public String helloWorld() {
    return "Hello World";
  }
}   
the corresponding cscript code to call this function:
#include "[pubserver]/plugin/com.priint.pubserver.comet.cscript.HelloWorld.c"
     
int main() {
    char * message = helloWorld();
    showmessage("Server said: %s", message);
    release(message);
    return 0;
}

Example 2 - Parameters

The second example shows, how to pass parameters to a method:

@Stateless(mappedName=HelloWorld.MAPPED_NAME)
@LocalBean
@PubServerPlugin
public class HelloWorld extends PluginControlDefault {
  
  public static final String MAPPED_NAME = "com.priint.pubserver.comet.cscript.HelloWorld";
  /**
   * Hello World
   * 
   * @return Hello World
   */
  @PubServerMethod(type=PluginMethod.MethodType.GENERIC)
  @CometCScriptFunction
  public String helloWorld2(@PubServerMethodParameter String login) {
    return "Hello " + login + "!";
  }
}   
The corresponding cscript code:
#include "[pubserver]/plugin/com.priint.pubserver.comet.cscript.HelloWorld.c"

int main() {
    char * login   = alloc(4096);
    char * message = 0;
    
    login   = system::login(login);
    message = helloWorld2(login);
    showmessage("Server said: %s", message);
    release(message);
    release(login);
    return 0;
}

Résumé

and and

More basic Information

The Java / Eclipse project DemoCScript delivered with the Ison workspace contains lots of more examples and information.

Functions, Procedures and Error Handling

The cscript language and Java language - though similar regarding the syntax - differ in many aspects:

The @CometCScriptFunction allows to specify, whether the cscript stub for a Java method uses should be called as a function or as a proceddure, no matter, if the method returns a value or not. Use the attribute callStyle:

If you use CallStyle.PROCEDURE for a method, which actually returns a value, an additional out variable is inserted at the front of the cscript function parameterlist.(see 3d example below).

Example 1 - "function"

@Stateless(mappedName=HelloWorld.MAPPED_NAME)
@LocalBean
@PubServerPlugin
public class HelloWorld extends PluginControlDefault {

  @PubServerMethod(type=PluginMethod.MethodType.GENERIC)
  @CometCScriptFunction(callStyle=CallStyle.FUNCTION /* = default! */ )
  public int max(
      @PubServerMethodParameter(name="n1") int n1, 
      @PubServerMethodParameter(name="n2") int n2) {
    return n1 > n2 ? n1 : n2;
  }
}   
The corresponding cscript code:
// function signature
int max(int n1, int n2);

// example call
int n = max(17, 23);

Example 2 - "procedure"

@Stateless(mappedName=HelloWorld.MAPPED_NAME)
@LocalBean
@PubServerPlugin
public class HelloWorld extends PluginControlDefault {

  @PubServerMethod(type=PluginMethod.MethodType.GENERIC)
  @CometCScriptFunction(callStyle=CallStyle.PROCEDURE)
  public void sendEmail(
      @PubServerMethodParameter(name="recipient") String recipient) throws CometException {
    try {
       deliverEmail(recipient);
    }
    catch (Exception e) {
      int errorCode = 13;
      throw new CometException(errorCode, "Could not deliver Email, reason: " + e.getMessage(), e);
    }
  }
}   
The corresponding cscript code:
// function signature
int sendEmail(char* recipient);

// example call
int errorCode = sendEmail("support@priint.com");

Example 3 - "procedure with return value"

@Stateless(mappedName=HelloWorld.MAPPED_NAME)
@LocalBean
@PubServerPlugin
public class HelloWorld extends PluginControlDefault {

  @PubServerMethod(type=PluginMethod.MethodType.GENERIC)
  @CometCScriptFunction(callStyle=CallStyle.PROCEDURE)
  public String sendEmail(
      @PubServerMethodParameter(name="recipient") String recipient) throws CometException {
    try {
       deliverEmail(recipient);
      String status = getMailDeliveryStatus();
      return status;
    }
    catch (Exception e) {
      int errorCode = 13;
      throw new CometException(errorCode, "Could not deliver Email, reason: " + e.getMessage(), e);
    }
  }
}   
The corresponding cscript code
// function signature
// An additional parameter is inserted at the front of the  
// function parameter list (char** out), see explanations
// above.
int sendEmail(char** out, char* recipient);

// example call
char* status = 0;

// please note the address-operator, we pass the address of
// the string (char*) pointer. If call succeeds, status points
// to a valid char* address, otherwise status will remain null.
int errorCode = sendEmail(&status, "support@priint.com");

if (errorCode == 0) {
  wlog("", "Sending mail succeeded, current status: %s\n", status);
}
else {
  wlog("", "Sending mail failed with error code '%d'\n", errorCode);
}
if (status != 0) {
  release(status);
}

Example 4 - error handling using function style

Unless specified otherwise, the return value of a function on errors is

If another value should be returned in case of errors, you can use the failureValue attribute of the @CometCScriptFunction annotation.

Note:

@Stateless(mappedName=HelloWorld.MAPPED_NAME)
@LocalBean
@PubServerPlugin
public class HelloWorld extends PluginControlDefault {
  
  public static final String MAPPED_NAME = "com.priint.pubserver.comet.cscript.HelloWorld";
  /**
   * Hello World
   * 
   * @return Hello World
   */
  @PubServerMethod(type=PluginMethod.MethodType.GENERIC)
  @CometCScriptFunction(failureValue="You are not welcome. Goodbye.")
  public String helloWorld3(@PubServerMethodParameter String login) throws PubServerException {
    if (login.equals("me")) {
        return "Hello " + login + "!";
    } 
    else {
      throw new AuthenticationFailureException();
    }
  }
}   
Corresponding cscript code:
#include "[pubserver]/plugin/com.priint.pubserver.comet.cscript.HelloWorld.c"

int main() {
    char * login   = alloc(4096);
    char * message = 0;
    int result     = 0;
    
    login   = system::login(login);
    message = helloWorld3(login);

    // in real life you chould check for null
    if (strcmp(message, "You are not welcome. Goodbye.") == 0) {
     showmessage("Hello world failed: %s", message);
      result = 1;
    }
    release(message);
    release(login);
    return result;
}

Error Reporting

With PublishingServer 4.1.5.1 Build #2327, a slight improvement has been introduced to control, how errors are reported to the user.

The @CometCScriptFunction annotation now supports the additional attribute errorReporting, which can be set to

Note, that these settings have no effect on error handling.

Example:

@PubServerMethod(type=PluginMethod.MethodType.GENERIC)
@CometCScriptFunction(errorReporting=CometCScriptFunction.ErrorReporting.ALERT)
public void myMethod() throws CometException {
  try {
    // working code  
  } catch (Exception e) {
    // we use the server side TranslationUtils to localize the message. 
    // This can be useful, if setting up the message also requires some
    // server side calculation:
    String checkFailureCause = TranslationUtils.getTranslation("permissionDenied"); 
    throw new CometCScriptException(checkFailureCause);
  }
}

Example 2:

@PubServerMethod(type=PluginMethod.MethodType.GENERIC)
@CometCScriptFunction(errorReporting=CometCScriptFunction.ErrorReporting.LOCALIZED)
public void myMethod() throws CometException {
  try {
    // working code  
  } catch (Exception e) {
    // we directly pass the translation key. Message will be translated on 
    // client side, because we set errorReporting=LOCALIZED.
    // The result will be exactly the same like in the first example
    throw new CometCScriptException("permissionDenied");
  }
}

Using Context 

Starting with PubServer 4.1.6, the Comet Project Default Context can be injected in Java methods exposed to cscript.

To configure the project default context, navigate to the comet project in the ISON Comet Explorer, right-click on the project and choose "Edit project properties". This will open an editor and allow to configure context criteria just like for the Product panel configuration, placeholder or table insert methods.

The following code snippets show, how to use context in Java methods exposed to cscript:

Java Method:

@PubServerMethod(type=PluginMethod.MethodType.GENERIC,
    label="Example for methods with context parameter",
    description="")
@CometCScriptFunction(
  name="printContext",
  resultDescription="Context as string")
public String printContext(@PubServerMethodParameter() Context context) {
  return context.toString();
}

CScript Function Signature:

char * printContext(); // no parameters!

Usage Example:

#include "[pubserver]/plugin/com.priint.pubserver.comet.cscript.UsingContext" 
int main() { 
   char * context = printContext(); 
   showmessage("Context: %s", context); 
   release(context); 
   return 0; 
}

The following rules apply for Context parameters:

Type Mapping

Overview

The Java / cscript interface supports the types listed below as parameter or return types. General directives in cscript:

Some examples:
// valid:
WorkflowStatus status = 0;
status = getStatusOfPublication("67110123");
publication::workflowstatus::release(status);

// invalid:
WorkflowStatus status = publication::workflowstatus::alloc();
status = getStatusOfPublication("67110123");
publication::workflowstatus::release(status);
// Why? The memory formerly allocated for status is not 
//      referenced any more and therefore can no more be released.

// valid, but dubious:
WorkflowStatus status = publication::workflowstatus::alloc();
publication::workflowstatus::release(status);
status = getStatusOfPublication("67110123");
publication::workflowstatus::release(status);

// valid, but dubious:
char * publicationId = "67110123";
publicationId = findParentPublication(publicationId);
release(publicationId);

// wrong!
int n = getMax(17, 23);
release(n);
Notes for Java developers:

Type Mapping Table

Java Type CScript Type CScript Type (out) Remarks
void, Void int - There is no void equivalent in cscript, the int return type can just be ignored.
boolean, Boolean int int* Possible loss of data
byte, Byte int int* Possible loss of data
short, Short int int* Possible loss of data
int, Integer int int*  
long, Long int int*  
BigDecimal int int* Possible loss of data
BigInteger int int* Possible loss of data
float, Float float float*  
double, Double float float*  
String char* char**

For the cscript result variable no memory must be allocated, but the variable must be released:

Release:
  release(str);

c.p.p.c.b.e.RecordId IDType IDType*

Release:
  idtype::release(id);

c.p.p.c.b.e.Element Element Element*

Release:
  element::release(obj);

c.p.p.c.b.e.Parameter Parameter Parameter*

Release:
  publication::parameter::release(param);

c.p.p.c.b.e.Product Product Product*

Release:
  product::release(p);

c.p.p.c.b.e.Planning Planning Planning*

Release:
  publication::planning::release(p);

c.p.p.c.b.e.Publication Publication Publication*

Release:
  publication::release(pub);

c.p.p.c.b.e.PublicationType PublicationType PublicationType*

Release  publication::publication::release(pubType);

c.p.p.c.b.e.WorkflowStatus WorkflowStatus WorkflowStatus*

Release:
  publication::workflowstatus::release(status);

java.util.List<Boolean> List List*

Possible loss of data

Release:
  list::release(intList);

java.util.List<Byte> List List*

Possible loss of data

Release:
  list::release(intList);

java.util.List<Short> List List*

Possible loss of data

Release:
  list::release(intList);

java.util.List<Integer> List List*

Release:
  list::release(intList);

java.util.List<Long> List List*

Release:
  list::release(intList);

java.util.List<BigDecimal> List List*

Possible loss of data

Release:
  list::release(intList);

java.util.List<BigInteger> List List*

Possible loss of data

Release:
  list::release(intList);

java.util.List<Float> FloatList FloatList*

Release:
  floatlist::release(floats);

java.util.List<Double> FloatList FloatList*

Release:
  floatlist::release(floats);

java.util.List<String> StringList StringList*

Release:
  stringlist::release(strings);

java.util.List<c.p.p.c.b.e.RecordId> IDTypeList IDTypeList*

Release:
  idtypelist::release(ids);

java.util.List<c.p.p.c.b.e.Element> ElementList ElementList*

Release:
  elementlist::release(elements);

java.util.List<c.p.p.c.b.e.Parameter> ParameterList ParameterList*

Release:
  publication::parameterlist::release(params);

java.util.List<c.p.p.c.b.e.Product> ProductList ProductList*

Release:
  productlist::release(products);

java.util.List<c.p.p.c.b.e.Planning> PlanningList PlanningList*

Release:
  publication::planninglist::release(plannings);

java.util.List<c.p.p.c.b.e.Publication> PublicationList PublicationList*

Release:
  publication::publicationlist::release(pubs);

java.util.List<c.p.p.c.b.e.PublicationType> PublicationTypeList PublicationTypeList*

Release:
  publication::publicationtypelist::release(pubTypes);

java.util.List<c.p.p.c.b.e.WorkflowStatus> WorkflowStatusList WorkflowStatusList*

Release:
  publication::workflowstatuslist::release(states);

java.util.List<c.p.p.c.b.e.KeyValue>

c.p.p.c.b.e.KeyValueList
KeyValues KeyValues*

Release:
  keyvalues::release(values);

c.p.p.p.e.Context - - See section Using Context
Context parameters are invisible for the cscript developer and will be initialized / released automatically

Helpers

The @PubServerMethod, @CometCScriptFunction and @PubServerMethodParameter annotations allow detailed documentation of the Java methods and corresponding cscript functions.
At the time (Version 4.0.5) there is no support for this in the Ison editor, so cscript developers must derive all information from the Java classes and methods - or download generated headers with documentation using a suitable tool.

You can use the Placeholder values panel in InDesogn Desktop (this requires a developer license for the comet plug-ins):

placeholder-values

Enter a File ID in the Script ID / File ID field and click the disk symbol to download this file.

Start with File-ID [pubserver]/plugins.c, this will list all available includes, Example::

#include "[pubserver]/plugin/com.priint.pubserver.comet.cscript.HelloWorld.c"
#include "[pubserver]/plugin/com.priint.pubserver.comet.cscript.PrimitiveTypes.c"
#include "[pubserver]/plugin/com.priint.pubserver.comet.bridge.cscript.CScriptStandardLibrary.c" 

Now you can repeat this procedure for any include of interest.

Alternatively you can download [pubserver]/plugins.h , this will show all function signatures including inline documentation:

/**
 * <p>sophisticated hello world example</p>
 * <p>
 *   This is a more sophisticated hello world example.
 *   
 *   The PubServerMethod label and description are used for cscript inline
 *   documentation.
 *   Try downloading
 *     [pubserver]/plugin/com.priint.pubserver.comet.cscript.HelloWorld.h
 *   from your client application (e.g. InDesign Desktop), to see the effect.
 *   The following cscript code calls this method:
 *   <pre>
 *     #include "[pubserver]/plugin/com.priint.pubserver.comet.cscript.HelloWorld.c"
 *     int main() {
 *       char * login = alloc(4096);
 *       char * hello = helloWorld3(system::login(login));
 *       showmessage("Server said: %s", hello);
 *       release(hello);
 *       release(login);
 *       return 0;
 *     }
 *   </pre>
 * </p>
 * @return char*
 * @param char* login user login on the client host
 */
char* helloWorld(char* login);
...
This works for all plug-ins or for a particular plug-in, e.g..
[pubserver]/plugin/com.priint.pubserver.comet.cscript.HelloWorld.c

 

Evident Questions


Question

Can I define only one cscript / Java method per plug-in?

Answer

Of course not, you can define an arbitrary number of methods. The more methods, the larger the generated cscript stub code will be, therefore you should not define too much methods in one plug-in.
There is a second reason, why too many methods in one plug-in could cause problems: function names must be unique in cscript. It is not possible to overload functions in cscript.

The resulting limitations apply for methods in one plug-in and also for methods in all plug-ins, that potentially are included in one cscript at the same time: if plug-in A contains a function named foo and plug-in B contains a function named foo also, they cannot be included in one cscript at the same time.


Question

Can I propagate existing Java methods as cscript functions?

Answer

Yes, if your method is a PubServer plug-in method and if you respect the limitations regarding supported types mentioned above. In this case, all you have to do is add the @CometCScriptFunction annotation to your method..


Question

What about performance?

Answer

When invoking Java methods from cscript, the following happens: cscript objects and values are serialized to XML, the XML is sent to the priint:publishing server via SOAP, the server unpacks the SOAP message and parameter XML and invokes the Java plug-in. The call result is again transformed in XML, sent back to InDesign via SOAP, unpacked, unserialized and assigned to the cscript result variables. Sounds like much effort and costs.

Typically a roundtrip (without any calculation done on the server) should take about 30ms. This is an acceptable overhead for complex operations, but far too much for simple calculations (such as strcmp, strcat ...), which can easily and much more efficient be implemented in cscript.


Question

Why is the char resp. Character type not supported?

Answer

There is no suitable corresponding XML Schema builtin type for single characters. But more important: single characters could be misinterpreted, because strings in cscript are utf-8 encoded. This means, a visible character can consist of several character bytes. All cscript string functions respect this: the length returned by strlen is the number of visible characters, not of character bytes; any string position in any function refers to visible characters (not bytes).


Question

What is the maximum length for Strings returned from Java methods?

Answer

Virtually infinite. The memory is allocated dynamically.


Question

Is the number and size of parameters limited?

Answer

No - at least not regarding translations and transport.


Questions

Are tags (such as <Record.Id> ...) supported in scripts, which include Java methods?

Answer

Java methods can be included in any cscript: placeholder scripts, panel scripts, layout rules, build scripts etc.
Which tags and global variables are available, depends on the call environment, there are no exceptions for scripts including Java methods.
Example:
if run in a Placeholder load script context, the global variables denoting the placeholder and product linked with this placeholders are available, auch as int gRecordID, char * gRecordStringID etc.
Refer to the InDesign Plugin cscript documentation (section "placeholder") for details or to the separate documentation about tags and evaluation rules in PubServer environemnt.


Question

Can I access document poperties from Java?

Answer

Only those passed as parameter. The communication is unidirectional, you cannot callback InDesign from the Java plug-in. Therefore it is neither possible to access document frame properties nor to manipulate page items from your Java plug-in.


Question

We were talking about InDesign, is the cscript / Java bridge supported in the PDF Renderer too?

Answer

yes, of course.