2015-11-09

New S1000D-related blog

I have started a new blog that focuses on my work and experiences related to S1000D. The blog can be followed at http://www.s1000d-developer.com/.

If there are any specific S1000D-related topics you would like me to address and/or discuss, feel free to contact me.

2015-11-05

Connecting to ActiveX control events in IE11 mode

I have had the unfortunate recent experience of trying to get a web-based application that uses a commercial ActiveX control to work in IE11. In previous versions of IE (10 and earlier), one can use the attachEvent() method to add a callback to an ActiveX control-defined event. In the application I am working on, a dynamic <object> element is constructed to instantiate the control on a page. Then, attachEvent() is used to add callbacks to the events provided by the control.

For example, if control represent a reference to the <object> node, the following is how I attached to an event:

control.addEvent('FileChanged', myfunc);

In IE11 standards (edge) mode, addEvent() is not available since it is non-standard, where MS states to use addEventListener() instead. Unfortunately, addEventListener() does not work with ActiveX control events.

After some searching around, it appears there exists a way to attach to ActiveX events by creating a function whose name is the ID of the control, followed by "::" and the event name. For example, if the ID of the control is "myAxId" (when using the <object>, the ID is specified by the id attribute of the element), and the event name is "FileChanged", you can do:

function myAxId::FileChanged() { ... }

There is also a way to use <script> tags to bind to ActiveX events (see https://stackoverflow.com/a/152724/719934).

The problem is these are static-based approaches and do not work well when ActiveX controls are instantiated dynamically. Therefore, based on on the approach used at https://stackoverflow.com/a/10557222/719934, I created the following generic JavaScript function for connecting to an ActiveX-defined event:

function connectToAXEvent(o, etype, func)
{
  if (o.attachEvent) {
    o.attachEvent(etype, func);
  } else {
    var id = o.id, f = func;
    eval('(function(){'+
      'function '+id+'::'+etype+'(){'+
        'f.apply(f,arguments);'+
       '}'+
    '})();');
  }
}

The parameter o is the DOM node for the ActiveX control (i.e. the DOM node for the <object> element that instantiates the control). The etype parameter is the name of the event to bind to. The func parameter is the function to be called when the event is triggered.

For cases where you may already have the ID, you can change the function so it takes an ID string vs the <object> node.

So far, the function has worked in all IE modes. Of course, the function is only invoked in IE browsing contexts since other browsers do not support ActiveX. I do find it ironic that IE11 edge mode, which attempts to be standards compliant requires the creation of a non-standard function identifier ("::" is not allowed in function names) to connect to an ActiveX event. It would have been nice if MS supported the use of addEventListener() for ActiveX events.

2013-12-30

How to monitor Subversion repository when you don't have local adminstrative access

For projects I am involved in, I like to be notified of all commits to the source code repository, including context diffs. This helps me monitor code changes that may affect what I am working on and allows on-the-fly code review (vs waiting some undetermined--likely never occurring--time in the future to review code changes).

I've worked on projects that use Subversion to manage the project's source code. Subversion allows administrators to configure the repository to send out email notifications when commits are made via post-commit hooks. When I administrate subversion repositories, I make sure to enable email notifications.

However, there are times when I may not have administrative access. This can occur during consulting gigs and/or when dealing with limiting/restricting/inexperienced IT personnel, making setting up such a facility unlikely.

What to do? Well, I've written a Perl script that allows one to monitor a remote subversion repository, and send out mail notifications if desired. The script is called svnrreport. For now, you can download it via the following link:

http://www.mhonarc.org/~ehood/blogspot/files/svnrreport

To view the documentation run one of the following commands:

perldoc svnrreport

or

svnrreport -man

Or, your can view it online here.

2011-08-11

IzPack standalone compiler: Custom langpacks

See my first post for a general introduction.

In this post, I discuss how to include custom langpacks in the installer when using the standalone-compiler.jar.

NOTE: The IzPack documentation talks about custom langpacks and how to reference them in your installation file. However, these langpacks are limited, and do not provide the ability to customize all the text strings used by an IzPack-based installer and uninstaller.

If you use the normal IzPack installation to create your installer, to customize the text strings for a given language, you edit/replace the appropriate XML file under bin/langpacks/installer of the IzPack installation. When creating an installer with IzPack, IzPack copies the files in that location into the the installer jar created, and if an uninstaller is created, they will be included in it also. Which files are copied is based upon the langpack setting(s) in your installation file.

For standalone-compiler.jar usage, all that is required is create the sub-tree bin/langpacks/installer in some directory, and place your customized language files in it.

If you are using the izpack ant task, just include the directory that contains the sub-tree in the classpath when defining the task, and you are good to go. For example:

<taskdef name="izpack"
classname="com.izforge.izpack.ant.IzPackTask">
<classpath>
<pathelement location="${project.build.rc-install}"/>
<pathelement location="${contrib.izpack.dir}/standalone-compiler.jar"/>
</classpath>
</taskdef>

In the above, ${project.build.rc-install} represents the pathname to the directory containing the bin/langpacks/installer sub-tree.

IzPack standalone compiler: Registering custom listeners

IzPack is an open source Java-based software installer. In a project I work on, I use it to create the installer for the project by using the standalone-compiler.jar, and things works pretty good.

One problem though is that for certain customizations tasks, the IzPack documentation is oriented in using an IzPack installation and its tree structure vs standalone-compiler.jar usage. This, post is the start of ways I have found to leverage the customization features of IzPack, but sticking with the standalone-compiler.jar.

Note, I'm working with IzPack 4.3.0, so no guarantees if the methods I describe are applicable to other versions.

In this first post, I figured out how to register a custom listener. In my case, I needed an uninstaller listener. The IzPack documentation for custom action discusses how to implement a listener, but the instructions, and previously noted, is based on working with a regular IzPack installation vs standalone-compiler.jar.

For standalone-compiler.jar, you still need create your listener class, and it must implement the appropriate interface. Since I needed an uninstall listener, I extended com.izforge.izpack.event.SimpleUninstallerListener. For sake of this blog post, my listener class is com.earlhood.izpack.MyUninstallListener.

I compile the class and jar it up in a file called MyInstallListener.jar.

In my IzPack installation file, I add the following tag within the
<listeners> element:
<listener uninstaller="MyUninstallListener"

jar="${path.to.dir.containing.my.jar}/MyUninstallListener.jar"/>

where ${path.to.dir.containing.my.jar} is the pathname to where the jar file is located.

IMPORTANT: The uninstaller attribute value must be the basename of the class. IzPack scans the jar file to find any .class entry whose basename matches this value. The package name is irrelevant for purposes of IzPack finding the .class file representing the listener.

NOTE: The jar attribute is undocumented. I discovered it when examining the IzPack source.

That is basically it. Of course, I have Ant build scripts to automate the entire build process and use the izpack ant task to create the project installer.

2011-04-18

Dynamically changing log4j levels in web application

Log4j is a popular logging library for Java, and I've been using it for many years.

One thing I have found handy, especially with web applications, is the ability to dynamically change the logging level (like to DEBUG) of any category (aka logger) in a running application. I can selectively choose which components I want to change w/o affecting the logging level of other components.

A long time ago, I wrote a servlet to provide this capability, but it requires registering in web.xml and it depends on some utility classes. Although this is generally sufficient, I'm recently working on a project where adding my own custom servlets to the application is awkward, but dropping in a JSP can be done easily. Plus, since servlet engines (e.g. Tomcat) generally support dropping in a JSP at any time, having a JSP allows setting of logging levels w/o having to modify an application's web descriptor.

As a quick hack, I created a JSP version of the servlet, one that is self-contained. The JSP may not be a good example of good JSP coding style since I wanted to spend minimal time in getting things to work. The JSP is self-contained, requiring no external dependencies except log4j.

The JSP can be downloaded from the following location:

http://www.earlhood.com/blogspot/files/log4j.jsp

The interface is simple, but it does the job. When started, it will list out all the logging categories that are currently defined, with each a link to an edit form to change its logging level.

If you need to set the logging level for a category not yet loaded, you can select any other category and then edit the Category field to the category name you desire before submitting the form.

Categories that have their level explicitly set will have the level rendered in bold. Categories that have an inherited value will have the applicable level rendered in italics.

2011-03-22

Detecting when DOM node comes into view with Dojo

Working on a project that uses dojo, the need arose to have the ability to automatically be notified when a DOM node comes into view in the browser window. For example, the node may below the fold, and when the page is scrolled, and the node comes into view, the application is notified.

Searching around, I came across this useful post:

http://www.dustindiaz.com/element-scroll-into-view/

However, the code uses YUI, while I needed something that works with dojo (v1.4). Also, what Mr. Diaz provides does not deal with resize events that can cause a node to become visible, or the case when the node is already visible (before any scroll or resize occurs).

So I wrote up a mini-library to provide something that works with dojo. A couple of the utility functions can probably be replaced if using a later version of dojo, but since I needed something to work with dojo 1.4, I could not use newer convenience functions to access window dimensions (like dojo.window.getBox).

Basic usage:
dojo.require('ewh.nodeview');
ewh.nodeview.connect(domNode, func, cdata);

domNode is the DOM node that you want monitored.
func is the function that is called when domNode comes into view.
cdata is an arbitrary object to pass into func when it is called.

Note: Some may say that call-data can be attached to func itself and/or use dojo.hitch(), but I wanted to provide an interface that was friendly to those who may not be familiar with such techniques.

When func is called, it is passed an object. The object contains the following properties:
  • node: The DOM node.
  • cdata: Callback object.
  • event: Associated event object, which can be undefined.
The function is only ever called once. Once called, the internal event hooks are disconnected. If for whatever reason the caller needs to be notified again for the node, they can call the connect method again for it.

Source code:
http://www.earlhood.com/blogspot/files/nodeview.js