Blogroll

The New Domino Admin

A great source of information for Lotus Domino administrators

ecmarchitect.com

Jeff Potts' excellent ECM/Portal blog

Jared Ottley

Lots of very useful Alfresco articles

Looking for web hosting?  I have been using DreamHost for years and have found them to be reliable, inexpensive and very good at what they do!  Click below to sign up, use promo code 'unorganized' to get a free domain registration and 10 bucks off your first year of hosting!

 

dreamhostBadge

Home
Moving! PDF Print E-mail
Written by Nathan McMinn   
Monday, 25 June 2012 12:41

Long story short, I'm moving my blog to a new home.  All of the content that is here will remain here for reference, but I have disabled comments on any published articles.  I have finally had enough of the "c1@lis!!!  V1@gra!!!  Cheap prices, shady vendors, get your credit card stolen!!!" spam that is posted multiple times a day.  Does anybody actually click that crap? 

 

My new home on the web can be found at nathanmcminn.com, and will launch in the next few days.  So if you want to keep in touch, or find my latest articles on brewing, technology, Alfresco or anything else that catches my attention, check the new site.

 
Robust Porter Recipe PDF Print E-mail
Written by Nathan McMinn   
Tuesday, 10 January 2012 22:02

It's no secret that I love dark beers (mmm, Belgian stout!), especially during the dark days of winter.  When the weather turns cold nothing else comes close to a hearty dark beer in a warm pub with friends.  One of my personal favorite styles is a porter.  Porters come in several styles, ranging from sweet to dry, with roasty, malty and chocolatey flavors being common across the range. 

 

One of my favorite styles of porter is the robust porter, BJCP style 12B.  I put together this recipe for a competition, and like it enough that it has become a staple in my brewing rotation.  Like any good English style of ale, it starts with a generous helping of Maris Otter pale malt.  A few lbs of speciality grains layer on the roast flavors in abundance.  Finally, I like to toss in a little sour (acidified) malt.  It isn't needed to lower the mash pH, as the roasted malts will do a fine job of that.  I just like the slightly lactic character that it adds to the finished beer.  Here's the total grain bill:

 

9.5lb Maris Otter pale malt

2lb biscuit malt

1lb chocolate malt

1lb black malt

0.5lb sour (acidified) malt

 

I dough this in at 1.25qt/lb with a target mash temp of ~150F.  60 or 70 minutes is more than enough time to complete conversion.  Sparge with your choice of method until you get about 6.5 gallons of wort.  Sticking with the English theme, the hops in this recipe are all traditional English fare:

 

1oz East Kent Goldings (5% AA) - 80 mins

1oz East Kent Goldings (5% AA) - 20 mins

1.25oz East Kent Goldings (5% AA) - 5 mins

 

The boil time is a little longer than the usual 60 minutes for a homebrew recipe.  I think this helps darken the beer a bit more (not that it needs it!) and helps develop the flavor a bit more.  Once the sweet wort is cooled down, pitch an appropriately English yeast strain.  I've brewed this with Wyeast 1275 Thames Valley and White Labs WLP004 Irish Ale, and both turned out great.  Just find something fairly neutral with good attenuation.  On my brewing rig the starting gravity usually comes out between 1.065 and 1.070.  After fermentation it gets down to around 1.015, yielding an ABV of 6.5%- 7%.  Keeping with the traditional theme, carbonate to about 2 volumes of CO2.  I've done this beer force carbonated in the keg and bottle conditioned and it turns out fine either way.  The beer will come out inky black, and should raise a firm mocha colored head.  For a fun variation, use this as the base recipe for some porter exploration.  I've brewed this with black licorice added, with some smoked malt and even with a couple handfuls of ground coffee.  Just the thing for a break from the snowball fights and sledding!

 

Happy brewing!

Last Updated on Thursday, 12 January 2012 21:19
 
Storing and Retrieving Images in MySQL PDF Print E-mail
Written by Nathan McMinn   
Tuesday, 10 January 2012 13:43

One of my ongoing projects is a rich client application written in Java.  A recent request from the users was the ability to add handwritten notes or drawings to some of their records.  It's simple enough to give the users a simple canvas on which they can draw and store that data as an image.  However, we want these notes to be available to the companion web application that runs on our server.  The clients synchronize their databases with the server fairly regularly using a database sync tool called Pervasync, so it should be possible to store the images as a BLOB in the MySQL database on the client and have them sent to the server when the client and server databases are synchronized.  As with any vendor claim, I wanted to test whether or not Pervasync could synchronize BLOB data properly.  

 

To test this, I needed to import a binary file of an image into a test client database, synchronize it to the server, export the BLOB to a file and make sure the image was intact.  After a little digging around, it turns out that the SQL to accomplish this is fairly simple.  I created a table called "image_test", with two fields.  The first is a simple numeric ID field, the second a MEDIUMBLOB.  Inserting an image from a file on disk is just this simple:

 

1
INSERT INTO image_test (id, image) VALUES (1, LOAD_FILE('c:/test/test.png'));

 

After running the data synchronization, I needed to get the BLOB data back out and into a file.  MySQL makes this easy.  The output from any query can be dumped to a file by using the INTO OUTFILE/DUMPFILE syntax in a select statement.  This is very handy for scheduled jobs that take data from a MySQL database and make it available to other systems.  For example:

 

1
SELECT * FROM table_name INTO OUTFILE 'c:/test/outfile.txt'

 

The SQL statement above will take all of the data in the table_name table and dump it out to a text file.  This statement can also take the same export_options parameters used while importing a file to set things such as the field and line delimiters.  But wait, aren't we dealing with binary data?  Wouldn't field and line delimiters just muck things up?  Yep, and that's why MySQL provides the DUMPFILE option instead.  Using INTO DUMPFILE instead of INTO OUTFILE, all of the returned data is dumped to a file as a single line.  This is just the ticket for fetching my image data and storing it in a local file for verification.  The SQL looks like this:

 

1
2
3
4
SELECT image 
INTO DUMPFILE 'C:/test/test2.png'
FROM image_test 
WHERE id = 1;

 

When I first tried to run this under the MySQL workbench it kept choking with an error code 1064, complaining about the syntax.  By default, the MySQL workbench inserts a LIMIT 0, 1000 statement into queries before they run.  This causes the statement above to fail.  To turn off the limit, go to the "edit" menu in MySQL workbench and select "preferences".  In the SQL Editor tab, uncheck the "limit rows" option and the query should run, outputting the BLOB data to a file.

 

Happy Coding!

 
Adding a User to an Alfresco Share Site via the Javascript API PDF Print E-mail
Written by Nathan McMinn   
Sunday, 14 August 2011 14:46

As much as I love working with Alfresco (and I do, really), there are some things about it that still make me scratch my head and say "huh?".  One of these things is the way Alfresco Share handles adding users to sites.  Typically, a site manager simply invites a user to a Share site via the invite mechanism.  95% of the time this is exactly how I want it to work.  Sometimes, however, I need to add a user directly without going through the invite process.  Strangely, Alfresco admin users cannot simply add users to an arbitrary site.  Under older versions of Alfresco (3.1), there was a simple workaround.  A user could be added directly to a site group, and thus the site, through the Alfresco Explorer admin interface.  In newer versions of Alfresco (3.3.3, in my case), this no longer works.  The site manager, collaborator, contributor and consumer groups no longer show up in the admin interface's group search.  Aaaaarrrrgh!!!

 

Thankfully, there is another way.  It takes a little more work.  I have written a few articles before about the Alfresco Javascript API, and we're going to dive into it again to accomplish the task at hand.  

 

So, let's get started!  To add a user directly to a Share site using the Javascript API, two things are required:

 

  1. Valid Alfresco user credentials.  The account needs to either have SiteManager access to the site to which the user will be added, or it need to be an admin account.
  2. A good REST client.  There are quite a few to choose from.  Depending on the task, I either use the WizTools.org RESTClient, or the Eclipse restclient-tool.  Either one will work just fine.

 

The first step is to fire up your REST client or a browser and get an Alfresco authentication ticket.  This is done via a simple HTTP GET operation.  Just hit the following URL:

 

http://<your_alfresco_host>/alfresco/service/api/login?u=username&pw=password

 

This will return an authentication ticket that can be appended to all of your future requests.  The exact response will look something like this:

 

1
<ticket>TICKET_ce2ffccb238802825af8ae7632160dd87aa234e7b</ticket>

 

The part of the ticket that should be appended to the URL is the content of the <ticket> element.  Make a note of the ticket, it will be required in a moment.  Now, fire up your REST client if you haven't already.  The client will be used to send a POST to the site membership web service.  This web service is used by Alfresco Share internally to (duh) add a user to a site with a specified role.  You can browse all of the available web scripts on your system at this URL:

 

http://<your_alfresco_host>/alfresco/service/index

 

The script we're looking for is in the package /org/alfresco/repository/site/membership, and is called the "Add Site Membership" script.  A full description of the script and its parameters can be found here:

 

http://<your_alfresco_host>

/alfresco/service/script/org/alfresco/repository/site/membership/memberships.post

 

The script itself is pretty simple.  Take a moment to read the description, go over the associated Javascript and get a feel for what it is going to do.  I'll wait.  Done?  Good.  If you look at the URL that will accept the POST, you'll see it has a pretty simple signature.  Only one part of the URL needs to be changed.  You'll need the short name of the site to which you want to add a user.  The short name is the "URL name" that you used when you created the site.  It's a part of every URL in the site.  For this example we'll assume I have created a site called My Test Site with a URL name (short name) of "mytestsite".  The URL specified by the script looks like this:

 

http://<your_alfresco_host>/alfresco/service/api/sites/{shortname}/memberships

 

Looking at the URL template above, it's pretty clear what to do.  Substitute the shortname of your site for {shortname}.  The last part of the URL is the Alfresco authentication ticket created earlier.  Simply append it as a URL parameter named "alf_ticket".  

 

http://<your_alfresco_host>/alfresco/service/api/sites/mytestsite/memberships?alf_ticket=TICKET_dc233f7e8212825ef8fa8f945762ee24df53bcd7b

 

When I first started working on this I tried to use the authentication built into my REST client, but Alfresco constantly complained about HTTP authentication.  Appending the ticket in this manner seems to work nicely with every web script I have tried.  The last step in this process is to create a JSON object that will go into the body of the POST request.  A quick look at the Javascript for the memberships script shows that it expects a JSON object that contains a role (SiteManager, SiteCollaborator, SiteContributor or SiteConsumer, corresponding to the Share site roles), and a person object with a username property.  Here's what such a request looks like:

 

JSON Body
1
2
3
4
5
6
7
{
"role":"SiteManager",
"person":
{
"userName" : "username"
}
}

 

Note that this isn't everything that Alfresco includes in a person object, but it's enough to get the job done in this case.  Once you have entered this JSON into the body of your POST request, there is one more task and we're ready to send the request.  Alfresco is expecting a certain content type for JSON requests.  A proper REST client will let you set any additional HTTP headers that the request requires.  Add a header called "Content-type" and set its value to "application/json".  Once the header is set and your request body is ready go ahead and send the request.  You should get back an HTTP OK response, with a body that contains another JSON object with a bit of detail about what was done.  For the request and test site in this example, the response should look something like this:

 

JSON Response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"role" : "SiteManager",
 
"authority":
{
"authorityType" : "USER",
"fullName" : "User Name",
"userName" : "username",
"firstName" : "User",
"lastName" : "Name",
"url" : "\/alfresco\/service\/api\/people\/username"
},
"url" : "\/alfresco\/service\/api\/sites\/mytestsite\/memberships\/username"
 
}

 

It's entirely possible that there was an easier way to do this that I completely missed.  Even if there is, it never hurts to get more familiar with the Javascript API.  The Alfresco Javascript API is a powerful tool for Alfresco users.  It doesn't matter if you are a developer or admin, learning the Javascript API is essential.

 

Happy coding!

Last Updated on Tuesday, 16 August 2011 11:47
 
Groovy, RCP, JasperReports and Code Reuse PDF Print E-mail
Written by Nathan McMinn   
Monday, 04 July 2011 19:22

One of my many responsibilities at my day job is guiding the architecture of and helping to develop a fairly large Eclipse RCP application.  The RCP is a pretty good platform for a desktop Java app.  SWT keeps the look and feel mostly native, and you get a lot of freebies like a help system and update engine that would have to be developed if one were to build a Java application from scratch.  Recently I was getting frustrated with the reusability of the service layer of this application and started looking for a new way.  The service layer of this application is a set of POJOs that implement the business logic of the application, mostly importantly a complex set of calculations.  The idea here was that we could reuse the same objects in the web version, ensuring that the calculations were performed consistently across both the client application and the web application (which serve different audiences, but a very similar purpose). 

 

This approach worked well, but it did turn out to have some drawbacks.  The service objects had dependencies that make it difficult to work with our reports (based on JasperReports).  The result is that the reports end up with their own code for handling the calculations, either in the field expression or in the report query itself.  This doubles the amount of work we have to do when a calculation is altered.  The service layer objects themselves are compiled and are part of the RCP plugin, meaning that an update to the calculation logic required an update to the plugins.  The update process, while very powerful and useful, can be cumbersome to the user community.  I don't like forcing a big update on the community any more often than I have to.  Finally, we want to have our analysts (who define the calculations) more involved in the development and maintenance of the service layer.  These folks are bright, but they aren't coders.  Java's syntax is a bit too verbose and obtuse for them to work with directly.

 

So, there are few problems to solve.  How can I build a service layer for this RCP app that can easily be reused in both a set of web services and a web application, and can also be easily called from JasperReports?  How can I deploy this layer in such a way that I don't have to push out a bulky RCP feature update every time an analyst wants to change a calculation?  How can I bake-in unit testing?  How can I get my analysts more involved in maintaining the calculations and double-checking them for correctness?

 

Groovy!

 

Groovy neatly fits all of my requirements.  It runs on the JVM, so it should play nicely alongside both the RCP and our existing web environment.  Groovy also can be used to develop scriptlets for JasperReports (with a little work).  It's a simple matter to have the RCP application download the latest scripts from the server when it has a chance, and the scripts are tiny making this a very quick operation and much less disruptive to the users (we do it via an embedded Subversion client).  Finally, Groovy's syntax is simple and clean enough that the analysts will be able to at least read it, if not write it.  As an added bonus, Groovy is also very well suited to writing a DSL (Domain Specific Language).  While this isn't planned for the first go, this capability could be handy in the future if we decide to define a DSL for this app.

 

Enough about the "why", time for the "how".  Let's look at some code!  The simplest way to embed Groovy in an existing Java application of any sort is by using the GroovyScriptEngine.  This class handles finding, loading, compiling and running a Groovy script.  Almost all of the complexity is hidden from the developer.  It's a simple as giving the script engine one or more paths to search, binding some variables (if you need them) and telling it which script to run.

 

Let's create a simplified example in which we take the unit price for an item and the quantity, using these values to calculate an extended price.  For this example we'll assume a working directory of /home/groovytest/scripts.  Here's what the Groovy script might look like:

 

ExtendedPrice.groovy
1
2
3
4
5
6
7
8
def ep = new ExtendedPriceService();
extended_price = ep.calcExtended(unit_price, qty);
 
class ExtendedPriceService {
	double calcExtended(double unit_price, int qty) {
		return unit_price * qty;
	}
}

 

Pretty simple stuff.  We're creating a new extended_price variable and assigning it the value of unit_price multiplied by quantity.  You might be asking yourself, why define the calculation in a class and not just do it in the script and return the result?  We'll get to that later.  Now, here's how we might call that script from the Java application:

 

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
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
 
public class ScriptEngineTest {
 
  public static void main(String args[]) {
    try {
      //set the paths the script engine will use to find the script
      String[] paths = new String[] {"/home/groovytest/scripts"};
      GroovyScriptEngine engine = new GroovyScriptEngine(paths);
 
      //create a Binding object to hold the variables
      Binding binding = new Binding();
      binding.setVariable("unit_price", 3.50);
      binding.setVariable("qty", 10);
 
      //execute the script
      engine.run("ExtendedPrice.groovy", binding);
 
      //print the returned result
      System.out.println("Ext Price: " + binding.getVariable("extended_price"));
    catch(Exception ex) {
      System.out.println("Exception: " + ex);
    }
  }
}

 

Looking at the code, it should be pretty clear what is happening here.  First, we're setting up an array of Strings that lists all the paths in which scripts are located.  Next, we instantiate the script engine and give it the list.  Once the script engine is ready, we create a Binding object to hold the variables that we will pass to the script as arguments and give us access to variables in the script itself.  Finally, we run the script and get the results back from the Binding object.  Simple, simple, simple, and this approach works for both the RCP application and our J2EE web app.  Of course our actual calculations are much more complex, but conceptually it's the same thing.

 

It's worth noting that you can pass ANY Java object to your Groovy script using the Binding object.  Using Hibernate?  Just pass in your mapped objects.  Alternately, you can pass in the db connection details and use Groovy's built in database features to pull whatever data you need.

 

So we've seen how Groovy can be used to externalize calculations (or any other logic, really) from an RCP application.  Now what about that promise of reuse?  Now that I've gone through all the effort of embedding Groovy and externalizing my calculations into Groovy scripts, how can I reuse these, say, in a web service?  There are a couple of options.  Probably the simplest way to reuse the scripts as a service is to use the GroovyWS module to expose the script as a WS-I compliant service.  How easy is this?  Surprisingly easy.

 

1
2
3
4
5
6
7
8
9
10
import groovyx.net.ws.WSServer
 
//define a new WSServer
def server = new WSServer()
 
//set a server node to point to the extended price class created earlier
server.setNode("ExtendedPriceService", "http://localhost:80/ExtendedPriceService")
 
//crank up the server!
server.start()

 

Earlier, when we defined the ExtendedPrice class, it would have been visibly simpler to just create a script that took the two variables and returned a calculated value.  The reason that we put the calculation in a class is so that we could reference it here and easily expose its methods as a service.  It is also pretty simple to take the class defined in ExtendedPrice and use it in a server framework like Grails.  It's nice to have options, right?

 

Now, what about JasperReports?  The whole point of this exercise is to build a single set of scripts that encapsulate my complex calculations and make them available to all of the parts of this application including the RCP app, web app / services and reporting.  This part is a bit trickier.  While JasperReports supports Groovy as an expression language (which greatly simplifies report development, IMHO), there is no direct support for scriptlets written in Groovy.  However, all is not lost.  It just takes a little extra work.  Since Groovy can be compiled to a regular old Java class (via groovyc), there's nothing stopping you from writing your scriptlets in Groovy, compiling them and using them in JasperReports.  Or, write the scriptlets in Java and call the calculation scripts via the GroovyScriptEngine.  Either way works.  It's not a perfect solution, but it's pretty simple to whip up an Ant script to compile the Groovy scriptlet wrappers and jar 'em up.  I'll have another article coming soon that details exactly how this works with an extended JRDefaultScriptlet that works nicely with Groovy.

 

Now that the service script can be used in the RCP and web apps, as a web service, and in the reporting engine, there's just one last piece of the puzzle.  Unit testing.  Again, Groovy makes this very simple.  JUnit is baked into the Groovy runtime.  It's simply a matter of creating a class that extends GroovyTestCase.  Here's a simple example that tests the calcExtended method of the ExtendedPriceService class that contains the calculation used throughout this article:

 

ExtendedPriceTest.groovy
1
2
3
4
5
6
7
8
9
10
//define the test case
class ExtendedPriceTest extends GroovyTestCase {
 
	//test the extended price calculation
	void testExtendedPrice() {
		//create an instance of the extended price service
		def ep = new ExtendedPriceService();
		assert 350 == ep.calcExtended(3.5, 100);
	}
}

 

That's all there is to it.  Classes that extend GroovyTestCase can either be run directly from the command line, or they can be compiled and run like any other JUnit test case.

 

Groovy is a relative newcomer (version 1.0 came out in 2007), but it's already finding a place in my toolbox.  Java has its fair share of detractors, but languages like Groovy help make it fun again and can give some significant gains in productivity when applied properly.

Last Updated on Tuesday, 05 July 2011 13:21
 
<< Start < Prev 1 2 3 4 5 6 7 8 9 10 Next > End >>

Page 1 of 12
RSS Feed Icon

About Me

 

My profile picture

 

My name is Nathan McMinn.  I'm a software engineer, beer geek, wannabe adventurer and genuinely curious guy.  Find me on Facebook, Linkedin or Twitter

Latest Comments