Skip navigation
All Places > Alfresco Content Services (ECM) > Blog > 2012 > January
2012
Prompted by Jeff's post the other day on developing custom actions for Share, I've spent a small amount of time this week revisiting the custom Document Library actions hosted on Share Extras, and making a few 4.0-compatibility updates.



There are now two add-ons within the project that provide custom doclib actions for 4.0



  • The Execute Script action provides a small JavaScript action with a dialogue for selecting a JavaScript file to run against a document or folder. Although duplicated by the new onActionFormDialog-powered document-execute-script action (configured out-of-the-box but not included in the default action groups) in 4.0, it provides a useful example of a simple client-side custom action.


  • The Document Geographic Details add-on provides a Geotag action for adding or modifying geo data on repository items, plus two other actions which complement the out-of-the-box View in Google Maps action by adding support for OpenStreetMap, as well as other mapping providers via the Geohack service used by Wikipedia articles.



Since the second add-on provides a few more varied examples of custom actions (which may not be obvious from the project name!) I thought it was worth stepping through each of these in turn.

Geotag Action



This JavaScript action was present in the original 3.x version, and allows geographic information - basically latitude and longitude values - to be added to items, or modified on items which already have it. If your camera doesn't have a GPS receiver to capture this information, or if you need to make minor corrections to it, then this might be useful.



To make the action compatible with 4.0 there weren't too many changes needed. Actions are now declared via Bubbling rather than as additional prototype methods of Alfresco.doclib.Actions, so a small refactoring of the code is necessary, but should not affect the main logic of your JavaScript action.



I'll try to post an update in the near future on converting your existing actions to 4.0.



Geotag action



The action works by calling a separate web script - included within the add-on - to display a modal dialogue with a Google Map view. You can use the cursor to position the marker indicating the item's location as you need, then the position of the marker is saved back to the repository when the OK button is clicked.

View in OpenStreetMap Action



One of the new features in Alfresco 4.0 is an additional page that allows you to view the geographic location of an item using an embedded Google Map. But sometimes Google Maps just doesn't have the detail that other mapping providers have - particularly for rural areas - and OpenStreetMap is a great example of a collaboratively-built map available on open terms.



The OpenStreetMap view looks similar to the Google Maps view. In fact, although it declares a new site page, it borrows everything but the map view itself from existing Share page components on the Google Maps page.



View in OpenStreetMap page



The map view is provided by a single additional web script, which delegates the work of setting up the map to a client-side component, as per the regular Share pattern.



When I first added the map view last year, I implemented the view using the popular OpenLayers viewer, but I had problems getting a marker and an associated pop-up that looked as good as Google's. So I switched the other day to the upstart Leaflet,  which provides a much easier-to-implement and cleaner-looking alternative.



From their site,

Leaflet is a modern, lightweight BSD-licensed JavaScript library for making tile-based interactive maps for both desktop and mobile web browsers, developed by CloudMade to form the core of its next generation JavaScript API.



It is built from the ground up to work efficiently and smoothly on  both platforms, utilizing cutting-edge technologies included in HTML5.  Its top priorities are usability, performance, small size, A-grade browser support, flexibility and easy to use API. The OOP-based code of  the library is designed to be modular, extensible and very easy to  understand. Find out more on the features page.


This is the first time I've tried Leaflet, but it works well here. I'd be interested in any feedback on this, as I may consider replacing the Google Maps view currently provided in the Document Geographic Details page component with it in the future.



There's no doubt a few enhancements possible here, such as displaying the location of nearby repository items on the map view in addition to the currently-selected item. Again I'd be interested in any feedback.



Back to the action definition. Now since the action itself just needs to link to the new page, it can be easily handled by the pagelink action type, much easier than implementing a custom client-side action handler. An action evaluator ensures that the action is only shown for geotagged items, and is borrowed from the configuration of the View in Google Maps action.



Lastly, if you look closely at the screenshot you'll see that as well as displaying the map itself, it also indicates the location of the marker, e.g.

Showing item in London Borough of Richmond upon Thames, Greater London, London, England, United Kingdom


This is looked up via a separate AJAX call to OpenStreetMap's Nominatim reverse geocoding service, which takes a latitude and longitude, and returns some text indicating the name of the area that it resides in. Nominatim is accessed via a custom Surf endpoint, declared in the add-on's configuration.

View Location on Geohack Action



The Geohack service allows a referring web site to specify a latitude and longitude as URL parameters and then returns links to that location on a wide range of mapping providers. If you want to view a location on other mapping services besides Google Maps and OpenStreetMap then you can use this action to do that.



Like the Geotag action this is implemented as a client-side JavaScript action, but since it only needs to open a new window with the Geohack service there is no additional dialogue or page definition needed.



With 4.0, it is possible to implement such actions much more easily using the link action type, which takes a target URL in its configuration, and which can include parameters that are substituted in at runtime, based on the node's properties. But implementing it in a client-side handler as I've done here can give you more control.

Developing your own actions



Hopefully the three examples I've walked through will provide examples of what is possible to achieve in Alfresco using custom actions. You can find the code linked to from the Document Geographic Details add-on page, and further docs in Client-side template and action extensions on docs.alfresco.com and in Mike Hatfield's DevCon 2012 presentation.
Since Alfresco Community 4.0.c was quietly slipped out over the Christmas break, I'd been meaning to get round to creating an AMI with the new release. After I was asked by a colleague who was trying to do something similar with our upcoming Enterprise version, I managed to grab a short amount of time to revisit the image creation process that I wrote about back in November.



First off, I decided to reduce some of the complexity by breaking with the past and going for an EBS boot image only, rather than the S3-backed instance-store types that I previously put together. As well as being simpler to create, EBS boot images offer several advantages, the chief one being that by virtue of their EBS-backed storage, they can persist their state when turned off.



From my initial testing, the EBS boot images seem to perform well too, so unless I get any reports of significant problems I'll be using the same method to create all future AMIs as well.

Running the images



The new 4.0.c image is listed on the Alfresco EC2 Images page. Launching it is easy using the AWS web console.



  1. Click the Launch in AWS Management Console icon next to the version you want to run


  2. Log in using the e-mail and password you registered with Amazon, if you are not already logged in


  3. Step through the wizard, making sure that you select Small in the instance size


  4. When you get to the security part, make sure you select a security profile that has inbound SSH (port 22) and HTTP (port 80) enabled, plus any other interfaces you want to allow into Alfresco



After the wizard has completed you can monitor the start-up of your instance in the Instances area of the EC2 web console. To connect to Alfresco Share on your instance, simply paste the public DNS name of the instance into your browser address bar. It may take up to five minutes for the repository to run through the bootstrap process, after which you will be redirected to the normal Share login page.

Creating your own images



There's nothing to stop you creating your own Alfresco AMIs, using the same procedure that I've used here. As I mentioned, when creating EBS boot instances the procedure is much simpler, and there is no need to copy private key files across the network onto the host.



There are a few steps to carry out, but I've tried to make these as straightforward as possible

1. Start up your base image



Start by running up a pre-configured Ubuntu or other Linux AMI – see Canonical’s list for the latest official versions. You should pick the right one for your geography and size requirements, but I use the most recent 32-bit EBS boot AMI from the West Europe region.



To start up the image, just click the relevant link in the list, or use the Launch wizard in the EC2 Management Console to pick an alternative Linux-based AMI. The scripts have been tested with Ubuntu 11.10 and 12.04 32-bit instances, so I'd recommend either of those where possible. 64-bit AMIs do not seem to run well on small instances, so if you use one of those then ensure you pick a larger instance size.

2. Log in via SSH



I use PuTTY to connect to the newly-created instance. You'll need to have created a keypair for your AWS account and have imported it into PuTTY. You can then connect via SSH as the relevant user account (the Ubuntu images use the user 'ubuntu') and the correct key file.



There is general information on connecting to Linux instances in the EC2 help and Eric Hammond has a page that explains some more about securely connecting to the official Ubuntu images, which should be accurate for all the AMIs listed on his site.

3. Download and extract the Ubuntu Quickstart scripts



These are now available as a ZIP download from the alfresco-ubuntu-qs project on Google Code. Find the latest version in the Downloads section and right click to copy the download link to your clipboard.



Back in your SSH client you can then download the files using the following commands (modify as needed for the latest version of the scripts), or you can skip the first line by downloading the file from the project site and SCP'ing it up to your Amazon instance.

curl http://alfresco-ubuntu-qs.googlecode.com/files/alfresco-ubuntu-qs-0.9.8.tar.gz -o alfresco-ubuntu-qs-0.9.8.tar.gz

tar xzf alfresco-ubuntu-qs-0.9.8.tar.gz


4. Install Alfresco



Now we can install the Alfresco files using the install.sh script supplied in the Ubuntu Quickstart package.

cd alfresco-ubuntu-qs

sudo ./install.sh --alf-version 4.2.b --jdk-version openjdk-7 --no-install-dod


Note that the --no-install-dod option to install.sh is only needed for version 4.0, for which the DoD5015.2 add-on module is currently unavailable.



For Alfresco Community 4.2, only JDK7 is required, which you must enable using --jdk-version openjdk-7.



You can follow the progress of the script in your SSH client. When you are prompted for a MySQL password, you must enter ‘alfresco’ - unless you have changed the value of $MYSQL_USER in install.sh to something else.



When the script has finished it will tell you that installation is complete and that Alfresco can be started. DO NOT START ALFRESCO UP AT THIS TIME - unless you want a pre-installed repository to be part of your AMI.



5. Create your AMI



One of the benefits of EBS boot instances is that you can create images with a single command using the AWS API tools, or directly from the EC2 web console.



To create your AMI from the web console, right-click the running instance in the Instances section, select Create Image from the menu, enter an appropriate file path in the 'name' field, and confirm.



The image creation process should take no more than a few minutes - far quicker than the instance-store method! Once it is done, you should be able to see your image in the AMIs section of the web console. You can test it by starting up an instance of it as per the steps in Running the images, above.

6. Cleaning up



Last but not least, make sure that the instance that you used to create your image is terminated, so it will not keep on consuming billable resources! You should do the same for any additional instances that you've fired up to test your image.

This post assumes reasonable sys-admin Alfresco knowledge and assumes you are already familiar with setting up the Alfresco Repository in a cluster configuration and familiar configuring an Apache web-server instance. You should read the previous post first.

Since my last blog on this subject, there has been quite a bit of interest in load balancing Alfresco Share. This is good news but also means that customers and the community have found some issues that needed looking at - good, because more use of Alfresco testing means more stability for everyone when we fix the issues - that's part of the fun of having a very active user community!

Three main points were raised:

  1. A bug relating to a problem with dashboard template layouts not updating between web-tier nodes - which was quickly resolved for 3.4.8 and 4.0 see ALF-12318
    1. A request for example load balancing config for Share itself as I only posted info on the forward proxy config for Share.
      1. An issue raised in regards to a noticeable drop in performance when the cache config detailed in the previous post was added to each Share instance (which was required for load balancing multiple instances) - see this related blog post http://blog.alfrescian.com/?p=146 and ALF-12336

       

      I'll address all issues here and share some exciting performance related to item 3 also!

       

      1. A bug that manifested when a user changed the template layout selection for a dashboard - for example from 2 to 3 column layout. The problem was that SpringSurf PageView objects were internally caching the Page object rather than just the PageId - easily and quickly fixed.

       

      2. Config for load balancing Share is very similar to that for load balancing Alfresco.

       

      Set up two tomcat instances containing 'share.war' webapp with the 'share-config-custom.xml' and 'custom-slingshot-application-context.xml' config as detailed in the previously post. Remember the ports exposed by Tomcat need tweaking if you have those instances on the same physical machine - increment the HTTP and AJP and redirect ports in the tomcat/conf/server.xml config. Also ensure you have set the 'jvmRoute' attributes to different values ready for the load balancing config, I used 'tomcat3' and 'tomcat4' as I now have a lot of servers running on a single box!

       

      Create another Apache instance, I just did a copy and paste from the one I used to load balance the Alfresco cluster. Again bump the Apache listener port value if it exists on the same physical machine. Finally configure Apache 'httpd.conf' to load balance against your Share web-tier instances:

       

      # Reverse Proxy Settings (Share multi-instance load balancing)

       

      ProxyRequests Off

       

      ProxyPassReverse /share balancer://app

       

      ProxyPass /share balancer://app stickysession=JSESSIONID|jsessionid nofailover=On

       

      <Proxy balancer://app>

       

      BalancerMember ajp://localhost:8019/share route=tomcat3

       

      BalancerMember ajp://localhost:8024/share route=tomcat4

       

      </Proxy>

       

      Simply point your client browsers at your new Apache instance. If you have set up your Share instances to themselves use an Apache which is load balancing against an Alfresco cluster then you now have a full 2x Alfresco Cluster + Apache + 2x Alfresco Share + Apache set up!

       

      This is great - BUT it leads onto point 3...

       

      3. Scalability and fail-over capability has improved by having multiple Share instances - but individual performance per Share node is reduced. Now you may consider (as I did first) that this is expected, there is after all some additional work here going on - in the case of the Alfresco cluster it's inter-node communication overhead, and in the case of the Share nodes it is reduced performance due to the caches that have been disabled. What's apparent here is that if you just cluster Alfresco and keep to a single Share instance, we find that a single instance of Share can easily service a 4 node Alfresco cluster so in practice there is little need to load balance Share for performance reasons - but you certainly might want to for high availability reasons. Perhaps a price worth paying for the ability to remove and drop in additional nodes without your users knowing or having to update their URLs... But it would be nice if there wasn't such a noticeable performance drop in Share.

       

      The good news is that this has all changed in Alfresco 3.4.8/4.0.1 - in response to the community blog post and our drive to continually improve the performance of Alfresco, a new clustering technique has now been implemented for the web-tier.

       

      For a load balanced environment, Alfresco Share now uses Hazelcast to provide multicast messaging between web-tier nodes. The end result of this is that all caches are now enabled again for each node, and we send very simple cache invalidation message when appropriate to all nodes. So the performance degradation is gone - each node is as fast a single Share instance.

       

      The only changes required for each node are in “custom-slingshot-application-context.xml” – generally located in \tomcat \shared\classes\alfresco\web-extension and used to override the Spring application context beans for Share. There is an example “custom-slingshot-application-context.xml.sample” provided in the Alfresco distribution which now includes this config.

       

      Enable this section on each Share tomcat instance to enable the Hazelcast cluster messaging:




         <!--

       

              Hazelcast distributed messaging configuration - Share web-tier cluster config (3.4.8 and 4.0.1)

       

              - see http://www.hazelcast.com/docs.jsp

       

              - and specifically http://www.hazelcast.com/docs/1.9.4/manual/single_html/#SpringIntegration

       

         -->

       

         <!-- Configure cluster to use either Multicast or direct TCP-IP messaging - multicast is default -->

       

         <!-- Optionally specify network interfaces - server machines likely to have more than one interface -->

       

         <!-- The messaging topic - the 'name' is also used by the persister config below -->

       

         <hz:topic id='topic' instance-ref='webframework.cluster.slingshot' name='slingshot-topic'/>

       

         <hz:hazelcast id='webframework.cluster.slingshot'>

       

            <hz:config>

       

               <hz:group name='slingshot' password='alfresco'/>

       

               <hz:network port='5801' port-auto-increment='true'>

       

                  <hz:join>

       

                     <hz:multicast enabled='true'

       

                           multicast-group='224.2.2.5'

       

                           multicast-port='54327'/>

       

                     <hz:tcp-ip enabled='false'>

       

                        <hz:members></hz:members>

       

                     </hz:tcp-ip>

       

                  </hz:join>

       

                  <hz:interfaces enabled='false'>

       

                     <hz:interface>192.168.1.*</hz:interface>

       

                  </hz:interfaces>

       

               </hz:network>

       

            </hz:config>

       

         </hz:hazelcast>

       

       

         <bean id='webframework.slingshot.persister.remote' class='org.alfresco.web.site.ClusterAwarePathStoreObjectPersister' parent='webframework.sitedata.persister.abstract'>

       

            <property name='store' ref='webframework.webapp.store.remote' />

       

            <property name='pathPrefix'><value>alfresco/site-data/${objectTypeIds}</value></property>

       

            <property name='hazelcastInstance' ref='webframework.cluster.slingshot' />

       

            <property name='hazelcastTopicName'><value>slingshot-topic</value></property>

       

         </bean>

       

       

         <bean id='webframework.factory.requestcontext.servlet' class='org.alfresco.web.site.ClusterAwareRequestContextFactory' parent='webframework.factory.base'>

       

            <property name='linkBuilderFactory' ref='webframework.factory.linkbuilder.servlet' />

       

            <property name='extensibilityModuleHandler' ref='webscripts.extensibility.handler' />

       

            <property name='clusterObjectPersister' ref='webframework.slingshot.persister.remote' />

       

         </bean>

       

      The config enables the Hazelcast Spring integration which starts the Hazelcast server, it is easily configurable and can use either multicast (the default and minimal effort) or TCP-IP direct if preferred. See http://www.hazelcast.com/docs.jsp for more info. For the default set up, identical config can be applied to each Share node and it will 'just work'.

       

       

      When you start Share you'll see something like this:

       

      INFO: /192.168.2.8:5801 [slingshot] Hazelcast 1.9.4.6 (20120105) starting at Address[192.168.2.8:5801]

      19-Jan-2012 13:58:57 com.hazelcast.system

      INFO: /192.168.2.8:5801 [slingshot] Copyright (C) 2008-2011 Hazelcast.com

      19-Jan-2012 13:58:57 com.hazelcast.impl.LifecycleServiceImpl

      INFO: /192.168.2.8:5801 [slingshot] Address[192.168.2.8:5801] is STARTING

      19-Jan-2012 13:58:59 com.hazelcast.impl.MulticastJoiner

      INFO: /192.168.2.8:5801 [slingshot]

      Members [1] {

              Member [192.168.2.8:5801] this

      }

      19-Jan-2012 13:58:59 com.hazelcast.impl.management.ManagementCenterService

      INFO: /192.168.2.8:5801 [slingshot] Hazelcast Management Center started at port 5901.

      19-Jan-2012 13:58:59 com.hazelcast.impl.LifecycleServiceImpl

      INFO: /192.168.2.8:5801 [slingshot] Address[192.168.2.8:5801] is STARTED


      This means the config has driven the initialisation of Hazelcast successfully. That's all there is to creating a Share instance in the cluster, if the config is present it will become a cluster node, if the config is not present (such as for a default install) then Hazelcast never starts . Once each node is started they will find each other automatically. Then once you users interact with Share, only when the following operations occur will cache invalidation messages will be sent from the affected node to the others in the cluster:

      • an existing site/user dashboard layout is modified
      • new site or user dashboard is created
      • runtime app properties are changed (the Share theme currently)

       

      This keeps chatter to a minimum and performance up!

      There have been a few recent updates to the Spring Surf extensibility capabilities that I’ve been blogging about recently which I thought I should capture in a quick roundup post. These features are currently available in the Alfresco Community 4.0c release.



      Improved i18n localization handling



      In the original implementation of the i18n extension handling there was a limitation in that it was necessary to explicitly provide extensions for the locale that was ultimately resolved. This meant that there was no graceful degradation of the specificity of locale (e.g. from “en_GB” to “en” to the default properties file).  This has now been updated so that all matching locale files will be merged from least to most specific so that extensions can provide a combination of i18n properties files which will all be merged into the WebScript properties file with the most specific overrides “winning”. This update should make it simpler to provide i18n overrides – especially where different region locales might be used to access the same application (e.g. “en_US” and “en_GB”).



      Module Auto-Deploy Changes



      Originally there were only two ways for modules to be deployed - they would either ALL be deployed automatically or would each need to be manually deployed.  It is now possible for a module to request to be automatically deployed if allowed which broadens the choices to the following:



      1. Automatically deploy all modules


      2. Only automatically deploy any modules that request it


      3. All modules must be manually deployed.


      This new option was introduced to support the forthcoming Records Management module and the Cloud offering. By default Alfresco remains configured in manual mode but the Records Management module will be able to override the default configuration to switch to “enable-auto-deploy” mode when it is AMPed in.



      To configure Spring Surf applications to support this new option you should use the following configuration:

      <module-deployment>

         <mode>manual</mode>

         <enable-auto-deploy-modules>true</enable-auto-deploy-modules>

      </module-deployment>


      …and to make a module request to be automatically deployed, add the following as a child element of <module>



      <auto-deploy>true</auto-deploy>



      Additional Dependencies



      One feature that was already available in the earlier 4.0 Community releases (but not blogged about) was the ability for modules to request for additional JavaScript and CSS dependency resources to be imported.  This works in the same way as i18n, JavaScript controller and FreeMarker template customizations in that you need to specify a target package and if a WebScript declared at that package is executed on a request then those additional dependencies will be added as imports to the <head> element of the page.



      A module that adds additional dependencies might look like this:

      <module>

         <id>Add dependencies</id>

         <customizations>

            <customization>

               <targetPackageRoot>org.acme</targetPackageRoot>

               <dependencies>

                  <css>/res/demo/dependencies/styles.css</css>

                  <js>/res/demo/dependencies/script.js</js>

               </dependencies>


            </customization>

         </customizations>

      </module>


      An additional dependency can only be added once even if multiple modules are evaluated successfully that request them.



      Updated Module Deployment Error Handling



      There have been some updates to the module deployment code to provide better handling of errors. One of the main problems that can occur when deploying modules is that different authentication mechanisms are used for WebScript and remote client calls. This means you might be authenticated to access a WebScript (i.e. the module deployment script) but not to actually persist changes to the remote client.  The end result would be that modules might be deployed in application memory but would not survive a server restart. There are now clearer error messages to indicate that this is happening and could occur for other reasons (for example the Alfresco repository might be down).  You might also notices that on server start-up some errors are shown in the log indicating that module settings could not be saved - this isn't actually a problem (previously persisted changes won't be effected), but is something that we're hoping to resolve. In the meantime it should at least be clearer when using the deployment WebScript when problems have occurred.



      Filter Blog

      By date: By tag: