<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-8387702312908643994</atom:id><lastBuildDate>Mon, 09 Jun 2025 16:43:32 +0000</lastBuildDate><category>Alfresco</category><category>Share</category><category>Java</category><category>News</category><category>Data lists</category><category>Dev Environment</category><category>Other</category><title>boulder dan</title><description>how-tos and snippets from an engineer at &lt;a href=&quot;http://www.livingsocial.com&quot;&gt;LivingSocial&lt;/a&gt;</description><link>http://www.boulderhopkins.com/</link><managingEditor>noreply@blogger.com (Dan)</managingEditor><generator>Blogger</generator><openSearch:totalResults>16</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-8019865304609591985</guid><pubDate>Sun, 06 May 2012 15:16:00 +0000</pubDate><atom:updated>2012-05-06T09:32:47.076-06:00</atom:updated><title>Thread safety with no threads in sight</title><description>&lt;br /&gt;
When people discuss thread safety in code it is almost entirely within the context of threading in a single process. The problems that come up are races conditions, &lt;a href=&quot;http://blog.regehr.org/archives/490&quot;&gt;data races&lt;/a&gt;, and unseen updates with solutions like locking, synchronized updates and immutability.&lt;br /&gt;
&lt;br /&gt;
This definition of thread safety is too focused. Much of the problems normally attributed to thread safety can be seen in programs that have no access to OS threads or are running on multiple servers.&lt;br /&gt;
&lt;br /&gt;
My primary work is in Ruby, where we normally labor under the illusion of thread safety by primarily deploying to a single threaded VM. The problem is that often this single thread hinders scaling. The standard solution is to take long running tasks and allow them to finish asynchronous in a queued process.&lt;br /&gt;
&lt;br /&gt;
The main web process and the backgrounded process don&#39;t share any data, so thread safety in the traditional sense isn&#39;t an issue, but if you step back to look at the application as a whole they *do* share a database. This database is often mutable and is therefore a big source of thread safety bugs. Data races are the most obvious, but lost updates and stale reads also need to be considered.&lt;br /&gt;
&lt;br /&gt;
How do you fix these problems? With the same concepts you would at the application layer but translated to whatever database you have.&lt;br /&gt;
&lt;br /&gt;
If you&#39;re using an ACID compliant RDBMS then you&#39;re probably used to transactions which correspond to locking and mutex constructs in most programming languages. You should also consider improving your schema so that the database can enforce as much of your business logic as possible. This way your database can reject data that would create an inconsistent state even if your application deems it okay (remember your application won&#39;t know about the other processes running simultaneously).&lt;br /&gt;
&lt;br /&gt;
To pick on Rails for a moment: most application built on Rails have their &lt;a href=&quot;http://stackoverflow.com/questions/2373262/are-foreign-keys-in-rails-generally-avoided&quot;&gt;database constraints in the application&lt;/a&gt;. This creates tough scenarios like the following.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;1. user = User.find_by_email(&quot;danielhopkins@gmail.com&quot;)&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; 2. if !user&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; 3. &amp;nbsp; User.create!(:email =&amp;gt; &quot;danielhopkins@gmail.com&quot;)&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; 4. end&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;An execution might look like this:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; Process #1 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;| &amp;nbsp; Process #2&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; |&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; ln 1 user = nil &amp;nbsp; | &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; ln 2 true &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; | ln 1 user = nil&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; ln 3 create! &amp;nbsp; &amp;nbsp; &amp;nbsp;| ln 2 true&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; ln 4 done. &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;| ln 3 create!&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; | ln 4 done.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;font-family: &#39;Courier New&#39;, Courier, monospace;&quot;&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
A worst case scenario of process execution has created identical users. &lt;b&gt;Even with no threads involved we have a clear threading bug.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Of course there are ways to prevent this from happening in Rails, this is illustrating how threading issues can manifest in a single threaded application. The same thing would apply to Java or any of the functional languages that pride themselves on their threading capabilities.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Remember that thread safety doesn&#39;t always involve threads.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
It&#39;s a broader application problem that requires diligent understanding of how your application interacts with shared state. None of us are immune; especially in the world of internet applications.&lt;br /&gt;
&lt;br /&gt;</description><link>http://www.boulderhopkins.com/2012/05/thread-safety-without-threads.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-6327925922469371268</guid><pubDate>Thu, 24 Feb 2011 02:22:00 +0000</pubDate><atom:updated>2011-02-23T19:23:18.713-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Other</category><title>Epic google error message fail</title><description>&lt;p&gt;I just spent a day without being able to send email because I enabled&lt;a href=&quot;http://googleblog.blogspot.com/2011/02/advanced-sign-in-security-for-your.html&quot;&gt; two factor authentication&lt;/a&gt; on my work account. That and a complicated gmail setup that apparently appeals to no one else ;)&lt;/p&gt;&lt;div&gt;I forward all my mail from my work, google apps account into my personal gmail account so that all my email is consolidated for reading, filtering, etc. I then use the “&lt;a href=&quot;http://mail.google.com/support/bin/answer.py?hl=en&amp;amp;ctx=mail&amp;amp;answer=22370&quot;&gt;Send mail as&lt;/a&gt;” functionality to email from my work account. Gmail even allows you to configure alternate SMTP information so that when I send from my work account people don&#39;t get a &quot;Sent on behalf of...&quot; in their Outlook.&lt;/div&gt;&lt;div&gt;&lt;p&gt;Well, when I turned on the new two factor authentication for my work email account the smtp password stopped working and this all broke...silently.&lt;/p&gt;&lt;div&gt;No errors when I sent the email, no errors on the SMTP configuration page and worst: &lt;strong&gt;all my emails made it successfully into the &quot;sent” folder!&lt;/strong&gt;&lt;/div&gt;&lt;p&gt;I finally just guessed what must be happening and fixed it.&lt;/p&gt;&lt;div&gt;Nice.&lt;/div&gt;&lt;/div&gt;&lt;p&gt; &lt;/p&gt;</description><link>http://www.boulderhopkins.com/2011/02/epic-google-error-message-fail.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-7110983334909893103</guid><pubDate>Sun, 20 Feb 2011 22:38:00 +0000</pubDate><atom:updated>2012-05-06T09:46:58.402-06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">Java</category><title>Connecting to an untrusted certificate in java</title><description>I was getting errors(&lt;em&gt;unable to find valid certification path to requested target&lt;/em&gt;) in &lt;a href=&quot;http://www.alfresco.com/&quot;&gt;Alfresco&lt;/a&gt; trying to connect to LDAP over ssl using an untrusted certificate and found a great hint on the interwebs that I thought I&#39;d share.&lt;br /&gt;
&lt;br /&gt;
Most of the hits on google mention this post:&amp;nbsp;&lt;a href=&quot;http://blogs.sun.com/gc/entry/unable_to_find_valid_certification&quot; style=&quot;color: #2a5db0;&quot; target=&quot;_blank&quot;&gt;http://blogs.sun.com/gc/entry/unable_to_find_valid_certification&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
My only criticism of that article is that it only minimally addresses how to actually use the fixed keystore. &amp;nbsp;I downloaded their source and hacked it &amp;nbsp;up a little change the name of the certificate store from &quot;jssecacerts&quot; into &quot;cacert&quot; which is the default certificate store for all java programs. &amp;nbsp;My goal was to fix the certificate store for the entire machine.&lt;br /&gt;
&lt;br /&gt;
To install an untrusted certificate into your keystore the process is like this:&lt;br /&gt;
&lt;script src=&quot;https://gist.github.com/2623005.js?file=process.sh&quot;&gt;
&lt;/script&gt;</description><link>http://www.boulderhopkins.com/2011/02/connecting-to-untrusted-certificate-in.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-5274348653716382223</guid><pubDate>Thu, 06 Jan 2011 16:12:00 +0000</pubDate><atom:updated>2011-01-06T09:14:47.991-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Other</category><title>Help improve the Kindle reading ecosystem.</title><description>&lt;p&gt;I sent this as an email to kindle-feedback but thought I would share it with the interwebs as well.  Let me be clear that I am absolutely in love with my Kindle.  I used to not particularly enjoy reading but the kindle has changed my opinion in two short weeks.  With that said my major sadness about it was access to personal content.  Hence this letter:&lt;/p&gt;&lt;div&gt;&lt;p&gt;Dear Amazon,&lt;/p&gt;&lt;p&gt;Please improve the reading ecosystem represented by the kindle device, and the plethora of applications that also represent &quot;kindle devices&quot; e.g. the iphone, ipad, web, etc.  Right now these other devices are treated like second class citizens because there is no way to get personal content onto them and to synchronize the reading of the content.&lt;/p&gt;&lt;p&gt;This hurts users who want to use their kindles as &quot;reading devices&quot; vs. &quot;buying from amazon&#39;s bookstores&quot; device.&lt;/p&gt;&lt;p&gt;One idea on how to improve this is to give access to a certain amount of disk space at Amazon, maybe 1 gb (per google docs) for free users and more for people that have bought kindles or have purchased books (maybe there is even a reward program, the more money you spend with Amazon, the more personal disk space you get).  This space could be used to store personal documents that then appear in the &quot;archived&quot; section of people&#39;s kindles.  Then you could add synchronization for: latest location, bookmarks, annotation.  Now I can read documents i&#39;m currently working on, review design specifications, etc.  All on my kindle.&lt;/p&gt;&lt;p&gt;This changes my kindle from a device I use for an hour a day at home before bed into my preferred reading device at work, at home and on the road.&lt;/p&gt;&lt;div&gt;Thanks for a fantastic device and feel free to contact me,&lt;/div&gt;&lt;div&gt;&lt;div&gt;--dan&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt; &lt;/p&gt;</description><link>http://www.boulderhopkins.com/2011/01/help-improve-kindle-reading-ecosystem.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-1003824590304746479</guid><pubDate>Tue, 14 Dec 2010 01:29:00 +0000</pubDate><atom:updated>2010-12-13T19:11:11.970-07:00</atom:updated><title>Remap Caps lock on a Macbook Pro</title><description>&lt;div style=&quot;background-color: transparent; margin-left: 0px; margin-right: 0px;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: inherit;&quot;&gt;&lt;span id=&quot;iab:&quot; style=&quot;vertical-align: baseline;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;I was fortunate enough to receive a CR-48 from Google last week. &amp;nbsp;My impressions of it are more or less in line with what &lt;a href=&quot;http://techcrunch.com/2010/12/12/cr-48-chrome-notebook-review/&quot; id=&quot;h85s&quot; title=&quot;MG Siegler over at TechCrunch&quot;&gt;MG Siegler over at TechCrunch&lt;/a&gt; has reported: &amp;nbsp;good battery, nice screen, light weight laptop. &amp;nbsp;The only problem is that I have needed to adjust my workflow when I&#39;m working on my regular computer, a Macbook Pro to facilitate sharing documents between the two machines. &amp;nbsp;For example, I have migrated away from OmniOutliner to a comparable online version, &lt;a href=&quot;http://workflowy.com/&quot; id=&quot;imkd&quot; title=&quot;Workflowy&quot;&gt;Workflowy&lt;/a&gt;.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;span style=&quot;font-style: normal; vertical-align: baseline;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;The other major change is that my keyboard habits are changing. &amp;nbsp;With only a weekend usage of ChromeOS I already prefer having the caps lock key replaced with a button to open up new tabs in my browser.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;span style=&quot;font-style: normal; vertical-align: baseline;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;When I got into work this morning I immediately missed my new favorite key. &amp;nbsp;I decided to try and figure out a way to replace this missing behavior.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;span style=&quot;vertical-align: baseline;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;A &lt;a href=&quot;http://news.ycombinator.com/item?id=1995857&quot; id=&quot;lx55&quot; style=&quot;font-style: normal;&quot; title=&quot;recent article outlines&quot;&gt;recent article outlines&lt;/a&gt; how to turn the caps lock key into a control, option or splat. &amp;nbsp;One benefit of an Apple approved method is that the keyboard caps lock light disables itself when you change the functionality. &amp;nbsp;A never ending flickering green light would have been a constant annoyance for me.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background-color: transparent; margin-left: 0px; margin-right: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background-color: transparent; margin-left: 0px; margin-right: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;This approach isn&#39;t enough, I wanted more - to be able to open a new tab in Chrome. &amp;nbsp;I found the &lt;a href=&quot;http://pqrs.org/macosx/keyremap4macbook/extra.html&quot; id=&quot;z75:&quot; title=&quot;PCKeyboardHack&quot;&gt;PCKeyboardHack&lt;/a&gt; preference pane/kext. &amp;nbsp;Using the instructions provided I remapped the caps lock key to F14, or keycode 107 on the &lt;a href=&quot;http://www.apple.com/keyboard/&quot; id=&quot;yt_e&quot; title=&quot;slim apple keyboard&quot;&gt;slim apple keyboard&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background-color: transparent; margin-left: 0px; margin-right: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background-color: transparent; margin-left: 0px; margin-right: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;Now that it was remapped to a real keyboard code it wasn&#39;t difficult to change the new tab shortcut in chrome to &lt;a href=&quot;http://lifehacker.com/343328/create-a-keyboard-shortcut-for-any-menu-action-in-any-program&quot; id=&quot;b16e&quot; title=&quot;respond to F14&quot;&gt;respond to F14&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background-color: transparent; margin-left: 0px; margin-right: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background-color: transparent; margin-left: 0px; margin-right: 0px;&quot;&gt;&lt;span style=&quot;font-family: inherit; font-size: small;&quot;&gt;These changes work on the laptop&#39;s keyboard in addition to the external keyboard and I&#39;m very pleased with the results.&lt;/span&gt;&lt;/div&gt;</description><link>http://www.boulderhopkins.com/2010/12/remapping-capslock-on-macbook-pro.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-8123537396999572525</guid><pubDate>Mon, 08 Nov 2010 18:22:00 +0000</pubDate><atom:updated>2010-11-08T11:22:14.010-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">News</category><title>Notes about Alfresco&amp;#39;s devcon</title><description>&lt;p&gt;Nate McMinn has a &lt;a href=&quot;http://www.unorganizedmachines.com/site/software-and-technology/42-events/110-alfresco-devcon-nyc-wrap-up&quot;&gt;brief wrap up&lt;/a&gt; of the Alfresco developers convention.  I was unable to attend and it sounds like I missed a lot of interesting news.  I was excited to here:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;One upcoming project that was discussed at DevCon is putting together a third-party components catalog for Alfresco.  Right now there is nothing like this available.  Alfresco community projects are scattered all over the place.  Some are in Alfresco Forge, some are on Google Code, still others are on developers&#39; blogs (mine included).  I&#39;m sure I&#39;m forgetting a few locations, but you get the idea.  Rolling all of this up in one queryable repository would be a fantastic addition to the Alfresco community.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;I wonder how this will be deployed and who will maintain it.  The wiki idea for documentation seems to be barely moving along.&lt;/p&gt;</description><link>http://www.boulderhopkins.com/2010/11/notes-about-alfresco-devcon.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-5518679496306129278</guid><pubDate>Mon, 08 Nov 2010 15:10:00 +0000</pubDate><atom:updated>2010-11-08T08:10:00.544-07:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">Java</category><title>Creating custom JMX MBeans for reporting in Alfresco</title><description>&lt;p&gt;JMX rocks.  When configuring a server it is a boon to developers.  Especially when combined with the Alfresco &lt;a href=&quot;http://wiki.alfresco.com/wiki/Alfresco_Subsystems&quot;&gt;subsystem architecture&lt;/a&gt;.  You can interate on changes to the LDAP sync without having to restart the server.  JMX also gives savvy system administrators a way to manage and monitor what’s going on within the repository.&lt;/p&gt;&lt;p&gt;If you’re still unfamiliar with the basics of JMX, especially within the context of Alfresco, &lt;a href=&quot;http://jared.ottleys.net/&quot;&gt;Jarred Ottley&lt;/a&gt; over at Alfresco has written a number of excellent tutorials.  I’ve added some additional articles and come up with the list below.&lt;/p&gt;&lt;p&gt;Some links:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;http://wiki.alfresco.com/wiki/JMX&quot;&gt;JMX basics in Alfresco&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://wiki.alfresco.com/wiki/JMX#Connecting_through_JMX_Consoles_.2F_JSR-160&quot;&gt;Some JMX console implementation&lt;/a&gt; - I prefer &lt;a href=&quot;https://visualvm.dev.java.net/&quot;&gt;Java VisualVM&lt;/a&gt;, which comes with the JDK&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://jared.ottleys.net/alfresco/alfresco-jmx-from-the-command-line&quot;&gt;JMX from the commandline&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;http://jared.ottleys.net/alfresco/tunneling-debug-and-jmx-for-alfresco&quot;&gt;Tunnelling JMX - for use with Amazon EC2 or any firewalled computer&lt;/a&gt;.  &lt;a href=&quot;http://jared.ottleys.net/alfresco/updated-tunneling-debug-and-jmx-for-alfresco&quot;&gt;3.2 sp2 and newer should review this update&lt;/a&gt; .&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;With the basics out of the way it is often interesting to create your own MBean that can report custom statistics or expose custom methods.  This tutorial creates a new MBean that shows the number of Asynchronous jobs being run.  Alfresco exports beans using standard &lt;a href=&quot;http://static.springsource.org/spring/docs/2.0.x/reference/jmx.html&quot;&gt;Spring practices&lt;/a&gt;.  This makes keeps everything well documented.  The list of things to create is small:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Context file to register new MBean&lt;/li&gt;&lt;li&gt;Annotated Java class&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;Context File&lt;/h2&gt;&lt;pre&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE beans PUBLIC &quot;-//SPRING//DTD BEAN//EN&quot; &quot;http://www.springframework.org/dtd/spring-beans.dtd&quot;&amp;gt;&lt;br /&gt;&amp;lt;beans&amp;gt;&lt;br /&gt;  &amp;lt;bean id=&quot;whySlow&quot; class=&quot;com.zia.jmx.WhySlow&quot;/&amp;gt;&lt;br /&gt;  &amp;lt;bean id=&quot;ziaExporter&quot;&lt;br /&gt;    class=&quot;org.springframework.jmx.export.MBeanExporter&quot;&amp;gt;&lt;br /&gt;    &amp;lt;property name=&quot;assembler&quot; ref=&quot;assembler&quot;/&amp;gt;&lt;br /&gt;    &amp;lt;property name=&quot;beans&quot;&amp;gt;&lt;br /&gt;      &amp;lt;map&amp;gt;&lt;br /&gt;        &amp;lt;entry key=&quot;Zia:name=WhySlow&quot; value-ref=&quot;whySlow&quot;/&amp;gt;&lt;br /&gt;      &amp;lt;/map&amp;gt;&lt;br /&gt;    &amp;lt;/property&amp;gt;&lt;br /&gt;  &amp;lt;/bean&amp;gt;&lt;br /&gt;  &amp;lt;bean id=&quot;jmxAttributeSource&quot;&lt;br /&gt;    class=&quot;org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource&quot;/&amp;gt;&lt;br /&gt;  &amp;lt;!-- will create management interface using annotation metadata --&amp;gt;&lt;br /&gt;  &amp;lt;bean id=&quot;assembler&quot;&lt;br /&gt;    class=&quot;org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler&quot;&amp;gt;&lt;br /&gt;    &amp;lt;property name=&quot;attributeSource&quot; ref=&quot;jmxAttributeSource&quot;/&amp;gt;&lt;br /&gt;  &amp;lt;/bean&amp;gt;&lt;br /&gt;&amp;lt;/beans&amp;gt;&lt;/pre&gt;&lt;h2&gt;Java Class&lt;/h2&gt;&lt;pre&gt;  @ManagedResource&lt;br /&gt;  public class WhySlow {&lt;br /&gt;    @ManagedAttribute( description = &quot;Asynchronous actions left to run&quot; )&lt;br /&gt;    public long getAsyncActions() {&lt;br /&gt;      AsynchronousActionExecutionQueueImpl aaeq = ( AsynchronousActionExecutionQueueImpl ) AlfUtil.getSpringBean( &quot;defaultAsynchronousActionExecutionQueue&quot; );&lt;br /&gt;      long ret = -1;&lt;br /&gt;      try {&lt;br /&gt;        Class&amp;lt;?&amp;gt; c = aaeq.getClass();&lt;br /&gt;        Field[] props = c.getDeclaredFields();&lt;br /&gt;        Field tpeField = c.getDeclaredField( &quot;threadPoolExecutor&quot; );&lt;br /&gt;        tpeField.setAccessible( true );&lt;br /&gt;        ThreadPoolExecutor tpe = ( ThreadPoolExecutor ) tpeField.get( aaeq );&lt;br /&gt;        ret = tpe.getTaskCount() - tpe.getCompletedTaskCount();&lt;br /&gt;      } catch( NoSuchFieldException nsfe ) {&lt;br /&gt;        nsfe.printStackTrace();&lt;br /&gt;      } catch( IllegalArgumentException e ) {&lt;br /&gt;        e.printStackTrace();&lt;br /&gt;      } catch( IllegalAccessException e ) {&lt;br /&gt;        e.printStackTrace();&lt;br /&gt;      }&lt;br /&gt;      return ret;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The &lt;a href=&quot;http://www.jarvana.com/jarvana/view/org/springframework/spring/1.2.9/spring-1.2.9-javadoc.jar!/org/springframework/jmx/export/metadata/ManagedAttribute.html&quot;&gt;annotations&lt;/a&gt; are important for documentation in the console.  There are some reflection shenanigans that allows access to private fields.  Your implementation will not need much of this code, except for the annotations.&lt;/p&gt;&lt;p&gt;When this code is deployed the WhySlow mbean will appear at the top level, next to Alfresco node.  This is controlled by the key of the map passed into the “beans” property(&lt;em&gt;Zia:name=WhySlow&lt;/em&gt;) and is explained in the &lt;a href=&quot;http://static.springsource.org/spring/docs/2.0.x/reference/jmx.html#jmx-naming&quot;&gt;Spring docs&lt;/a&gt;.&lt;/p&gt;&lt;h2&gt;Wrap up&lt;/h2&gt;&lt;p&gt;A bean created under JMX tends to keep the separation of concerns better than many of the alternatives.  We have created “consoles” in webscripts, but it seems to be difficult to train the sys admins to go to multiple places for configuration.  Once the repository is started it is consistent to point users at JMX for all administration and reporting.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt; &lt;/p&gt;</description><link>http://www.boulderhopkins.com/2010/11/creating-custom-jmx-mbeans-for.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-2094885589300841863</guid><pubDate>Tue, 02 Nov 2010 14:37:00 +0000</pubDate><atom:updated>2010-11-02T09:06:27.601-06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">News</category><category domain="http://www.blogger.com/atom/ns#">Share</category><title>Using Alfresco for authentication in Spring Surf</title><description>&lt;p&gt;Kevin Roast’s blog had a great post about &lt;a href=&quot;http://blogs.alfresco.com/wp/kevinr/2010/11/01/springsurf-authenticating-users-against-an-alfresco-repository/&quot;&gt;Authentication User’s against an Alfresco Repository&lt;/a&gt;.  When we first explored Surf, when it starting to get traction in 3.2 or 3.3 our major complaint was that it seemed difficult to actually connect to the repository.&lt;/p&gt;&lt;p&gt;I’m glad to see that this has changed.&lt;/p&gt;&lt;p&gt;Or that it is now better documented.&lt;/p&gt;&lt;!-- Technorati Tags Start --&gt;&lt;p&gt;Technorati Tags: &lt;a rel=&quot;tag&quot; href=&quot;http://technorati.com/tag/Alfresco&quot;&gt;Alfresco&lt;/a&gt;&lt;/p&gt;&lt;!-- Technorati Tags End --&gt;</description><link>http://www.boulderhopkins.com/2010/11/using-alfresco-for-authentication-in.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-8607475883682379169</guid><pubDate>Mon, 01 Nov 2010 19:26:00 +0000</pubDate><atom:updated>2010-11-02T08:40:44.467-06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">Dev Environment</category><title>Rapid development in Alfresco Share - the refresh webscript</title><description>&lt;p&gt;&lt;a href=&quot;http://www.ziaconsulting.com&quot;&gt;Zia Consulting&lt;/a&gt; develops in Eclipse using a setup different than described by Alfresco.  We run an embedded version of Jetty with the Alfresco default WARs.  At runtime we mix in changes using extra classpath entries and web overlays.  This has many benefits: most importantly that we develop functionality without having to restart the repository.  When there is a reason to restart the server we make efforts to fix the problem through programming or configuration.&lt;/p&gt;&lt;p&gt;Share has caches that make the webscripts and surf run faster.  These caches have documentation on how to turn them off but this documentation changes with every release of share or surf.  This is difficult to keep up with.&lt;/p&gt;&lt;p&gt;If you&#39;re interested in the documented process I have a list of articles to read:&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://www.benh.co.uk/alfresco/surf-part-1-getting-started/&quot;&gt;Old Surf intro&lt;/a&gt; - refreshing the Surf page cache.  I don&#39;t believe this is needed in the most recent versions of share.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://wiki.alfresco.com/wiki/Surf_Platform_-_Deployment_Configurations#Web_Framework_Configuration_2&quot;&gt;Official wiki documentation&lt;/a&gt; - For Alfresco 3.0 ➔ 3.3, before Surf joined the Spring project.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://wiki.alfresco.com/wiki/Surf_Platform_-_Developers_Guide&quot;&gt;Official developer guide&lt;/a&gt; -  The section for debugging didn’t get finished.  Likely because they moved over to Spring.&lt;/p&gt;&lt;p&gt;I’m sure there are more.  Feel free to add them to the comments.&lt;/p&gt;&lt;p&gt;We have given up on following the documentation and developed a custom Web Script that invalidates these caches whenever a source file is changed.  The code for this Web Script is from the service index refresh that is part of “org.springframework.extensions.webscripts.bean.IndexUpdate”.  We paid particular attention to the reset method of “org.springframework.extensions.webscripts.AbstractRuntimeContainer”.  The reset method gets run when you goto http://localhost/share/service/index and click the reset button.  The important code is trivial:&lt;/p&gt;&lt;pre&gt;  /* public void reset(){ ...*/&lt;br /&gt;  scriptProcessorRegistry.reset();&lt;br /&gt;  templateProcessorRegistry.reset();&lt;br /&gt;  getRegistry().reset();&lt;br /&gt;  configService.reset();&lt;br /&gt;  /* } */&lt;/pre&gt;&lt;p&gt;Running all four of these resets takes a couple seconds and is impractical to do after every save.  Taking just the script and template reset and putting them in a Web Script keeps it pretty lean.  Running time drops from a 2 - 3 seconds to a few hundred ms.  It is fast enough that the refresh will happen before you can cmd + tab to your web browser and refresh.&lt;/p&gt;&lt;p&gt;We created a refresh Web Script using the above code and then setup an &lt;a href=&quot;http://www.eclipse.org/articles/Article-Builders/builders.html&quot;&gt;Eclipse builder&lt;/a&gt; to run when the source is updated.  To do this you need to create a few things.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Refresh Web Script.  &lt;a href=&quot;http://wiki.alfresco.com/wiki/Web_Scripts#Creating_a_Description_Document&quot;&gt;It’s best to use “none” authentication.&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Ant script that will call the new Web Script.  Shown below.&lt;/li&gt;&lt;li&gt;In Eclipse turn the directory containing javascript and freemarker templates (the config directory if your projects look like exploded amp files) into a source directory so it triggers the auto build.&lt;/li&gt;&lt;li&gt;Create a builder according to &lt;a href=&quot;http://help.eclipse.org/galileo/index.jsp?topic=/org.eclipse.platform.doc.user/gettingStarted/qs-93_project_builder.htm&quot;&gt;this tutorial&lt;/a&gt; with one change: &lt;br /&gt;&lt;ul&gt;&lt;li&gt;In step #11 they recommend &lt;em&gt;NOT&lt;/em&gt;&lt;strong&gt;&lt;/strong&gt;creating an auto build.  &lt;strong&gt;Ignore this direction - &lt;/strong&gt;auto building is really handy for our purposes.  It doesn’t cause performance issues, so turn it on and configure it to run “run-refresh-webscript”.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The ant file that we use:&lt;/p&gt;&lt;pre&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;&lt;br /&gt;&amp;lt;project name=&quot;project&quot; default=&quot;run-refresh-webscript&quot;&amp;gt;&lt;br /&gt;  &amp;lt;target name=&quot;run-refresh-webscript&quot;&amp;gt;&lt;br /&gt;    &amp;lt;get src=&quot;http://localhost:8080/share/service/zia/util/refresh&quot;&lt;br /&gt;      dest=&quot;refresh-results.dat&quot; /&amp;gt;&lt;br /&gt;  &amp;lt;/target&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;I found &lt;a href=&quot;http://blogs.alfresco.com/wp/kevinr/2010/04/07/developer-tips-for-alfresco-share-33/&quot;&gt;this article&lt;/a&gt; by Kevin Roast that has some great tips on Share development.  The “mode” selection is of great interest.&lt;/p&gt;</description><link>http://www.boulderhopkins.com/2010/11/rapid-development-in-alfresco-share.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-4591523258618249020</guid><pubDate>Thu, 28 Oct 2010 14:17:00 +0000</pubDate><atom:updated>2010-10-30T15:54:19.746-06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">Data lists</category><category domain="http://www.blogger.com/atom/ns#">Share</category><title>Creating calculated datalist columns in Alfresco Share 3.4b</title><description>&lt;p&gt;This is a short one since not much changed.   If you didn&#39;t have a chance to read the 3.3 version of this &lt;a href=&quot;http://www.boulderhopkins.com/2010/10/creating-calculated-datalist-columns-in.html&quot;&gt;please review it now&lt;/a&gt; since I&#39;m not going to post the entire tutorial again. We only need to change the filter and specifically how the dynamic columns are added to the form.&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;pre&gt;﻿public class ProjectStatusFilter extends AbstractFilter﻿&amp;lt;Object, NodeRef&amp;gt; {&lt;br /&gt;  @Override public void afterGenerate(Object item, List fields,&lt;br /&gt;  List forcedFields, Form form, Map context) {&lt;br /&gt;    ﻿boolean typeDefCorrect = (item instanceof TypeDefinition &amp;amp;&amp;amp; ((TypeDefinition) item)&lt;br /&gt;      .getName().equals(PmoModel.TYPE_PROJECT_STATUS_DL));&lt;br /&gt;    boolean nodeRefCorrect = (item instanceof NodeRef &amp;amp;&amp;amp; AlfUtil.services()&lt;br /&gt;      .getNodeService().getType((NodeRef) item).equals(&lt;br /&gt;      PmoModel.TYPE_PROJECT_STATUS_DL));&lt;br /&gt;    ﻿if (typeDefCorrect || nodeRefCorrect) {&lt;br /&gt;      double varianceValue = 0d;&lt;br /&gt;      double percentComplete = 0d;&lt;br /&gt;      double percentExpended = 0d;&lt;br /&gt;      // If this is a node then do the calculations&lt;br /&gt;      if (nodeRefCorrect) {&lt;br /&gt;        NodeRef itemNode = (NodeRef) item;&lt;br /&gt;        /* Some calculations that aren&#39;t interesting */&lt;br /&gt;      }&lt;br /&gt;      /* Here are the changes for 3.4b */&lt;br /&gt;      form.addField(FieldUtils.makePropertyField(﻿PmoModel.variancePropDef,&lt;br /&gt;      varianceValue,﻿AlfUtil.services().getNamespaceService()));&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  /* Left the other 3 fuctions for completeness */&lt;br /&gt;  @Override&lt;br /&gt;  public void afterPersist(Object item, FormData data, NodeRef persistedObject)&lt;br /&gt;  {}&lt;br /&gt;  @Override&lt;br /&gt;  public void beforeGenerate(Object item, List fields,&lt;br /&gt;    List forcedFields, Form form, Map context) {}&lt;br /&gt;  @Override&lt;br /&gt;  public void beforePersist(Object item, FormData data) {}&lt;br /&gt;}&lt;/pre&gt;</description><link>http://www.boulderhopkins.com/2010/10/creating-calculated-datalist-columns-in_28.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-4635129045814400339</guid><pubDate>Tue, 26 Oct 2010 16:15:00 +0000</pubDate><atom:updated>2010-10-30T15:42:20.511-06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">News</category><title>JBPM Workflow in Alfresco Share 3.4a community release</title><description>&lt;p&gt;Dick Weisinger over at &lt;a href=&quot;http://www.formtek.com/&quot;&gt;FormTek&lt;/a&gt; posted a great article on adding new workflows to the Share 3.4 using the newly updated form service.  I&#39;m currently developing similar functionality for 3.4b and found the same results - jbmp workflows just seems to work in the share interface.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://keytocontent.blogspot.com/2010/10/jbpm-workflow-in-alfresco-share-34a.html&quot;&gt;Article&lt;/a&gt;&lt;/p&gt;</description><link>http://www.boulderhopkins.com/2010/10/jbpm-workflow-in-alfresco-share-34a.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-7633805474281391239</guid><pubDate>Mon, 25 Oct 2010 13:55:00 +0000</pubDate><atom:updated>2010-10-30T15:55:14.023-06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">Data lists</category><category domain="http://www.blogger.com/atom/ns#">Share</category><title>Creating calculated datalist columns in Alfresco Share 3.3</title><description>&lt;p&gt;Datalists out-of-the-box don&#39;t have a whole lot of functionality outside of simply capturing data.  A simple way to help users is to add calculated columns that transform other columns or related data from the repository.  In Alfresco 3.3 this functionality is straightforward and allows creating powerful datalists that even an Excel user could love.&lt;/p&gt;&lt;p&gt;For this demo we will create a part of a project management datalist that takes allows users to enter the following: Estimated to completion, estimated at completion, actuals and budget.  From these inputs we will generate the variance, percent complete and percent expended.&lt;/p&gt;&lt;h2&gt;Overview&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;Create a node and type form filter&lt;/li&gt;&lt;li&gt;Create a couple of new static fields&lt;/li&gt;&lt;li&gt;Create a new context file to instantiate the filters&lt;/li&gt;&lt;li&gt;Tell Share to display the fields in the forms&lt;/li&gt;&lt;/ul&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;h2&gt;Create a form filter&lt;/h2&gt;&lt;p&gt;Form filters derive from an AbstractFilter and must implement the four abstract functions in that class.  Filters are called whenever the form service runs and will be pass either a node instance or a typedef.  I created this implementation when I didn&#39;t understand the difference so I implemented both.  I also liked that I could show zeros before the node was created.&lt;/p&gt;&lt;pre&gt;  public class ProjectStatusFilter extends AbstractFilter﻿&amp;lt;Object, NodeRef&amp;gt; {&lt;br /&gt;  @Override public void afterGenerate(Object item, List fields,&lt;br /&gt;  List forcedFields, Form form, Map context) {&lt;br /&gt;    ﻿boolean typeDefCorrect = (item instanceof TypeDefinition &amp;amp;&amp;amp; ((TypeDefinition) item)&lt;br /&gt;      .getName().equals(PmoModel.TYPE_PROJECT_STATUS_DL));&lt;br /&gt;    boolean nodeRefCorrect = (item instanceof NodeRef &amp;amp;&amp;amp; AlfUtil.services()&lt;br /&gt;      .getNodeService().getType((NodeRef) item).equals(&lt;br /&gt;      PmoModel.TYPE_PROJECT_STATUS_DL));&lt;br /&gt;    ﻿if (typeDefCorrect || nodeRefCorrect) {&lt;br /&gt;      double varianceValue = 0d;&lt;br /&gt;      double percentComplete = 0d;&lt;br /&gt;      double percentExpended = 0d;&lt;br /&gt;      // If this is a node then do the calculations&lt;br /&gt;      if (nodeRefCorrect) {&lt;br /&gt;        NodeRef itemNode = (NodeRef) item;&lt;br /&gt;        /* Some calculations that aren&#39;t interesting */&lt;br /&gt;      }&lt;br /&gt;      ContentModelFormProcessor.generatePropertyField(&lt;br /&gt;        PmoModel.variancePropDef, form, varianceValue, AlfUtil&lt;br /&gt;        .services().getNamespaceService());&lt;br /&gt;      ContentModelFormProcessor.generatePropertyField(&lt;br /&gt;        PmoModel.percentCompleteDef, form, percentComplete, AlfUtil&lt;br /&gt;        .services().getNamespaceService());&lt;br /&gt;      ContentModelFormProcessor.generatePropertyField(&lt;br /&gt;        PmoModel.percentExpendedDef, form, percentExpended, AlfUtil&lt;br /&gt;        .services().getNamespaceService());&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  /* Left the other 3 fuctions for completeness */&lt;br /&gt;  @Override&lt;br /&gt;    public void afterPersist(Object item, FormData data, NodeRef persistedObject) {}&lt;br /&gt;  @Override&lt;br /&gt;    public void beforeGenerate(Object item, List fields,&lt;br /&gt;    List forcedFields, Form form, Map context) {}&lt;br /&gt;  @Override&lt;br /&gt;    public void beforePersist(Object item, FormData data) {}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;The key to this is the ContentModelProcessor.generatePropertyField.  In order for this to work we need to create a field to be passed in.  I created these fields as anonymous classes and put them into the model interface.&lt;/p&gt;&lt;h2&gt;Create new field definitions&lt;/h2&gt;&lt;p&gt;I&#39;ll give one example, the others are simple variations.  This is the class that is built when you do any content modeling, and the documentation and possible values are the same.  &lt;a href=&quot;http://wiki.alfresco.com/wiki/Data_Dictionary_Guide&quot;&gt;Check the alfresco wiki for more information&lt;/a&gt;.&lt;/p&gt;&lt;pre&gt;static final PropertyDefinition percentExpendedDef = new PropertyDefinition() {&lt;br /&gt;    @Override&lt;br /&gt;    public boolean isStoredInIndex() {&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public boolean isProtected() {&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public boolean isOverride() {&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public boolean isMultiValued() {&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public boolean isMandatoryEnforced() {&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public boolean isMandatory() {&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public boolean isIndexedAtomically() {&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public boolean isIndexed() {&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public String getTitle() {&lt;br /&gt;        return &quot;Percent Expended&quot;;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public QName getName() {&lt;br /&gt;        return PROP_CALC_PERCENT_EXPENDED;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public ModelDefinition getModel() {&lt;br /&gt;        /* This is the name of a model that this type will belong to,&lt;br /&gt;        it&#39;s at the top of a model file for example&lt;br /&gt;        ﻿&amp;lt;model name=&quot;pmodl:risksDataListModel&quot; xmlns=&quot;http://www.alfresco.org/model/dictionary/1.0&quot;&amp;gt;&lt;br /&gt;        */&lt;br /&gt;        return AlfUtil.services().getDictionaryService().getModel(&lt;br /&gt;                MODEL_RISKS_DATA_LIST);&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public IndexTokenisationMode getIndexTokenisationMode() {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public String getDescription() {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public String getDefaultValue() {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public DataTypeDefinition getDataType() {&lt;br /&gt;        return AlfUtil.services().getDictionaryService().getDataType(&lt;br /&gt;                DataTypeDefinition.INT);&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public ClassDefinition getContainerClass() {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;    @Override&lt;br /&gt;    public List getConstraints() {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;};&lt;/pre&gt;&lt;h2&gt;﻿Create a new context file to instantiate the filters&lt;/h2&gt;&lt;p&gt;In the usual places (e.g. WEB-INF/alfresco/extension/custom-form-context.xml) create a new context file and instantiate the filter.&lt;/p&gt;&lt;pre&gt;  &amp;lt;?xml version=&#39;1.0&#39; encoding=&#39;UTF-8&#39;?&amp;gt;&lt;br /&gt;  &amp;lt;!DOCTYPE beans PUBLIC &#39;-//SPRING//DTD BEAN//EN&#39; &lt;br /&gt;      &#39;http://www.springframework.org/dtd/spring-beans.dtd&#39;&amp;gt;&lt;br /&gt;  &amp;lt;beans&amp;gt;&lt;br /&gt;      &amp;lt;bean id=&quot;projectStatusFilterNode&quot; &lt;br /&gt;          class=&quot;com.ziaconsulting.forms.ProjectStatusFilter&quot; &lt;br /&gt;          parent=&quot;baseFormFilter&quot;&amp;gt;&lt;br /&gt;          &amp;lt;property name=&quot;filterRegistry&quot; ref=&quot;nodeFilterRegistry&quot; /&amp;gt;&lt;br /&gt;      &amp;lt;/bean&amp;gt;&lt;br /&gt;      &amp;lt;bean id=&quot;projectStatusFilterType&quot; &lt;br /&gt;          class=&quot;com.ziaconsulting.forms.ProjectStatusFilter&quot; &lt;br /&gt;          parent=&quot;baseFormFilter&quot;&amp;gt;&lt;br /&gt;          &amp;lt;property name=&quot;filterRegistry&quot; ref=&quot;typeFilterRegistry&quot; /&amp;gt;&lt;br /&gt;      &amp;lt;/bean&amp;gt;&lt;br /&gt;  &amp;lt;/beans&amp;gt;&lt;/pre&gt;&lt;h2&gt;﻿Tell Share to display the fields in the forms&lt;/h2&gt;&lt;p&gt;Using the standard method to modify a form in share using the form service tell the node and type forms to display.  This means creating a share-config-custom.xml and adding a few extra elements to your currently configured data list.&lt;/p&gt;&lt;pre&gt;  ﻿&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;&lt;br /&gt;  &amp;lt;alfresco-config&amp;gt;&lt;br /&gt;    &amp;lt;config evaluator=&quot;model-type&quot; condition=&quot;pmodl:projectStatus&quot;&amp;gt;&lt;br /&gt;      &amp;lt;forms&amp;gt;&lt;br /&gt;        &amp;lt;!-- Data Grid view --&amp;gt;&lt;br /&gt;        &amp;lt;form id=&quot;datagrid&quot;&amp;gt;&lt;br /&gt;          &amp;lt;field-visibility&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:projectName&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:projectStatus&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:budget&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:actuals&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:etc&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:eac&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;!-- New Calculated column --&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:pecentExpendedCalculated&quot;/&amp;gt;&lt;br /&gt;          &amp;lt;/field-visibility&amp;gt;&lt;br /&gt;        &amp;lt;/form&amp;gt;&lt;br /&gt;      &amp;lt;/forms&amp;gt;&lt;br /&gt;    &amp;lt;/config&amp;gt;&lt;br /&gt;    &amp;lt;config evaluator=&quot;node-type&quot; condition=&quot;pmodl:projectStatus&quot;&amp;gt;&lt;br /&gt;      &amp;lt;forms&amp;gt;&lt;br /&gt;        &amp;lt;!-- Data Grid view --&amp;gt;&lt;br /&gt;        &amp;lt;form id=&quot;datagrid&quot;&amp;gt;&lt;br /&gt;          &amp;lt;field-visibility&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:projectName&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:projectStatus&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:budget&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:actuals&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:etc&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:eac&quot;/&amp;gt;&lt;br /&gt;            &amp;lt;!-- New Calculated column --&amp;gt;&lt;br /&gt;            &amp;lt;show id=&quot;pmodl:pecentExpendedCalculated&quot;/&amp;gt;&lt;br /&gt;          &amp;lt;/field-visibility&amp;gt;&lt;br /&gt;        &amp;lt;/form&amp;gt;&lt;br /&gt;      &amp;lt;/forms&amp;gt;&lt;br /&gt;    &amp;lt;/config&amp;gt;&lt;br /&gt;  &amp;lt;/alfresco-config&amp;gt;&lt;/pre&gt;&lt;h2&gt;Next Steps, upgrading to 3.4b&lt;/h2&gt;&lt;p&gt;The upgrade to 3.4 isn&#39;t too bad.  Alfresco changed the API  and didn&#39;t bother to leave any hints about how to upgrade, but fortunately I was able to re-figure out how to do this.  I&#39;ll publish the updates later this week.&lt;/p&gt;</description><link>http://www.boulderhopkins.com/2010/10/creating-calculated-datalist-columns-in.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-2207986813309607049</guid><pubDate>Mon, 18 Oct 2010 21:54:00 +0000</pubDate><atom:updated>2010-10-30T15:42:40.157-06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">Java</category><title>Attaching to a running Alfresco instance - running code in Alfresco</title><description>&lt;p&gt;A coworker and I have been scheming for a number of months on ways to run migration scripts and develop workflow in Alfresco.  The common thread is that we have a repository that&#39;s running and we want run arbitrary code against it.  We have had a number of ideas that have taken us in two different directions.  I&#39;d like to talk about a few of them, outline the issues we&#39;ve run into and then outline our merged solution.&lt;/p&gt;&lt;p&gt;&lt;a name=&quot;more&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;h2&gt;Migration&lt;/h2&gt;&lt;p&gt;One approach (the merits depend on the project) to migration is load a bunch of data into the repository and then run some code on the repository to translate the data, extract metadata or whatever.  This can be run with an external suite of tools (e.g. an ETL), but this is often overkill.  The code to accomplish the same thing in Alfresco is often easier than 3rd party tools, involves less tools or languages and can be cheaper.  There are a number of ways to run javascript or java code.&lt;/p&gt;&lt;h3&gt;Command Servlet&lt;/h3&gt;&lt;p&gt;There is an old (from the age of the documentation) servlet that can run arbitrary javascript that seems to fit the bill except that it will only run code stored as nodes in the repository and only in &quot;insecure&quot; mode, which means no access to native java and you have to develop over cifs/webdav or directly in a wysiwig editor in Alfresco.  None if this is ideal.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://wiki.alfresco.com/wiki/URL_Addressability#CommandServlet&quot;&gt;Command Servlet&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://wiki.alfresco.com/wiki/URL_Addressability#CommandServlet&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/a&gt;&lt;a href=&quot;http://forums.alfresco.com/en/viewtopic.php?f=3&amp;amp;t=20500&quot;&gt;Secure webscripts&lt;/a&gt;&lt;/p&gt;&lt;h3&gt;As a webscript&lt;/h3&gt;&lt;p&gt;Creating a web script gives you access to the real Rhino engine, i.e. &quot;secure mode&quot; or could write the script in Java giving you access to the entire API.  This is kludgy because you&#39;re executing your migration script by refreshing a webpage.  Also, the &quot;standard out&quot; is either logging messages or the free marker template.  The last nail in the coffin is that you must run your script in a massive transaction.  This seems to really slow down the system and if you are processing more than 1000 nodes you will get cache error messages.  Turning off the caching in the web script&#39;s descriptor file means no more access to the company home.&lt;/p&gt;&lt;h2&gt;Workflow development&lt;/h2&gt;&lt;p&gt;The slowest part of workflow development is stepping through a long workflow.  To speed up the development process my coworker has developed a series of junit tests that setup the workflow and run it to a particular step that he&#39;s working on.  This allows all of the previously developed steps to be skipped over so that he can develop and debug the current step, it also provides a certain level of integration/regression testing.  He also spends a lot of time developing the java parts of workflow from within a breakpoint, allowing the jvm to swap the class files and then stepping through the code.&lt;/p&gt;&lt;p&gt;There are couple issue with this approach.  The first is the startup and memory usage.  The junit test spins up an entire alfresco repository, which is slow and very memory intensive.  Since it is sharing the same alf_data as the real repository it tends to corrupt the lucene indexes and sometimes the entire repository.  He ends up dropping and reloading the entire schema multiple times per day and reindexing the indexes.  Even with these drawbacks the development time improvements, due to faster iterations, are substantial.&lt;/p&gt;&lt;h2&gt;Java vm attachment - a new approach&lt;/h2&gt;&lt;p&gt;Along with Java 6 came a new feature that I only recently discovered.  You can now &lt;a href=&quot;http://download.oracle.com/javase/6/docs/technotes/guides/attach/index.html&quot;&gt;attach to a running virtual machine&lt;/a&gt; and run code loaded from a jar file and pushed into the running virtual machine.  Conceptually I can write a program (using a Java main), bundle it up in a jar, load it onto a server with a running Alfresco repository and then execute the code which will run in the same virtual machine and therefore have access to the applicationContext and any managed beans.  It works like JMX, for developers.&lt;/p&gt;&lt;p&gt;If attaching to the VM is all you&#39;re interested in feel free to stop here and read through &lt;a href=&quot;http://blogs.sun.com/sundararajan/entry/using_mustang_s_attach_api&quot;&gt;this tutorial&lt;/a&gt; of the process.  If you&#39;re interest in a more Alfresco specific, or at least a more web container specific implementation then stay with me.&lt;/p&gt;&lt;p&gt;I took what I learned in that tutorial and created a program and agent main function.  Both are bundled into a jar file.  The jar has a manifest that makes it executable and takes a classpath directory that is added to the running container and a &lt;a href=&quot;http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Runnable.html&quot;&gt;Runnable&lt;/a&gt; class to be run in a new thread from within the container.  The relevant code in the program class is:&lt;/p&gt;&lt;pre&gt;String classPathToRunnable = args[0];&lt;br /&gt;String runnableClassname = args[1];&lt;br /&gt;String agentArgs = classPathToRunnable + &quot;,&quot; + runnableClassname;&lt;br /&gt;List&amp;lt;VirtualMachineDescriptor&amp;gt; listAfter = null;&lt;br /&gt;try {&lt;br /&gt;  listAfter = VirtualMachine.list();&lt;br /&gt;  boolean connected = false;&lt;br /&gt;  for (VirtualMachineDescriptor vmd : listAfter) {&lt;br /&gt;    if (vmd.displayName().contains(&quot;Main&quot;)) {&lt;br /&gt;      vm = VirtualMachine.attach(vmd);&lt;br /&gt;      vm.loadAgent(agentJarPath, agentArgs);&lt;br /&gt;      connected = true;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;  if (!connected) {&lt;br /&gt;    System.err.println(&quot;Couldn&#39;t connect, never found the server.&quot;);&lt;br /&gt;  }&lt;br /&gt;} catch (Exception e) {&lt;br /&gt;  e.printStackTrace();&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;ClassPathToRunnable is a directory, e.g. &quot;bin&quot;, when run in a typical java environment.  The runnable classname is a fully qualified classname, such as &quot;com.ziaconsulting.MyRunnable&quot;.  The classpath directory is going to be added to the classpath of the container and then we&#39;ll use reflection to call the class that is passed in.  The way the attach process works is to run on a jar file that lists a class to run in the manifest file.  I have a simple ant file that manages the build and the manifest file:&lt;/p&gt;&lt;pre&gt;&amp;lt;project name=&quot;Attach&quot; default=&quot;jar&quot;&amp;gt;&lt;br /&gt;  &amp;lt;target name=&quot;jar&quot;&amp;gt;&lt;br /&gt;    &amp;lt;jar destfile=&quot;agent.jar&quot;&amp;gt;&lt;br /&gt;      &amp;lt;fileset dir=&quot;./bin&quot;&amp;gt;&lt;br /&gt;        &amp;lt;include name=&quot;**&quot;/&amp;gt;&lt;br /&gt;      &amp;lt;/fileset&amp;gt;&lt;br /&gt;      &amp;lt;manifest&amp;gt;&lt;br /&gt;        &amp;lt;attribute name=&quot;Main-Class&quot;&lt;br /&gt;          value=&quot;com.ziaconsulting.Attach&quot; /&amp;gt;&lt;br /&gt;        &amp;lt;attribute name=&quot;Agent-Class&quot;&lt;br /&gt;          value=&quot;com.ziaconsulting.Agent&quot; /&amp;gt;&lt;br /&gt;      &amp;lt;/manifest&amp;gt;&lt;br /&gt;    &amp;lt;/jar&amp;gt;&lt;br /&gt;  &amp;lt;/target&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Pay special attention to the &quot;Agent-Class&quot; manifest attributes.&lt;/p&gt;&lt;p&gt;The next step is to work with the agent main class.  This proved to be a very tricky part because of the complexity inherit with java containers, specifically the number of classloaders.  I developed code specific to Jetty&#39;s container which will probably not work with tomcat.  Though the ideas should be the same:&lt;/p&gt;&lt;pre&gt;public static void agentmain(String agentArgs, Instrumentation inst) {&lt;br /&gt; // Execute the class that was passed in&lt;br /&gt; try {&lt;br /&gt;   // Split the arguments - should be a classpath and a class&lt;br /&gt;   String[] args = agentArgs.split(&quot;,&quot;);&lt;br /&gt;   if (args.length != 2) {&lt;br /&gt;     throw new IllegalArgumentException(&lt;br /&gt;     &quot;Not enought arguments passed&quot;);&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   // Begin the jetty specific stuff, we are trying to get the&lt;br /&gt;   // classloader from the Alfresco web app&lt;br /&gt;   Server s = Main.server;&lt;br /&gt;   WebAppDeployer wap = s.getBeans(WebAppDeployer.class).get(0);&lt;br /&gt; &lt;br /&gt;   HandlerCollection h = wap.getContexts();&lt;br /&gt;   Handler[] handlers = h.getHandlers();&lt;br /&gt;   for (Handler handler : handlers) {&lt;br /&gt;     if (handler.toString().contains(&quot;alfresco&quot;)) {&lt;br /&gt;       if (handler instanceof WebAppContext) {&lt;br /&gt;         WebAppContext wac = (WebAppContext) handler;&lt;br /&gt; &lt;br /&gt;         WebAppClassLoader cl = (WebAppClassLoader) wac&lt;br /&gt;         .getClassLoader();&lt;br /&gt; &lt;br /&gt;         // Check and see if this classpath is already added, if&lt;br /&gt;         // not then add it&lt;br /&gt;         boolean classPathAlreadyExists = false;&lt;br /&gt;         for (String entry : System.getProperty(&lt;br /&gt;           &quot;java.class.path&quot;).split(&lt;br /&gt;           System.getProperty(&quot;path.separator&quot;))) {&lt;br /&gt;             if (entry.equals(args[0])) {&lt;br /&gt;             classPathAlreadyExists = true;&lt;br /&gt;             break;&lt;br /&gt;           }&lt;br /&gt;         }if (!classPathAlreadyExists) {&lt;br /&gt;           cl.addClassPath(args[0]);&lt;br /&gt;         }&lt;br /&gt;         // Run the passed in class&lt;br /&gt;         final Class runnableClass = wac.getClassLoader()&lt;br /&gt;         .loadClass(args[1]);&lt;br /&gt;         final Thread thread = new Thread(&lt;br /&gt;         (Runnable) runnableClass.newInstance());&lt;br /&gt;         thread.setContextClassLoader(cl);&lt;br /&gt;         thread.start();&lt;br /&gt; &lt;br /&gt;       }&lt;br /&gt;     }&lt;br /&gt;   }&lt;br /&gt; } catch (Exception e) {&lt;br /&gt;   e.printStackTrace();&lt;br /&gt; }&lt;/pre&gt;&lt;p&gt;This may seem like beating around the bush a little, my goal was for all the agent/attaching code to be abstracted so that all users have to do is create their own runnable class that knows nothing about how to get into the VM.  This is demonstrated in the simplicity of my runnable class:&lt;/p&gt;&lt;pre&gt;public class MyRunnable implements Runnable {&lt;br /&gt;  public void run() {&lt;br /&gt;    try {&lt;br /&gt;      ApplicationContext ac = AlfUtil.applicationContext;&lt;br /&gt;      System.out.println(ac.toString());&lt;br /&gt;    } catch (Exception e) {&lt;br /&gt;      e.printStackTrace();&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;There isn&#39;t much more to show.  I haven&#39;t had time to expand this to anything more than a proof of concept, but even at this stage it&#39;s pretty compelling.&lt;/p&gt;&lt;p&gt;The output of this code is directed to the stdout of the container e.g. catalina.out or alfresco.log.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt; &lt;/p&gt;</description><link>http://www.boulderhopkins.com/2010/10/attaching-to-running-alfresco-instance.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-8126035252868822468</guid><pubDate>Mon, 11 Oct 2010 21:54:00 +0000</pubDate><atom:updated>2010-10-30T15:43:13.789-06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">Dev Environment</category><title>Building Alfresco enterprise from source v3.4 beta</title><description>&lt;p&gt;Our development environment runs off an exploded a war file in an embedded version of jetty, all running in Eclipse.  We typically build the war from source pulled from Alfresco Enterprise SVN.  Knowing how to do this means we can checkout from any of the development branches and get a feeling for how different features are progressing without waiting for the official build to be distributed.  It also give me a warm fuzzy to know how the beast is built.&lt;/p&gt;&lt;p&gt;&lt;a name=&quot;more&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Up until 3.4b it was an easy process to run &quot;ant incremental&quot; within the enterpriseprojects directory and then unzip the wars that were produced.  The build was changed in 3.4 so that this process now creates a Circular Dependency and fails to run immediately.&lt;/p&gt;&lt;p&gt;I posted this as a regression bug to &lt;a href=&quot;https://issues.alfresco.com/jira/browse/ALF-5115&quot;&gt;JIRA&lt;/a&gt; and got a response today from Paul Holmes-Higgin to try &lt;em&gt;ant -Dbuild.script=enterpriseprojects/build.xml -f continuous.xml distribute&lt;/em&gt;.  This still causes a failure unless you setup extra some folder structure to support building all of the distribution packages (see &lt;a href=&quot;http://forums.alfresco.com/en/viewtopic.php?f=10&amp;amp;t=18822&quot;&gt;this discussion&lt;/a&gt;) but at least you get the war file in the assemble directory, which is good enough for me.&lt;/p&gt;&lt;p&gt;One thing he didn&#39;t mention is that you need to add version numbers as arguments to ant so that some of the jars that are created have valid filenames.&lt;/p&gt;&lt;p&gt;I settled on this:&lt;/p&gt;&lt;pre&gt;$ ﻿ant -Dbuild.script=enterpriseprojects/build.xml&lt;br /&gt;-Dversion.major=0 -Dversion.minor=0 -Dversion.revision=0&lt;br /&gt;-f continuous.xml distribute&lt;/pre&gt;&lt;p&gt;Again, this build &lt;strong&gt;will fail&lt;/strong&gt; unless you setup the folder structure described in the above discussion.&lt;/p&gt;&lt;p&gt;Check for war files:&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;pre&gt;$ ls -l build/assemble/web-server/webapps/&lt;br /&gt;total 239080&lt;br /&gt;-rw-r--r--  1 dhopkins  staff    97M Oct 13 07:52 alfresco.war&lt;br /&gt;-rw-r--r--  1 dhopkins  staff    20M Oct 13 07:52 share.war&lt;br /&gt;&lt;/pre&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;Which leads eventually to...&lt;/p&gt;&lt;p&gt;&lt;img title=&quot;Screen shot 2010-10-11 at 10.34.21 AM.png&quot; src=&quot;http://bolderdanh.files.wordpress.com/2010/10/screen-shot-2010-10-11-at-10-34-21-am.png&quot; border=&quot;0&quot; alt=&quot;Screen shot 2010-10-11 at 10.34.21 AM.png&quot; width=&quot;417&quot; height=&quot;73&quot; /&gt;&lt;/p&gt;&lt;p&gt;How sweet it is!&lt;/p&gt;</description><link>http://www.boulderhopkins.com/2010/10/building-alfresco-enterprise-from.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-6378193895638171621</guid><pubDate>Sun, 10 Oct 2010 21:49:00 +0000</pubDate><atom:updated>2010-10-30T15:39:00.621-06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><title>Using the patch service to run arbitrary code on AMP install</title><description>&lt;p&gt;There seems to be many times that the Alfresco repository needs to be modified when an AMP is first installed e.g. migration of current nodes.  While Alfresco provides a data bootstrap mechanism it appears it is inadequate for anything except the most basic tasks.  This post was predicated on finding a use of the patch service in the WCM installation script.&lt;/p&gt;&lt;p&gt;I haven’t seen many docs on the proper or improper use of the patch service.  At first blush it is used as a Repository internal tool to update the repository during major releases and hotfixes. As I’ve mentioned, the inspiration for this came from the WCM &quot;installation&quot; during which you insert the wcm-bootstrap-context.xml into the repositories classpath and then start it up.  In that file is a new bean that uses the patch service to create a couple of folders.&lt;/p&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;h2&gt;Creating my own patch&lt;/h2&gt;&lt;p&gt;To create a new patch we need a couple of things&lt;/p&gt;&lt;ul&gt;&lt;li&gt;A context file to create the spring beans&lt;/li&gt;&lt;li&gt;A properties file with description information&lt;/li&gt;&lt;li&gt;A java class to instantiate that will run the patch&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The context file can exist any where in the alfresco/extension package.  It should contain a bean that inherits from the basePatch abstract bean.&lt;/p&gt;&lt;pre&gt;&amp;lt;bean id=&quot;patch.pmoUsers&quot; class=&quot;com.ziaconsulting.bootstrap.FreshProjectData&quot;&lt;br /&gt;  parent=&quot;basePatch&quot;&amp;gt;&lt;br /&gt;  &amp;lt;property name=&quot;id&quot;&amp;gt;&lt;br /&gt;    &amp;lt;value&amp;gt;patch.pmoUsers&amp;lt;/value&amp;gt;&lt;br /&gt;  &amp;lt;/property&amp;gt;&lt;br /&gt;  &amp;lt;property name=&quot;description&quot;&amp;gt;&lt;br /&gt;    &amp;lt;value&amp;gt;patch.pmoUsers.description&amp;lt;/value&amp;gt;&lt;br /&gt;  &amp;lt;/property&amp;gt;&lt;br /&gt;  &amp;lt;property name=&quot;fixesFromSchema&quot;&amp;gt;&lt;br /&gt;    &amp;lt;value&amp;gt;0&amp;lt;/value&amp;gt;&lt;br /&gt;  &amp;lt;/property&amp;gt;&lt;br /&gt;  &amp;lt;property name=&quot;fixesToSchema&quot;&amp;gt;&lt;br /&gt;    &amp;lt;value&amp;gt;${version.schema}&amp;lt;/value&amp;gt;&lt;br /&gt;  &amp;lt;/property&amp;gt;&lt;br /&gt;  &amp;lt;property name=&quot;targetSchema&quot;&amp;gt;&lt;br /&gt;    &amp;lt;value&amp;gt;10000&amp;lt;/value&amp;gt;&lt;br /&gt;  &amp;lt;/property&amp;gt;&lt;br /&gt;  &amp;lt;property name=&quot;services&quot; ref=&quot;ServiceRegistry&quot;/&amp;gt;&lt;br /&gt;  &amp;lt;property name=&quot;groupAuthorityName&quot; value=&quot;freshprojectadmins&quot;/&amp;gt;&lt;br /&gt;  &amp;lt;property name=&quot;groupDisplayName&quot; value=&quot;Fresh Project Admins&quot;/&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;/pre&gt;&lt;p&gt;﻿This is a simple bean that inherits from basePatch.  The settings that are the most important are the three versions that are passed in (﻿fixesFromSchema, fixesToSchema and targetSchema).  This is in contrast to the way that Alfresco typically uses this service, where the patch is meant to be applied to one specific schema.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;The &quot;id&quot; is written to the patch table in the database and the description is a reference to a key in a properties file that has been loaded by the i18n Resource Bundle Bootstrap Component.&lt;/p&gt;&lt;p&gt;The properties file looks like any normal properties and is loaded by the I18N service.  The bean that we will use is:&lt;/p&gt;&lt;pre&gt;&amp;lt;bean id=&quot;﻿patch.pmoUsers.resourceBundles&quot;&lt;br /&gt;class=&quot;org.alfresco.i18n.ResourceBundleBootstrapComponent&quot;&amp;gt;&lt;br /&gt;    &amp;lt;property name=&quot;resourceBundles&quot;&amp;gt;&lt;br /&gt;    &amp;lt;list&amp;gt;&lt;br /&gt;      &amp;lt;value&amp;gt;alfresco.extension.pmoPatches&amp;lt;/value&amp;gt;&lt;br /&gt;    &amp;lt;/list&amp;gt;&lt;br /&gt;  &amp;lt;/property&amp;gt;&lt;br /&gt;&amp;lt;/bean&amp;gt;&lt;/pre&gt;&lt;p&gt;Nothing fancy here.  Just make sure that the ID is unique so you don&#39;t stomp the original message bundle bean.&lt;/p&gt;&lt;p&gt;The last thing to create is the java class that runs when the patch is installed.  It&#39;s pretty easy to find loads of examples in the repository of other patches and copy the ideas.  Here is one for completeness.&lt;/p&gt;&lt;pre&gt;public class FreshProjectData extends AbstractPatch {&lt;br /&gt;...... La la la, details ......&lt;br /&gt;@Override&lt;br /&gt;protected String applyInternal() throws Exception {&lt;br /&gt;    // Create the new user group&lt;br /&gt;    String freshProjectAdmingroup = services.getAuthorityService()&lt;br /&gt;      .createAuthority(AuthorityType.GROUP, groupAuthorityName);&lt;br /&gt;    services.getAuthorityService().setAuthorityDisplayName(&lt;br /&gt;      freshProjectAdmingroup, groupDisplayName);&lt;br /&gt;    return &quot;Created &quot; + freshProjectAdmingroup;&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;One thing to note is that the return string is placed into the &quot;report&quot; field in the database (see next section).&lt;/p&gt;&lt;h2&gt;Changes to the database&lt;/h2&gt;&lt;p&gt;The database stores a record of the applied patch in the alf_applied_patch table.  Which you shouldn&#39;t need to check unless you&#39;re curious.&lt;/p&gt;&lt;p&gt; &lt;/p&gt;&lt;p&gt;&lt;img style=&quot;float: left;&quot; title=&quot;Screen shot 2010-09-03 at 9.07.48 PM.png&quot; src=&quot;http://www.ziaconsulting.com/wp-content/uploads/2010/10/Screen-shot-2010-09-03-at-9.07.48-PM1.png&quot; border=&quot;0&quot; alt=&quot;Screen shot 2010-09-03 at 9.07.48 PM.png&quot; width=&quot;781&quot; height=&quot;233&quot; /&gt;&lt;span style=&quot;font-size: large;&quot;&gt;&lt;span style=&quot;font-size: 18px;&quot;&gt;&lt;strong&gt;&lt;span style=&quot;font-weight: normal; font-size: medium;&quot;&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description><link>http://www.boulderhopkins.com/2010/10/using-patch-service-to-run-arbitrary.html</link><author>noreply@blogger.com (Dan)</author></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-8387702312908643994.post-1877326480766377143</guid><pubDate>Thu, 02 Sep 2010 21:33:00 +0000</pubDate><atom:updated>2010-10-30T16:06:10.589-06:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Alfresco</category><category domain="http://www.blogger.com/atom/ns#">Share</category><title>Using Jar deployment to solve conflicts with multiple customizations in Alfresco Share</title><description>&lt;img alt=&quot;Screen shot 2010-09-02 at 10.09.53 AM.png&quot; border=&quot;0&quot; height=&quot;120&quot; src=&quot;http://www.ziaconsulting.com/wp-content/uploads/2010/09/Screen-shot-2010-09-02-at-10.09.53-AM.png&quot; style=&quot;float: right;&quot; width=&quot;400&quot; /&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;Installing customizations using AMPs into a new Alfresco repository is pretty easy, there isn’t much room for configuration overwriting other configuration. &amp;nbsp;In older systems that have been installed by a client or in systems that have multiple SI’s customizations installed it maybe the case, especially in Share, where configuration from one amp overwrites the configuration of another amp. &amp;nbsp;You can get into difficult to detect problems depending on the order in which amps are applied to Alfresco (probably alphanumeric, last one wins).&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;/div&gt;&lt;a href=&quot;&quot; name=&quot;more&quot;&gt;&lt;/a&gt;&lt;br /&gt;
With the release of 3.3 Alfresco has created a new way to deploy customizations to Share using Jar files and some trickery with Servlets to handle loading web resources e.g. Css, and client-side javascript. &amp;nbsp;The major benefit with this approach is that it allows developers to have some config files loaded from the jar file, where they won’t be overwritten. &amp;nbsp;Not all config, it seems that only the alfresco config i.e. One that end with -custom can be loaded this way. &amp;nbsp;You can see the list in the file slingshot-application-context.xml:&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;font-family: monospace;&quot;&gt;&amp;lt;list&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;div&gt;&lt;span style=&quot;font-family: monospace;&quot;&gt; &amp;lt;!-- Spring Web Scripts --&amp;gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre&gt;&amp;nbsp;&amp;lt;value&amp;gt;classpath:org/springframework/extensions/webscripts/spring-webscripts-config.xml&amp;lt;/value&amp;gt;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&amp;lt;value&amp;gt;classpath:META-INF/spring-webscripts-config-custom.xml&amp;lt;/value&amp;gt;&lt;/pre&gt;&lt;pre&gt;&amp;nbsp;&lt;strong&gt;&amp;lt;value&amp;gt;jar:*!/META-INF/spring-webscripts-config-custom.xml&amp;lt;/value&amp;gt;&amp;nbsp;&lt;/strong&gt;&lt;/pre&gt;&lt;div&gt;The files that can be deployed this way in Share are:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;spring-webscripts-config-custom.xml&lt;/li&gt;
&lt;li&gt;spring-surf-config-custom.xml&lt;/li&gt;
&lt;li&gt;share-config-custom.xml&lt;/li&gt;
&lt;li&gt;spring-surf-config-custom.xml&lt;/li&gt;
&lt;li&gt;spring-webscripts-config-custom.xml&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;It turns out you can also do this in DM Explorer for web-client-config-custom.xml.&lt;/div&gt;&lt;div&gt;The idea here is not to stop using amps to deploy the code but instead to include the config &lt;em&gt;within the jar&lt;/em&gt; with the other java code, instead of bundling it &lt;em&gt;within the amp&lt;/em&gt;.&lt;/div&gt;&lt;div&gt;Amp structure before:&lt;/div&gt;&lt;pre&gt;/amp&lt;/pre&gt;&lt;pre&gt;/lib&lt;/pre&gt;&lt;pre&gt;myAmp.jar ** Showing the contents&lt;/pre&gt;&lt;pre&gt;/com&lt;/pre&gt;&lt;pre&gt;/ziaconsulting&lt;/pre&gt;&lt;pre&gt;/MyClass.class&lt;/pre&gt;&lt;pre&gt;/config&lt;/pre&gt;&lt;pre&gt;&lt;strong&gt;share-config-custom.xml&lt;/strong&gt;&lt;/pre&gt;&lt;pre&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;  /web&lt;/pre&gt;&lt;pre&gt;And then after:&lt;/pre&gt;&lt;pre&gt;/amp&lt;/pre&gt;&lt;pre&gt;/lib&lt;/pre&gt;&lt;pre&gt;myAmp.jar ** Showing the contents&lt;/pre&gt;&lt;pre&gt;/META-INF&lt;/pre&gt;&lt;pre&gt;&lt;strong&gt;share-config-custom.xml&lt;/strong&gt;&lt;/pre&gt;&lt;pre&gt;/com&lt;/pre&gt;&lt;pre&gt;/ziaconsulting&lt;/pre&gt;&lt;pre&gt;/MyClass.class&lt;/pre&gt;&lt;pre&gt;/config&lt;/pre&gt;&lt;pre&gt;/web&lt;/pre&gt;&lt;div&gt;Using this idea it’s going to be very unlikely that another customization will override the share-config-custom.xml.&lt;/div&gt;&lt;h2&gt;Creating the jar&lt;/h2&gt;&lt;div&gt;Using this information we can create a project to deploy to share and include a share-config-custom.xml without fear that another amp will overwrite our configuration e.g. Records Management.&lt;/div&gt;&lt;div&gt;If you use ant to build the amp file your jar task might look like this.&lt;/div&gt;&lt;pre&gt;&amp;lt;jar destfile=&quot;${work.dir}/lib/myAmp.jar&quot;&amp;gt;&lt;/pre&gt;&lt;pre&gt;&amp;lt;fileset dir=&quot;${work.dir}/bin&quot; includes=&quot;com/**&quot; /&amp;gt;&lt;/pre&gt;&lt;pre&gt;&amp;lt;metainf dir=&quot;config-share-metainf&quot;&amp;gt;&lt;/pre&gt;&lt;pre&gt;&amp;lt;patternset&amp;gt;&lt;/pre&gt;&lt;pre&gt;&amp;lt;include name=&quot;**&quot;/&amp;gt;&lt;/pre&gt;&lt;pre&gt;&amp;lt;/patternset&amp;gt;&lt;/pre&gt;&lt;pre&gt;&amp;lt;/metainf&amp;gt;&lt;/pre&gt;&lt;pre&gt;&amp;lt;/jar&amp;gt;&lt;/pre&gt;&lt;h2&gt;Deploying the finished product&lt;/h2&gt;&lt;div&gt;Assuming that your build is already creating an amp file the amp is deployed just like usual. &amp;nbsp;If you want to double check that the new source file is being picked up then turn on debug logging for the UrlConfigSource. &amp;nbsp;Open up the log4j.properties in the share webapp (typically tomcat/webapps/share/WEB-INF/classes/log4j.properties) and add this to bottom of the file.&lt;/div&gt;&lt;div&gt;Log4j.logger.org.springframework.extensions.config.source.UrlConfigSource=debug&lt;/div&gt;&lt;div&gt;If all has gone to plan you should see a log message from that class.&lt;/div&gt;&lt;pre&gt;13:28:36,038 &amp;nbsp;INFO &amp;nbsp;[config.source.UrlConfigSource] &lt;strong&gt;Found&lt;/strong&gt; META-INF/&lt;strong&gt;share-config-custom.xml&lt;/strong&gt; in&lt;/pre&gt;&lt;pre&gt;file:/Users/dhopkins/Public/work/test/alfresco-enterprise-tomcat-3.3.1/tomcat/webapps/share/WEB-INF/lib&lt;/pre&gt;&lt;pre&gt;/&lt;strong&gt;pmoShare.jar&lt;/strong&gt;&lt;/pre&gt;&lt;pre&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;/pre&gt;&lt;div&gt;That’s what we want to see.&lt;/div&gt;&lt;/div&gt;</description><link>http://www.boulderhopkins.com/2010/10/using-jar-deployment-to-solve-conflicts.html</link><author>noreply@blogger.com (Dan)</author></item></channel></rss>