Wednesday Jul 30, 2008

Ruby Eye for the Java guy: Constants

In Java, it is common to use public, final, static fields to represent utility constants. Since the fields are public, they can be used from any package; and since they are final, values cannot be assigned to them once they have been initialized. If the field is a primitive type, such as java.lang.Integer.MAX_VALUE then it is also effectively immutable (and, by extension, effectively thread-safe). These two conditions - finality and immutability - are what guarantee that the field will remain constant. Some classes, like java.lang.String, are also immutable, and so can be used to represent constants. A common design pattern is to group sets of related constants together in a public final class, such as for example javax.xml.XMLConstants.

It would seem like the need for an API to expose constants is universal, and so should find expression in every language. How to do this in Ruby?

In Ruby, any field that begins with an upper-case letter is treated as constant. If you attempt to assign to a constant more than once, the interpreter will issue a warning. Constants may be defined inside of a class, and since the Ruby Class class inherits from Module, you can use the module "::" accessor syntax to read the constant's value:

  class MyClass
    MY_VAR = "xxx"
  end

  puts MyClass::MY_VAR           #=> "xxx"
  MyClass::MY_VAR = "yyy"        #=> "warning: already initialized constant MY_VAR"
However, the Ruby String class is mutable, so the constant is still open to modification:
  MyClass::MY_VAR[1] = "z"
  puts MyClass::MY_VAR           #=> "xzx"
Everything in Ruby is an object, and all objects inherit from the Object class. Object defines a freeze method, which, once invoked, prevents any further modification to the object's state. Once frozen, an object can never be "thawed". So a final, immutable class constant in Ruby might look like this:
  class MyClass
    MY_VAR = "xxx"
    MY_VAR.freeze
  end

  puts MyClass::MY_VAR           #=> "xxx"
  MyClass::MY_VAR = "yyy"        #=> "warning: already initialized constant MY_VAR"
  MyClass::MY_VAR[1] = "z"       #=> "in '[]=': can't modify frozen string (TypeError)
The attempt to modify the frozen object results in an unchecked error, and execution halts. Re-assigning to the constant results in a warning only, and execution continues. It is possible to make assignment a fatal error, too, if we wrap the constants in a module, and freeze the module:
  module MyModule
    MY_VAR = "xxx"
    MY_VAR.freeze
  end
  MyConstants.freeze

  MyModule::MY_VAR = "YYY"       #=> "can't modify frozen module (TypeError)
Be careful, if you want to "mix in" the module's functionality, callers of your class will access the module's constants through an unfrozen path:
  class MyClass
    include MyConstants
  end

  MyClass::VAR = "qqq"
  MyClass::VAR[1] = 'r'
  puts MyClass::VAR              #=> "qrq"
One solution might be to freeze your class, if you want to guarantee the immutability of everything in the class. If you want to leave the class open to extension or modification, you could wrap the included constants in an inner module:
  module MyConstants
    VAR = "xxx"
    VAR.freeze
  end
  MyConstants.freeze

  class MyClass
    class Constants
      include MyConstants
    end
    Constants.freeze
  end

  puts MyClass::Constants::VAR   #=> "xxx"

Monday May 14, 2007

A rack up with sketchup

A few months ago, I started to play with the free version of Google Sketchup. After mastering the basics, I was able to produce a few, prototypical architectural features, such as cornices and fluted columns. Last week I put the tool to a more practical use. My wife wanted a small wine rack in our kitchen. Counter space is scarce, so we decided to try something under the cupboards. I used Sketchup to design the rack. Being able to visualize it from any angle was helpful, and we eliminated some early designs when we realized they would look funny from certain angles, or prove impractical in the small space.

Here is the final "sketchup":

I downloaded the wine bottle from the Google Sketchup 3D Warehouse, and dropped it in the model.

And here is the actual rack:

I probably spent about six hours on the design, including the two that I discarded. With practice I'll become faster, I'm sure. The rack itself took only a few hours - including ten minutes to sand the initial coat of primer that my three-year old applied.

Thursday Apr 26, 2007

Ruby script to toggle CVS root in a workspace

Sandip Chitale posted a Java program that toggles the CVS root setting of a workspace that is already checked out (see A simple program to toggle CVSROOT of existing checked out workspace). At the end of his post, Sandip solicits alternative solutions. I have been playing around with Ruby, and was curious as to how much more compactly the same logic could be expressed in Ruby.

Stripped of empty lines and comments, Sandip's Java solution weighs in at 83 lines. That's roughly four times the 16 lines required for a roughly equivalent program in Ruby, which I give below.

The Java program has explicit exception handlers, which I chose to leave out, since both programs will leave the repository in a corrupted state if IO fails midway.

# A simple program to fix the CVSROOT.  
# 
# One may check out a cvs workspace using a CVSROOT e.g.
# :pserver:username@extranet.foo.com:/cvs. This value of CVSROOT 
# is recorded in the CVS/Root file in the checkedout directories.
# At some later time, depending on the network connectivity, it may be faster
# to use an alternate (aliased) CVSROOT e.g. 
# :pserver:username@intranet.foo.com:/cvs.
#
# This simple program provides the functionality to fix the CVSROOT settings
# in each CVS/Root file. It skips entries found in .cvsignore.
#
# CAUTION: Care must be taken to make sure that both cvs servers are really the
# same servers, or mirrors that are in sync.
#
# DISCLAIMER: This is an experimental program. Use at your own risk.

require 'find'
abort 'No server name specified' unless $\*[0]

roots = []
ignore_patterns = []


# Use the find module to traverse the source code repository, collecting all CVS root
# files found, and patterns of directories to ignore

Find.find('./') do |path|
  if path =~ /CVS\\/Root$/
    roots << path
  elsif path =~ /.cvsignore$/
    ignores = IO.readlines(path).collect{ |line| line.chop }
    ignore_patterns << File.dirname(path) + '/(' + ignores.join('|') + ')'
  end
end


# Construct a regexp of all directories to ignore, use it to prune the array of root
# files, then, update each root file with the new pserver name

ignore_expr = Regexp.new('\^(' + ignore_patterns.join('|') + ')')
roots.reject { |root| ignore_expr.match(root) ? true : false }.each do |root|
  (pserver = File.open(root, 'r').gets)[/@[\^:]+:/] = ('@' + $\*[0] + ':')
  File.open(root, 'w').puts pserver
end 

Tuesday Mar 06, 2007

"Neutered Theme" for Java Studio Creator

The basic components that ship with Java Studio Creator (and with its successor, Visual Web Pack> for NetBeans) derive their look and feel from a theme. If you need to apply your corporate colors to an entire application, themes make the job easier. But if you want to adjust the appearance of just one component, or make a set of components play nicely with other styles, themes can get in the way.

Unfortunately, you can't just unplug the theme from an application. In addition to things like fonts and colors that affect only the look of an application, the theme defines things that are critical to component behavior, like messages and client-side scripts.

To help in these situations, I put together a "neutered theme." This is a theme from which everything extraneous has been excised. With the neutered them, most of the components end up looking pretty much like their standard JSF counterparts. For example, here is a basic button rendered with the default theme:

Notice the border, gradient color, and text font, all of which are derived from the default theme. Switch to the neutered theme, and the button looks pretty much like a plain HTML input button:

What if you want one button on your page to be yellow? If your application relies on the default theme, and you set a button's style property so that it's background is yellow, the component remains colored a gradient blue. That's because your CSS settings in the component property are conflicting with CSS settings in the theme. But if you use the neutered theme, you'll get what you want:

Neutered themes are also useful when developing portlets, as it reduces the likelihood of conflict between portlet theme styles and styles assigned to a whole portal page.

How to use the Neutured Theme

  1. This theme will work in Java Studio Creator, and in Visual Web Pack for Java EE 1.4 project types (the theme format for Java EE 5 projects is different).
  2. Download the theme JAR: NeuteredTheme.jar
  3. From the tools menu, choose "library manager", then "import new library". Add the them JAR to the library class path, calling it "Neutered Theme" or whatever else suits your fancy.
  4. Open up the "themes" node in your project's logic view, right click on the new theme, and choose to make it the project's theme.

Wednesday Dec 13, 2006

Update to PropertyResourceProvider

There was a bug in the first version of the PropertyResourceProvider that I made available from this blog a few months ago (see DataProvider component for internationalizing Creator applications), which manifested itself in the glassfish application server. The order of two elements in the library's faces-config file was incorrect. I have fixed the bug and updated the complib available for download.

You can download the updated PropertyResourceProvider Component Library here.

Monday Dec 04, 2006

NetBeans Free-Form Projects as Library Wrappers

When the developer defines a library in NetBeans 5.x, the library definition is stored locally, in the user directory. This makes it difficult to share library definitions among more than one workspace. While library definitions are stored locally, references to them, in the form of entries in a project's properties file, are typically stored along with all other project files, in a remote code repository. When a user first fetches the project from the repository and opens it in NetBeans, a missing references error will occur. All missing local library definitions must be created anew in each local workspace.

One solution to this problem is to store shared libraries in the remote workspace, and to make projects depend directly on the libraries' constituent JAR files. Such dependencies are stored in the project's properties file as relative paths to the JAR files, so assuming that the directory structure that includes both project and library remains the same in all users' workspaces, no local definitions are needed.

There are however drawbacks to this solution. Under the Libraries node of the project logical view, only the JAR file name is displayed. If your project depends on a large number of libraries, it can be difficult to distinguish among them by file name alone. The names may by cryptic, or worse, there may be files in different directories that share the same name. There is also no way of determining from the project logical view that a group of JAR files form a logical unit, unless they share a common infix.

As an example, consider the following project, in which one library provided JAR files aaa and bbb, and another library provided another JAR file, also named aaa:

There is a hack that I have used to improve the quality of the information available about JAR files in the project logical view, which does not require use of the local storage. Instead of grouping them into library definitions, I add them to free-form projects. Projects may then be declared to depend on the library wrapper projects.

Here is the same example, with the JAR files from the two libraries exported from two free-form library projects:

How to create a free-form library wrapper project

To create a free-form library wrapper project, start by putting all JAR files that belong together into a directory, which will serve as the project root directory. For this example, I will define two libraries, 01 and 02, the first containing two JAR files, aaa.jar and bbb.jar, and the second containing just one, aaa.jar:

    lib01/aaa.jar
    lib01/bbb.jar
    lib02/aaa.jar
Each directory will server as one logical library, represented by one free-form project. To create the project skeleton, in each directory create an empty source directory, e.g. ./src. You will also need a stub ant build script. Add the following contents to the file build.xml in the project directory:
  <project name="01_Library_Wrapper" default="build" basedir=".">
    <target name="build"/>
    <target name="clean"/>
  </project>

To create the project, from within NetBeans, choose File->New Project, and choose a "Java Project with Existing Ant Script" within the General category. In the next wizard panel, select as location the project's directory, e.g. ./lib01. NetBeans will fill in the build file information. You'll want to choose a short, but descriptive, project name, as this will be the name that appears under the Libraries node once you have declared a dependency on this library wrapper project. I chose the names "01 Library Wrapper Project" and "02 Library Wrapper Project". In the next, penultimate wizard panel, NetBeans will fill in the ant targets found, and in the final panel it will prompt you for a source directory. Choose the source directory that you created earlier.

[It may seem superfluous to create an empty source directory and empty build targets, but I discovered that without these, some actions in the IDE can have the side effect of removing the exported JAR definintions.]

After the project is created, bring up the project's properties window, and choose Output. Here you will declare the JARs that this free-form project exports. Select all the JARs that you put into the library wrapper project directory. When later you declare a dependency on this project, these are the JARs that will be added to the class path. Here is what this step looks like for my first library, 01:

And that's it! Now you are ready to declare project dependencies on your new library wrapper projects. Right-click on the Libraries node in the project logical view, and choose "Add Project...". Select the project directory. All JARs in the project will be added as dependencies.

Wednesday Aug 23, 2006

DataProvider component for internationalizing Creator applications

How to use a PropertyResourceProvider component in Java Studio Creator to bind component values to localizable resource keys.[Read More]

Wednesday Feb 08, 2006

Rough Guide: Editing Themes for Creator 2.0

The components that ship with Java Studio Creator 2.0 derive their look and feel, and in some cases, their behavior, from a theme. At the most basic level, a theme is simply a JAR file that contains resources such as CSS stylesheets and graphical icons.

An easy way to create your own look and feel is to edit the contents of the default Creator theme. The following guidelines explain how to do it.

A theme JAR file has three main types of content:

  • manifest file: information about the theme's name, version, prefix, and locations of properties and resources
  • properties files: a map of theme keywords to theme resources, used by the theme manager to location theme resources
  • resource files: the images, Javascript files, CSS stylesheets, and messages that constitute the theme itself

You'll find a copy of the default theme jar (defaulttheme.jar) in the Creator install directory, under rave2.0/modules/ext. Or, after you have deployed an application, in the application directory build/web/WEB-INF/lib.

Expand the file into a new directory. Users of M$ Windows may find it more convenient to rename defaultheme.jar to defaulttheme.zip, so that they can expand its contents using the Explorer.

Step One: Updating the manifest

The first thing to do is to give your theme a name, and to edit the manifest accordingly. I'll start by renaming the Creator default theme, from defaulttheme to isopaleocopria. The theme name is given in the manifest entry X-SJWUIC-Theme-Name. At the same time, I'll modify the package names for the various resources so that they are based on a namespace that I own.

Here's the new manifest:

    Manifest-Version: 1.0
    Ant-Version: Apache Ant 1.6.2
    Created-By: 1.4.2_07-b05 (Sun Microsystems Inc.)

    Name: com/sun/rave/web/ui/theme/
    X-SJWUIC-SWAED-Version: 3.0
    X-SJWUIC-Theme-Version: 1.0
    X-SJWUIC-Theme-Name: isopaleocopria
    X-SJWUIC-Theme-Prefix: /theme
    X-SJWUIC-Theme-Messages: org.isopaleocopria.themes.messages.messages
    X-SJWUIC-Theme-Images: org.isopaleocopria.themes.properties.images
    X-SJWUIC-Theme-JavaScript: org.isopaleocopria.themes.properties.javascript
    X-SJWUIC-Theme-Stylesheets: org.isopaleocopria.themes.properties.stylesheets

A few important points about the manifest:

  • To be a valid theme JAR, the manifest must contain an attributes section named "com/sun/rave/web/ui/theme/"
  • The attibute X-SJWUIC-Theme-Name must be present, and it must contain a unique name (a name not used by any themes already loaded into Creator).
  • The last four entries are package names that must correspond to property files in the theme JAR. For example, org.isopaleocopria.themes.properties.images should correspond to the file org/isopaleocopria/themes/properties/images.properties. Do not edit the keys in these property files!

If the above conditions are not met, Creator will reject the theme.

Nota Bene: According to the spec, lines in a JAR manifest file are limited to a maximum length of 72 bytes. When you extract and first open the manifest for editing, you may notice that some lines longer than 72 bytes have been wrapped, and the enjambed portion has been indented with a single space character. You'll need to patch these lines back together again before reconstituting the JAR or ZIP file.

Step Two: Editing theme content

Before editing the content, you'll want to move it to your own package. In order for the theme manager to find the content in its new location, you'll need to update the path values in the various property files listed above. For example, as part of developing my new theme I would move the Javascript file /com/sun/rave/web/ui/defaulttheme/javascript/table.js to org/isopaleocopria/themes/javascript/table.js, and update the corresponding value in the javascript.properties file.

After updating the theme name and the locations of the property files, it's time to edit the theme content. In some cases, the key names in the property files will help you to identify which theme artifact belongs to which component. The CSS stylesheets also generally use a truncated version of the name of the component as the prefix of style class names that apply to the component.

Step Three: Importing the edited theme into Creator

When you are finished modifying the theme content, you will need to repackage the contents as a JAR or ZIP file which can then be imported into Creator.

To import the theme file, start Creator and open or create a project. From the menu, open Tools -> Library Manager. Select the Theme Libraries section, and click on "New Library". Give the library a name, e.g. IsoPaleoCopria (note that spaces are not accepted, probably a bug), and add the theme JAR or ZIP file to the library's classpath and to its runtime, using the appropriate tabs.

Finally, to apply the theme to the current project, select it in the project hierarchy, right-click, and choose "Apply Theme".

Have fun!

Wednesday Nov 10, 2004

Fraters Libertas (sic)

What, I wonder, were the deep thinkers at Fraters Libertas thinking? That a Latin motto is cool? Perhaps they are under the spell of their beloved leader's English malapropisms. If they are what I suspect they want to be - the brothers of liberty - their devise should read Fratri Libertatis. On the other hand, I think the pidgeon Latin better suits the general tenor of their political commentary.

Thursday Oct 28, 2004

Craving crack

This is an actual piece of spam that was waiting for me in my inbox this morning. The English is priceless. I wish I knew the redactor's native language; "crave to feel crack" seems like a direct translation of some foreign idiom.


    From:       Candida Rebux 
    Date:       28 Oct 21:33 (PDT)
    Subject:    Do you crave to feel crack following morning?

    Our recent view shows that it takes usual of just 1.2
    boozings to effect a hangover. But this pills
    services you evade hangovers and waken sensitive great from head
    to stomach and all over else.

Tuesday Oct 12, 2004

NetBeans sex exception?

NetBeans 3.6 threw an IllegalStateException today while I was creating a new project. The exception was announced in a pop-up window, and also on the command line, with the message:

    gris-118% sex exception: java.lang.IllegalStateException: 
              can't declare any more prefixes in this context
Perhaps it should try whispering them softly in my ear?

Friday Oct 01, 2004

New meta-blog about XML

There is a new website that aggregates RSS feeds from several bloggers well-known in the XML community, dubbed "Planet XMLhack": http://planet.xmlhack.com.

Sunday Sep 26, 2004

Markup as protest

Michael Sperberg-McQueen used to say that all markup is interpretation. And some markup is demonstration: </Bush>. I suppose I don't need to point out that the Bush element isn't well formed. Or that maybe it should be empty.

Actually

Thanks to Gary Trudeau, I now know what "actually" means: Doonesbury.

Monday Sep 20, 2004

Twice baked

Sometimes a word describing a technique can take on localized forms, which effectively describe local variations in the technique. Take biscuit. The word comes to us from Latin via French, and means, literally, "twice baked". Baked goods were passed a second time through the oven to ensure their longevity. At the English table, it refers generally to savories; at the French, to sweets. Italy has given us a sweet variant, often with anise, that we have imported along with the Italian name for the technique: biscotto. And from Germany, that ultimate in crackers for teething babies, zwieback. These are baked once as bread, and then toasted.

About

gjmurphy

Search

Top Tags
Archives
« July 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
       
Today