Geekdom

Invoking a Task via XPRESS in Sun's Identity Manager 7.1

Occasionally, I've wanted to invoke an Identity Manager Task from within a rule in XPRESS. Here is a rule that will do that:

<Rule name="runTask">
    <RuleArgument name='taskName'/>
    <RuleArgument name='taskVariables'/>
    <RuleArgument name='execMode'/>
    
    <block>
        <defvar name='processView'>
            <new class='com.waveset.object.GenericObject'>
                <ref>taskVariables</ref>
            </new>
        </defvar>
        
        <invoke name='setId'>
            <ref>processView</ref>
            <s>Process</s>
        </invoke>
        
        <invoke name='put'>
            <ref>processView</ref>
            <s>task.process</s>
            <ref>taskName</ref>
        </invoke>
        
        <invoke name='put'>
            <ref>processView</ref>
            <s>task.taskName</s>
            <ref>taskName</ref>
        </invoke>
        
        <cond>
            <ref>execMode</ref>
            <invoke name='put'>
                <ref>processView</ref>
                <s>task.execMode</s>
                <ref>execMode</ref>
            </invoke>
        </cond>
        
        <invoke name='checkinView'>
            <rule name='getCallerSession'/>
            <ref>processView</ref>
            <null/>
        </invoke>
    </block>
</Rule>

And here is a sample invocation:

<rule name='runTask'>
    <argument name='taskName' value='myTask'/>
    <argument name='taskVariables'>
        <map>
            <s>taskArgument1</s>
            <ref>myVariable</ref>
            <s>taskArgument2</s>
            <s>String Data</s>
        </map>
    </argument>
    <argument name='execMode' value='async'/>
</rule>

The value of execMode should be one of the following:

sync
Specifies synchronous or foreground execution
async
Specifies asynchronous or background execution
asyncImmediate
Specifies asynchronous with immediate thread launch

HTTP File Uploads with Sun's Identity Manager 7.1

Identity Manager has a display class called FileUpload which you can use to do HTTP File Uploads. In a form, it would look something like this:

<Field name='fileUpload'>
    <Display class='FileUpload'>
        <Property name='Title' value='File to upload'/>
    </Display>
</Field>

In the above example, the field fileUpload would be set to an object of type DataSource. I want to use the contents of the file as a string in my XPRESS rules, but how do I do that?

The most obvious thing to try would be to invoke the appropriate java directly from XPRESS. Here is a rule that does that:

<Rule name="getStringFromFileUpload">
    <RuleArgument name='fileUpload'/>
 
    <block>
        <defvar name='bufferedReader'>
            <new class='java.io.BufferedReader'>
                <new class="java.io.InputStreamReader">
                    <invoke name='getInputStream'>
                        <ref>fileUpload</ref>
                    </invoke>
                </new>
            </new>
        </defvar>
         
        <defvar name='fileContentAsString'/>
         
        <defvar name='nextLine'>
            <invoke name='readLine'>
                <ref>bufferedReader</ref>
            </invoke>
        </defvar>
         
        <while>
            <notnull>
                <ref>nextLine</ref>
            </notnull>
            <block>
                <setvar name='fileContentAsString'>
                    <concat>
                        <ref>fileContentAsString</ref>
                        <ref>nextLine</ref>
                    </concat>
                </setvar>
                <setvar name='nextLine'>
                    <invoke name='readLine'>
                        <ref>bufferedReader</ref>
                    </invoke>
                </setvar>
            </block>
        </while>
        <ref>fileContentAsString</ref>
    </block>
</Rule>

Unfortunately, when I attempt that, I get this:

XPRESS exception ==> com.waveset.util.WavesetException: XPRESS exception ==> com.waveset.util.WavesetException: XPRESS exception ==> com.waveset.util.WavesetException: Couldn't find method getInputStream() in class com.waveset.ui.util.BufferedRequest$AttachmentDataSource

That's odd, because there really is a method called getInputStream on AttachmentDataSource. Ah well, so I seem to have hit a bug in XPRESS. No bother, I'll just try invoking the appropriate Java in Javascript. Here is a rule that does that:

<Rule name="getStringFromFileUpload">
    <RuleArgument name='fileUpload'/>
 
    <script>
        importPackage(Packages.javax.activation);
        importPackage(Packages.com.waveset.ui.util);
        importPackage(Packages.java.io);
        importPackage(Packages.java.lang);
       
        var attachment = env.get('fileUpload');
       
        var fileContentsAsString = '';
       
        try {
       
            var bufferedReader = new BufferedReader(new InputStreamReader(attachment.getInputStream()));
       
            var line = '';
       
            while ((line = bufferedReader.readLine()) != null) {
                fileContentsAsString += line + "\n";
            }
        }
            catch(err) {
            result = err.getMessage();
        }
         
        fileContentsAsString;
    </script>
</Rule>

Hmm, no dice that way, either:

org.mozilla.javascript.EvaluatorException: Class org.mozilla.javascript.NativeJavaMethod can not access a member of class com.waveset.ui.util.BufferedRequest$AttachmentDataSource with modifiers "public"

OK, so it seems that it's not just XPRESS, but Javascript also has a problem invoking the method. The only solution I could find is to write a custom bit of Java to do the actual work like this:

package net.woogie.idm.util;
 
import java.io.*;
import javax.activation.*;
import com.waveset.ui.util.*;
 
public class Attachment {
 
    public Attachment() {
    }
 
    public String toString(DataSource attachment) throws IOException {
 
        String fileContentsAsString = "";
 
        try {
            InputStream inputStream = attachment.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
 
            String line;
 
            while ((line = bufferedReader.readLine()) != null) {
                fileContentsAsString += line + "\n";
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
 
        return fileContentsAsString;
    }
}

And to invoke it via XPRESS like this:

<Rule name="getStringFromFileUpload">
    <RuleArgument name='fileUpload'/>
 
    <invoke name='toString'>
        <new class='net.woogie.idm.util.Attachment'/>
        <ref>fileUpload</ref>
    </invoke>
</Rule>

This works, but it sort of bums me out because it would be nice to have an XML only solution to this.

Parsing XML in XPRESS Rules Within Sun's Identity Manager

I've been doing a lot of work with Sun's Identity Manager lately, and have some things that might be of interest to other's so I'll try to post them here as time permits.

I think the coolest thing I've done so far is to build a library for parsing XML within XPRESS. It's actually fairly straightforward, but it took me long enough to get right that it seems like a good thing to share. I have 2 XPRESS rules:

transformXML
Takes 2 strings, xml and xsl, as input, and returns the transformed data.
validateXML
Takes 2 strings, xml and xsd, as input, and returns 1 true if the XML validates against the XSD, and returns false otherwise.

Both rules invoke Java to do the actual work, but for reasons I'll explain later, validateXML is a bit more complex than transformXML. The XML transformation is simply a matter of figuring out the proper incantation to invoke:

<Rule name='transformXML'>
  <RuleArgument name='xml'/>
  <RuleArgument name='xsl'/>
 
  <block>
    <defvar name="StringWriter">
      <new class="java.io.StringWriter"/>
    </defvar>
    <invoke name="transform">
      <invoke name="newTransformer">
        <invoke name="newInstance" class="javax.xml.transform.TransformerFactory"></invoke>
        <new class="javax.xml.transform.stream.StreamSource">
          <new class="java.io.StringReader">
            <ref>xsl</ref>
          </new>
        </new>
      </invoke>
      <new class="javax.xml.transform.stream.StreamSource">
        <new class="java.io.StringReader">
          <ref>xml</ref>
        </new>
      </new>
      <new class="javax.xml.transform.stream.StreamResult">
        <ref>StringWriter</ref>
      </new>
    </invoke>
 
    <invoke name="toString">
      <ref>StringWriter</ref>
    </invoke>
  </block>
</Rule>

That looks fairly complicated, but it is pretty much a straight translation from the Java to XPRESS invocations. Unfortunately, XML validation isn't quite as straightforward. If there is a way to do it without exception handling, which is not available in XPRESS, I don't know what that way is. So, the direct invocations similar to the code above will not work.

It would be possible to write a bit of simple Java code to wrap the exception handling in a nice set of return codes, deploy that code with the IDM deployment and then invoke that from XPRESS, but I was really hoping to avoid custom Java if possible. In this case, JavaScript comes to the rescue (man, I never thought I would say that):

<Rule name='validateXML'>
  <RuleArgument name='xsd'/>
  <RuleArgument name='xml'/>
 
  <cond>
    <and>
      <notnull>
        <ref>xsd</ref>
      </notnull>
      <notnull>
        <ref>xml</ref>
      </notnull>
    </and>
    <script>
      importPackage(Packages.javax.xml.validation);
      importPackage(Packages.java.io);
      importPackage(Packages.javax.xml.transform.stream);
 
      var xsd = env.get('xsd');
      var xml = env.get('xml');
 
      var schemaFactory = SchemaFactory.newInstance('http://www.w3.org/2001/XMLSchema');
      var schema = schemaFactory.newSchema(new StreamSource(new StringReader(xsd)));
 
      var validator = schema.newValidator();
 
      var result = 'true';
 
      try {
        validator.validate(new StreamSource(new StringReader(xml)));
      }
      catch(err) {
        result = 'false';
      }
 
      result;
    </script>
    <s>false</s>
  </cond>
</Rule>

I've attached a rule library containing both these rules that you should be able to import and use in your project.

Have Fun.

linksysmon Is Dead, Long Live linksysmon

Well, it looks like there might still be some interest in linksysmon, so I've put a link to the final tar file here.

linksysmon Is Dead, Long Live openwrt

With the update of the new site, I'm dropping support for linksysmon all together. A cursory glance at the logs finds no downloads from anything but the search engines, and I've upgraded my router hardware to a Linksys WRT54GL, so I don't even use it anymore. Instead, I use openwrt.

Yet Another New Design

I'm in the process of upgrading the woogie.net server hardware, and have taken the opportunity to redesign the site again. The layout still needs some tweaking, and it will take a bit to get all the old content ported, but it's close enough that I'm tired of waiting. I don't know why I spend more time designing the site than actually updating content, but that's the way I am.