Jan 012012
 

This morning I came across a post by Chris Brogan about his three words for 2012, and for once, I was pleased with what the blogosphere had to say during this new year time period.  Usually we’re all inundated with articles about “the top 10 whatevers of 2011″ or “the 7 things to do in 2012″, or posts with predictions for the new year.  Chris wrote about how he sets his goals for the new year, which is exactly how I do it as well.

So in the spirit of sharing (and to begin executing on one of mine immediately), here are my three words for 2012: Plan, execute, network.

  1. Plan. For me, planning encompasses big picture thinking, strategy, and consistent weekly planning.  I intend to spend some time every week thinking about how I’m spending my time, and whether it’s the best use of my time in pursuit of my goals.  I will also continue my practice of planning by the role in my life (husband, father, student, employee, etc.), as suggested by Stephen Covey in “The 7 Habits of Highly Effective People“, which I had to re-read this year for a class I took.  I’ve been doing this for a couple of months now, and together with the help of OmniFocus for the iPad, it’s been working very well for me.
  2. Execute. This is about focus for me.  Part of the changeless core within me is that I find everything interesting, and typically think I can get anything (and everything) done, regardless of how busy my life is.  Though I know that I have my limits, I always struggle with this.  In 2012, I intend to take pause before I commit to taking on a new project of any kind.  I need to learn to separate my enthusiasm from my workload, and pay more attention to the latter.
  3. Network. This is a big one for me that encapsulates several things at once, namely strategic thinking in IT, making new industry connections, solution selling, relationship development, and social media.  That may seem like just a big list of stuff, but it does all come together.  Now that I’ll be a bit more freed up this year since I’ll complete my MBA in May, I intend to spend some of that newly found time developing my base of knowledge regarding what CIOs are thinking about and struggling with.  I expect to do some blogging along the way, make new connections in pursuit of that knowledge via Twitter, LinkedIn, etc., and use that knowledge to help solve problems for my customers and prospects.

So those are my three words for 2012.  I’d love to hear what you think about it, so please, drop me a note here, or catch me on Twitter.  What are your three words for 2012?

Jan 232011
 

I’m currently taking International Business as part of my MBA program at Rutgers, and decided to share my outline for what I’m studying at the moment – international trade theory.  These notes are a combination of my own interpretation of the materials presented in “International Business” by Charles W. L. Hill, and direct quotes from that text (which I did not explicitly demarcate).  Assume all thoughts and ideas presented here are Hill’s.

1. Mercantilism

  • 1630, Thomas Mun: “…to increase our wealth…sell more to strangers yearly than we consume of theirs in value”

2. Absolute Advantage

  • 1776, Adam Smith.  A country has an absolute advantage in the production of a product when it is more efficient than any other country in producing it
  • If two countries specialize in production of different products (in which each has an absolute advantage) and trade with each other, both countries will have more of both products available to them for consumption

3. Comparative Advantage

  • 1817, David Ricardo - Even if one country has an absolute advantage in producing two products over another country, trading with that other country will still yield more output for both countries than if the more efficient producer did everything for themselves.
  • The country with the absolute advantage in producing both products would still produce both products, but less of the one they would trade for, allowing them to essentially allocate more resources to producing the product that they’re comparatively most efficient at producing
  • Assumes many things:
    • Only 2 countries and 2 goods
    • No transportation costs
    • No price differences for resources in both countries
    • Resources can move freely from producing one product to producing another product
    • Constant returns to scale
    • Fixed stock of resources
    • Free trade does not affect production efficiency
    • No effects of trade on income distribution within a country
  • There are some descriptions of potential outcomes of relaxing some of these assumptions, but I’ll leave this as a thought exercise for you, the reader

4. Heckscher-Ohlin Theory

  • 1919, Eli Heckscher and 1933, Bertil Ohlin – Comparative advantage arises from differences in national factor endowments, such as land, labor, or capital, as opposed to Ricardo’s theory which stresses productivity
  • 1953, Wassily Leontief – The Leontief Paradox – theorized that since the U.S. has abundant capital compared to other nations, they would expor capital-intensive goods and import labor-intensive goods.  Data showed that was not the case.
  • Therefore, Ricardo’s theory seemed to be more predictive.
  • However, controlling for technological differences (e.g. eliminating them) does yield a predictive model based on factor endowments

5. The Product Life-Cycle Theory

  • 1960′s, Raymond Vernon – attempts to explain global trade patterns.  First, new products are introduced in the United States.Then, as demand grows in the U.S., it also appears in other developed nations, to which the U.S. exports.  Then, other developed nations begin to produce the product as well, thus causing U.S. companies to set up production in those countries as well, and limiting exports from the U.S.  Then, it all happens again, but this time production comes online in developed nations.  Ultimately, the U.S. becomes an importer of the product that was initially introduced within its borders.
  • Weakness – Not all new products are created in the United States.  Many come from other countries first, such as video game consoles from Japan, new wireless phones from Europe, etc.  Several new products are introduced in several developed countries simultaneously

6. New Trade Theory

  • 1970′s – Via the achievement of economies of scale, trade can increase the variety of goods available to consumers and decrease the average cost of those goods.  Further, the ability to capture economies of scale before anyone else is an important first-mover advantage.
  • Nations may benefit from trade even when they do not differ in resource endowments or technology
  • Example – If two nations both want sports cars and minivans, but neither can produce them at a low enough price within their own national markets, trade can allow each to focus on one product, allowing for the achievement of economies of scale that will increase the variety of products in both countries at low enough prices
  • Example – Airbus spent $14 billion to develop a new super-jumbo jet.  Demand is estimated at 400-600 units over the next 20 years, and Airbus will need to sell at least 250 of them to become profitable in this line of business.  Boeing estimates the demand to be much lower, and has chosen not to compete.  Airbus will have the first mover advantage in this market, and may never see competition in this market segment.
  • New trade theory is not at odds with Comparative Advantage, since it identifies first mover advantage as an important source of comparative advantage
  • Debate – should government provide subsidies that spawn industries such that companies can gain first mover advantages?  Later chapter (and blog post) covers this.

7. National Competitive Advantage – Porter’s Diamond

  • 1990, Michael Porter – seeks to answer the question of why a nation achieves international success in a particular industry.  Based on four attributes:
    • Factor endowments
      • Basic factors – natural resources, climate, location, demographics
      • Advanced factors – communication infrastructure, sophisticated and skilled labor, research facilities, and technological know-how
      • Advanced factors are a product of investment by individuals, companies, and governments
      • Porter argues that advanced factors are the most significant for competitive advantage
    • Demand conditions – if customers at home are sophisticated and demanding, companies will have to produce innovative, high quality products early, which leads to competitive advantage
    • Relating and supporting industries – If suppliers or related industries exist in the home country that are themselves internationally competitive, this can result in competitive advantage in the new industry.
    • Firm strategy, structure, and rivalry
      • Different nations are characterized by different management ideologies, which can either help or hurt them in building competitive advantage
      • If there is a strong domestic rivalry, it helps to create improved efficiency, making those firms better international competitors
  • Porter also notes that chance (such as new breakthrough innovations) and government policies (such as regulation, investments in education, etc.) can influence the “national diamond”

Implications for Managers

  • Location – productive activities should be done in the location in which it is most efficient
  • First-mover implications – “the idea is to preempt the available demand, gain cost advantages related to volume, build an enduring brand ahead of later competitors, and, consequently, establish a long-term sustainable competitive advantage”
  • Policy implications – lobbying for or against free trade or government restrictions.
  • It’s in a firm’s best interest to invest in upgrading advanced factors of production; for example, to invest in better training for its employees and to increase its commitment to R&D
  • Businesses should lobby for investment in education, infrastructure, and basic research and any policy promoting strong domestic competition
Jan 132011
 

Today I was asked if you could prevent users from removing the “Versionable” aspect in Alfresco Share.  In short, yes you can, but I didn’t find anything that specifically documents how to do it, so here it is!

In your extension directory, your custom Share configuration typically located at <Alfresco>/tomcat/shared/classes/alfresco/web-extension/share-custom-config.xml should be modified as follows:

   <!-- Document Library config section -->
   <config evaluator="string-compare" condition="DocumentLibrary" replace="true">

      <tree>
         <!--
            Whether the folder Tree component should enumerate child folders or not.
            This is a relatively expensive operation, so should be set to "false" for Repositories with broad folder structures.
         -->
         <evaluate-child-folders>false</evaluate-child-folders>

         <!--
            Optionally limit the number of folders shown in treeview throughout Share.
         -->
         <maximum-folder-count>-1</maximum-folder-count>
      </tree>

      <!--
         Used by the "Manage Aspects" action

         For custom aspects, remember to also add the relevant i18n string(s)
            cm_myaspect=My Aspect
      -->
      <aspects>
         <!-- Aspects that a user can see -->
         <visible>
            <aspect name="cm:generalclassifiable" />
            <aspect name="cm:complianceable" />
            <aspect name="cm:dublincore" />
            <aspect name="cm:effectivity" />
            <aspect name="cm:summarizable" />
            <aspect name="cm:templatable" />
            <aspect name="cm:emailed" />
            <aspect name="emailserver:aliasable" />
            <aspect name="cm:taggable" />
            <aspect name="app:inlineeditable" />
            <aspect name="cm:versionable" />
         </visible>

         <!-- Aspects that a user can add. Same as "visible" if left empty -->
         <addable>
            <aspect name="cm:generalclassifiable" />
            <aspect name="cm:complianceable" />
            <aspect name="cm:dublincore" />
            <aspect name="cm:effectivity" />
            <aspect name="cm:summarizable" />
            <aspect name="cm:templatable" />
            <aspect name="cm:emailed" />
            <aspect name="emailserver:aliasable" />
            <aspect name="cm:taggable" />
            <aspect name="app:inlineeditable" />
            <aspect name="cm:versionable" />

         </addable>

         <!-- Aspects that a user can remove. Same as "visible" if left empty -->
         <removeable>
            <aspect name="cm:generalclassifiable" />
            <aspect name="cm:complianceable" />
            <aspect name="cm:dublincore" />
            <aspect name="cm:effectivity" />
            <aspect name="cm:summarizable" />
            <aspect name="cm:templatable" />
            <aspect name="cm:emailed" />
            <aspect name="emailserver:aliasable" />
            <aspect name="cm:taggable" />
            <aspect name="app:inlineeditable" />
         </removeable>
      </aspects>
</config>

Note that in the configuration above, the versionable aspect is in the “visible” and “addable” lists, but not in the “removable” list, while everything else is in all three lists.

This is an example of this configuration in action:

manageaspects

Note that this is applicable for any aspect in Alfresco, including custom aspects that are defined in your own content models.

Feel free to download Alfresco and try it out for yourself.

May 052010
 

One of the new features in Alfresco 3.3 is a new interface for managing content rules in Alfresco Share.  Not only is this an improved user experience as compared to the content rules wizard in the Alfresco Explorer interface, but it also includes new functionality as well, which I’ll describe here.  But first, let’s start with the basics.  How can we create a content rule?  In the document library view, for any folder, you can hover over it, click more, and select “Manage Rules” as depicted below:

12

This action is also available from a details view of a folder.  Next, you’re presented with a choice to create rules, or link to rules.

3

For now, let’s talk about creating a new rule.  Let’s say that we want to make sure all documents that enter this folder can be categorized according to the established taxonomy of categories.  This is not default behavior, so we’ll need a rule that automates it.  Here’s what it will look like:

4

So we can give our rule a name for quick reference, and a detailed description.  Next we define when the rule will fire, which can be one or more of the following: 1) when items are created or enter this folder, 2) when items are updated, and 3) when items are deleted or leave this folder.  For those of you already familiar with content rules in Alfresco, notice the ‘+’ symbol on the right side – this is a nice, new usability feature, allowing all three conditions to be defined within a single rule, rather than having to create three separate rules as in the Alfresco Explorer interface.  Convenient, right?

Next, you can define one or more criteria, or rule conditions that determine if the rule will fire. In our example, we want all items to be categorizable.  Other examples of criteria are modified date, created date, author, creator, description, modifier, name, title, type, subtype, etc.  There is a full library of criteria that you can choose from, which include pretty much any value of any property for any type or aspect defined in the system.  Pretty flexible, right?  Also, another improvement to content rules is the ability to specify negative conditions (“Unless all criteria are met”).  So if we wanted all items to fire the rule, unless the item was already categorizable, we can use the negative condition to specify that.

4b

Next, we specify what action or actions to perform.  In our case, we’re going to simply add the “Classifiable” aspect to the items coming into this folder, so we’ll choose “Add an aspect”.  Many additional options are available such as moving the document, sending an E-Mail, or executing a script.  That last one allows you to define your own action, implemented in JavaScript, which can leverage the full JavaScript API.  Also, this list is extensible as well.  If you wanted to implement an action in Java and expose it here, you could do that as well.

4c1

Again, don’t forget that you can specify more than one action to execute for this rule; yet another improvement over the content rule wizard in the Alfresco Explorer interface.

The last thing to check is the remaining options for the rule – whether you want the rule to apply to subfolders, whether you want it to run in the background, and the new option to specify a script to run in the event an error occurs when the rule fires.  Once you create the rule, you get a view like this:

5

Here we see two rules that I’ve created for this folder: one to make documents categorizable, another to send a notification E-Mail.  These rules can be dragged up or down to determine the order of execution, which is yet another new feature over the Alfresco Explorer interface.

So we’ve created a couple of rules on this “Budget” folder…but what if we wanted to use the very same rule set on our “Forecasts” folder as well?  This is where linking rule sets comes into play.  So I can go into the “Manage Rules” interface for my other folder, and I choose “Link to Rule Set”.  Then, I’m presented with a dialog to locate the rule set I want to link to:

6

I have the option to browse my Share sites or the Repository root for my rule set.  In this case, the rules I want to link to are in the Operations Share site, specifically the Budget folder.  When I click on that, I get to see the names and descriptions of the rules associated with that folder.  Once I click “Link”, I get the following screen, which describes the linked rule set:

7

So for my “Forecasts” folder, I’ve linked to the rule set defined on the “Budget” folder.  So via this new linking mechanism, I could define a rule site in a Share site, and apply that same rule set to other folders within my site’s document library very easily.  Another way to set your rules up would be to make your own folder in the Repository root, under the Data Dictionary; maybe a folder called “Rules Library”.  You could create multiple folders within there, defining sets of rules that you anticipate reusing throughout the repository.

The last thing to mention regarding the new content rule capability is that once rules have been either defined or linked to, an icon is displayed next to the folder in the document library to indicate that fact:

8Hopefully you’ve gathered that this content rule interface is both extremely powerful and easy to use.  You can pretty much automate whatever you want using this capability to save your organization a lot of time and money.  But don’t take my word for it, try it for yourself (currently only in Community 3.3 at the moment).

Aug 312009
 

In parts 1 and 2 of this series, I discussed how to optimize XML Metadata Extraction in your Alfresco WCM environment, and how (and why) to disable permissions checking, respectively.  Besides those two optimizations, there are several other things that you can do to tweak an Alfresco System Receiver (ASR) for better performance, which I will discuss now.

First, optimize your JVM settings!  By default, Alfresco ships with a maximum heap size of 512MB of RAM.  As such, under high load, an ASR can max out a heap of that size and thus serve content a bit more slowly.  So you definitely want to increase the maximum heap size to as high as you can.  Also, depending on how you set up your minimum and maximum heap sizes, you may want to adjust the size of newly allocated heap space using the -XX:NewSize command line option.  There are a few other things you can adjust as well regarding your JVM like enabling hotspot pre-compilation, and the size of the stack, amongst others.  See the JVM Tuning page on the Alfresco wiki for more details.  Ultimately for my testing on my Macbook Pro running Alfresco on Windows XP via VMWare Fusion, I used the following settings (in alfresco.bat):

set JAVA_OPTS=-Xms768m -Xmx1536m -Xss1m -XX:MaxPermSize=128m -Xcomp -Xbatch -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:NewSize=384m -XX:CMSInitiatingOccupancyFraction=80 -server

Speaking of virtualization, that reminds me.  Don’t virtualize!  For best performance, use the actual hardware!  Virtualization adds overhead.  I have not yet tested ASR performance without virtualization to be able to prove the impact that it has, but I will, and when I do, you can be sure that I’ll update this post, so come back sometime soon!

Anyway, now that you’ve tuned the JVM, what about the database?  Increasing the size of the database connection pool will enable your ASR to handle more concurrent users.  Therefore, in custom-repository.properties in your extension directory (Alfresco 3.x) or in alfresco-global.properties (Alfresco 3.2+), you can set the following:

db.pool.initial=10
db.pool.max=350

In addition to increasing the database thread pool, you should increase the tomcat thread pool as well.  This can be done by modifying the following line in alfresco/tomcat/conf/server.xml:

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
maxThreads="350" minSpareThreads="4"/>

Now that we’ve tuned some parts of the server, let’s turn off some things we don’t need.  For example, you should remove the share web application (share.war) that is installed with Alfresco by default, as well as Web Studio (studio.war) and the mobile web application (mobile.war) if you’re running Alfresco Community.  Be sure to remove (or just move elsewhere) all of the unneeded .war files as well as their exploded directories.  The only application you need on an ASR is alfresco.war.  Additionally, you may want to download a clean copy of alfresco.war (making sure you have the correct version for your installation), and be sure NOT to apply the SharePoint Protocol AMP file to it.  No sense running another listener that no one will ever use!  Also, you typically want to lock down an ASR so that content is only accessed by your web application via the web scripts you’ve authored to expose your web content.  Therefore, you don’t need to be running virtual filesystems such as CIFS for shared drives, and FTP.  This can be done very simply, by setting the following in custom-repository.properties (3.1) or alfresco-global.properties (3.2):

ftp.enabled=false
cifs.enabled=false

Another very useful exercise is to optimize your web scripts.  For example, if you have a page in your web application that has three content areas managed by Alfresco, don’t have the web application call three separate web scripts, each of which returns only content related to that area.  Network hops are expensive, so they should be minimized to just one hop per page type.  In this scenario, performance would likely be improved by having the web application call a single web script per page type which returns all of the content to be displayed by that page at that time.

That leads to the next point: build caching into your web application, or as a layer between your web application and Alfresco, and be sure to determine and implement a cache update strategy.  If the web application can simply hit a cache that is local to it while the cache is updated asynchronously via some other process, the web application will fly as compared to making calls to the ASR every single time a user views a page.

To conclude, here’s a handy list of steps you can take to optimize ASR performance, from my estimation of decreasing impact:

  1. Cache
  2. Turn permissions checking off
  3. Optimize your web scripts per page type on your web site
  4. Don’t virtualize
  5. Tune your JVM for performance
  6. Increase the database connection pool
  7. Increase the tomcat thread pool
  8. Remove/disable things you don’t need
    • CIFS Server
    • FTP Server
    • Share
    • Web Studio
    • Mobile
    • SharePoint Protocol (SPP) support
  9. Configure XML metadata extraction so that it occurs on your authoring server, not the ASR

If you have additional suggestions, please share, I’d love to hear them!

Aug 312009
 

In my first post in this series, I discussed how and where to set up XML Metadata extraction in an Alfresco WCM environment to optimize Alfresco System Receiver (ASR) performance.  Though this is a useful optimization, it’s probably not the #1 most important thing you can optimize.  ASRs are designed to enable the retrieval of content, so optimizing that process is going to provide the most impactful results.

One of the major features Alfresco brings to the table as an ECM system is the ability to secure content by user, group, or role (or some combination thereof).  As such, when searches are carried out on behalf of a user, results are pruned by default by the Alfresco PermissionService, thus only returning results that the authenticated user has read access to.

In the case of an ASR though, content is typically retrieved by a single calling application using a single login.  This very common scenario does not require “pruning” of content that the calling application is trying to retrieve.  Avoiding the unnecessary (and expensive) calls to the database for permission checking, particularly as the resulting content set grows in size, would yield significant savings in response time.  Therefore, it is very simple in Alfresco to turn off permissions checking via configuration, which will turn off permission checking repository wide.  Note that this should be done with extreme care – not all use cases are created the same, so be sure to fully evaluate your specific requirements before taking this action.

In my tests, the response time with permission checking turned off was 33% faster on average over comparative tests for 1, 2, 3, 4, 5, 10, 20, and 110 concurrent users.  See beautiful chart reflecting these numbers:

picture-2

To disable permission checking for your entire repository, simply rename “alfresco/tomcat/shared/classes/alfresco/extension/unsecured-public-services-security-context.xml.sample” to “alfresco/tomcat/shared/classes/alfresco/extension/unsecured-public-services-security-context.xml” in your installation.  Note that I found a defect in 3.1.1 Enterprise and 3.2 Community regarding this issue.  You will need to use the “unsecured-public-services-security-context.xml.sample” file attached to this issue: https://issues.alfresco.com/jira/browse/ETHREEOH-2604 instead of the one that the installer lays down for you.

Aug 282009
 

In this first part of what I expect to be a few posts about Alfresco System Receiver (ASR) optimizations, I’ll talk specifically about XML Metadata Extractors for Alfresco Web Content Management (WCM).  So what is an XML metadata extractor, and why should you care about it?  Let’s put it in the context of a diagram:

xmlmetadataextractor

In the diagram above we see that the WCM authoring environment is configured with web forms.  This allows business users to enter content into the system; an article to be published for example.  To do so, the user does not have to be skilled in web related technologies, such as HTML; they simply fill out a form with the content to be published.  Once their content is entered it is saved as XML, submitted to the staging sandbox, and ultimately deployed, in this case to an ASR.  The ASR is seen as being configured with an XML Metadata Extractor and a DM content model, which defines aspects that will be applied to the deployed content.  So for an article content type on the authoring side, there would be an article aspect defined in the content model on the ASR.  The XML metadata extractor is used to extract content from the deployed content (the article) and store is according to the aspect defined in the DM content model.  As such, the content delivered via the web form can be indexed by Lucene, enabling optimized search performance on retrieval.

The problem with this approach is that the ASR is likely serving a live production web site that may have thousands (or more) visitors:

xmlmetadataextractor-site

As such, it is less than ideal to have the ASR execute the processing required to extract metadata from the XML content on the ASR itself.  Wouldn’t it be better if that was done on the authoring server?  You bet it would.  Hence the first ASR optimization; perform XML metadata extraction in the authoring environment:

xmlmetadataextractor-optimizedBy configuring the XML metadata extraction to occur on the authoring environment, we save some cycles on the ASR, which is certainly a good thing if we’re using it to back a high traffic web site.

One last note: for assistance setting up XML metadata extraction for WCM, see this page on the Alfresco wiki.

Jun 172009
 
The Mega Powers (Hulk Hogan and Randy Savage) ...

Recently I’ve been working on a REST API for reporting workflow status information in Alfresco.  After getting some of the functionality nailed down, it really bothered me that I wasn’t able to use Test Driven Development (TDD) in the process.  So I went looking, and I found quite a few open source tools out there in the wild that made good prospects for acceptance testing these REST APIs that I was working on.  It was time for a SMACKDOWN! OOOOOOOH YEAAAAAAAH!

Contender #1 – Selenium

I heard of Selenium in the past and have wanted to tinker with it for a long time, so I tried this one out first.  Note that Selenium is really just the “brand name”; there are actually several inter-related offerings here.  The first is Selenium IDE, which comes as an add-on to Firefox, which is pretty awesome.  Using this tool, you can basically record your tests, and play them back.  This is very easy to try out – literally within minutes I had recorded my first tests.

That was very cool, but I needed to test result sets with dynamic data, so I had to take a look at Selenium-RC, which has APIs that enable the use of your favorite programming language: Java, C#, Perl, PHP, Python, or Ruby.  The good news with this tool is that it actually uses the real browser to do its testing.  That’s also the bad news.  For each and every test, a new Firefox was launched, which would certainly take a while as the test suite grew larger.  The main advantage that I saw with this tool is that it would be great for testing web applications with Javascript for cross-browser compatibility.  In fact, Selenium-RC is leveraged by Selenium Grid, which allows you to test cross-browser and on different operating systems.  I dig it, but I just have some simple REST APIs to test, so the whole Selenium suite is overkill for me.

Reporting of results using Selenium-RC ultimately would have to be via JUnit reporting mechanisms, which are pretty decent as I recall, but you will have an extra step to set that up in your Ant build file.

Oh, one other note.  You can use Selenium IDE to record your tests and export them as a Selenium-RC Java class.  Pretty cool, but the implementation felt like it was quick and dirty as I recall – something about the class name I gave it and the source that was generated was ‘off’.

Contender #2 – HTMLUnit

This is basically just a Java API that makes it easy to extract information from web pages programatically.  Because I knew that several other tools were built using HTMLUnit at the core, I didn’t spend any time investigating the possibility of using it directly, which was a smart decision.

Contender #3 – JWebTest

This one is cool in concept.  It’s basically an abstraction of Selenium and HTMLUnit.  If you want to test using HTMLUnit most of the time for speed but want to switch to Selenium for a cross browser sanity check from time to time, JWebTest could be your answer.  I spent maybe an hour setting it up in my environment and writing a test with it.  It didn’t handle HTTP basic authentication right out of the box (when using a URL like “http://admin:admin@localhost:8080/alfresco/my/rest/service.json”), whereas Selenium did handle such a URL properly.  I did look (just now) to see if it handles HTTP basic authentication, and it looks like it does via the API – see WebTestCase.getTestContext() and TestContext.setAuthorization().

Regardless, the bottom line with this is that it’s a Java based API that I would have to use to program all of my tests, and the ability to switch on “Selenium mode” isn’t very compelling to me for testing REST APIs.  Therefore, this one doesn’t really add much value over using HTMLUnit directly for this use case.

Contender #4 – Canoo WebTest

Initially, my first reaction regarding Canoo was “Oh man, I don’t like that it uses Ant so heavily”.  For reasons I won’t go into here, I’m using Ant (not Maven) as my build tool for this project, so ultimately, that’s not a deal breaker.  That said, getting up and running with Canoo was pretty awesome.  The instructions say to put the WebTest bin directory in your path, which generally bothers me, but I did it anyway.  Then there’s a way to generate a project skeleton (a very maven-esque thing to do), which I did, and by running their shell script within the generated test project’s directory, I was off and running.  I then created my own test files (in Ant based XML with custom Canoo tasks), plugged them into the main build script, and BOOYAH!  I was off to the races.

The execution of the tests is pretty fast, certainly faster than Selenium, and result reporting is tight:

canooreport

Canoo WebTest also has the advantage that adding new tests is a declarative exercise – no programming and compilation required.  HTTP Basic authentication is handled nicely via simple attributes on the <invoke> step.  Here’s an example:

        <webtest name="Check end date capability for assigned tasks">
            <invoke url="http://localhost:8080/alfresco/service/api/workflow/status/user/admin.json?endDate=2009-05-23"
                    description="Admin with end date 2009-05-23"
                    username="admin"
                    password="admin"/>
            <verifyText text='{"description":"Review","priority":2,"due":null,"properties":null,"percent":0,"completed":null,"status":"Not Yet Started","duration":null,"created":"2009-05-23 23:59:59.0","name":"wf:reviewTask"}'/>
            <not>
                <verifyText text='{"description":"Adhoc Task","priority":2,"due":null,"properties":null,"percent":0,"completed":null,"status":"Not Yet Started","duration":null,"created":"2009-05-24 00:00:00.0","name":"wf:adhocTask"}'/>
            </not>
        </webtest>

The Winner – Canoo WebTest

Just to spell it out clearly: Canoo is my tool of choice for REST API testing, due to ease of use, speed of execution, good reporting, and easy handling of HTTP basic authentication per test.  If/when I move to using a Maven build system, it looks like that’s alright by Canoo, since they have a maven plugin.

Other Alternatives

Other possibilities for folks out there are:

  • Celerity – Ruby based testing framework.  Not for me since I’m not a Ruby wonk.
  • JSFUnit – Specifically geared towards testing JSF applications, which is not the case here.
  • WebDriver – Similar to Selenium, and is in fact rolling into Selenium according to the FAQ.  As such I didn’t look at this for longer than 5 minutes.
Reblog this post [with Zemanta]
May 272009
 

As I was testing the functionality of the user based workflow status reporting API that I posted about recently, I discovered that the Javascript API for workflow in Alfresco only enabled viewing workflow related information for the authenticating user.  I however would like to enable monitoring by other users who are either administrators or members of a designated group, such as a “Managers” group of users who will monitor workflow statistics.  Fortunately, there is a convenient way to handle this thanks to the magic of the Spring Framework (upon which Alfresco is built).

As described on the wiki, you can add custom Javascript APIs that are backed by Java implementations.  In fact, it is via this mechanism that the documented Javascript API is exposed.  But for our workflow status reporting scenario, we want to replace the implementation of the exposed “workflow” object with an implementation that allows for our new authorization requirement.  Therefore we need to find where the current “workflow” object is configured.  A simple grep command revealed that this object (along with all of the others) is configured via the script-services-context.xml Spring configuration file, which has the following bean entry for workflow:

    <bean id="workflowScript" parent="baseJavaScriptExtension"
            class="org.alfresco.repo.workflow.jscript.WorkflowManager">
        <property name="extensionName">
            <value>workflow</value>
        </property>
        <property name="serviceRegistry">
            <ref bean="ServiceRegistry"/>
        </property>
    </bean>

Ideally, we would want to have all of the current capabilities that the WorkflowManager class already implements plus whatever our extension class would add to it via the ‘workflow’ object (including handling for our new authorization requirement in this case).  For some reason though, which I chose not to investigate thoroughly, extending an existing class intended for exposure via Javascript isn’t working the way I would have expected (see here for the JIRA report I filed about it).  Only the new public methods in my class were being exposed, despite the extension of the existing WorkflowManager class.

Since it’s not terribly critical though, we’ll just build a new class and expose a new object via Javascript.  We’ll call it WorkflowStatusManager and configure it via Spring as follows:

    <bean id="workflowScript" parent="baseJavaScriptExtension"
            class="org.alfresco.repo.workflow.jscript.WorkflowStatusManager">
        <property name="extensionName">
            <value>workflowstatus</value>
        </property>
        <property name="serviceRegistry">
            <ref bean="ServiceRegistry"/>
        </property>
    </bean>

Now we’re free to add parameterized accessors for assigned tasks and completed tasks (where the parameter will be the user authority for which to obtain results for).  As you can see, this class will have a reference to the ServiceRegistry from the Alfresco Java API, which can be used to gain access to a variety of services, including the WorkflowService which will be used in this case.

One addition that I’m considering adding into the Spring configuration would be a property to set the name(s) of the group(s) that should have access to other users’ workflow data.  If you have ideas for other workflow status reporting capabilities to expose to Javascript, please share!

May 122009
 

Now that I’ve had a chance to dig into implementing some of the workflow status reporting capabilities, it’s time to get a little more specific.  First, let’s take a look at the architecture.

workflow_status_architecture1

As you can see from the image above, the workflow reporting web scripts will run on the Alfresco server, exposing information via JSON (and/or other formats) to other presentation layer technologies like Alfresco Share, JBoss Portal, etc.  Later on in this process Share dashlets or JSR-168 portlets can be written to consume and present the exposed data.

Because a lot of workflow information is already exposed via the Workflow JavaScript API, I’ll use JavaScript backed web scripts to expose the data as appropriate for the reports I want to generate.  I suspect that as I progress further, I may need to extend the workflow service at the Java level to expose additional workflow information.  If/when that happens, I’ll have a couple of choices for exposing that new information:

For now though, I need to flesh out what the API will look like.  At this point I’m assuming that most custom workflows will extend the out of the box models, and will thus have certain metadata available for extraction and reporting.  Note that only JSON will be exposed for now.  Exposing XML, RSS, ATOM, or even an HTML representation of the data is easily possible though by using the Alfresco web scripts templating capabilities.

User

Returns current and historical workflow information for the authenticating user.  It’s also possible to request another user’s workflow information via the id parameter.

URL

http://yourhost:8080/alfresco/service/api/workflow/status/user.format

Formats

JSON

HTTP Method(s)

GET

Requires Authentication

Yes, as the user for which workflow information is being requested or as an administrative role otherwise

Parameters

  • id.  Optional.  Specifies the id of the user whose workflow information is being requested.
    • Example: http://yourhost:8080/alfresco/service/api/workflow/status/user/brobinson.json
  • startDate.  Optional.  Specifies the start date after which to retrieve workflow information.
    • Example: http://yourhost:8080/alfresco/service/api/workflow/status/user/brobinson.json?startDate=01-01-2008
  • endDate.  Optional.  Specifies the end date before which to retrieve workflow information.
    • Example: http://yourhost:8080/alfresco/service/api/workflow/status/user/brobinson.json?endDate=12-31-2008 or http://yourhost:8080/alfresco/service/api/workflow/status/user/brobinson.json?startDate=01-01-2008&endDate=12-31-2008
  • timePeriod.  Optional.  Specifies the time period to use to delimit data, for example in a table or graph.  The value must be one of {daily, weekly, biweekly, bimonthly, monthly, quarterly, biannually, annually}. When specified, this parameter must be accompanied by either startDate, endDate, or both
    • Example: http://yourhost:8080/alfresco/service/api/workflow/status/user/brobinson.json?startDate=01-01-2008&endDate=12-31-2008&timePeriod=monthly
  • taskName. Optional. Specifies a taskName to filter by. Workflow information related to any other task will NOT be included if this parameter is specified.
    • Example: http://yourhost:8080/alfresco/service/api/workflow/status/user/brobinson.json?taskName=wf:reviewTask
  • sortBy.  Optional.  Specifies the parameter to sort by.  Must be one of {taskName, priority, duration, status, percent, dueDate, created, description}.
  • showProperties.  Optional.  Specifies whether task properties should be included in the output for every task.

Response

{
      user: "brobinson",
      assignedTasks: [
            {
            	name: "wf:reviewTask",
                description: "Take a look",
                priority: 3,
                due: null,
                percent: 0,
                completed: null,
                status: "Not Yet Started",
                duration: null,
                created: "2009-05-12 10:25:52.51",
                properties: [
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}startDate",
                            value: null
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}packageItemActionGroup",
                            value: "edit_package_item_actions"
                        },
						{
                            key: "{http://www.alfresco.org/model/bpm/1.0}completionDate",
                            value: null
                        },
						{
                            key: "{http://www.alfresco.org/model/bpm/1.0}percentComplete",
                            value: 0
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}context",
                            value: "workspace://SpacesStore/707f218a-0967-4ebc-9bb4-872e9cb67597"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}priority",
                            value: 3
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}pooledActors",
                            value: "[]"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}package",
                            value: "workspace://SpacesStore/f602c326-ea5f-40f0-91b4-21f27249aff7"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}packageActionGroup",
                            value: ""
                        },
                        {
                            key: "{http://www.alfresco.org/model/content/1.0}owner",
                            value: "admin"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}dueDate",
                            value: null
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}hiddenTransitions",
                            value: ""
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}description",
                            value: "Take a look"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}outcome",
                            value: ""
                        },
                        {
                            key: "{http://www.alfresco.org/model/content/1.0}created",
                            value: "2009-05-12 10:25:52.51"
                        },
                        {
                        	key: "{http://www.alfresco.org/model/bpm/1.0}status",
                            value: "Not Yet Started"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}taskId",
                            value: 6586373
                        }
                  ]
            }, ...(truncated)...
      ],
      completedTasks: [
            {
            	name: "wf:submitReviewTask",
                description: "Hey, take a look at this, will you?",
                priority: 3,
                due: null,
                percent: 0,
                completed: "2009-04-15 16:22:49.808",
                status: "Completed",
                duration: 17.468,
                created: "2009-04-15 16:22:49.128",
                properties: [
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}startDate",
                            value: null
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}packageItemActionGroup",
                            value: "start_package_item_actions"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}completionDate",
                            value: "2009-04-15 16:22:49.808"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}percentComplete",
                            value: 0
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}context",
                            value: "workspace://SpacesStore/6c5bf7d2-806c-4f04-99bc-3a4b84ed7073"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}workflowDueDate",
                            value: null
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}priority",
                            value: 3
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}pooledActors",
                            value: "[]"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}package",
                            value: "workspace://SpacesStore/6c5bf7d2-806c-4f04-99bc-3a4b84ed7073"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}packageActionGroup",
                            value: "add_package_item_actions"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}assignee",
                            value: "workspace://SpacesStore/9a4684c8-18ef-4f3e-a119-e5bb08c9da52"
                        },
                        {
                            key: "{http://www.alfresco.org/model/content/1.0}owner",
                            value: "admin"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}workflowPriority",
                            value: 2
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}dueDate",
                            value: null
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}hiddenTransitions",
                            value: ""
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}description",
                            value: "Hey, take a look at this, will you?"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}outcome",
                            value: ""
                        },
                        {
                            key: "{http://www.alfresco.org/model/content/1.0}created",
                            value: "2009-04-15 16:22:49.128"
                        },
                        {
                            key: "{http://www.alfresco.org/model/bpm/1.0}status",
                            value: "Completed"
                        },
                        {
                        	key: "{http://www.alfresco.org/model/bpm/1.0}taskId",
                            value: 2785280
                        },
                        {
                        	key: "{http://www.alfresco.org/model/bpm/1.0}workflowDescription",
                            value: "Hey, take a look at this, will you?"
                        }
                  ]
            }, ...(truncated)...
      ]

}

Group

Returns current and historical workflow information for the identified group

URL

http://yourhost:8080/alfresco/service/api/workflow/status/group.format

Formats

JSON

HTTP Method(s)

GET

Requires Authentication

Yes, as an administrative role

Parameters

  • id.  Required.  Specifies the id of the user whose workflow information is being requested.
    • Example: http://yourhost:8080/alfresco/service/api/workflow/status/group.json?id=Engineering
  • startDate.  Optional.  Specifies the start date after which to retrieve workflow information.
    • Example: http://yourhost:8080/alfresco/service/api/workflow/status/group.json?id=Engineering&startDate=01-01-2008
  • endDate.  Optional.  Specifies the end date before which to retrieve workflow information.
    • Example: http://yourhost:8080/alfresco/service/api/workflow/status/group.json?id=Engineering&endDate=12-31-2008 or http://yourhost:8080/alfresco/service/api/workflow/status/group.json?id=Engineering&startDate=01-01-2008&endDate=12-31-2008
  • timePeriod.  Optional.  Specifies the time period to use to delimit data, for example in a table or graph.  The value must be one of {daily, weekly, biweekly, bimonthly, monthly, quarterly, biannually, annually}. When specified, this parameter must be accompanied by either startDate, endDate, or both
    • Example: http://yourhost:8080/alfresco/service/api/workflow/status/group.json?id=Engineering&startDate=01-01-2008&endDate=12-31-2008&timePeriod=monthly
  • taskName. Optional. Specifies a taskName to filter by. Workflow information related to any other task will NOT be included if this parameter is specified.
    • Example: http://yourhost:8080/alfresco/service/api/workflow/status/group.json?id=Engineering&taskName=wf:reviewTask
  • sortBy.  Optional.  Specifies the parameter to sort by.  Must be one of {taskName, priority, duration, status, percent, dueDate, created, description}.
  • showProperties.  Optional.  Specifies whether task properties should be included in the output for every task.

Response

To be implemented, but is intended to contain tasks assigned to group members, group pooled tasks (which are by definition unassigned), and completed tasks, all of which will have the following fields associated with each task: taskName, priority, duration, status, percent, dueDate, created, description.

Task

Returns current and historical workflow information for the identified task.  Further details TBD.

Workflow

Returns current and historical workflow information for the identified workflow.  Further details TBD.

If you have any feedback on what I’ve presented so far, as always, please speak up.

Reblog this post [with Zemanta]