<?xml version="1.0"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title><![CDATA[Blog - Rubicon Red]]></title><link>http://www.rubiconred.com/</link><description><![CDATA[Innovative IT professional services firm focused on Oracle SOA Suite Training & Certification, plus peerless BPM Training & Cloud Computing.]]></description><language>en-us</language><pubDate>Wed, 19 Jun 2013 10:01:58 -1000</pubDate><lastBuildDate>Wed, 19 Jun 2013 10:01:58 -1000</lastBuildDate><webMaster>josh.trotter@rubiconred.com</webMaster><item><title><![CDATA[Industrialized SOA - Where are you? - Part 1]]></title><link>http://www.rubiconred.com/blog/industrial-soa-where-are-you-part-1/</link><description><![CDATA[In my job I have had the privilege of working on some high profile SOA projects but more satisfyingly I have been able to see most of them through to success. This process can be rewarding but it...]]></description><content:encoded><![CDATA[<p><span style="letter-spacing: 0em;">In my job I have had the privilege of working on some high profile SOA projects but more satisfyingly I have been able to see most of them through to success. This process can be rewarding but it also has its downsides and it is not (unfortunately) all Greenfields and jelly beans. In fact a lot of what I do is fire fighting and it is not easy rescuing projects on the verge of failure. You know the ones I am talking about, right? The toxic ones&hellip; the ones that no one wants to touch with a 10-foot pole.&nbsp;</span></p><p>My wife and I were fortunate to be eating an amazing Indian cuisine cooked by my colleague Arun&rsquo;s wife (some of you may know Arun from his popular&nbsp;<a href="http://beatechnologies.wordpress.com/">blog</a> on Oracle BPM or as the author of <a href="http://www.packtpub.com/oracle-soa-suite-11g-administrators-handbook/book">Oracle SOA Suite 11g Administrator&rsquo;s Handbook</a>). This catch up over great food and wine turned out to be an excellent opportunity to really talk about our passion for software delivery; perhaps much to the boredom of our respective partners.</p><p>One of the things Arun had mentioned was that he had recently been impressed by Mark Nelson&rsquo;s musing about SOA Development &amp; Delivery. As a long-time subscriber to Mark&rsquo;s blog, I was keen to check it out. So after a sound night&rsquo;s sleep, I was quick to Google &lsquo;RedStack&rsquo; and here it was&hellip;.</p><p align="center"><a href="http://redstack.wordpress.com/2013/05/01/soa-development-and-delivery/">http://redstack.wordpress.com/2013/05/01/soa-development-and-delivery/</a></p><p>&nbsp;Short</p><p>&nbsp;Sweet</p><p>&nbsp;To-the-point</p><p>&nbsp;&hellip;.And it hit home!</p><p>While Mark did not say this directly, it seems clear to me that the SOA brand is tainted (in at least some contexts) and people are asking questions&hellip;</p><ul><li><em>Is SOA really agile when you need to wait months for a new complex feature to be released and 15 components are impacted by it?<br /><br /></em></li><li><em>Does BPM really empower the business when your server goes down and the root cause is not easily discoverable?<br /><br /></em></li><li><em>Is Cloud really a way to lower risk and increase productivity when the elasticity comes with lengthy periods of non-realised benefits, ramp up and confusion?</em></li></ul><p>If this resonates with you, you are at the very least... not alone.</p><p>So what is going wrong? Why in some cases, is the buzz seen to be spin, to be vendor marketing; little more than bended truths?</p><p>While I obviously don&rsquo;t know all the answers I&rsquo;ll take a stab and say it has more to do with maturity levels and insensible decisions then the failings of SOA and its promise.</p><p>As novelist Ellen Glasgow once said &ldquo;<em>All change is not growth, as all movement is not forward.</em>&rdquo; and with that I hope you&rsquo;ll allow me to take this opportunity to wax lyrical on Mark Nelson&rsquo;s gem by way of a few practical tips learnt in the trenches.</p><p>Click <a href="http://blog.rubiconred.com/2013/05/industrial-soa-where-are-you-part-1.html">here</a> to read the full article:</p><p><a href="http://blog.rubiconred.com/2013/05/industrial-soa-where-are-you-part-1.html">http://blog.rubiconred.com/2013/05/industrial-soa-where-are-you-part-1.html</a></p>]]></content:encoded><pubDate>Sun, 05 May 2013 00:00:00 -1000</pubDate><guid>http://www.rubiconred.com/blog/industrial-soa-where-are-you-part-1/</guid></item><item><title><![CDATA[Unified Workflow with the Oracle BPM Suite]]></title><link>http://www.rubiconred.com/blog/unified-workflow/</link><description><![CDATA[Rubicon Red oneSpot provides an extension to the Oracle SOA and BPM Suite 11gR1; without copying, presents users with a single unified view of all their tasks across multiple workflow platforms....]]></description><content:encoded><![CDATA[<p><strong><em>Rubicon Red <a title="oneSpot" href="/onespot/">oneSpot</a> provides an extension to the Oracle SOA and BPM Suite 11gR1; without copying, presents users with a single unified view of all their tasks across multiple workflow platforms.</em></strong></p><p>Business processes represent sets of logically organized activities spanning multiple IT systems, departments, and roles. Some activities are automated and performed by machines, whereas others are manual and performed by people.</p><p>In a typical enterprise, these processes are fragmented and buried across multiple applications (such as Oracle eBusiness Suite, SAP, Peoplesoft, Siebel and custom apps), making processes rigid and hard to change.</p><p>Oracle BPM and SOA Suite 11gR1 is the next generation solution that allows businesses to define and implement end to end business processes that integrate these process fragments together, allowing processes to be easily reconfigured to meet the evolving requirements of the business</p><p>Human Tasks (or workflows) are a fundamental part of any business process as they provide the channel for communication between business processes and there participants.</p><p>Whilst Oracle BPM provides a comprehensive workflow solution, the process fragments that execute in the existing IT systems, often require users to perform certain tasks like acting on requests pending approval or flagging a manual fulfillment as complete. Each of these applications includes its own workflow engine, which creates and manages tasks for the part of the business process that it covers.</p><p>As a result, business users regularly have to log into multiple applications, to determine which tasks the have assigned to them at any one time. This means it&rsquo;s impossible for a user to get a single view of all their tasks, or allow there manager to easily monitor or managing outstanding tasks for all their employees.</p><p>Rubicon Red oneSpot an extension to the Oracle SOA and BPM Suite, presents users with a single unified view of all their tasks in one spot. This is achieved through the use of a Virtual Task Repository (VTR); which is designed to be connected to multiple workflow task stores and present users with a single integrated view of all tasks assigned to them.</p><p>&nbsp;<img style="vertical-align: middle;" title="oneSpot Virtual Task Repository" src="/uploads/50840/ufiles/oneSpot/VirtualTaskRepository.PNG" alt="Virtual Task Repository" width="550" height="405" /></p><p>Form within oneSpot a user can open a "virtual" task and work on it as if it were a local task, with all actions / updates performed on the task executed againt the real task in the source workflow application.&nbsp;</p><p>All tasks have a common set of attributes; and enable standard operations to be performed against them in a common way, as well as allowing comments, attachments, etc. to be stored against them.</p><p><strong>Conclusion</strong><br />No one should have to open multiple browser windows / applications to check their workflow tasks, so <a href="http://www.rubiconred.com/onespot/">oneSpot</a> allows you to track all your tasks in one view and instantly lets you know when new tasks arrive.</p>]]></content:encoded><pubDate>Sat, 17 Nov 2012 00:00:00 -1000</pubDate><guid>http://www.rubiconred.com/blog/unified-workflow/</guid></item><item><title><![CDATA[Reading compressed files with Oracle Service Bus]]></title><link>http://www.rubiconred.com/blog/reading-compressed-files-with-oracle-service-bus/</link><description><![CDATA[A reasonably common B2B interface convention is to compress the contents of exchanged files, to reduce the impact on network traffic and archiving requirements (see Figure below). Although Oracle...]]></description><content:encoded><![CDATA[<p>A reasonably common B2B interface convention is to compress the contents of exchanged files, to reduce the impact on network traffic and archiving requirements (see Figure below). &nbsp;Although Oracle Middleware does not support such interfaces &ldquo;out of the box,&rdquo; it is reasonably straight-forward to piece together a simple adapter using existing tools.</p><p><img style="display: block; margin-left: auto; margin-right: auto;" title="Figure 1 - B2B example using compression" src="/uploads/50840/ufiles/blog_osb_compressedfiles/01_B2B_Overview.png" alt="Figure 1 - B2B example using compression" width="568" height="341" /></p><p>This recipe will guide you through a sample implementation of a proxy service which polls a directory for XML files compressed using the <span style="color: #cc0000;">gzip</span> utility.</p><p>Gzip is a data compression software application, most commonly encountered as the version implemented by the GNU Project. Gzip only natively supports the compression of one file at a time (although of course that file may itself be the combination of several smaller files), and has a simple format consisting of compressed binary content between a standard header and footer.</p><p>J2SE includes a standard library of compression/decompression utilities in the <span style="color: #cc0000;">java.util.zip</span> package, which, among other things, includes functionality for working with the gzip data compression algorithm.</p><h1>Getting ready...</h1><h2>Oracle Enterprise Pack for Eclipse</h2><p>Although it is possible to develop for the Oracle Service Bus using only the Web Console interface, for a fully featured IDE (including support for version control, source code editing plug-ins, lower latency and independence of development environments) it is generally preferable to use the <a title="OSB Training" href="/training/">OSB</a> Workshop perspective of the OEPE.</p><p>This recipe assumes the use of an existing OSB Configuration project within the OSB Workshop for development so ensure that you have installed and familiarised yourself with it prior to beginning.</p><h2>Test Data</h2><p>Prior to beginning this recipe, you will want to prepare some test data, consisting of XML files compressed using gzip.</p><h1>How to do it...</h1><h2>Writing the Java method</h2><p>Before jumping into creating anything for OSB, you&rsquo;ll first need a java library which handles the extraction.</p><ul><li>Start up Eclipse and switch to the Java perspective.</li></ul><p style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/02_Java_Perspective.png" alt="" width="627" height="95" /></p><ul><li>Right-click in the Project Explorer area and select <span style="color: #006600;">New &gt; Java Project</span>.</li></ul><p style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/03_new_project.png" alt="" width="627" height="142" /></p><ul><li>Name the project <span style="color: #cc0000;">GzExtractor</span> and then select <span style="color: #006600;">Next</span>.</li><li>Select the <span style="color: #006600;">Libraries</span> tab and click <span style="color: #006600;">Add External JARs...</span>.</li></ul><p style="text-align: center;"><img style="text-align: center;" src="/uploads/50840/ufiles/blog_osb_compressedfiles/04_add_library.png" alt="" width="533" height="404" /></p><ul><li>Select <span style="color: #cc0000;">com.bea.core.xml.xmlbeans_2.2.0.0.jar</span> from the <span style="color: #cc0000;">modules</span> subdirectory of your Oracle middleware installation and click <span style="color: #006600;">Open</span>.</li></ul><p style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/05_xmlbeans_selection.png" alt="" width="696" height="434" /></p><ul><li>Click <span style="color: #006600;">Finish</span> in the Add External JARs dialog, and then <span style="color: #006600;">Finish</span> again in the New Java Project dialog box.</li><li>Right click on the new project and select <span style="color: #006600;">New-&gt;Class</span>.</li></ul><p style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/06_new_class.png" alt="" width="800" height="207" /></p><ul><li>Set the package to <span style="color: #cc0000;">com.rubiconred</span> and the class name to <span style="color: #cc0000;">GzExtractor</span>. &nbsp;Leave everything else as default and then click <span style="color: #006600;">Finish</span>.</li></ul><p><img style="display: block; margin-left: auto; margin-right: auto;" src="/uploads/50840/ufiles/blog_osb_compressedfiles/07_class_name.png" alt="" width="542" height="429" /></p><ul><li>Replace the contents of the new file <span style="color: #cc0000;">GzExtractor.java</span> with the code below.&nbsp;</li></ul><hr style="width: 100%;" width="100%" /><pre><span style="color: #333333;">package</span><span style="color: #990000;"> com.rubiconred;</span></pre><pre><span style="color: #990000;"><span style="color: #333333;">import</span> org.apache.xmlbeans.*;</span><br /><span style="color: #990000;"><span style="color: #333333;">import</span> java.io.*;</span><br /><span style="color: #990000;"><span style="color: #333333;">import</span> java.util.zip.GZIPInputStream;</span></pre><pre><span style="color: #990000;"><span style="color: #333333;">public class</span> GzExtractor</span><br /><span style="color: #333333;">{</span></pre><pre style="padding-left: 30px;"><span style="color: #990000;"><span style="color: #333333;">public static</span> XmlObject processFile( Object param )</span><br /><span style="color: #990000;"><span style="color: #333333;">throws</span> IOException, XmlException</span><br /><span style="color: #333333;">{</span></pre><pre style="padding-left: 60px;"><span style="color: #990000;">byte[] bytes = (byte[])param;</span><br /><span style="color: #990000;"><span style="color: #333333;">if</span> (bytes != null)</span><br /><span style="color: #333333;">{</span></pre><pre style="padding-left: 90px;"><span style="color: #990000;">InputStream input = new ByteArrayInputStream(bytes);</span><br /><span style="color: #990000;">InputStream gzipInput = new GZIPInputStream(input);</span><br /><br /><span style="color: #990000;">Writer writer = new StringWriter();</span><br /><span style="color: #990000;">char[] buffer = new char[1024];</span><br /><br /><span style="color: #333333;">try {</span></pre><pre style="padding-left: 120px;"><span style="color: #990000;">BufferedReader reader = new BufferedReader</span><span style="color: #990000;">( new InputStreamReader(gzipInput, "UTF-8") );</span></pre><pre style="padding-left: 120px;"><span style="color: #990000;">int n;</span><br /><span style="color: #990000;"><span style="color: #333333;">while</span> ((n = reader.read(buffer)) != -1)</span></pre><pre style="padding-left: 150px;"><span style="color: #990000;">writer.write(buffer, 0, n);</span></pre><pre style="padding-left: 90px;"><span style="color: #333333;">}</span><br /><span style="color: #333333;">finally {</span></pre><pre style="padding-left: 120px;"><span style="color: #990000;">gzipInput.close();</span></pre><pre style="padding-left: 90px;"><span style="color: #333333;">}</span><br /><br /><span style="color: #006600;">// return the contents of the file</span><br /><span style="color: #990000;">return XmlObject.Factory.parse( writer.toString() );</span></pre><pre style="padding-left: 60px;"><span style="color: #333333;">}</span><br /><span style="color: #333333;">else</span><br /><span style="color: #333333;">{</span></pre><pre style="padding-left: 90px;"><span style="color: #006600;">// input parameter is null, return null</span><br /><span style="color: #990000;">return null;</span></pre><pre style="padding-left: 60px;"><span style="color: #333333;">}</span></pre><pre style="padding-left: 30px;"><span style="color: #333333;"> }</span></pre><pre><span style="color: #333333;">}</span></pre><hr style="width: 100%;" width="100%" /><h2>Incorporating the Jar File</h2><p>The following steps explain how the java method created above can be incorporated into an OSB project.</p><ul><li>Switch to the <span style="color: #006600;">Oracle Service Bus</span> perspective.</li><li>If you don&rsquo;t have one already, create a new OSB Configuration Project and a new OSB project within it. &nbsp;Name the project <span style="color: #cc0000;">GzFileAdapter</span>.</li><li>Right click on the <span style="color: #cc0000;">GzExtractor</span> Java project and select <span style="color: #006600;">Export-&gt;Export...</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/08_export_java.png" alt="" width="564" height="359" /></div><ul><li>Select <span style="color: #006600;">Java-&gt;JAR file</span> and then click <span style="color: #006600;">Next</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/09_export_to_jar.png" alt="" width="483" height="384" /></div><ul><li>Provide the <span style="color: #006600;">export destination</span> as <span style="color: #cc0000;">GzFileAdapter/GzExtractor.jar</span> and then click <span style="color: #006600;">Finish</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/10_jar_destination.png" alt="" width="462" height="545" /></div><ul><li>Right click on the <span style="color: #cc0000;">GzFileAdapter</span> OSB project and select <span style="color: #006600;">Import-&gt;Import...</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/11_import_import.png" alt="" width="713" height="479" /></div><ul><li>Select <span style="color: #006600;">File System</span> in the <span style="color: #006600;">General</span> category and then click <span style="color: #006600;">Next</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/12_import_file_system.png" alt="" width="465" height="365" /></div><ul><li>Select the <span style="color: #cc0000;">com.bea.core.xml.xmlbeans_2.2.0.0.jar</span> XMLBeans library from the <span style="color: #cc0000;">modules</span> subdirectory of your Oracle middleware installation, as used earlier in the Java project, and then click <span style="color: #006600;">Finish</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/13_xmlbeans_selection.png" alt="" width="587" height="577" /></div><ul><li>Right click on <span style="color: #cc0000;">GzExtractor.jar</span> in the Project Explorer and select <span style="color: #006600;">Oracle Service Bus -&gt; Modify Jar Dependencies</span> from the context menu.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/14_osb_modify_jar_dependencies.png" alt="" width="709" height="404" /></div><ul><li>Select the XMLBeans library, then click <span style="color: #006600;">Add</span>, then <span style="color: #006600;">OK</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/15_jar_dependencies_selection.png" alt="" width="681" height="350" /></div><div style="text-align: center;"></div><h2>Creating the Proxy Service</h2><p>The next step is to create the file adapter, as a binary file-based message proxy service, from the Oracle Service Bus IDE.</p><ul><li>Right-click on the <span style="color: #cc0000;">GzFileAdapter</span> OSB project in the Project Explorer and select <span style="color: #006600;">New -&gt; Proxy Service</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/16_new_proxy_service.png" alt="" width="465" height="417" /></div><ul><li>Name it <span style="color: #cc0000;">GzFileAdapter</span> and click <span style="color: #006600;">Finish</span>.</li><li>In the <span style="color: #006600;">General</span> tab of the Proxy Service editor, select <span style="color: #006600;">Message Service</span> as the Service Type.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/17_general_configuration.png" alt="" width="630" height="400" /></div><ul><li>In the <span style="color: #006600;">Messaging</span> tab, select <span style="color: #006600;">Binary</span> as the Request Message Type. &nbsp;Leave the Response Message Type as <span style="color: #006600;">None</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/18_message_type_configuration.png" alt="" width="630" height="335" /></div><ul><li>In the <span style="color: #006600;">Transport</span> tab, select <span style="color: #006600;">file</span> as the Protocol and provide a directory name where you would like to poll for files from as the <span style="color: #006600;">Endpoint URI</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/19_transport_configuration.png" alt="" width="630" height="430" /></div><ul><li>In the <span style="color: #006600;">File Transport</span> tab, customise the adapter to suit. &nbsp;In this example, we&rsquo;ll select a file mask of <span style="color: #cc0000;">*.gz</span> as well as configuring an <span style="color: #006600;">archive</span> Post Read Action and specifying all the necessary Directory file destinations.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/20_file_transport_configuration.png" alt="" width="630" height="536" /></div><div></div><h2>Defining the message flow</h2><p>The next step is to incorporate a call to the GzExtractor java library into the Proxy Service <span style="color: #006600;">Message Flow</span>.</p><ul><li>Start by dragging a new <span style="color: #006600;">Pipeline Pair</span> from the <span style="color: #006600;">Design Palette</span> into the <span style="color: #006600;">Message Flow</span> editor.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/21_pipeline_pair.png" alt="" width="772" height="269" /></div><ul><li>Add a new <span style="color: #006600;">Stage</span> to the Request Pipeline and name it <span style="color: #cc0000;">Extract GZIP</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/22_extract_gzip_stage.png" alt="" width="772" height="411" /></div><ul><li>Drag an <span style="color: #006600;">Assign</span> Action into the new Stage.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/23_assign.png" alt="" width="772" height="507" /></div><ul><li>In the <span style="color: #006600;">Properties</span> tab for the Assign action, provide the <span style="color: #006600;">Expression</span> as <span style="color: #cc0000;">$body/ctx:binary-content</span> and the <span style="color: #006600;">Variable</span> as <span style="color: #cc0000;">gzBinaryContent</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/24_assign_properties.png" alt="" width="492" height="335" /></div><ul><li>Drag a new <span style="color: #006600;">Java Callout</span> Action from the Design Palette to just under the <em>Assign</em> action.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/25_java_callout.png" alt="" width="771" height="489" /></div><ul><li>In the <span style="color: #006600;">Properties</span> tab for the new Java Callout, select <span style="color: #006600;">Browse</span> next to the <span style="color: #006600;">Method</span> field.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/26_java_callout_properties.png" alt="" width="960" height="622" /></div><ul><li>Select <span style="color: #cc0000;">GzExtractor/com.rubiconred.GzExtractor.processFile</span>.</li><li>Select the <span style="color: #006600;">Expression</span> for the only parameter and specify <span style="color: #cc0000;">$gzBinaryContent</span>.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/27_java_callout_properties_part_2.png" alt="" width="758" height="270" /></div><ul><li>Specify the <span style="color: #006600;">Result Value</span> variable as <span style="color: #cc0000;">payload</span>.</li></ul><h2>Testing the implementation</h2><p>To test the new file adapter, you can place a GZIP&rsquo;d file in the input directory and confirm the contents are read successfully following these steps:</p><ul><li>Create a new <span style="color: #cc0000;">Reporting</span><span style="color: #006600;">Stage</span> below the <span style="color: #cc0000;">GZIP Extract</span> Stage and drag a new <span style="color: #006600;">Log</span> Action in.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/28_log_report.png" alt="" width="731" height="361" /></div><ul><li>Set the Log <span style="color: #006600;">Expression</span> as <span style="color: #cc0000;">$payload</span> and the <span style="color: #006600;">Annotation</span> as &ldquo;<span style="color: #cc0000;">GzFileAdapter message payload</span>&rdquo;.</li></ul><div style="text-align: center;"><img src="/uploads/50840/ufiles/blog_osb_compressedfiles/29_log_properties.png" alt="" width="701" height="369" /></div><ul><li>Change the <span style="color: #006600;">Severity</span> to <span style="color: #cc0000;">Error</span> so that it is guaranteed to appear on your weblogic&rsquo;s console output.</li><li>Deploy the service to your server.</li><li>Copy a GZIP&rsquo;d text file to the input directory you specified earlier.</li><li>Observe that the contents are written to the console.</li></ul>]]></content:encoded><pubDate>Mon, 27 Feb 2012 00:00:00 -1000</pubDate><guid>http://www.rubiconred.com/blog/reading-compressed-files-with-oracle-service-bus/</guid></item><item><title><![CDATA[Email Notification with SOA Suite 11g]]></title><link>http://www.rubiconred.com/blog/email-notification-with-soa-suite-11g/</link><description><![CDATA[Oracle SOA Suite 11g supports a number of different notification channels through the User Messaging Service (UMS). This blog provides a step-by-step guide for configuring and testing email...]]></description><content:encoded><![CDATA[<p>Oracle SOA Suite 11g supports a number of different notification channels through the User Messaging Service (UMS). This blog provides a step-by-step guide for configuring and testing email notification. Once email notification has been configured, emails can be sent from SOA Suite components such as BPEL processes and Human Tasks. For this example, Gmail will be used as the mail server.</p><h2>Configuring Email Notification</h2><p>To support email notification from SOA Suite 11g the UMS email driver and workflow notification properties to be configured with the mail server details. This can be done through the <strong>Enterprise Manager Fusion Middleware Control</strong> console (e.g. http://localhost:7001/em) as follows:</p><ol><li>Navigate to the <strong>Email Driver Properties</strong> page available under&nbsp;<strong>User Messaging Service </strong>&gt; <strong>usermessagingdriver-email<br />&nbsp;<br /><img src="/uploads/50840/ufiles/blog_soa_email/UMS-NavigationToEmailDriverProperties-2.png" alt="" width="813" height="366" /><br /><br /></strong></li><li>Update the following properties according to your mail server configuration:<br /><br /><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><strong>OutgoingMailServer</strong> - The SMTP hostname</blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><strong>OutgoingMailServerPort</strong> - The SMTP port</blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><strong>OutgoingMailServerSecurity</strong> - The type of security (e.g. SSL)</blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><strong>OutgoingUsername</strong> - The mail user account</blockquote><blockquote style="margin: 0 0 0 40px; border: none; padding: 0px;"><strong>OutgoingPassword</strong> - The mail user password</blockquote><br /><img src="/uploads/50840/ufiles/blog_soa_email/UMS-EmailDriverProperties.png" alt="" width="930" height="426" /><br />&nbsp;</li><li>Click <strong>Apply</strong> to save the changes.<br />&nbsp;<img src="/uploads/50840/ufiles/blog_soa_email/UMS-EmailDriverApply.png" alt="" width="654" height="217" /><br /><br /></li><li>Right-click on <strong>soa-infra</strong>&nbsp;under <strong>SOA</strong> and navigate to the&nbsp;<strong>Workflow Notification Properties</strong>&nbsp;page available under&nbsp;<strong>SOA Administration</strong>.&nbsp;<br /><br /><img src="/uploads/50840/ufiles/blog_soa_email/UMS-NavigateToWorkflowNotificationProperties.png" alt="" width="598" height="623" /><br />&nbsp;</li><li>Set the <strong>Notification Mode</strong> to 'Email'&nbsp;and enter the default values which you wish to use for from, actionable and reply to email addresses.<br /><br /><img src="/uploads/50840/ufiles/blog_soa_email/UMS-WorkflowNotificationProperties.png" alt="" width="605" height="299" /><br />&nbsp;</li><li>Click <strong>Apply</strong>&nbsp;to save the changes to the workflow notification properties.<br />&nbsp;</li><li>Restart the SOA server for the changes to take effect. If you are using Gmail or any other mail server that requires SSL you will need to perform the steps in the next section. In this case, you may wish to hold off on restarting the server before these steps are performed. This will save you the trouble of having to restart twice.</li></ol><h2><strong><a name="ssl"></a>Import the SSL Certificates to the Java Keystore</strong></h2><p>Before SOA Suite can send emails with Gmail there are some SSL certificates that will need to be imported. This step can be ignored if sending from an alternative mail server that does not require SSL.</p><ol><li>Download the Gmail IMAP and SMTP certificates. This can be done with OpenSSL (<a href="http://www.openssl.org/source/" target="_blank">Unix</a>&nbsp;/&nbsp;<a href="http://www.slproweb.com/products/Win32OpenSSL.html" target="_blank">Windows</a>) by running the following from command prompt:</li><ul><li><pre>openssl s_client -connect&nbsp;<em>host</em>:<em>port</em></pre></li><ul><li>where Gmail SMTP&nbsp;<em>host</em>:<em>port</em>&nbsp;is&nbsp;<strong>smtp.gmail.com:465</strong></li><li>where Gmail IMAP&nbsp;<em>host</em>:<em>port</em>&nbsp;is&nbsp;<strong>imap.gmail.com:993<br />&nbsp;</strong></li></ul></ul><li>Create a new file for the SMTP and IMAP certificates using the content between BEGIN CERTIFICATE &amp; END CERTIFICATE<br /><br /><img src="/uploads/50840/ufiles/blog_soa_email/Gmail-SSL-Certificate.png" alt="" width="684" height="585" /><br /><br /></li><li>Use the Java Keytool to import the two certificates into a new or existing keystore. The keytool executable is available under the JAVA_HOME/bin directory. The following command can be used to import a certificate:<br /><ul><li><pre>keytool -import -alias&nbsp;<em>AliasName</em>&nbsp;-keystore&nbsp;<em>KeystoreLocation</em>&nbsp;-file&nbsp;<em>CertificateLocation</em></pre></li><ul><li>For example:</li><ul><li>keytool -import -alias gmail-imap&nbsp;-keystore&nbsp;gmail-keystore.jks&nbsp;-file&nbsp;imap.txt</li><li>keytool -import -alias&nbsp;gmail-smtp&nbsp;-keystore&nbsp;gmail-keystore.jks&nbsp;-file&nbsp;smtp.txt<br /><br /></li></ul></ul><li>When prompted, enter a password for the keystore.<br />&nbsp;</li></ul></li><li>Add the following system properties to the WebLogic server startup arguments:
<pre>-Djavax.net.ssl.trustStore=<em>CertificateLocation<br /></em>-Djavax.net.ssl.trustStorePassword=<em>CertificatePassword</em></pre><p>This can be done in&nbsp;<strong>Arguments</strong>&nbsp;field available under the&nbsp;<strong>Server Start</strong>&nbsp;tab for the SOA Server (e.g. soa_server1) in the&nbsp;<em>WebLogic Administration Console</em>&nbsp;(e.g. http://localhost:7001/console) or by adding the properties to the DOMAIN_HOME/bin/setDomainEnv.cmd (or setDomainEnv.sh). For example:&nbsp;</p><pre>set EXTRA_JAVA_PROPERTIES=%EXTRA_JAVA_PROPERTIES% -Djavax.net.ssl.trustStore=%WL_HOME%\server\lib\gmail-keystore.jks -Djavax.net.ssl.trustStorePassword=changeit</pre></li><li>Start the SOA server (or restart it if it is already running).</li></ol><h2>Testing Email Notification</h2><p>Now that we have configured outbound email notification, we want to test our configuration easily without having to create a BPEL process or human task to do this. Fortuntately, we can do this from <strong>Enterprise Manager Fusion Middleware Control</strong>.</p><ol><li><span>Right-click on&nbsp;</span><strong>soa-infra</strong><span>&nbsp;under&nbsp;</span><strong>SOA</strong><span>&nbsp;and n</span><span>avigate to the&nbsp;</span><strong>Human Workflow</strong><span>&nbsp;page available under&nbsp;</span><strong>Service Engines</strong><span>.&nbsp;<br /><br /><img src="/uploads/50840/ufiles/blog_soa_email/Test-NavigateToHumanWorkflow.png" alt="" width="530" height="528" /><br />&nbsp;</span></li><li>Select the <strong>Notification Management</strong> tab.<br />&nbsp;</li><li>Click <strong>Send Test Notification<br /><br /><img src="/uploads/50840/ufiles/blog_soa_email/Test-SendTestNotification.png" alt="" width="829" height="210" /><br />&nbsp;</strong></li><li>The <strong>Send Test Notification</strong>&nbsp;dialog box should now appear.&nbsp;Enter the email subject, content, to address and be sure to select 'Email' for the channel. Click <strong>Send</strong>.&nbsp;<span>Once the email has been sent, you should receive a value of 'SENT' for&nbsp;</span><strong>Response</strong>.&nbsp;<span>You can now close the dialog box.</span><br /><br /><img src="/uploads/50840/ufiles/blog_soa_email/Test-Notification.png" alt="" width="494" height="198" /><br />&nbsp;</li><li>Ensure that the email has been received.</li></ol><h2>Troubleshooting</h2><p>If you encounter an issue with the outgoing email notification, it is a good idea to check the SOA server log <span>for error messages. There are log files available at:</span></p><ul><li>DOMAIN_HOME/servers/<em>&lt;soa server name&gt;</em>/logs/<em>&lt;soa server name&gt;</em>.log</li><li><span>DOMAIN_HOME/servers/</span><em>&lt;soa server name&gt;</em><span>/logs/</span><em>&lt;soa server name&gt;</em>.out</li><li>DOMAIN_HOME/servers/<em>&lt;soa server name&gt;</em>/logs/<em>&lt;soa server name&gt;</em>-diagnostic.log</li></ul><p>Furthermore, it is a good idea to tick the UMS email driver debug flag.</p><p>Listed below are some common errors with their solution:</p><ul><li><span style="color: #990000;"><strong>Error Message:</strong></span> javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target<br /><br /><span style="color: #990000;"><strong>Diagnosis: </strong></span>There is an issue with the SSL certificate.<br /><br /><span style="color: #990000;"><strong>Remedy: </strong></span>Ensure all of the steps have been followed as described in "<a href="#ssl">Import the SSL Certificates to the Java Keystore</a>". Also make sure that the mail server certificates in the Java Keystore have not expired. You can ensure a fresh copy of the certificates are downloaded by executing the <strong>openssl s_client -connect</strong> command followed by the host and port details for the mail server.<br />&nbsp;</li><li><span style="color: #990000;"><strong>Error Message:</strong></span> ORABPEL-31023 -&nbsp;Cannot send email notification to <em>&lt;email address&gt;</em> address. The address&nbsp;is being marked as invalid and no further notification would be sent to this address.<br /><br /><span style="color: #990000;"><strong>Diagnosis: </strong></span>Due to a number of previous failed send attempts to an email address, SOA Suite has added the address to the <strong>Bad Address List</strong>&nbsp;thus preventing subsequent sends to this address from failing.&nbsp;The previous failed attempts may been caused by a configuration issue.<br /><br /><span style="color: #990000;"><strong>Remedy:</strong>&nbsp;</span>Remove the email address from the <strong>Bad Address List</strong>. This can be done by selecting <strong>View Bad Addresses</strong>&nbsp;from the <strong>Notification Management </strong>page in&nbsp;Enterprise Manager. When the <strong>Bad Address List</strong>&nbsp;dialog box is displayed, remove the email address from this list.<br />&nbsp;</li><li><span style="color: #990000;"><strong>Error Message:</strong></span>&nbsp;<span>No matching drivers found for sender address</span><br /><br /><strong><span style="color: #990000;">Diagnosis:</span>&nbsp;</strong>The UMS Driver for the appropriate channel is configured with a specific list of&nbsp;<span class="italic">SenderAddresses</span>, and the message sent by the application has a Sender Address that does not match.<br /><br /><span style="color: #990000;"><strong>Remedy:</strong></span> Make the Sender Address<span>&nbsp;blank. This will ensure that all outbound messages will be sent regardless of the sender address.</span></li></ul>]]></content:encoded><pubDate>Tue, 14 Feb 2012 00:00:00 -1000</pubDate><guid>http://www.rubiconred.com/blog/email-notification-with-soa-suite-11g/</guid></item><item><title><![CDATA[Caching Service Responses with OSB]]></title><link>http://www.rubiconred.com/blog/caching-service-responses-with-osb/</link><description><![CDATA[Over the course of a complex service orchestration, it may be useful or necessary to query the same set of configuration data for an entity or process multiple times during and/or between executions. ...]]></description><content:encoded><![CDATA[<p>Over the course of a complex service orchestration, it may be useful or necessary to query the same set of configuration data for an entity or process multiple times during and/or between executions. Where retrieving this configuration data involves interfacing with an external system or database, there can be reasonably significant I/O and network latency overheads associated which create bottlenecks in a service&rsquo;s execution.</p><p>A popular technique for eliminating such bottlenecks is the concept of caching results, keeping a copy in memory paired with the key of the original request in case the same request is made again. The assumption of course is that configuration data changes infrequently enough to make these &ldquo;old&rdquo; results still valid for use for some time after the first query.</p><p>Oracle offers a distributed in-memory cache in a product called Coherence. Coherence features fast and reliable performance, redundancy across clustered servers, an intuitive API and many options for customising how it uses and retains information.<br />Coherence is also packaged as a component of the Oracle Weblogic server and its functionality is incorporated into Oracle Service Bus.</p><p>This recipe will walk you through caching the responses from a business service using OSB.</p><h1>Getting Ready...</h1><h2>Oracle Service Bus 11g</h2><p>This recipe assumes you have already configured an OSB environment on which to deploy the service.</p><h2>Oracle Enterprise Pack for Eclipse</h2><p>Although it is possible to develop for the Oracle Service Bus using only the Web Console interface, for a fully featured IDE (including support for version control, source code editing plug-ins, lower latency and independence of development environments) it is generally preferable to use the OSB Workshop perspective of the OEPE.</p><p>This recipe assumes the use of an existing OSB Configuration project within the OSB Workshop for development so ensure that you have installed and familiarised yourself with it prior to beginning.</p><h2>External Service</h2><p>Prior to beginning this recipe, it is assumed that you already have access to the service you wish to cache.</p><p>If you wish to follow along exactly with the &ldquo;Fruit info&rdquo; example in these instructions you will require an Oracle database and a copy of the JCA database adapter used in the example. &nbsp;You can obtain a copy of this, along with the database schema creation scripts from the following URL:</p><p><a href="/uploads/50840/ufiles/blog_osb_cachingresponses/FruitInfo-Database-and-JCA-Adapter.zip">FruitInfo-Database-and-JCA-Adapter.zip</a></p><p>You will need to execute the database schema creation script, and then create a corresponding connection factory on your weblogic administration server &nbsp;identified by &ldquo;<span style="color: #cc0000;">jndi.cf.cookbook</span>&rdquo;.</p><h1>How to do it...</h1><h2>Check the server configuration</h2><p>In order to allow caching, the OSB weblogic server must be appropriately configured. &nbsp;Log into the OSB Administration Console and confirm the setting.</p><ul><li>Direct a web browser to <a href="http://localhost:7001/sbconsole/">http://localhost:7001/sbconsole/</a></li><li>Log in using a system administrator account (by default, <span style="color: #cc0000;">weblogic</span>).</li><li>Select <span style="color: #cc0000;">Global Settings</span> from the <span style="color: #cc0000;">Operations</span> menu.</li><li>Confirm that <span style="color: #cc0000;">Enable Result Caching</span> is check (by default, it should be).</li></ul><p><img src="/uploads/50840/ufiles/blog_osb_cachingresponses/result_caching_enabled.png" alt="" width="653" height="528" /></p><p>If it is not enabled, complete the following steps to enable it.</p><ul><li>Click the <span style="color: #cc0000;">Create</span> button in the top left. &nbsp;Check the <span style="color: #cc0000;">Enable Result Caching</span> option and then click <span style="color: #cc0000;">Update</span>.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/enable_step_1.png" alt="" width="676" height="476" /></li><li>Click the <span style="color: #cc0000;">Activate</span> button, type a <span style="color: #cc0000;">Description</span> of the change and then click <span style="color: #cc0000;">Submit</span>.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/enable_step_2.png" alt="" width="811" height="511" /></li></ul><h2>Create the Business Service</h2><p>In order to cache results using OSB, the first thing you need to do is wrap the service you wish to cache using an OSB Business Service. &nbsp;If you&rsquo;ve done this already, skip ahead to the next section "<em>Configure the Business Service</em>".</p><ul><li>If you don't have one already, create a new OSB Configuration Project.</li><li>Create a new OSB Project.</li><li>Add a subfolder for the business service and create a new OSB Business Service, configured to wrap the service you wish to cache the responses from. &nbsp;For example, for the provided "FruitInfo" example:</li><ul><li><span style="color: #cc0000;">Import</span> all the files from the &ldquo;Database Adapter&rdquo; folder provided into a subfolder called <span style="color: #cc0000;">fruitinfo_db</span>.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/import_import.png" alt="" width="782" height="518" /><img src="/uploads/50840/ufiles/blog_osb_cachingresponses/import_files.png" alt="" width="459" height="394" /><img src="/uploads/50840/ufiles/blog_osb_cachingresponses/import_files_choice.png" alt="" width="622" height="513" /></li><li>Right-click on the <span style="color: #cc0000;">fruitinfo_db.jca</span> adapter definition and select <span style="color: #cc0000;">Generate Service</span>.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/generate_service.png" alt="" width="782" height="303" /></li><li>Accept the defaults and click <span style="color: #cc0000;">OK</span>.</li></ul></ul><h2>Configure the Business Service</h2><p>This is the key configuration step.</p><p><img src="/uploads/50840/ufiles/blog_osb_cachingresponses/enable_caching.png" alt="" width="960" height="455" /></p><ol><li>Open your Business Service file for editing (the .biz file).</li><li>Select the <span style="color: #cc0000;">Message Handling</span> tab.</li><li>Expand the <span style="color: #cc0000;">Advanced Settings</span>.</li><li>Check the <span style="color: #cc0000;">Supported</span> box next to <span style="color: #cc0000;">Result Caching</span>.</li><li>Select the <span style="color: #cc0000;">Duration</span> radio button to indicate that cached results should expire after a set duration.</li><li>Key in the amount of time you&rsquo;d like for the Duration. &nbsp;(In this example we&rsquo;ve chosen <span style="color: #cc0000;">5 minutes</span> to make it easy to test).</li><li>Click on the <span style="color: #0000ff;">&lt;Expression&gt;</span> link next to <span style="color: #cc0000;">Cache Token Expression</span>.</li><li>From the variable structures on the right, locate the appropriate request operation and drag the key field from your request over into the text field on the left.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/cache_expression.png" alt="" width="909" height="379" /></li><li>Manually add <span style="color: #cc0000;">/text()</span> to the end of the expression to ensure that the contents of the node are used as the cache key.</li><li>Select <span style="color: #cc0000;">OK</span> and then save all changes.</li></ol><h2>Expose using a Proxy Service</h2><p>If you haven&rsquo;t already done so, you will need to expose your Business Service via an OSB Proxy Service.</p><ol><li>Right click on the OSB Project and select <span style="color: #cc0000;">New-&gt; Proxy Service</span> giving it an appropriate name. This will be the external facing service which wraps your cached service. Open the service for editing. &nbsp;For the simplest implementation you can pass the payload straight through by re-using the WSDL from the business service.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/select_wsdl.png" alt="" width="923" height="504" /></li><li>Select <span style="color: #cc0000;">WSDL Web Service</span> as the Service Type.</li><li><span style="color: #cc0000;">Browse</span></li>
to select an existing WSDL.
<li>In the resulting dialog browse to the WSDL of the business service and select its port.</li><li>Click <span style="color: #cc0000;">OK</span>.</li><li>When prompted to change the transport configuration, select <span style="color: #cc0000;">No</span> to retain the HTTP transport.</li><li>Select the <span style="color: #cc0000;">Message Flow</span> tab.</li><li>Drag a <span style="color: #cc0000;">Route</span> Node across into the flow.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/route_node.png" alt="" width="806" height="353" /></li><li>Drag a <span style="color: #cc0000;">Routing</span> Action into the Route Node.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/routing.png" alt="" width="806" height="218" /></li><li>Select the <em>Routing</em> Action to bring up the Properties tab.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/route_to_biz.png" alt="" width="806" height="381" /></li><li>Click <span style="color: #cc0000;">Browse</span> next to Service.</li><li>In the resulting dialog, navigate to the business service which encapsulates the caching mechanism.</li><li>Click <span style="color: #cc0000;">OK</span>.</li><li>Ensure that the correct operation is invoked in the Properties tab.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/invoking.png" alt="" width="581" height="156" /></li><li>Save all changes.</li></ol><p>You&rsquo;re done! &nbsp;You can now use the Proxy Service to access your cached business service operation.</p><h2>Testing and confirming the Cache</h2><p>Deploy and test your service. &nbsp;Then complete the following steps to confirm the caching behaviour.</p><ol><li>Navigate to the OSB Console at <a href="http://localhost:7001/sbconsole">http://localhost:7001/sbconsole</a> and login.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/osb_console.png" alt="" width="960" height="737" /></li><li>Select <span style="color: #cc0000;">Project Explorer</span> from the main menu on the left.</li><li>Navigate to the OSB Project you just created.</li><li>Click on the <span style="color: #cc0000;">Debug</span> symbol to test the Proxy Service.</li><li>In the resulting window, select the cached operation. &nbsp;Note that the other operations will not correctly route.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/execute_test.png" alt="" width="624" height="542" /></li><li>Modify the payload to specify the key for your request. &nbsp;In this example we chose &ldquo;<span style="color: #cc0000;">Apple</span>&rdquo;.</li><li>Call the service by clicking <span style="color: #cc0000;">Execute</span>.</li><li>Confirm the response.</li></ol><p style="padding-left: 60px;"><img src="/uploads/50840/ufiles/blog_osb_cachingresponses/response.png" alt="" width="219" height="121" /></p><ul><li>Now modify the state of your reference data. E.g. in our example we will modify the database entry for &ldquo;<span style="color: #cc0000;">Apple</span>&rdquo; to have color &ldquo;<span style="color: #cc0000;">Green</span>&rdquo;.<img src="/uploads/50840/ufiles/blog_osb_cachingresponses/change_color.png" alt="" width="524" height="262" /></li><li>Re-run the same Proxy Service test and note the response.</li></ul><p style="padding-left: 60px;"><img src="/uploads/50840/ufiles/blog_osb_cachingresponses/response.png" alt="" width="219" height="121" /></p><p>The color in the database is &ldquo;<span style="color: #cc0000;">Green</span>&rdquo;, yet the service still returns &ldquo;<span style="color: #cc0000;">Red</span>&rdquo;. &nbsp;This is because the result was successfully cached.</p><ul><li>Wait for the cached result to expire and then call the proxy service again with the same test to confirm that it will eventually refresh the result.</li></ul><p style="padding-left: 60px;"><img src="/uploads/50840/ufiles/blog_osb_cachingresponses/new_result.png" alt="" width="234" height="122" />.<br /><br /></p><h1>How it works...</h1><p>As you have seen, caching using Coherence in OSB is very simple to activate and use. &nbsp;The following figure illustrates what is going on behind the scenes.</p><p><img src="/uploads/50840/ufiles/blog_osb_cachingresponses/coherence_simplified.png" alt="" width="582" height="465" /></p><p>Because for most cases Coherence will be able to retrieve the result from the in-memory grid on the same application server, there will be no latency introduced by network or database I/O. &nbsp;This should greatly reduce the response time of your service, assuming frequent requests for the same data are made.</p><h1>There's more...</h1><h2>More complex cache tokens</h2><p>Your desired cache token key may not be as straight forward as selecting the contents of a single, predictably placed node.<br />It is worth noting that the Cache Token may be specified as any expression you like, using the XQuery functional programming language.<br />XQuery has a rich syntax for constructing queries, including the use of many system libraries with common functions such as numeric operations and string manipulation.</p><h2>Clear the cache explicitly</h2><p>It might be that you can&rsquo;t risk the cache becoming stale under certain circumstances. &nbsp;If that is the case, rather than using a time parameter to manage the lifetime of the cached responses, mark critical requests with an additional flag and select <span style="color: #cc0000;">XQuery Expression</span> in the expiration time options to test for the existence of this flag.</p><p><img src="/uploads/50840/ufiles/blog_osb_cachingresponses/expiration_xquery.png" alt="" width="587" height="285" /></p>]]></content:encoded><pubDate>Mon, 13 Feb 2012 00:00:00 -1000</pubDate><guid>http://www.rubiconred.com/blog/caching-service-responses-with-osb/</guid></item><item><title><![CDATA[Dynamic Split-Join in OSB]]></title><link>http://www.rubiconred.com/blog/dynamic-split-join-in-osb/</link><description><![CDATA[As part of implementing a web service it is often necessary to delegate to a number of independent subtasks. For a synchronous service, carrying out these tasks sequentially may take an unacceptable...]]></description><content:encoded><![CDATA[<p>As part of implementing a web service it is often necessary to delegate to a number of independent subtasks. For a synchronous service, carrying out these tasks sequentially may take an unacceptable amount of time causing the client to time out waiting on the service. Therefore the preferred approach is to process all independent tasks in parallel and consolidate the results.</p><p>This pattern is referred to as a &ldquo;Split-Join&rdquo; and comes in two flavours:</p><ul><li>&ldquo;Static&rdquo; in which the subtasks are always the same. &nbsp;For example, in planning a holiday one needs to book both a flight and accommodation, each of which represents an independent subtask which may be completed in parallel.</li><li>&ldquo;Dynamic&rdquo; in which there are a variable number of subtasks, to be determined at runtime. &nbsp;For example, to complete an internet shopping order a bookstore must query each book before confirming the total price, but has no way of knowing how many different items will be required prior to reviewing the order.</li></ul><p>This recipe will guide you through a sample implementation of the second example using Oracle Service Bus.</p><p><img title="Example of a Dynamic Split-Join" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/bookstore_split_join.png" alt="Example of a Dynamic Split-Join" width="551" height="275" /></p><h1>Getting Ready...</h1><h2>Oracle Enterprise Pack for Eclipse</h2><p>Although it is possible to develop for the Oracle Service Bus using only the Web Console interface, for a fully featured IDE (including support for version control, source code editing plug-ins, lower latency and independence of development environments) it is generally preferable to use the OSB Workshop perspective of the OEPE.<br />This recipe assumes the use of an existing OSB Configuration project within the OSB Workshop for development so ensure that you have installed and familiarised yourself with it prior to beginning.</p><h2>Target Operation</h2><p>Prior to beginning this recipe, you will need to prepare the target WSDL operation which will be invoked to process individual items. &nbsp;In the example, this will be the <span style="color: #cc0000;">priceCheck</span> operation of the <span style="color: #cc0000;">Book</span> service, which determines how much each should cost.</p><h2>Schema and WSDL</h2><p>You should always finalise the service contract prior to beginning development.<br />If you wish to follow along exactly with these instructions you will require a copy of the schema and WSDL files used in the example. You can obtain a copy of these, as well as a mock implementation of the <span style="color: #cc0000;">Book</span> service from the following link:&nbsp;<a href="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/BookStore.zip">BookStore.zip</a></p><h1>How to do it...</h1><h2>Creating the Split-Join</h2><ul><li>If it doesn&rsquo;t exist already add an OSB Project (in this example, <span style="color: #cc0000;">BookStore</span>) to your OSB Configuration Project and then create a sub folder within called <span style="color: #cc0000;">SplitJoin</span>.</li><li>Right click on this new subfolder and select <span style="color: #006600;">New -&gt; Split-Join</span> from the context menu.<img title="New Split-Join" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.1_02_New_split_join.png" alt="New Split-Join" width="751" height="599" /></li><li>Enter a descriptive filename (e.g., <span style="color: #cc0000;">getTotalPriceSplitJoin</span>) and then click <span style="color: #006600;">Next</span>.</li><li>Expand the project structure to select the &ldquo;parent&rdquo; operation used to invoke the split-join (e.g., <span style="color: #006600;">BookStore.wsdl -&gt; BookStoreBinding -&gt; operation: getTotalPrice</span>) and then click <span style="color: #006600;">Finish</span>.<img title="Specify parent operation for Split-Join" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.1_04_Specify_parent_operation_for_splitjoin.png" alt="Specify parent operation for Split-Join" width="463" height="421" /></li></ul><p>A new flow will appear in the main editing window.</p><h2>Editing the flow</h2><ul><li>Select the root node and expand its properties by clicking on the small triangle on its left. &nbsp;Select the <span style="color: #006600;">request</span> variable and click <span style="color: #006600;">Edit...</span>.<img title="Edit request variable" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.2_01_edit_variable_request.png" alt="Edit request variable" width="655" height="516" /></li><li>Rename the variable to match the parent operation (e.g., <span style="color: #cc0000;">getTotalPrice</span>). &nbsp;This will help prevent ambiguity later on.</li><li>Similarly, rename the <span style="color: #006600;">response</span> variable (to e.g., <span style="color: #cc0000;">getTotalPriceResponse</span>).</li><li>Drag an <span style="color: #006600;">Assign</span> action over from the Design Palette, to between the Receive and Reply nodes.<img title="New Assign action" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.2_04_New_Assign.png" alt="New Assign action" width="789" height="681" /></li><li>Label the new scope as <span style="color: #cc0000;">Initialisation</span> and the <em>Assign Action</em> as <span style="color: #cc0000;">Assign output variable</span>.</li><li>Click on the new Assign action. &nbsp;In the properties tab below, select the Variable as the payload of the parent operation&rsquo;s response (e.g., <span style="color: #cc0000;">getTotalPriceResponse.payload</span>).</li><li>Next, click on the <span style="color: #006600;">&lt;Expression&gt;</span> link. &nbsp;Provide XML similar to the following and then click <span style="color: #006600;">OK</span>.</li></ul><pre><span style="color: #cc0000;">&lt;stor:getTotalPriceResponse xmlns:stor="http://www.example.org/BookStore/"&gt;</span><br /><span style="color: #cc0000;"> &lt;totalPrice&gt;0&lt;/totalPrice&gt;</span><br /><span style="color: #cc0000;">&lt;/stor:getTotalPriceResponse&gt;</span></pre><p>Note in the above example the aggregate total has been initialized to <span style="color: #cc0000;">0</span>.</p><ul><li>Drag a <span style="color: #006600;">For Each</span> construct from the Design Palette to just below the Initialisation scope.<img title="For Each" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.2_08_For_Each.png" alt="For Each" width="789" height="513" /></li><li>Click on the new <em>For Each</em> construct. &nbsp;In the Properties tab, set the Counter Variable Name to <span style="color: #cc0000;">counter</span> and the starting value to <span style="color: #cc0000;">1</span>. &nbsp;Click on the ellipses next to Final Counter Value to launch the expression editor.<img title="For Each properties" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.2_09_For_Each_properties.png" alt="For Each properties" width="750" height="610" /></li><li>Select the <span style="color: #006600;">XPath Functions</span> tab, and drag the <span style="color: #006600;">count</span> function out into the Expression text area.<img title="Count" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.2_10_count.png" alt="Count" width="641" height="251" /></li><li>Click on the <span style="color: #006600;">Variable Structures</span> tab and expand the request structure to find the recurring element (e.g., the book <span style="color: #cc0000;">id</span>) on which the split should be based. &nbsp;Drag it out to replace the place-holder<span style="color: #cc0000;"> $arg-nodeset</span> and then click <span style="color: #006600;">OK</span> to complete the expression.<img title="count id variable" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.2_11_count_id_variable.png" alt="count id variable" width="714" height="251" /></li><li>Drag an <span style="color: #006600;">Invoke Service</span> action into the loop&rsquo;s Scope. &nbsp;Label it per the &ldquo;child&rdquo; service and operation you intend to loop over (e.g., <span style="color: #cc0000;">Book.priceCheck</span>).<img title="Invoke Service" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.2_12_invoke_service.png" alt="Invoke Service" width="582" height="453" /></li><li>In the Properties tab below, select the <span style="color: #006600;">Operation</span> category. &nbsp;Click Browse, select the child operation (e.g., <span style="color: #cc0000;">Book.proxy -&gt; priceCheck</span>), and click <span style="color: #006600;">OK</span>.<img title="Select child operation" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.2_13_select_child_operation.png" alt="Select child operation" width="792" height="532" /></li><li>Select the <span style="color: #006600;">Input Variable</span> category in the Properties tab. &nbsp;From the Message Variable dropdown select <span style="color: #006600;">Create Message Variable...</span>. &nbsp;Provide the name of the child operation (in our example, <span style="color: #cc0000;">priceCheck</span>) as the Name and then click <span style="color: #006600;">OK</span>.<img title="Child input variable" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.2.2_14_child_input_variable.png" alt="Child input variable" width="713" height="180" /></li><li>Use the same method to create and set the Output Variable (e.g., as <span style="color: #cc0000;">priceCheckResponse</span>).</li><li>Drag an <span style="color: #006600;">Assign</span> Action to the start of the Loop Scope and label it as <span style="color: #cc0000;">Extract Individual Request</span>.</li><li>Select the <em>Assign Action</em>. &nbsp;In the Properties tab, set the Variable as the request payload of the child operation (e.g., <span style="color: #cc0000;">priceCheck.payload</span>) and then click the <span style="color: #006600;">&lt;Expression&gt;</span> link.</li><li>Use the expression editor to generate each individual request using the <span style="color: #cc0000;">$counter</span> index defined earlier. &nbsp;For example:</li></ul><pre><span style="color: #cc0000;">&lt;book:priceCheck xmlns:book="http://www.example.org/Book/"&gt;</span><br /><span style="color: #cc0000;"> {$getTotalPrice.parameters/id[xs:integer($counter)]}</span><br /><span style="color: #cc0000;">&lt;/book:priceCheck&gt;</span></pre><ul><li>Following the <em>Invoke Service</em> Action, apply any aggregate logic. &nbsp;For our BookStore example, we would add a Replace Action with the following properties:</li><ul><li><p>XPath: <span style="color: #cc0000;">./totalPrice</span></p></li><li><p>Variable: <span style="color: #cc0000;">getTotalPriceResponse.parameters</span></p></li><li><p>Expression:&nbsp;<span style="color: #cc0000;">xs:float($getTotalPriceResponse.parameters/totalPrice)&nbsp;+ xs:float($priceCheckResponse.parameters/price)</span></p></li><li>Replace node contents</li></ul><li>Save your progress by select <span style="color: #006600;">File -&gt; Save</span> from the menu.</li></ul><h2>Wrapping the service</h2><p>Before the Split-Join can be used in a proxy service, it must first be encapsulated in a standard OSB Business Service.</p><ul><li>In the Project Explorer on the left, right-click on the Split-Join file and then select <span style="color: #006600;">Oracle Service Bus -&gt; Generate Business Service</span>.</li><li>Accept the default name and location, and click <span style="color: #006600;">OK</span>.</li></ul><p>The business service is now ready for use in any OSB Proxy Service. &nbsp;Test it out.</p><h1>How it works...</h1><p><img title="Full flow" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.3_01_Full_flow.png" alt="Full flow" width="599" height="863" /></p><p>Refer to the more completely labelled version of the Split-Join message flow above for an end-to-end, annotated view of the final solution. &nbsp;Procedurally, the pseudo-code for the BookStore example might look to be (just going by the annotations) as follows:</p><pre><strong><span style="color: #000099;">Operation getTotalPrice( book_list ):</span></strong><br /><span style="color: #000099;"> totalPrice := 0</span><br /><span style="color: #000099;"> for each id in book_list</span><br /><span style="color: #000099;"> loop</span><br /><span style="color: #000099;"> total_price := total_price + Book.priceCheck( id )</span><br /><span style="color: #000099;"> end loop</span><br /><span style="color: #000099;"> return total_price</span></pre><p>The key difference is that the <em>For Each</em> section has a property called &ldquo;Parallel&rdquo; set by default to <span style="color: #cc0000;">yes</span> (Note, if desired, this can be set to no to force sequential execution). This instructs Oracle Service Bus to execute all (or as many as it has threads) iterations of the <span style="color: #cc0000;">Loop</span> scope within the <em>For Each</em> statement concurrently.</p><p>Readers paying close attention will also have noticed that the <em>For Each</em> block does not actually iterate over the book IDs directly; rather the OSB determines the number of Loop scopes simply by counting the number of <span style="color: #cc0000;">id</span> nodes and then assigning each scope a different <span style="color: #cc0000;">$counter</span> variable integer between 1 and that total count. So a more accurate representation of the pseudo code would be as follows:</p><pre><strong><span style="color: #000099;">Operation getTotalPrice( book_list ):</span></strong><br /><span style="color: #000099;"> totalPrice := 0</span><br /><span style="color: #000099;"> for counter in 1 .. size(book_list)</span><br /><span style="color: #000099;"> thread concurrently</span><br /><span style="color: #000099;"> total_price := total_price + Book.priceCheck( book_list[counter].id )</span><br /><span style="color: #000099;"> end thread</span><br /><span style="color: #000099;"> return total_price</span></pre><p>Performing this addition in parallel allows the BookStore service to compute the total much faster, dividing the total time of priceChecks by the number of concurrent threads.</p><h1>There's more...</h1><p>The recipe above represents a reasonably standard, cookie-cutter implementation of how one would use the Split-Join feature of Oracle Service Bus to iterate over a dynamic sequence of identical elements in a list. &nbsp;It should be enough to get you started on any similar problem; however it only scratches the surface of the possibilities for what can be accomplished with a Split-Join message flow.</p><h2>Fault Handling</h2><p>Without appropriate Error Handling logic, the first fault thrown by a service invocation within any one of the Split-Join&rsquo;s threads will re-raise in the Split-Join and halt the entire message flow.<br />In order to prevent this, &ldquo;Catch&rdquo; clauses need to be added to the scope of each thread as follows:</p><ul><li>Right-click on the loop&rsquo;s scope and select <span style="color: #006600;">Add Catch</span>.<img title="Add Catch" src="/uploads/50840/ufiles/blog_osb_dynamicsplitjoin/1.4.1_01_Add_Catch.png" alt="Add Catch" width="544" height="421" /></li><li>Select the new <em>Catch</em> block, <span style="color: #006600;">label</span> it with the name of the Fault you wish to catch and then review the Properties tab below.</li><li>Click on <span style="color: #006600;">&lt;Soap Fault Variable Name&gt;</span> and assign any name you like (the default is simply <span style="color: #cc0000;">soapFault</span> which should be fine).</li><li>Select <span style="color: #006600;">Define Fault</span> and enter the Fault Name and Namespace of the fault you expect to catch.</li><li>Drag in a new <span style="color: #006600;">Scope</span> below the Catch and add any mitigation Actions as necessary to resolve the Fault. &nbsp;It may be appropriate to do nothing, simply log the error, or perhaps aggregate a default value into the total &nbsp;All variables available within the normal scope are also available to you within the <em>Catch</em> block.</li><li>Repeat the steps above for each expected Soap Fault.</li><li>Optionally, add a <em>Catch All</em> clause to capture any unexpected Faults.</li></ul><h2>Other aggregation logic</h2><p>Rather than simply summing up numerical values, you can aggregate the results of service calls any way you like. &nbsp;A common example is appending the results to a dynamic sequence using an <em>Insert</em> Action.</p><h2>More service calls</h2><p>Note that you are not limited to a single <em>Invoke Service</em> Action. Multiple &ldquo;child&rdquo; operations may be invoked sequentially or in parallel.<br />In fact the premise of a &ldquo;Static&rdquo; Split-Join is that instead of using a <em>For Each</em> loop, you would use an explicit <em>Parallel</em> construct (see Flow Control in the Design Palette) and drop a different <em>Invoke Service</em> Action into each lane.<br />Any combination of flow constructs desired can be layered to create complex concurrent processing systems within a single Split-Join Message Flow.</p><h2>Conflicts</h2><p>With any software system involving multi-threading, there is always a possibility of Deadlocks or Conflicts. Although variables within a Split-join message flow are protected from these scenarios, Oracle Service Bus does not provide any built-in mitigation tools for external systems.<br />It is outside the scope of this discussion to prescribe how one might resolve concurrent update issues in external systems; however designers and developers should always be aware when there is such a possibility and take appropriate action.</p>]]></content:encoded><pubDate>Tue, 31 Jan 2012 00:00:00 -1000</pubDate><guid>http://www.rubiconred.com/blog/dynamic-split-join-in-osb/</guid></item><item><title><![CDATA[Continuous Delivery (or putting agility back into SOA)]]></title><link>http://www.rubiconred.com/blog/continuous-delivery-or-putting-agility-back-into-soa/</link><description><![CDATA[One of the key principles of SOA is that systems are no longer built to last, but rather built to change. The promise being, that if you follow SOA principals, it will not only enable you to more...]]></description><content:encoded><![CDATA[<p>One of the key principles of <a title="SOA Best Practices" href="/soak-centre/" target="_blank">SOA</a> is that systems are no longer built to last, but rather built to change.&nbsp;</p><p style="padding-left: 30px;"><em>The promise being, that if you follow SOA principals, it will not only enable you to more rapidly implement new solutions through re-using existing functionality, but also reduce the time it takes to modify and adapt existing SOA based solutions in response to ever changing business requirements.&nbsp;</em></p><p style="padding-left: 30px;"><em>Being able to adapt to changing business requirements is not only important once a system has gone live but is equally important during development. Too often projects experience significant delays due to changes during implementation; a SOA-based approach provides a way to manage this change and help mitigate the risk of project delayed due to change.</em></p><p>Yet, the reality is often different. Often we come across projects that are struggling to get from development into System and Integration Testing (SIT), or beyond SIT into production.</p><p>As Jez Humble puts it in his book Continuous Delivery</p><p style="padding-left: 30px;"><em>the most important problem that we face a software professionals is this: if somebody thinks of a good idea, how do we deliver it to users as quickly as possible?</em></p><p>Now if we follow SOA principles, then the design and implementation (i.e. writing code) should be optimized, however what SOA doesn't address is how we build, deploy, test and finally released that code into production.&nbsp;</p><p>And this seems to be an area that has been largely ignored by the vast majority of SOA projects, which are still following a <strong>manual, resource intensive and highly error prone process</strong> that requires someone to piece together all the relevant components, compile and package them before deploying the end result into a test environment; and then run a series of tests to validate the build. Once over this hurdle the next step is to deploy, configure and test the build in each environment that it must progress through before it reaches production.</p><p>We&rsquo;re not making this statement likely; at Rubicon Red we have had the privilege of working with over 100 <a href="http://www.rubiconred.com/" target="_blank">Oracle SOA</a> success stories, including helping to rescue many failing projects. This gives us a pretty big sample in which to gauge the overall level of maturity in the industry when implementing SOA projects.&nbsp;</p><p>In addition, we have recently run a series of workshops entitled &ldquo;Breaking through the SOA Glass Ceiling&rdquo; (we are planning another series so let us know if you are interested in attending). One of the biggest issues that customers were facing was the complexity of deploying a new release into production (another being to successfully manage and monitor a solution once in production).</p><p>At the core of the issue, was the resource intensive nature of building and testing software, meaning this tended to be carried out infrequently, so often developers would work in isolation for days, weeks (or even months) before integrating different components.&nbsp;</p><p>The result?&nbsp;</p><p style="padding-left: 30px;"><em>Code shared between multiple people and frequent changes often introduce a lot of integration complications, dependency bugs etc. The longer these bugs remain undiscovered, the more effort it takes to troubleshoot and fix, resulting in significant project delays.</em></p><p>Yet, continuous integration and testing (the first part of continuous delivery) has shown to reduce &nbsp;the risk of project delays, providing better visibility into the overall progress and state of the software development, as well as less issues once deployed into production.</p><p>It has also been shown to provide a significant return on investment, due to the impact of finding and fixing integration bugs early in the development process; which saves both time and money over the lifespan of a project.</p><p>It is precisely for these reasons that continuous integration and testing as a practice is being widely adopted within the software industry; so you would expect that with SOA which is all about integration to be at the front of the queue!</p><p>Now Rubicon Red was founded with the vision of providing Oracle Fusion Middleware customers, a robust SOA adoption methodology incorporating agile development and KPI based governance. So the establishment of best practice around Continuous Delivery has always been a central pillar in our strategy.</p><p>We have been using Continuous Integration and Testing internally for some time now, and have extended our platform to provide support for Continuous Delivery. We have recently introduced this to a number of our customers, and the feedback has been incredibly positive.</p><p>So much so, that one of our customer suggested that we write a series of blogs about what we have done, to hopefully inspire others as well as share some of the lessons learnt. So expect to see a few more blogs on this topic over the coming months.</p><p>In addition, I would love to receive feedback from anyone else on this topic. Particularly if you are implementing (or at least trying to) CIT or Continuous Delivery, what are some of the obstacles you have faced (both technical and organizational) as well as any tips you have to succeed. Many thanks. Matt</p>]]></content:encoded><pubDate>Thu, 11 Aug 2011 00:00:00 -1000</pubDate><guid>http://www.rubiconred.com/blog/continuous-delivery-or-putting-agility-back-into-soa/</guid><enclosure type="image/png" length="9995" url="http://www.thewebshowroom.com.au//media/pics/site/imagecache/0/A/0AB14C24CB329FEF28707FE7426F1EA5.PNG"/></item><item><title><![CDATA[GWT: Working with Google Calendar using gwt-gdata]]></title><link>http://www.rubiconred.com/blog/gwt-working-with-google-calendar-using-gwt-gdata/</link><description><![CDATA[A recent showcase for one of our products included basic integration with Google Calendar through GWT. Here we aim to present a simple step by step guide to achieving basic interaction with a Google...]]></description><content:encoded><![CDATA[<h1><span style="font-size: 13px; line-height: 20px; font-weight: normal;">A recent showcase for one of our products included basic integration with Google Calendar through GWT. Here we aim to present a simple step by step guide to achieving basic interaction with a Google Calendar through GWT.&nbsp;</span></h1><p>Google allows access to Calenader via the Data API, available as a library for .NET, Java, JavaScript, PHP and Python clients. We will use the gwt-gdata API, which is a GWT wrapper for the JavaScript Data API.&nbsp;</p><h2>Our demo application</h2><h2><span style="color: #333333; font-size: 13px; line-height: 20px; font-weight: normal;">Users of our application will be able to:</span></h2><ul><li><span style="color: #333333; font-size: 13px; line-height: 20px; font-weight: normal;">authenticaticate to a calendar</span></li><li>query the events in a calendar</li><li>create, delete and edit calendar events</li><li>view a read only (public) version of the calendar</li></ul><h2>1. Setting up</h2><h3>1.1 Calendar</h3><p>We will need to create a calendar to work with against your google account. If you do not have an account, you can create one here (<a href="https://www.google.com/accounts/NewAccount?service=mail">https://www.google.com/accounts/NewAccount?service=mail</a>).</p><p>Creating a new Calendar:</p><ul><li>Sign in to calendar (<a href="http://calendar.google.com">calendar.google.com</a>) using your google account.</li><li>Click on <em>Add </em>under your list of available calendars.</li></ul><p><img style="display: block; margin-left: auto; margin-right: auto;" src="/uploads/50840/ufiles/gwtcalendarblog/calendar_create_1.PNG" alt="" width="207" height="139" /></p><ul><li>Give your calendar a name -&nbsp;<em>GWTCalendar</em> for example.</li><li>Select "Make this calendar public".</li><li>Optionally under S<em>hare with specific people</em> enter the google accounts of additional users that should have authenticated access to the calendar. Set their permissions to <em>Make changes AND manage sharing</em>. This step configures the users that will be able to create and edit events in the calendar. If no additional google accounts are added, only the google account that owns the calendar will be able to create/edit events.</li></ul><p><img style="display: block; margin-left: auto; margin-right: auto;" src="/uploads/50840/ufiles/gwtcalendarblog/calendar_create_2.PNG" alt="" width="960" height="203" /></p><ul><li>Finally click <em>Create Calendar</em>.</li></ul><h3>1.2 Development Environment</h3><p>This tutorial assumes you have Eclipse &amp; the GWT plugin installed and ready to create GWT projects.</p><ul><li>create a new GWT project for the sample Calendar application</li><li>download gwt-gdata-2.2.1.zip from <a href="http://code.google.com/p/gwt-gdata/">http://code.google.com/p/gwt-gdata/</a></li><li>extract gwt-gdata.jar from the archive and add it to the project build path</li><li>make sure your project inherits the module com.google.gwt.gdata.GData</li><li>append the following line to your .gwt.xml file:</li></ul><p style="padding-left: 30px;"><span></span><span style="color: #cc0000;">&lt;inherits name='com.google.gwt.gdata.GData' /&gt;</span></p><h2>2. Getting a view of our calendar</h2><p>To start with, let's get a read only view of the calendar displaying in our application. Whenever we make changes to the calendar we can refresh this view and see the result of our work immediately.</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="/uploads/50840/ufiles/gwtcalendarblog/calendar_embed.PNG" alt="" width="960" height="206" /></p><ul><li>Under "My calendars" in the main home page of your calendar, click on the dropdown next to our newly created "GWTCalendar"</li><li>Select the "Calendar Settings" options</li><li>Under "Embed This Calendar" copy the URL in the "src" attribute from the iframe snippet. This is the URL to the public view of our calendar. You can paste it in a browser window to verify.</li><li>We will display a view of the calendar in an iframe - create a Frame with the calendar URL as source.</li></ul><p>Add the following code to your EntryPoint class:</p><ul></ul><address style="padding-left: 30px;"><span></span><span style="color: #009900;">// Your calendar URL here!!</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">private static final String CAL_PUBLIC_URL = "https://www.google.com/calendar/embed?src=phihl3hmbh48lq5ml3ek9cj19o%40group.calendar.google.com&amp;ctz=Australia/Brisbane";</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;"> private Frame googleCalendar = new Frame(CAL_PUBLIC_URL);</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;"><br /></span></address><p>Let's display that calendar:</p><address><span style="color: #cc0000;">public void onModuleLoad() {</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setWidth("500px"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setHeight("500px"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">RootPanel.get().add(googleCalendar); </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><p>If you run the project you should be able to see the calendar we just created:</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="/uploads/50840/ufiles/gwtcalendarblog/calendar_view_1.PNG" alt="" width="622" height="453" /></p><h2>3. Setting up our UI</h2><p>Let's proceed by setting up the UI controls we will need for interacting with the calendar.<br />We will be creating a simple event that has a title, start date and end date. So let's create a vertical panel with a text box for the title, and two date pickers specifying the start and end dates for our event. We'll also need a button which when clicked creates our event. Add the following code to your EntryPoint class:</p><address><br /><span></span><span style="color: #cc0000;">private TextBox eventTitle = new TextBox(); </span></address><address><span style="color: #cc0000;">private DatePicker startDate = new DatePicker(); </span></address><address><span style="color: #cc0000;">private DatePicker endDate = new DatePicker(); </span></address><address><span style="color: #cc0000;">private Widget createNewEventPanel(){</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;"> VerticalPanel createPanel = new VerticalPanel(); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(new Label("Title:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(eventTitle); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(new Label("Event Start Date:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(startDate); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(new Label("Event End Date:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(endDate); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(new Button("Create Event",new ClickHandler(){ </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onClick(ClickEvent event) { </span></address><address style="padding-left: 90px;"><span style="color: #009900;">//TODO create the event with the given title and duration</span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">})); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">return createPanel; </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><p><span></span>When editing our event, we will be able to change the title or remove the event entirely. To do that we will need a textbox to specify the URL of the event we would like to edit (more on this later), a textbox for the new title, and two buttons - Update and Remove. The following code creates a simple panel consisting of those controls. Add it to your EntryPoint class.</p><address><span style="color: #cc0000;">private TextBox newTitle = new TextBox(); </span></address><address><span style="color: #cc0000;">private TextBox eventUrl = new TextBox(); </span></address><address><span style="color: #cc0000;">private Widget createEventEditPanel(){ </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">VerticalPanel editPanel = new VerticalPanel(); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Label("Event URL:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(eventUrl); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Label("New Event Title:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(newTitle); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Button("Update", new ClickHandler(){ </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onClick(ClickEvent event) { </span></address><address style="padding-left: 90px;"><span style="color: #009900;">//TODO update the event given by the URL </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">})); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Button("Delete", new ClickHandler(){ </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onClick(ClickEvent event) { </span></address><address style="padding-left: 90px;"><span style="color: #009900;">// TODO delete the event given by the URL </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">})); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">return editPanel;</span></address><address><span style="color: #cc0000;"> }</span></address><p><br />Let's pull it all together and display our UI controls. Modify the onModuleLoad() method as follows:<span></span></p><address><span></span><span></span><span style="color: #cc0000;">public void onModuleLoad() { </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">HorizontalPanel controlsPanel = new HorizontalPanel();&nbsp; </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setWidth("500px"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setHeight("500px"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(googleCalendar); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(createNewEventPanel()); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(createEventEditPanel()); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.setSpacing(10); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">RootPanel.get().add(controlsPanel); </span></address><address><span style="color: #cc0000;">}</span></address><p>If we run the project again our application should now look something like this:</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="/uploads/50840/ufiles/gwtcalendarblog/ui_basic.PNG" alt="" width="897" height="535" /></p><h2>4. Authenticating to Google Calendar</h2><p>We are ready to start interacting with our calendar. The first step in our application should always ensure that we have the GData Calendar API loaded. If the API is not loaded we load it. This requires a slight refactoring. Firstly, we extract the contents of our onModuleLoad() method to a new method - loadModule():</p><address><br /><span></span><span style="color: #cc0000;">public void loadModule(){ </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">HorizontalPanel controlsPanel = new HorizontalPanel();&nbsp; </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setWidth("500px"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setHeight("500px"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(googleCalendar); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(createNewEventPanel()); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(createEventEditPanel()); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.setSpacing(10); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">RootPanel.get().add(controlsPanel); </span></address><address><span style="color: #cc0000;">}</span></address><p><br />Next, we rewrite onModuleLoad() so that it checks for the presence of the calendar API. If it is absent we load the API by a call to GData.loadGDataApi(). At this stage we need to provide an API key from google (available freely from <a href="http://code.google.com/apis/gdata/signup.html">http://code.google.com/apis/gdata/signup.html</a>). For the purposes of the demo, you could use the key supplied, but you should get your own for a real application. Note the asynchronous nature - we need a Runnable which will complete at some time in the future. Only once the Runnable has completed can we proceed loading our application as in the code below:</p><address><span style="color: #cc0000;">public final static String GDATA_API_KEY = "ABQIAAAABGWvCfqj7yJHpNUWKnPf8xS33zGBuY57s7EfWCbD5ZXtDEt-shSPCo3EL0Dtuj-0TG3CmT93zHHI9Q"; </span></address><address><span style="color: #cc0000;">public void onModuleLoad() {  &nbsp; &nbsp;</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">if (!GData.isLoaded(GDataSystemPackage.CALENDAR)) {&nbsp;</span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">GData.loadGDataApi(GDATA_API_KEY, new Runnable() {</span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">public void run() {  &nbsp; &nbsp; &nbsp; &nbsp;  &nbsp;</span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">loadModule(); <span style="color: #006600;">// Load application  &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span></span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">}  &nbsp; &nbsp; &nbsp; &nbsp;</span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">}, GDataSystemPackage.CALENDAR);  &nbsp; &nbsp;</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">} else {  &nbsp; &nbsp;  &nbsp;</span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">loadModule(); <span style="color: #006600;">// Load application  &nbsp; &nbsp;</span></span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">} </span></address><address><span style="color: #cc0000;">}</span></address><p><br />Before interacting with our calendar, we must authenticate to it (either as the google account that owns the calendar, or one of the users we decided to share it with in step 1.1. To this end, the GData API uses AuthSub authentication, made available to us via the User class. When we execute the login() method of the User class, our user is taken to a standard Google login page where they can authenticate with their Google account. The user is then prompted to allow our application permission to use data (the calendar) associated with their Google account. Permission must be granted if we are to proceed successfully. We wrap the authentication step in a simple method:</p><address><br /><span></span><span style="color: #cc0000;">private void doAuthSubLogin() { </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">AuthSubStatus status = User.getStatus(calendarUrl); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">if (status != AuthSubStatus.LOGGED_IN) { </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">User.login(calendarUrl); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">} </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><p><span></span>We'll also slightly tweak loadModule() to ensure that the user has logged in to the calendar service before proceeding:</p><address><br /><span></span><span style="color: #cc0000;">public void loadModule(){ </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">doAuthSubLogin(); </span></address><address style="padding-left: 30px;"><span style="color: #006600;">// Page refreshed while logging in - no need to initialize the page further  &nbsp; &nbsp;</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">if (User.getStatus() == AuthSubStatus.LOGGING_IN) return;  &nbsp; </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">HorizontalPanel controlsPanel = new HorizontalPanel();&nbsp; </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setWidth("500px"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setHeight("500px"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(googleCalendar); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(createNewEventPanel()); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(createEventEditPanel()); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.setSpacing(10); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">RootPanel.get().add(controlsPanel); </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><h2><span></span>5. Accessing our calendar</h2><p>We're finally ready to start playing with our calendar. But how do we access it? Under the hood, the GData API interacts with the calendar by sending/receiving Atom or JSON-C feed &nbsp;messages. Fortunately we don't need to worry about that and the GWT API abstracts the details away from us. However, because the calendar is in effect an Atom/JSON feed, it and its events are accessed through an identifying URL. A calendar can be accessed via a URL such as the one below. For more info on the calendar URL structure see [4].</p><p style="padding-left: 30px;"><span></span><span style="color: #cc0000;">http://www.google.com/calendar/feeds/CALENDAR_ID/private/full</span></p><p><span></span>...where CALENDAR_ID identifies the calendar of interest. You can find your CALENDAR_ID in "Calendar settings" for each calendar (see the screenshot in step 2 of this post). Let's put that URL somewhere where we can reuse it (in the EntryPoint class):</p><address><span></span><span style="color: #006600;">// Use YOUR OWN calendar ID in here! </span></address><address><span style="color: #cc0000;">private static String calendarUrl = "http://www.google.com/calendar/feeds/phihl3hmbh48lq5ml3ek9cj19o@group.calendar.google.com/private/full";</span></address><address><br /></address><h2>5.1 Inserting calendar events</h2><p>Next, we'll create a class which in essence will wrap (think Facade pattern) some of capability of the GData CalendarService class for the purposes of accessing a calendar with a specific URL. I've decided to go with the somewhat generic name of <em>GoogleCalendarUtils</em>. Our class will have only two member fields - the calendar URL, and an instance of CalendarService which contains all calendar capabilities. The calendar URL will be passed in, to identify the calendar we are working with, and we'll create our own instance of the CalendarService. Create a new class called GoogleCalendarUtils, and add the following code:</p><address><span></span><span style="color: #cc0000;">private String calendarUrl; </span></address><address><span style="color: #cc0000;">private CalendarService calendarService; </span></address><address><span style="color: #cc0000;"> public GoogleCalendarUtils(String calendarUrl){ </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">this.calendarUrl = calendarUrl; </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">this.calendarService = CalendarService.newInstance("GWTGoogleCalendarDemoApplication"); </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><p><span></span>We'll add a simple method for creating calendar events with just a title, start and end dates by creating the createCalendarEventEntry() method as below:</p><address><span></span><span style="color: #cc0000;">private CalendarEventEntry createCalendarEventEntry(String title, Date startDate, Date endDate){</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">CalendarEventEntry eventEntry = CalendarEventEntry.newInstance();</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">eventEntry.setTitle(Text.newInstance());  &nbsp; &nbsp;</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">eventEntry.getTitle().setText(title);  &nbsp; &nbsp;</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">When when = When.newInstance();</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">when.setStartTime(DateTime.newInstance(startDate));</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">when.setEndTime(DateTime.newInstance(endDate));</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">eventEntry.addTime(when); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">return eventEntry; </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><p><span></span>Note that you can create events that are much more complex and detailed, by examining the composition of the CalendarEventEntry class (you can additionally add event locations, participants, recurrence, etc - in essence everything you can do with a google calendar). However, for simplicity we'll stick to a date range and a title. No doubt you will have noticed this method uses the GData types like Text and When to create the event object. These are simply GWT wrapper types that map to XML Atom/JSON elements which are used under the hood.</p><p>Let's proceed by creating a way to insert new events into our calendar. This is simple: we call createCalendarEventEntry(), and pass the resulting event to the calendar via the CalendarService instance we have (in GoogleCalendarUtils). We expose this operation via the insertCalendarEvent() method, which provides a callback that we will use to update the UI.</p><address><span></span><span style="color: #cc0000;">public void insertCalendarEvent(String title, Date startDate, Date endDate, CalendarEventEntryCallback callback){</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">CalendarEventEntry eventEntry = createCalendarEventEntry(title, startDate, endDate)</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">calendarService.insertEntry(calendarUrl, eventEntry, callback); </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><p><span></span>Next, we should update our UI code to read those fancy input controls we created earlier and create a calendar entry for us. Let's revisit the createNewEventPanel() method. We created a button for new events, however left the event handler empty. Now it's time to actually do something in there. Update your createNewEventPanel() method so it now looks like this:</p><address><span></span><span style="color: #cc0000;">private Widget createNewEventPanel(){ </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">VerticalPanel createPanel = new VerticalPanel(); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(new Label("Title:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(eventTitle); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(new Label("Event Start Date:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(startDate); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(new Label("Event End Date:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(endDate); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">createPanel.add(new Button("Create Event",new ClickHandler(){ </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onClick(ClickEvent event) { </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">String title = eventTitle.getText(); </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">Date start = startDate.getValue(); </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">Date end = endDate.getValue(); </span></address><address style="padding-left: 90px;"><span style="color: #006600;"> // Use an instance of our CalendarService wrapper for our calendar. </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">GoogleCalendarUtils ourCalendar = new GoogleCalendarUtils(calendarUrl); </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;"><span style="color: #006600;">// Call the insert method we defined, and pass a callback to handle the</span> response</span></address><address style="padding-left: 90px;"><span style="color: #cc0000;"> ourCalendar.insertCalendarEvent(title, start, end, new CalendarEventEntryCallback() {</span></address><address style="padding-left: 120px;"><span style="color: #cc0000;"> @Override </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">public void onSuccess(CalendarEventEntry result) { </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">Window.alert("The event " + result.getTitle().getText() + " was added to the calendar"); </span></address><address style="padding-left: 150px;"><span style="color: #006600;">// Refresh the static view of the calendar if we are successful</span></address><address style="padding-left: 150px;"><span style="color: #cc0000;"> googleCalendar.setUrl(googleCalendar.getUrl()); </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;"> } </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;"> @Override </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">public void onFailure(CallErrorException caught) { </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">Window.alert("Failed to insert an event in the calendar.."); </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">}); </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">})); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">return createPanel; </span></address><address><span style="color: #cc0000;">}&nbsp;</span></address><address><span style="color: #cc0000;"><br /></span></address><p><span></span>Try running the project now. Enter a title and select a start and end date for the event. Click <em>Create Event</em>. You will have run into a strange exception, something like:</p><address style="padding-left: 30px;"><span></span><strong><span style="color: #cc0000;">com.google.gwt.core.client.JavaScriptException: (Error): An image of the same domain is required on this page for authenticated reads and all writes.</span></strong></address><address style="padding-left: 30px;"><br /></address><p><span></span>For some mysterious reason, the fix is exactly as given in the error message. Our HTML page must have an image displayed originating from our domain for the GData Calendar API to work. All I know about this is that there is some workaround in browser behaviour that Google utilizes to call out to their services. If anyone knows more I would love to learn the exact details as to the why and how, so please leave a comment below. Anyway, let's make this error go away. Find and add any image to your project, and link to it in your GWT project HTML document. In tribute to Google greatness and mystery I will include the Google Logo (<a href="http://www.google.com/images/logo_sm.gif">http://www.google.com/images/logo_sm.gif</a>). Practically speaking this can be any image, even a 1x1px dot hiding in some corner. Remember, we must serve up the image from our domain and not just link to it. So we download it, add it to our project, and link that way. I am lazy, so I will put mine in the /war directory, right next to the html document hosting our app. We then display the image in the HTML document:<span></span></p><address style="padding-left: 60px;"><span></span><span></span><strong><span style="color: #cc0000;">. </span></strong></address><address style="padding-left: 60px;"><strong><span style="color: #cc0000;">. </span></strong></address><address style="padding-left: 60px;"><strong><span style="color: #cc0000;">. </span></strong></address><address style="padding-left: 60px;"><span style="color: #cc0000;">&lt;img src="logo_sm.gif"/&gt; </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">&lt;/body&gt; </span></address><address><span style="color: #cc0000;">&lt;/html&gt;</span></address><p><br />Try running the project now. Create a new event by selecting a start and end date, and entering a title in the boxes. Click <em>Create Event</em> to add the event to the calendar.</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="/uploads/50840/ufiles/gwtcalendarblog/create_event.PNG" alt="" width="918" height="649" /></p><p>Everything works! If you are still in disbelief about our image trick, go back to the HTML doc and comment out the &lt;img&gt; tag, rerun the project and see it crash and burn.</p><h2>5.2 Accessing the events in our calendar</h2><p>Since all operations with the calendar are done via a URL, it would be good to display the URLs for each of our events. Let's add a method to GoogleCalendar utils to do this:</p><address><span></span><span style="color: #cc0000;">public void getEvents(CalendarEventFeedCallback callback){ </span></address><address style="padding-left: 30px;"><span style="color: #006600;">// Simply forward the caller's callback to our CalendarService instance</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;"> calendarService.getEventsFeed(calendarUrl, callback);</span></address><address><span style="color: #cc0000;"> }</span></address><address><span style="color: #cc0000;"><br /></span></address><p><span></span>Let us also add a way to display our calendar events as a list in our application. We'll use the method below any time we want to update our list of calendar events. We'll add it to our EntryPoint class:</p><address><span></span><span style="color: #cc0000;">private FlexTable eventsDisplayTable = new FlexTable(); </span></address><address><span style="color: #cc0000;">private void displayCalendarEntries(CalendarEventEntry[] entries){</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;"> eventsDisplayTable.clear();</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;"> eventsDisplayTable.setText(0, 0, "Event Title"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">eventsDisplayTable.setText(0, 1, "Event URL"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">for(int row = 0; row &lt; entries.length; row++){ </span></address><address style="padding-left: 60px;"><span style="color: #006600;">// Display the event's title </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">eventsDisplayTable.setText(row+1, 0, entries[row].getTitle().getText()); </span></address><address style="padding-left: 60px;"><span style="color: #006600;">// Display the event's URL </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">eventsDisplayTable.setText(row+1, 1, entries[row].getEditLink().getHref()); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">} </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><p><span></span>And we will add a way to query our events via:</p><address><span></span><span style="color: #cc0000;">private void getAllEvents(){ </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">GoogleCalendarUtils calendar = new GoogleCalendarUtils(calendarUrl);</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;"> calendar.getEvents(new CalendarEventFeedCallback() { </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onSuccess(CalendarEventFeed result) {</span></address><address style="padding-left: 90px;"><span style="color: #cc0000;"> displayCalendarEntries((CalendarEventEntry[])result.getEntries()); </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onFailure(CallErrorException caught) { </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">Window.alert("Failed getting calendar events.."); </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">}); </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><p>Further, let's provide a single method that updates both our calendar view and the list of events all at once. We add updateCalendarView() to our EntryPoint class:</p><address><br /><span></span><span style="color: #cc0000;">private void updateCalendarView(){ </span></address><address style="padding-left: 30px;"><span style="color: #006600;">// Refresh the iframe displaying our calendar </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setUrl(googleCalendar.getUrl()); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">getAllEvents();</span></address><address><span style="color: #cc0000;"> }</span></address><p><br />Once more we modify our loadModule() method, this time to add a vertical layout panel in order to place the list of events at the bottom of our page. Change loadModule() in EntryPoint so it is now:</p><address><br /><span></span><span style="color: #cc0000;">public void loadModule(){ </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">doAuthSubLogin(); </span></address><address style="padding-left: 30px;"><span style="color: #006600;">// Page refreshed while logging in - no need to initialize the page further  &nbsp; &nbsp;</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">if (User.getStatus() == AuthSubStatus.LOGGING_IN) return; </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">HorizontalPanel controlsPanel = new HorizontalPanel();&nbsp; </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setWidth("500px"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">googleCalendar.setHeight("500px"); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(googleCalendar); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(createNewEventPanel()); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.add(createEventEditPanel()); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">controlsPanel.setSpacing(10); </span></address><address style="padding-left: 30px;"><span style="color: #006600;">// Add a vertical layout to our page - events list at bottom </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">VerticalPanel vp = new VerticalPanel(); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">vp.add(controlsPanel); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">vp.add(eventsDisplayTable); </span></address><address style="padding-left: 30px;"><span style="color: #006600;">// Get all events that exist when the application loads </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">updateCalendarView(); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">RootPanel.get().add(vp); </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><p><span></span>If we run our project now, a table of event names and URLs appears below our view of the calendar.</p><p><img style="display: block; margin-left: auto; margin-right: auto;" src="/uploads/50840/ufiles/gwtcalendarblog/display_events.PNG" alt="" width="926" height="704" /></p><h2>5.3 Editing a calendar event</h2><p>Next, we would like to be able to edit calendar events. We can achieve this in a three step process:</p><ul><li>firstly we need to obtain the event via its URL&nbsp;</li><li>then we apply our changes&nbsp;</li><li>and lastly we update it back to the calendar&nbsp;</li></ul><p>In our simplified scenario, we will simply rename the event using the following method which we will add to GoogleCalendarUtils:&nbsp;&nbsp;<span></span></p><address><span></span><span style="color: #cc0000;">public void renameEvent(final String eventUrl, final String newTitle, final Callback&lt;CalendarEntry&gt; callback){ </span></address><address style="padding-left: 30px;"><span style="color: #006600;">// Get the event via its URL&nbsp; </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">calendarService.getCalendarEntry(eventUrl, new CalendarEntryCallback(){ </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onFailure(CallErrorException caught) { </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">Window.alert("Failed to access calendar service."); </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onSuccess(CalendarEntry result) { </span></address><address style="padding-left: 90px;"><span style="color: #006600;">// Once we have the entry, change it </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">Text title = Text.newInstance(); </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">title.setText(newTitle); </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">result.setTitle(title); </span></address><address style="padding-left: 90px;"><span style="color: #006600;">// Update the calendar with our changes </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">calendarService.updateEntry(eventUrl, result, callback); </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">}); </span></address><address><span style="color: #cc0000;">}</span></address><address><span style="color: #cc0000;"><br /></span></address><p><span></span>What remains is to hook up the inputs in our UI that we created earlier (remember the <em>New Event Title</em> textobx and the <em>Update </em>button). We will update the createEventEditPanel() method in our EntryPoint class to read in the new event title, and add an event handler to the <em>Update</em>&nbsp;button which will call GoogleCalendarUtils.renameEvent() with the new name. As a quick and dirty solution, we will identify the event that we want to modify by copying the event URL from the display table below our calendar view and pasting it into the <em>Event URL</em> text box. With the URL in there, we can type in the new event title we would like to use - I used <em>Updated Event</em>, and upon pressing the <em>Update </em>button our request will be pushed to our calendar. The updated version of createEventEditPanel() is:</p><address><span></span><span style="color: #cc0000;">private Widget createEventEditPanel(){ </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">VerticalPanel editPanel = new VerticalPanel(); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Label("Event URL:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(eventUrl); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Label("New Event Title:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(newTitle);</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;"> editPanel.add(new Button("Update", new ClickHandler(){ </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onClick(ClickEvent event) { </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">GoogleCalendarUtils ourCalendar = new GoogleCalendarUtils(calendarUrl); </span></address><address style="padding-left: 90px;"><span style="color: #006600;">// Rename the event and handle the result </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">ourCalendar.renameEvent(eventUrl.getText(), newTitle.getText(), new Callback&lt;CalendarEntry&gt;() { </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">public void onSuccess(CalendarEntry result) { </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">Window.alert("Event update success!"); </span></address><address style="padding-left: 150px;"><span style="color: #006600;">// When we are done, update our view </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">updateCalendarView(); </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">public void onFailure(CallErrorException caught) { </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">Window.alert("Failed updating calendar event.."); </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">}); </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">})); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Button("Delete", new ClickHandler(){ </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onClick(ClickEvent event) { </span></address><address style="padding-left: 90px;"><span style="color: #006600;">// TODO delete the event given by the URL </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">})); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">return editPanel; </span></address><address><span style="color: #cc0000;">}</span></address><address><br /></address><p><img style="display: block; margin-left: auto; margin-right: auto;" src="/uploads/50840/ufiles/gwtcalendarblog/updated_event.PNG" alt="" width="926" height="702" /><br /><br /></p><h2>5.4 Deleting a calendar event</h2><p>The last operation we set out to achieve was the ability to delete a calendar entry. This follows a very similar process to editing an event:</p><ul><li>we need to obtain the event via its URL&nbsp;</li><li>we then delete it through CalendarService &nbsp;</li></ul><p>We'll add a deleteEvent() method to GoogleCalendarUtils, which performs the above operations for us. The callback enables us to notify our caller of the outcome resulting from the delete operation.</p><address><span></span><span style="color: #cc0000;">public void deleteEvent(String eventUrl, final Callback&lt;CalendarEntry&gt; callback){ </span></address><address style="padding-left: 30px;"><span style="color: #006600;">// Obtain the calendar event of interest </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">calendarService.getCalendarEntry(eventUrl, new CalendarEntryCallback(){ </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onFailure(CallErrorException caught) { </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">Window.alert("Failed to access calendar service."); </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onSuccess(CalendarEntry result) { </span></address><address style="padding-left: 90px;"><span style="color: #006600;">// and delete it </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">result.deleteEntry(callback); </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">}); </span></address><address><span style="color: #cc0000;">}&nbsp;</span></address><address><span style="color: #cc0000;"><br /></span></address><p>As before we need to wire in the delete operation in our UI. Again we'll have to paste the URL of the calendar we want to delete into the <em>Event URL</em> textbox, and we'll set up the <em>Delete </em>button so that it calls out to GoogleCalendarUtils.deleteEvent() when it is clicked. As with edit previously, we achive this by adding the click handler for the <em>Delete </em>button in the createEventEditPanel() method in our EntryPoint class:</p><address><span></span><span style="color: #cc0000;">private Widget createEventEditPanel(){ </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">VerticalPanel editPanel = new VerticalPanel(); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Label("Event URL:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(eventUrl); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Label("New Event Title:")); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(newTitle); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Button("Update", new ClickHandler(){ </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onClick(ClickEvent event) { </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">GoogleCalendarUtils ourCalendar = new GoogleCalendarUtils(calendarUrl); </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;"><span style="color: #006600;">// Rename the event and handle the result</span> ourCalendar.renameEvent(eventUrl.getText(), newTitle.getText(), new Callback&lt;CalendarEntry&gt;() { </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">public void onSuccess(CalendarEntry result) { </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">Window.alert("Event update success!"); </span></address><address style="padding-left: 150px;"><span style="color: #006600;">// When we are done, update our view </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">updateCalendarView(); </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">public void onFailure(CallErrorException caught) { </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">Window.alert("Failed updating calendar event.."); </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">}); </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">})); </span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">editPanel.add(new Button("Delete", new ClickHandler(){ </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">public void onClick(ClickEvent event) { </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">GoogleCalendarUtils ourCalendar = new GoogleCalendarUtils(calendarUrl); </span></address><address style="padding-left: 90px;"><span style="color: #006600;">// Rename the event and handle the result </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">ourCalendar.deleteEvent(eventUrl.getText(), new Callback&lt;CalendarEntry&gt;(){ </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">public void onSuccess(CalendarEntry result) { </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">Window.alert("Event delete success!"); </span></address><address style="padding-left: 150px;"><span style="color: #006600;">// When we are done, update our view </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">updateCalendarView(); </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">@Override </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">public void onFailure(CallErrorException caught) { </span></address><address style="padding-left: 150px;"><span style="color: #cc0000;">Window.alert("Failed deleting calendar event.."); </span></address><address style="padding-left: 120px;"><span style="color: #cc0000;">} </span></address><address style="padding-left: 90px;"><span style="color: #cc0000;">}); </span></address><address style="padding-left: 60px;"><span style="color: #cc0000;">}</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">}));</span></address><address style="padding-left: 30px;"><span style="color: #cc0000;">return editPanel; </span></address><address><span style="color: #cc0000;">}</span></address><p><br />If we now run the application, copy the URL from <em>Updated Event</em> into the <em>Event URL</em> box, and click <em>Delete </em>the event should be deleted.&nbsp;</p><h2>6. More on Google Calendar</h2><ul><li>If you would like a closer look into the gwt-gdata API, I suggest going through the sample project included in the zip file we downloaded in step 1.2.</li><li>You could also look at the documentation for the Java/JavaScript APIs to discover more of the capability.</li><li>If you are interested in a front end calendar implementation check out gwt-cal (http://code.google.com/p/gwt-cal/). You could potentially use this with the GData APIs to build your own front end for Google Calendar.</li><li>Look through the reference list below for more resources.</li><li>Use Google and experiment!</li></ul><h2>7. References</h2><p>[1] Calendar GData API - <a href="http://code.google.com/apis/calendar/data/2.0/developers_guide.html">http://code.google.com/apis/calendar/data/2.0/developers_guide.html</a></p><p>[2] GWT wrapper for GData API: gwt-gdata - <a href="http://code.google.com/p/gwt-gdata/">http://code.google.com/p/gwt-gdata/</a></p><p>[3] More on google calendar authentication: AuthSub - <a href="http://code.google.com/apis/accounts/docs/AuthSub.html">http://code.google.com/apis/accounts/docs/AuthSub.html</a></p><p>[4] More on calendar feeds and URL structure: Data API Atom Reference - <a href="http://code.google.com/apis/calendar/data/2.0/reference.html">http://code.google.com/apis/calendar/data/2.0/reference.html</a></p><h2>8. Source</h2><p>You can download my version of the EntryPoint class GoogleCalendarUtils in the zip file below:</p>]]></content:encoded><pubDate>Tue, 02 Aug 2011 00:00:00 -1000</pubDate><guid>http://www.rubiconred.com/blog/gwt-working-with-google-calendar-using-gwt-gdata/</guid></item><item><title><![CDATA[Using GWT MVP with multiple views on the page]]></title><link>http://www.rubiconred.com/blog/using-gwt-mvp-with-multiple-views-on-the-page/</link><description><![CDATA[The Issue: When developing a GWT app with the MVP approach it is common to make use of Activities and Places as described in the developer guide Under this approach an ActivityManager makes use of an ...]]></description><content:encoded><![CDATA[<p><strong>The Issue:</strong></p><p>When developing a GWT app with the MVP approach it is common to make use of Activities and Places as described in the <a href="http://code.google.com/webtoolkit/doc/latest/DevGuideMvpActivitiesAndPlaces.html" target="_blank">developer guide </a></p><p>Under this approach an ActivityManager makes use of an ActivityMapper which you define in order to respond to PlaceChangeEvents by running the relevant Activity. The Activity is passed a reference to an AcceptsOneWidget panel which is typically a container for the entire application. The Activity is responsible for constructing the application view and setting it into this container.</p><p>The potential pitfall of this approach is that every time a new activity is run we clear out the entire application view and reconstruct everything from scratch. If your application is composed of multiple distinct regions, for example a Menu and a Content area, and the view for each region can change independently of the other, then you will be forcing the browser to do extra unnecessary work to rebuild the DOM with the Menu region, even though only the Content region has changed.</p><p><strong>A Possible Solution:</strong></p><p>The solution is presented in two stages</p><p>1) Examine a simple approach that will allow for multiple regions on a page to be managed and changed independently of one another.</p><p>2) Explore a way to dynamically remove regions that are not required in a given context (e.g. removing the menu region when on the login screen)</p><p>We will use this simple example application layout</p><table><tbody><tr><td><img src="/uploads/50840/ufiles/ViewScreen1.png" alt="Login Screen" width="400" height="300" /></td><td><img src="/uploads/50840/ufiles/ViewScreen2.png" alt="Main Screen" width="400" height="300" /></td></tr></tbody></table><p>&nbsp;</p><p>We will first define our application layout giving each region it's own AcceptsOneWidget panel.</p><p><em>appContainer = new DockLayoutPanel(Unit.PX);</em><br /><em>mainContent = new ScrollPanel();</em><br /><em>leftMenu = new ScrollPanel();</em><br /><em>banner = new SimplePanel();</em><br /><br /><em>appContainer.addNorth(banner, 50);</em><br /><em>appContainer.addWest(leftMenu, 300);</em><br /><em>appContainer.add(mainContent);</em></p><p>For each region that we want to be able to update the view independently of the other regions (the menu and the content region) we will define a new ActivityMapper and ActivityManager</p><p><em>ActivityMapper contentActivityMapper = new ContentActivityMapper(clientFactory);</em><br /><em>ActivityManager contentActivityManager = new ActivityManager(contentActivityMapper, eventBus);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </em><br /><em>contentActivityManager.setDisplay(mainContent);</em><br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br /><em>ActivityMapper menuActivityMapper = new MenuActivityMapper(clientFactory);</em><br /><em>ActivityManager menuActivityManager = new ActivityManager(menuActivityMapper, eventBus);</em><br /><em>menuActivityManager.setDisplay(leftMenu);</em></p><p>Note that a different panel is set into the display of each ActivityManager.</p><p>Because both managers share an EventBus, both ActivityMappers will be called whenever a PlaceChangeEvent is fired. Since each manager should only run a new activity in response to a subset of the possible Places in your app, we will make some changes to the mapper so that it only responds to the Places that affect the given region. By having the mapper hold a reference to its currently executing Activity, it can just return this same activity whenever a Place change it is not interested in gets fired. This will have no affect as the currently executing Activity will not be run again.</p><p><em>public class MenuActivityMapper implements ActivityMapper {</em><br /><br /><em>&nbsp;&nbsp;&nbsp; private Activity currentActivity;</em><br />&nbsp;&nbsp;&nbsp; <br /><em>&nbsp;&nbsp;&nbsp; @Override</em><br /><em>&nbsp;&nbsp;&nbsp; public Activity getActivity(Place place) {</em><br /><em>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (place instanceof MainMenuPlace)</em><br /><em>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; {</em><br /><em>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return new MainMenuActivity();</em><br /><em>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; } else if (place instanceof SubMenuPlace) {</em></p><p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new SubMenuActivity();</em></p><p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {</em></p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em> //Some other content Place that we aren't interested in</em><br /><em>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return currentActivity;</em></p><p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</em><em>&nbsp;</em></p><p><em> } &nbsp; </em></p><p>That completes the first stage. Now say we want to allow the menu region to be hidden when the user is at the login screen and just use the content region to display the login view.</p><p>Create a new panel HideableScrollPanel which extends ScrollPanel. Set the menu to use the new panel instead of ScrollPanel. HideableScrollPanel will take as input the parent DockLayoutPanel container and the expected size of the panel when it is displayed. Override the setWidget method so that it will minimize the panel if null is set.</p><p><em>public void setWidget(IsWidget activityWidget) {</em><br /><em>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Widget widget = Widget.asWidgetOrNull(activityWidget);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </em><br /><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (widget == null)</em><br /><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </em><br /><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; parent.setWidgetSize(this, 0);</em><br /><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else {</em><br /><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; parent.setWidgetSize(this, size);</em><br /><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</em><br /><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super.setWidget(widget);</em><br /><em>&nbsp;&nbsp;&nbsp; }</em></p><p>Now you can define a HideActivity which when run simply sets the AcceptOneWidget panel it is passed a value of null. If the AcceptOneWidget panel is an instance of your HideableScrollPanel then the panel will be removed from the screen. When a LoginPlace change is received the MenuActivityMapper should return a HideActivity.<em><br /></em></p>]]></content:encoded><pubDate>Wed, 20 Jul 2011 00:00:00 -1000</pubDate><guid>http://www.rubiconred.com/blog/using-gwt-mvp-with-multiple-views-on-the-page/</guid></item><item><title><![CDATA[Deploying Hudson on Weblogic]]></title><link>http://www.rubiconred.com/blog/deploying-hudson-on-weblogic/</link><description><![CDATA[During the past few months, we have been setting up hudson on our servers to automate our GWT and SOA builds, testing and deployments to Weblogic. Using the helpful wiki page provided on the hudson...]]></description><content:encoded><![CDATA[<p>During the past few months, we have been setting up hudson on our servers to automate our GWT and <a title="SOA Best Practices" href="/soak-centre/" target="_blank">SOA</a> builds, testing and deployments to Weblogic.</p><p>Using the helpful wiki page provided on the&nbsp;<a class="external-link" href="http://wiki.hudson-ci.org/display/HUDSON/Weblogic" rel="nofollow" target="_top">hudson wiki</a>&nbsp;and thank you to Maxence Button's blog post, this is a step by step installation guide to get Hudson 2.0.1 deployed on to Weblogic 10.3.4.0.&nbsp;</p><p>Due to Weblogic's class loading structure, if the hudson.war is directly deployed, the application will fail to startup. This is to because there are jar conflicts between Hudson and Weblogic. To get around the issue, we use the FilteringClassLoader mechanism so that specific hudson jars get priority in the classpath over Weblogic's jars.&nbsp;</p><p>&nbsp;</p><h2><strong><a name="DeployingHudsononWeblogic-1.Downloadhudson.war" target="_top"></a>1. Download hudson.war</strong></h2><p>Download hudson.war from&nbsp;<a class="external-link" href="http://hudson-ci.org/downloads/war/" rel="nofollow" target="_top">hudson downloads</a>.</p><p>&nbsp;</p><h2><strong><a name="DeployingHudsononWeblogic-2.Createadirectorystructureforthenewhudson.ear" target="_top"></a>2. Create a directory structure for the new hudson.ear</strong></h2><div class="code panel"><div class="codeContent panelContent"><div><div id="highlighter_381481" class="syntaxhighlighter nogutter  java"><table style="padding-left: 30px;" border="0" cellspacing="0" cellpadding="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="java plain">hudson/</code></div><div class="line number2 index1 alt1"><code class="java spaces">&nbsp;</code><code class="java plain">|_____ META-INF/</code></div><div class="line number3 index2 alt2"><code class="java spaces">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code><code class="java plain">|_____ application.xml</code></div><div class="line number4 index3 alt1"><code class="java spaces">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="white-space: pre;"></span>&nbsp;&nbsp;&nbsp;</code><code class="java plain">|_____ weblogic-application.xml</code></div><div class="line number5 index4 alt2"><code class="java spaces">&nbsp;</code><code class="java plain">|_____ hudson.war</code></div><div class="line number5 index4 alt2" style="padding-left: 30px;"></div></div></td></tr></tbody></table></div><div class="syntaxhighlighter nogutter  java"></div></div></div></div><p>&nbsp;</p><p><strong>Note:</strong> This step will not work correctly on MacOSX as it will generate .DS_STORE files&nbsp;within the folders.</p><p><strong><br /></strong></p><h2><strong><a name="DeployingHudsononWeblogic-3.Populateapplication.xml" target="_top"></a>3. Edit application.xml</strong></h2><p>Enter the following into application.xml</p><div class="code panel"><div class="codeContent panelContent"><div><div id="highlighter_566645" class="syntaxhighlighter nogutter  java"><table border="0" cellspacing="0" cellpadding="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2" style="padding-left: 30px;"><code class="java plain">&lt;?xml version=</code><code class="java string">"1.0"</code><code class="java plain">encoding=</code><code class="java string">"UTF-8"</code><code class="java plain">?&gt;&lt;?xml version=</code><code class="java string">"1.0"</code><code class="java plain">encoding=</code><code class="java string">"UTF-8"</code><code class="java plain">?&gt;</code></div><div class="line number2 index1 alt1" style="padding-left: 30px;">&nbsp;</div><div class="line number3 index2 alt2" style="padding-left: 30px;"><code class="java plain">&lt;application xmlns=</code><code class="java string">"<a href="http://java.sun.com/xml/ns/javaee" target="_top">http://java.sun.com/xml/ns/javaee"</a></code></div><div class="line number4 index3 alt1" style="padding-left: 30px;"><code class="java plain">xmlns:xsi=</code><code class="java string">"<a href="http://www.w3.org/2001/XMLSchema-instance" target="_top">http://www.w3.org/2001/XMLSchema-instance"</a></code></div><div class="line number5 index4 alt2" style="padding-left: 30px;"><code class="java plain">xsi:schemaLocation=</code><code class="java string">"http://java.sun.com/xml/ns/javaee <a href="http://java.sun.com/xml/ns/javaee/application_5.xsd" target="_top">http://java.sun.com/xml/ns/javaee/application_5.xsd"</a></code></div><div class="line number6 index5 alt1" style="padding-left: 30px;"><code class="java plain">version=</code><code class="java string">"5"</code><code class="java plain">&gt;</code></div><div class="line number7 index6 alt2" style="padding-left: 30px;">&nbsp;</div><div class="line number8 index7 alt1" style="padding-left: 30px;"><code class="java plain">&lt;module id=</code><code class="java string">"hudson"</code><code class="java plain">&gt;</code></div><div class="line number9 index8 alt2" style="padding-left: 30px;"><code class="java plain">&lt;web&gt;</code></div><div class="line number10 index9 alt1" style="padding-left: 30px;"><code class="java plain">&lt;web-uri&gt;hudson.war&lt;/web-uri&gt;</code></div><div class="line number11 index10 alt2" style="padding-left: 30px;"><code class="java plain">&lt;context-root&gt;/hudson&lt;/context-root&gt;</code></div><div class="line number12 index11 alt1" style="padding-left: 30px;"><code class="java plain">&lt;/web&gt;</code></div><div class="line number13 index12 alt2" style="padding-left: 30px;"><code class="java plain">&lt;/module&gt;</code></div><div class="line number14 index13 alt1" style="padding-left: 30px;">&nbsp;</div><div class="line number15 index14 alt2" style="padding-left: 30px;"><code class="java plain">&lt;application xmlns=</code><code class="java string">"<a href="http://java.sun.com/xml/ns/javaee" target="_top">http://java.sun.com/xml/ns/javaee"</a></code><code class="java plain">&lt;/module&gt;</code></div></div></td></tr></tbody></table></div><div class="syntaxhighlighter nogutter  java"></div><div class="syntaxhighlighter nogutter  java"></div><div class="syntaxhighlighter nogutter  java"></div><div class="syntaxhighlighter nogutter  java"></div><div class="syntaxhighlighter nogutter  java"></div></div></div></div><h2><strong><a name="DeployingHudsononWeblogic-4.Populateweblogicapplication.xml" target="_top"></a>4. Edit weblogic-application.xml</strong></h2><p>Enter the following into&nbsp;weblogic-application.xml</p><div class="code panel"><div class="codeContent panelContent"><div><div id="highlighter_38275" class="syntaxhighlighter nogutter  java"><table style="padding-left: 30px;" border="0" cellspacing="0" cellpadding="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="java plain">&lt;?xml version=</code><code class="java string">"1.0"</code><code class="java plain">encoding=</code><code class="java string">"UTF-8"</code><code class="java plain">?&gt;</code></div><div class="line number2 index1 alt1"><code class="java plain">&lt;wls:weblogic-application</code></div><div class="line number3 index2 alt2"><code class="java plain">xmlns:wls=</code><code class="java string">"<a href="http://www.bea.com/ns/weblogic/weblogic-application" target="_top">http://www.bea.com/ns/weblogic/weblogic-application"</a></code></div><div class="line number4 index3 alt1"><code class="java plain">xmlns:xsi=</code><code class="java string">"<a href="http://www.w3.org/2001/XMLSchema-instance" target="_top">http://www.w3.org/2001/XMLSchema-instance"</a></code></div><div class="line number5 index4 alt2"><code class="java plain">xsi:schemaLocation="http:</code><code class="java comments">//java.sun.com/xml/ns/javaee <a href="http://java.sun.com/xml/ns/javaee/javaee_5.xsd" target="_top">http://java.sun.com/xml/ns/javaee/javaee_5.xsd</a></code></div><div class="line number6 index5 alt1"><code class="java plain">http:</code><code class="java comments">//www.bea.com/ns/weblogic/weblogic-application &gt;</code></div>
&nbsp;</div><div class="line number8 index7 alt1"><code class="java plain">&lt;!-- server-version: </code><code class="java value">10.3</code><code class="java plain">--&gt;</code></div><div class="line number9 index8 alt2"><code class="java plain">&lt;wls:application-param&gt;</code></div><div class="line number10 index9 alt1"><code class="java plain">&lt;wls:param-name&gt;webapp.encoding.</code><code class="java keyword">default</code><code class="java plain">&lt;/wls:param-name&gt;</code></div><div class="line number11 index10 alt2"><code class="java plain">&lt;wls:param-value&gt;UTF-</code><code class="java value">8</code><code class="java plain">&lt;/wls:param-value&gt;</code></div><div class="line number12 index11 alt1"><code class="java plain">&lt;/wls:application-param&gt;</code></div><div class="line number13 index12 alt2">&nbsp;</div><div class="line number14 index13 alt1">&nbsp;</div><div class="line number15 index14 alt2"><code class="java plain">&lt;wls:prefer-application-packages&gt;</code></div><div class="line number16 index15 alt1"><code class="java plain">&lt;wls:</code><code class="java keyword">package</code><code class="java plain">-name&gt;org.apache.*&lt;/wls:</code><code class="java keyword">package</code><code class="java plain">-name&gt;</code></div><div class="line number17 index16 alt2"><code class="java plain">&lt;wls:</code><code class="java keyword">package</code><code class="java plain">-name&gt;javax.xml.stream.*&lt;/wls:</code><code class="java keyword">package</code><code class="java plain">-name&gt;</code></div><div class="line number18 index17 alt1"><code class="java plain">&lt;wls:</code><code class="java keyword">package</code><code class="java plain">-name&gt;org.dom4j.*&lt;/wls:</code><code class="java keyword">package</code><code class="java plain">-name&gt;</code></div><div class="line number19 index18 alt2"><code class="java plain">&lt;/wls:prefer-application-packages&gt;</code></div><div class="line number19 index18 alt2"><span style="font-family: monospace;">&lt;/wls:weblogic-application&gt;</span></div><div class="line number20 index19 alt1" style="padding-left: 30px;"></div></td></tr></tbody></table></div></div></div></div><p>&nbsp;</p><p>Weblogic needs this because (unlike JBoss, Tomcat, Jetty et. al) Weblogic will not load the JARs in the WAR before the Weblogic installed JARs, and usually the resulting error is that there is an old version of Ant 1.7 in scope, instead of Ant 1.8.x as Hudson wants. There is also a conflict with stax-api-1.0.1.jar for the javax.xml package listed.</p><p>&nbsp;</p><h2><strong><a name="DeployingHudsononWeblogic-5.Packageintoanenterpriseapplicationarchive" target="_top"></a>5. Package into an enterprise application archive</strong></h2><p>Inside the hudson directory, at the same level as META-INF and hudson.war, run the following unix commands or zip the contents of the directory and then rename to hudson.ear</p><div class="code panel"><div class="codeContent panelContent"><div><div id="highlighter_877705" class="syntaxhighlighter nogutter  java"><table style="padding-left: 30px;" border="0" cellspacing="0" cellpadding="0"><tbody><tr><td class="code"><div class="container"><div class="line number1 index0 alt2"><code class="java plain">[server]$ zip -r hudson *</code></div><div class="line number1 index0 alt2"><span style="font-family: monospace;">[server]$ mv hudson.zip hudson.ear</span></div></div></td></tr></tbody></table></div><div class="syntaxhighlighter nogutter  java"></div></div></div></div><p>&nbsp;</p><p><strong>Note:</strong> Do not zip the hudson folder directly, this will not work. Zip the contents of the hudson folder.&nbsp;</p><p><strong><br /></strong></p><h2><strong><a name="DeployingHudsononWeblogic-6.Deploythehudson.eartotheWeblogicserver%3A" target="_top"></a>6. Deploy the hudson.ear to the Weblogic server:</strong></h2><p><span style="color: #000000;"><strong>Note:</strong> remember to delete any old Hudson WAR or EAR deployments first.&nbsp;</span></p><p><span style="color: #000000;"><strong><br /></strong></span></p><h2><strong><a name="DeployingHudsononWeblogic-7.UseHudson" target="_top"></a>7. Use Hudson</strong></h2><p><strong><a name="DeployingHudsononWeblogic-" target="_top"></a></strong></p><p><span style="color: #000000;">The application will deploy to&nbsp;</span><a class="external-link" href="http://serverNameport" rel="nofollow" target="_top">http://serverName:port/hudson</a><span style="color: #000000;">.</span></p><ul><li>e.g.&nbsp;<a class="external-link" href="http://localhost:10090/hudson" rel="nofollow" target="_top">http://localhost:10090/hudson</a></li></ul><p>&nbsp;</p><p><strong><a name="DeployingHudsononWeblogic-Troubleshooting" target="_top"></a>Troubleshooting</strong></p><p>If you run into certain java exceptions after deployment (see below for an example). Try removing the hudson working directory (In linux, this is in &lt;user_home&gt;/.hudson) and redeploying the hudson.ear file</p><p style="padding-left: 30px;"><em>org.jvnet.hudson.reactor.ReactorException: org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed. (Caused by org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed.) (Caused by org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed. (Caused by org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed.)) at org.jvnet.hudson.reactor.Reactor.execute(Reactor.java:246) at hudson.model.Hudson.executeReactor(Hudson.java:727) at hudson.model.Hudson.(Hudson.java:630) at hudson.model.Hudson.(Hudson.java:569) at hudson.WebAppMain$2.run(WebAppMain.java:248) Caused by: org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed. (Caused by org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed.) (Caused by org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed. (Caused by org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed.)) at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:543) at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:235) at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:209) at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:351) at org.springframework.context.support.MessageSourceSupport.(MessageSourceSupport.java:42) at org.springframework.context.support.AbstractMessageSource.(AbstractMessageSource.java:63) at org.springframework.context.support.ResourceBundleMessageSource.(ResourceBundleMessageSource.java:57) at org.acegisecurity.AcegiMessageSource.(AcegiMessageSource.java:34) at org.acegisecurity.AcegiMessageSource.getAccessor(AcegiMessageSource.java:41) at org.acegisecurity.providers.dao.AbstractUserDetailsAuthenticationProvider.(AbstractUserDetailsAuthenticationProvider.java:72) at hudson.security.AbstractPasswordBasedSecurityRealm$Authenticator.(AbstractPasswordBasedSecurityRealm.java:175) at hudson.security.AbstractPasswordBasedSecurityRealm.createSecurityComponents(AbstractPasswordBasedSecurityRealm.java:68) at hudson.security.SecurityRealm.getSecurityComponents(SecurityRealm.java:387) at hudson.security.HudsonFilter.reset(HudsonFilter.java:134) at hudson.model.Hudson.setSecurityRealm(Hudson.java:1999) at hudson.model.Hudson$14.run(Hudson.java:2420) at org.jvnet.hudson.reactor.TaskGraphBuilder$TaskImpl.run(TaskGraphBuilder.java:147) at org.jvnet.hudson.reactor.Reactor.runTask(Reactor.java:260) at hudson.model.Hudson$4.runTask(Hudson.java:708) at org.jvnet.hudson.reactor.Reactor$2.run(Reactor.java:187) at org.jvnet.hudson.reactor.Reactor$Node.run(Reactor.java:94) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:909) at java.lang.Thread.run(Thread.java:619) Caused by: org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed. (Caused by org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed.) at org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFactoryImpl.java:397) at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:529) at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:235) at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:209) at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:351) at org.springframework.context.support.MessageSourceSupport.(MessageSourceSupport.java:42) at org.springframework.context.support.AbstractMessageSource.(AbstractMessageSource.java:67) at org.springframework.context.support.ResourceBundleMessageSource.(ResourceBundleMessageSource.java:59) ... 17 more Caused by: org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed. at org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFactoryImpl.java:385) ... 24 more</em></p>]]></content:encoded><pubDate>Fri, 08 Jul 2011 00:00:00 -1000</pubDate><guid>http://www.rubiconred.com/blog/deploying-hudson-on-weblogic/</guid></item></channel></rss> 