tag:blogger.com,1999:blog-24364800614611529652024-02-08T07:47:29.898-08:00Eclipse Driven Rich Application DevelopmentModel-driven Rich Client/Application Development using Eclipse Platform and Eclipse Modeling (EMF)Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.comBlogger37125tag:blogger.com,1999:blog-2436480061461152965.post-71416430686958723842012-02-25T14:28:00.001-08:002012-02-26T01:00:01.154-08:00XMPP/GTalk Shell Connector for Apache Karaf / Felix GoGo<div class="posterous_autopost">
<div class="p_embed p_image_embed">
<a href="http://getfile5.posterous.com/getfile/files.posterous.com/eclipsedriven/7TgIDheLFerHsa9JdERsEAfY4bSi4wC5RgewiZbPwadvpHHlUCgHchkb7PQv/karaf-felix-gogo-xmpp-shell.png"><img alt="Karaf-felix-gogo-xmpp-shell" height="510" src="http://getfile6.posterous.com/getfile/files.posterous.com/eclipsedriven/TbpZYOfgNVjjVDDap3L0Bsmv8kgRdIzHP8CUSBT4atpwVgzkT7BDe3hhTOxL/karaf-felix-gogo-xmpp-shell.png.scaled.500.jpg" width="500" /></a> </div>
<br />
Connecting <b>Felix GoGo Shell through XMPP / Google Talk</b> ?<br />
<div>
<br />
I did a pretty interesting experiment (and will be useful at least to myself) connecting the Felix GoGo Shell in Apache Karaf/ServiceMix to XMPP/Google Talk Instant Messaging Protocol via Smack library. I spent almost 24 hours just for this. The results is pretty amazing though. :-)</div>
<div>
<br />
I also spent quite some time to make ANSI colors sort of "working". It's not strictly correct, but at least it's not entirely boring.<br />
<br /></div>
<div>
What do you think ?<br />
<br />
<b>UPDATE</b>: The OSGi Blueprint-compatible XMPP Shell connector is available as open source with Apache License 2.0 at <a href="https://github.com/soluvas/com.soluvas.shell.xmpp">https://github.com/soluvas/com.soluvas.shell.xmpp</a></div>
<br /><b>Recommended Resources</b><br />
For more in-depth explanation on OSGi, I highly recommend <a href="http://www.amazon.com/gp/product/1933988916/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=1933988916">OSGi in Action: Creating Modular Applications in Java</a>.</div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com30tag:blogger.com,1999:blog-2436480061461152965.post-86543086873306838242011-10-29T22:02:00.001-07:002011-10-29T22:02:26.677-07:00How to Fix Eclipse BIRT Designer 3.7.1 Crash when Prevewing Reports in Linux 64-bit<div class='posterous_autopost'><div>When <b>previewing a report</b> or<b> running the report</b> in <b>embedded web browser</b>, <b>Eclipse Reporting / BIRT 3.7.1 running in 64-bit Linux crashes</b> with only the following messages:</div> <p /><div>No bp log location saved, using default.</div><div>[000:000] Browser XEmbed support present: 1</div> <div>[000:000] Browser toolkit is Gtk2.</div><div>[000:000] Using Gtk2 toolkit</div> <div>[000:000] Warning(optionsfile.cc:22): Load: Could not open file</div><div>[000:000] No bp log location saved, using default.</div> <div>[000:000] Browser XEmbed support present: 1</div><div>[000:000] Browser toolkit is Gtk2.</div> <div>[000:000] Using Gtk2 toolkit</div><div>[000:273] Warning(optionsfile.cc:22): Load: Could not open file</div> <div>[000:273] No bp log location saved, using default.</div><div>[000:273] Browser XEmbed support present: 1</div> <div>[000:273] Browser toolkit is Gtk2.</div><div>[000:273] Using Gtk2 toolkit</div> <div>Opened debug file '/home/ceefour/tmp/mozdebug'</div><p /><div>It happens with all <b>reports</b>, even with a blank one.</div> <p /><div>To reproduce:</div><div><ol><li>Launch E<b>clipse for Java & Report Developers</b> 3.7.1 64-bit in Ubuntu 11.04 64-bit</li><li>Create a new report</li><li>On the blank report, click Preview tab ...... <b>crash</b></li> </ol></div><div>It is reported as <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=362416">Eclipse Bugzilla <b>Bug</b> 362416</a>.</div><p /><div>Here's a <b>workaround </b>to <b>fix</b> it:</div><p /><div>./eclipse -vmargs -Dorg.eclipse.swt.browser.DefaultType=mozilla</div><p /><div>This <b>solves</b> the <b>crashing problem</b> entirely. So it's not a fix but a temporary <b>workaround </b>until the<b> BIRT developers </b>come up with a fix to this <b>problem</b> permanently.</div> <p /><div><b>Workaround</b> found from: <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=349837#c14">https://bugs.eclipse.org/bugs/show_bug.cgi?id=349837#c14</a></div><p /><div>The (upstream) Eclipse bug was marked fixed as of 3.7.1 however it still requires the manual command-line argument to avoid the crash in Eclipse Reporting/BIRT 3.7.1.</div> <p /><div>My sistem info:</div><p /><blockquote class="gmail_quote" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0.8ex; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"> Linux annafi 2.6.38-12-generic #51+kamal3~mjgbacklight5-Ubuntu SMP Wed Oct 5<br />20:13:06 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux<br />java version "1.6.0_26"<br />Java™ SE Runtime Environment (build 1.6.0_26-b03)<br /> Java HotSpot™ 64-Bit Server VM (build 20.1-b02, mixed mode) </blockquote><p /><div>I first reported this bug in <a href="http://www.birt-exchange.org/org/forum/index.php/topic/22743-birt-designer-crashes-when-prevewingrunning-in-embedded-web-browser">BIRT Exchange forum thread here</a>.</div> <p /><div>Want to create<b> </b>complex <b>business reports</b> easily? Get <b><a href="http://www.amazon.com/gp/product/0321733584/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399373&creativeASIN=0321733584">BIRT: A Field Guide (3rd Edition)</a></b> for an excellent starter resource.</div> <div>To integrate BIRT reports with your application, check out <b><a href="http://www.amazon.com/gp/product/0321772822/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399373&creativeASIN=0321772822">Integrating and Extending BIRT (3rd Edition)</a></b>.</div></div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com69tag:blogger.com,1999:blog-2436480061461152965.post-80811809278398941482011-10-05T09:18:00.001-07:002011-10-05T09:18:40.186-07:00Generation Gap Pattern vs Protected Regions in Xtext MDD<div class='posterous_autopost'><p>During the development of the awesome <b><a href="https://github.com/danieldietrich/xtext-protectedregions">Xtext Protected Regions Support</a></b> created by <b><a href="http://danieldietrich.net/">Daniel Dietrich</a></b>, Daniel asked a really interesting question:<br /> </p><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"><p>Just interested in your opinion / your 2 cents:</p><p>It's best <b>practice to separate generated from non-generated code</b> (when it comes to put it in a <b>versioning system</b>).<br /> Therefor people use the <b>generation gap pattern</b> - the generated class is a base class (or <a href="http://s.th">s.th</a>. similar), the manual written / generated-once classes implement the generated classes.</p><p>When using protected regions, all files (including the generated) have to be checked in. Some say, this ends up in a <b>versioning disaster</b>.</p><p>I prefer the protected regions approach. the base classes of the ggp nearly double the count of classes. this is a technical vehicle which makes no sense for the application. but how could be avoided, that generated classes are checked in?</p><p>my only answer is, that the generated code has to be cut of the files. empty files will disappear, protected regions are preserved. when checking out files, the generator has to be started. before checking in files, the generated code has to be stripped (into a special dir which will be checked in?).</p> <p>this sounds like a technical vehicle, too. would that make sense?</p></blockquote> <p>To which replied: (note that these are my opinion, and I don't claim to be an expert, so feel free to challenge my assumptions and prove me wrong!) :-)<br /></p><div class="content-body wikistyle markdown-format"><p> I have nothing against GAP. It definitely has its valid uses. So for certain problems, GAP is a valid solution, and is even preferable to regions.</p> <p>About versioning, I'm not sure it's a "disaster". Redundant maybe, but dangerous? I don't think so. Avoiding to checkin generated files maybe preferable where the source DSL files are "authoritative" and the target files are "final".</p> <p>Let me illustrate: wsdlimport. The WSDL file is an authoritative source. The generator is stable. Generated Java Proxy Classes are "dumb" target files. You can regenerate those anytime, no need for customization. And the generator is never changed so you can be sure generated files will actually compile and work. No need to checkin the Java targets.</p> <p>Xtext can be used for projects of that kind, and very easy for that.</p> <p>There are also projects that fall in the middle: need some customization. So you do GAP: generate the base class and the customizable subclass. This technique is excellent for some cases, but fails when:</p> <ol><li>You're restricted by the class hierarchy in some way.</li><li>Target language doesn't support subclassing, say: XML. Or my case, this would be yet another custom DSL.</li></ol><p>There are also cases where GAP technically works, but the generated class structure is complex enough that separating the base class from the actual class makes it "unnatural". I'm sure you've seen stuff like that. Oh, let me give a concrete example from one of my prototypes:</p> <div class="CodeRay"> <div class="code"><pre>def StringConcatenation genMainFile(String fileName) { val result = new StringConcatenation() result.append( augmenter.beforeMainFile(fileName) ) result.append( augmenter.aroundMainFile(fileName, [fn1 | { doMainFile(fn1, [ fn2 | augmenter.innerMainFile(fn2, [fn3 | { genImportBlock() genClassBlock() null }]) ]) }]) ) result.append( augmenter.afterMainFile(fileName) ) result }</pre></div> </div> <p>Mind you the above code actually works, but it's <strong>damn ugly</strong>! At least with protected regions there will only be (marker) comments. And with most editors, comments can be folded and not so distracting, so it's much more bearable.</p> <p>Some may say "it's only the base class, nobody will touch it". On the contrary, you'll see those <em>structural methods</em> on your debugging <strong>stack traces</strong>, just "perfect" at the time when you're in deep need of clear & cohesive program structure, but oh... you're buried in nested method calls. :-( AspectJ/AOP/weaving also has this problem, though it's manageable to some extent. They only fall apart when pushed too far, so moderate use is OK.</p> <p>The third class is projects that require extensive customization. The source DSL only comprises of ~20% of the target, providing structure or supporting form, and gives places to fill, where these are filled by the programmer or some other DSL.</p> <p>In my (currently hypothetical, but hopefully realized soon) the Entity->UI generator will not generate JSF/GWT forms/pages directly, but generate to an intermediate UI DSL. (you can argue this is Model-to-Model, not M2T, but hey, textual models are <em>much</em> easier to inspect/hack than something buried in XML!) The UI DSL can then be processed to generate JSF or GWT.</p> <p>I'm not really interested in supporting class inheritance in a UI DSL. In fact the "class" concept itself may not exist in a UI domain, it only matters to OO world. So protected regions is the only option, and thankfully Xtext supports comments by default.</p> <p>With that, it's possible to customize the generated UI DSL files right in the places where they're needed.</p> <p>Another use case that I'm exploring, is two or more generators (which may or may not be sourcing the same model) generating to the same file, but in different regions. And the FSA should automagically merge them.</p> <p>And then, add to the mix that the <strong>generators themselves</strong> are in constant development. That means the same source DSL when processed, may yield target files that are broken, uncompilable, buggy, etc. And the target files are needed because they form the foundation of yet another project, so unless the previous "working" target files can be recovered, the development of the derived project effectively stops.</p> <p>For those uses cases, would I checkin the target files? Of course.</p> <p>With all of the above said, I have nothing against GAP or un-checkin generated files. GAP & un-checkin may be common, but I believe there are classes of problems where they're inappropriate.</p><p> To learn <b>Modeling with Eclipse Modeling Framework (EMF)</b>, I highly recommend the book <a href="http://www.amazon.com/dp/0321331885?tag=springjavaee-20&camp=213381&creative=390973&linkCode=as4&creativeASIN=0321331885&adid=00KT2CC2MX25236YJ0QX&"><b>EMF: Eclipse Modeling Framework</b></a>. </p></div></div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com11tag:blogger.com,1999:blog-2436480061461152965.post-35052313080100962682011-08-03T20:08:00.001-07:002011-08-03T20:08:52.523-07:00How to Fix Ant Build Error: "Could not load a dependent class com/jcraft/jsch/Logger. It is not enough to have Ant's optional JARs"<div class='posterous_autopost'>If you get an <b>error message</b> like this while running a <b>problematic Ant build script file</b> :<p /><div style="margin-left: 40px;">remote.flush:<p />BUILD FAILED<br />/home/ceefour/git/magento-id/build.xml:13: The following error occurred while executing this line:<br /> /home/ceefour/git/magento-id/build.xml:22: Problem: failed to create task or type sshexec<br />Cause: Could not load a dependent class com/jcraft/jsch/Logger<br /> It is not enough to have Ant's optional JARs<br /> you need the JAR files that the optional tasks depend upon.<br /> Ant's optional task dependencies are listed in the manual.<br />Action: Determine what extra JAR files are needed, and place them in one of:<br /> -/home/opt/eclipse_web/plugins/org.apache.ant_1.8.2.v20110505-1300/lib<br /> -/home/ceefour/.ant/lib<br /> -a directory added on the command line with the -lib argument<p />Do not panic, this is a common problem.<br />The commonest cause is a missing JAR.<p />This is not a bug; it is a configuration problem<br /> </div><br />It means your <b>Ant build script file</b> using <b>optional Ant libraries</b> and need to tell <b>Ant</b> where to find them.<p />In my case, it needs <b>jsch.jar</b> aka <b>libjsch-java</b> package in <b>Debian/Ubuntu Linux</b>.<p /> First you need to install <b>ant-optional</b> package for Ant <b>optional libraries support </b>:<br /><span style="font-family: courier new,monospace;">sudo apt-get install ant-optional</span><p />Then the <b>libjsch-java Debian/Ubuntu package</b>:<br /> <span style="font-family: courier new,monospace;">sudo apt-get install libjsch-java</span><p /> Then put it in correct directories so that Ant can find them :<p /><code></code><span style="font-family: courier new,monospace;">sudo ln -s /usr/share/java/jsch.jar /usr/share/ant/lib/</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;">mkdir -vp ~/.ant/lib</span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;">ln -s /usr/share/java/jsch.jar ~/.ant/lib/</span><p /> Ubuntu's <b>Ant</b> look for libraries in <span style="font-family: courier new,monospace;">/usr/share/ant/lib</span> folder, while Eclipse IDE's Ant look for <b>optional Ant libraries</b> in <span style="font-family: courier new,monospace;">$HOME/.ant/lib</span> folder.<p /> Note that for <b>Eclipse IDE</b>, you may need to refresh <b>Eclipse Ant Plug-in's Runtime Classpath</b>, by going to Window > Preferences > Ant > Runtime, and clicking "Restore Defaults".<br />Make sure that the required libraries are now listed under "Global Entries".<p /><b>Ant build system</b> is frequently used in typical <i>Java EE 6 enterprise application development</i>. I highly recommend <a href="http://www.amazon.com/gp/product/0137081855/ref=as_li_ss_tl?ie=UTF8&tag=springjavaee-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=0137081855" target="_blank"><b>The Java EE 6 Tutorial: Basic Concepts (4th Edition)</b></a> for a practical guide to the <b>Java EE 6 technology</b>.</div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com10tag:blogger.com,1999:blog-2436480061461152965.post-15241782462027419462011-07-16T13:19:00.001-07:002011-07-16T13:33:34.542-07:00How to Create A Software Site from Plug-in Bundles for Eclipse Tycho Maven<div class='posterous_autopost'>As of <b>Eclipse Tycho Maven plugin</b> 0.12.0, it can only use "InstallationUnit" type (aka <b>Software Site sources</b>) from a <b>target platform definition file</b>. So you cannot use Features, Installation, or Directory in your target definition file.<p /> If all the <b>plug-ins</b> your <b>Eclipse Platform application</b> requires are already in a <b>Software Site/Update Site</b>, you're lucky. But if not, don't despair. Gather all the <b>plug-ins</b> in a directory and...<br /> (note: you may need to OSGify some files beforehand using <a href="http://www.aqute.biz/Bnd/Bnd"><b>Bnd</b></a> or <b>Eclipse PDE</b>)<p /><b>The lighting way:</b><br />If it already contains feature(s) that list the available plug-ins, just create a .target definition file and in Eclipse right click the .target and export it. Done!<br /> If not, continue below...<p /><b>The quick way:</b><br /><ol><li>Create a Feature project, initial plug-in list is empty.</li><li>Create a <b>.target platform definition file in PDE</b>, inside the <b>Feature Project</b>. Add the bundles. Activate it.</li> <li>Open the feature.xml, Plug-ins tab. Select all plug-ins (use "*.*" filter)</li><li>Right click the Feature project, Export... as a deployable feature :-) There, you get your <b>Software Site</b> neatly prepared and ready for some serious <b>Tycho</b> action! :D<br /> </li></ol><br /><b>The hard way:</b><br /><a href="https://docs.sonatype.org/display/TYCHO/How+to+make+existing+OSGi+bundles+consumable+by+Tycho">https://docs.sonatype.org/display/TYCHO/How+to+make+existing+OSGi+bundles+consumable+by+Tycho</a><br /> ;-)<br /><p> To learn more about <b>Eclipse platform programming</b>, I highly recommend <a href="http://www.amazon.com/gp/product/0321603788/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=0321603788"><b>Eclipse Rich Client Platform (2nd Edition)</b></a>.<br /> </p></div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com9tag:blogger.com,1999:blog-2436480061461152965.post-33352078023167943902011-07-16T10:37:00.001-07:002011-07-16T13:35:10.719-07:00Configuring Eclipse Tycho Maven Plugin to use Target Definition File and Publishing Target Platform Bundles to Update Site<p>I answered an <a href="http://stackoverflow.com/questions/6269703/resolving-dependencies-with-tycho/6718818#6718818"><strong>Eclipse Tycho Maven Plugin</strong> question on StackOverfow</a> and I think I should repeat it here (for my own purpose, hehe.. I am forgetful :-)</p><p>I should expand this to have better coverage of Tycho workflow but usually I'll get lazy so here it is pretty much verbatim.</p><p>Create a <strong>Target Definition file</strong> (.target) and put it inside a <strong>Maven project,</strong> see here for example target: <a href="https://github.com/eclipsesource/com.eclipsesource.tycho.aspectj.demo/blob/master/platform/indigo.target">https://github.com/eclipsesource/com.eclipsesource.tycho.aspectj.demo/blob/master/platform/indigo.target</a></p><p>You need to attach the .target file to the artifact, using the build helper:</p><pre><code><plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.3</version>
<executions>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>indigo.target</file>
<type>target</type>
<classifier>indigo</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin></code></pre><p>(from <a href="https://github.com/eclipsesource/com.eclipsesource.tycho.aspectj.demo/blob/master/platform/pom.xml">https://github.com/eclipsesource/com.eclipsesource.tycho.aspectj.demo/blob/master/platform/pom.xml</a> )</p><p>Then, in the parent POM or the plug-in projects that use that target definition file, you need to configure the "target" of target-platform-configuration Maven plugin, for example:</p><pre><code><plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>${tycho-version}</version>
<configuration>
<resolver>p2</resolver>
<ignoreTychoRepositories>true</ignoreTychoRepositories>
<target>
<artifact>
<groupId>com.eclipsesource.sandbox.weaving.demo</groupId>
<artifactId>com.eclipsesource.sandbox.weaving.demo.platform</artifactId>
<version>0.1.0-SNAPSHOT</version>
<classifier>indigo</classifier>
</artifact>
</target>
<environments>
<environment>
<os>${build.os}</os>
<ws>${build.ws}</ws>
<arch>${build.arch}</arch>
</environment>
</environments>
</configuration>
</plugin>
</code></pre><p>(taken from <a href="https://github.com/eclipsesource/com.eclipsesource.tycho.aspectj.demo/blob/master/releng/pom.xml">https://github.com/eclipsesource/com.eclipsesource.tycho.aspectj.demo/blob/master/releng/pom.xml</a> )</p><p>Then your project(s) should build very nicely using Tycho. :-) If your .target references remote p2 repositories and not already in the p2 bundle pool, the necessary artifacts will be downloaded automatically.</p><p>Good luck!</p><p><em>Known Issue:</em></p><pre><code>[WARNING] Target location type: Profile is not supported </code></pre><p>As of Tycho 0.12.0, It means the "Eclipse Installation" target source type <a href="http://software.2206966.n2.nabble.com/target-file-artifacts-tp4879861p4880268.html">cannot be used</a> with Tycho (yet?), along with "Directory" and "Features".</p><p><em>Solution:</em> Use the "Update Site" target source.</p><p>If you don't have yet an update site, here's to <strong>generate an update site</strong> from an Eclipse installation (or from any folder containing bundles, for that matter):</p><pre><code>/opt/eclipse_rcp/eclipse -consolelog -nosplash -verbose \
-application org.eclipse.equinox.p2.publisher.FeaturesAndBundlesPublisher \
-metadataRepository file:/home/ceefour/p2/bonita/ \
-artifactRepository file:/home/ceefour/p2/bonita/ \
-source /home/ceefour/BOS-5.5.1/studio/ \
-publishArtifacts</code></pre><p>Note:</p><ul><li>change /opt/eclipse_rcp to your own Eclipse SDK installation</li>
<li>metadataRepository and artifactRepository is the folder where the new update site will be created</li>
<li>source is --you guessed it-- the folder/installation containing the original bundles</li>
</ul><p>To learn more about <strong>Eclipse platform programming</strong>, I highly recommend <a href="http://www.amazon.com/gp/product/0321603788/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=0321603788"><strong>Eclipse Rich Client Platform (2nd Edition)</strong></a></p>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com11tag:blogger.com,1999:blog-2436480061461152965.post-27740081505648413812011-06-23T11:23:00.001-07:002011-07-16T13:33:34.601-07:00Hot Deploy & F5/Refresh-Driven Web Application Development Are Ancient Compared to Eclipse RAP!<div class='posterous_autopost'><div class='p_embed p_image_embed'> <a href="http://posterous.com/getfile/files.posterous.com/eclipsedriven/APkTFskE1oEEBOfvO8Xzw4NvXxaQCUKMDHz6daB49KnYfJoaRWeilg19aaaF/eclipse-RAP-riena-hot-deploy-w.png"><img alt="Eclipse-rap-riena-hot-deploy-w" height="400" src="http://posterous.com/getfile/files.posterous.com/eclipsedriven/aAv7QYMlybqjgbDq296COxZiG7EKeLyITvDWTUpqqNPECeILDXg7QCVbZf5A/eclipse-RAP-riena-hot-deploy-w.png.scaled.500.jpg" width="500" /></a> </div> <p>Most <b>web applications developer</b> would be very familiar with <b>F5/Refresh-Driven</b> development. You know, make a little change and press <b>F5</b> in the <b>web browser</b> and you can view the updated <b>page</b>. This was the good old PHP days.<p /> <b>Java EE web application developers</b> <i>used to</i> not that lucky. While some changes like <b>JSP pages, JSF facelets,</b> etc. take effect immediately (and thus, "<b>refresh-driven"),</b> in some cases they have to "<b>redeploy".</b> This usually means developer changes a <b>Java class backing bean</b> or an "important" file like web.xml. Redeploy means undeploying the web app from the Java EE container or application server, then redeploying the web app or WAR again. IDEs like the excellent <b>Eclipse IDE Indigo</b> (yay!) automate this but a redeploy can take anything between a few seconds to... <b>minutes!</b> I think typical web apps would deploy in about 20-30 seconds so that is <b>painful</b>.<p /> <a href="http://www.zeroturnaround.com/jrebel/">JRebel from ZeroTurnaround</a> (which just won the <a href="http://blogs.oracle.com/java/entry/jax_innovation_awards_2011">Most Innovative Java Technology in JAX Innovation Awards 2011</a>, congratulations guys!) really helps here, by allowing most common changes to not cause a full redeploy, but just... <b>hot deploy</b>! It's like JSP/JSF but for the rest of Java app, Spring beans, etc. JRebel is a commercial plug-in but is definitely worth it.<p /> But I'd argue <a href="http://www.eclipse.org/rap/"><b>Eclipse RAP</b></a> should won the Most Innovative Java Technology title... Here's why!<p />(<b>Eclipse Rich Ajax Platform/RAP</b> is framework to <b>develop AJAX-powered web applications</b> easily based on <i>Eclipse RCP programming</i> model, see <a href="http://www.amazon.com/gp/product/0321603788/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=0321603788"><b>Eclipse Rich Client Platform (2nd Edition)</b></a> for more information.)<p /> I've just noticed something today. I know I should've noticed this long ago, but when you launch an <b>Eclipse RAP rich internet application</b> from <b>Eclipse IDE</b> using <b>Debug (F11 key)</b>, <b>ALL</b> your code changes take effect <b>immediately</b>! No exceptions!<p /> <b>No need to even refresh the web browser!</b><p />Change the code for a menu item or a view or an action, save the .java file, go to the browser and click it... <b>your new code is there!</b><p /></p><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> "No refresh? But how can it be!"<br /></blockquote><br />Part of the magic is due to <b>OSGi Dynamic Module System</b>, that is brilliantly integrated as part of the <b>Eclipse platform</b> itself.<p />So when you save a Java file, Eclipse IDE will compile your class (and only your class, due to incremental builder feature, so it's <b>very</b> <i>fas</i>t!), then update the OSGi bundle or Eclipse plug-in in the Eclipse RAP application. And <b>only</b> your bundle/plug-in is updated/refreshed in the application, so again, even if it's a different process it's also <b>very</b> <i>fast</i>. The whole process typically takes <i>less than a second</i> on a typical developer workstation, even on moderately complex apps! Most of the time the process is already done before you have a chance to hit Alt+Tab. ;-)<p /> The other part of the magic is even though Eclipse RAP application comes with <b>full AJAX features</b> by default (it's not an option, it's actually a requirement), most of the business logic is server-side Java. So even if the most of the render JavaScript/HTML presentation layer in the web browser, when you perform an action for example by clicking a menu item, this will trigger a server request...<p /> <b>Which means your updated code! Yay! :)</b><p />Also important feature of Eclipse RAP is that for <b>background/long-running jobs</b> or <b>"server push"</b> operations, Eclipse RAP supports several approaches: <a href="http://www.eclipse.org/forums/index.php/m/638245/?srch=push#msg_638245">Eclipse Jobs API or session-long UICallback</a>.<p /> This is pretty much automatic if you're already an Eclipse RCP programmer utilizing Jobs API. There's no need to do workarounds and hacks like traditional AJAX web development or learn yet another new API (and programming model) just for server push.<p /> To learn more about <b>Eclipse platform programming</b>, I highly recommend <a href="http://www.amazon.com/gp/product/0321603788/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=0321603788"><b>Eclipse Rich Client Platform (2nd Edition)</b></a>. It's really good for learning Eclipse RCP/RAP development, most of the things that apply to RCP also applies to RAP. In fact, you can <i>single-source</i> an application to two target platforms (RCP for desktop, RAP for web) simultaneously. :-)</div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com11tag:blogger.com,1999:blog-2436480061461152965.post-1821640540321818452011-06-23T05:03:00.001-07:002011-07-16T13:33:34.573-07:00Eclipse Virgo IDE Tooling 1.0.0.M01 Released<div class='posterous_autopost'> <p>Martin Lippert from SpringSource <a href="http://www.springsource.org/node/3161">announced</a>:<br /></p><blockquote class="gmail_quote" style="margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 204); padding-left: 1ex;"> <p>I am happy to announce that we <b>released</b> the first <b>milestone build</b> of the <b>Virgo IDE tooling</b>. For installation instructions, please take a look at the this wiki page:<br /> <a href="http://wiki.eclipse.org/Virgo/Tooling">http://wiki.eclipse.org/Virgo/Tooling</a></p><p>This is the first milestone build after the code contribution from SpringSource and there aren't that much changes with regards to features or bugs in there compared to the latest <b>dm server tooling </b>releases. But this will change from now on... :-)</p> <p>Enjoy!</p></blockquote> <p><a href="http://eclipse.org/virgo/"><b>Eclipse Virgo Web Server / Kernel</b></a> is an <b>Enterprise OSGi web server</b>, capable of serving <b>dynamic OSGi web applications</b> via <b>OSGi Web Bundles (WABs)</b>. It works with <a href="http://www.eclipse.org/gemini/"><b>Eclipse Gemini</b></a> project to provide <b>Java EE 6</b> capabilities to <b>server-side OSGi applications</b>.</p> <p>For more in-depth explanation on using <b>OSGi</b> for <b>enterprise applications</b>, I highly recommend <a href="http://www.amazon.com/gp/product/1933988916/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=1933988916"><b>OSGi in Action: Creating Modular Applications in Java</b></a>.</p> </div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com7tag:blogger.com,1999:blog-2436480061461152965.post-76403983151139321222011-06-16T07:43:00.001-07:002011-07-16T13:33:34.618-07:00How to Create Felix GoGo Commands with OSGi Blueprint in Eclipse Virgo Kernel / Web Server<div class='posterous_autopost'><b><a href="http://felix.apache.org/site/apache-felix-gogo.html">Felix GoGo</a> aka <a href="http://felix.apache.org/site/rfc-147-overview.html">OSGi RFC-147</a></b> is a <b>standard-based </b>way to <b>implement modular CLI (command-line interface / console / shell) commands</b> in <b>Java</b>.<p /> <a href="http://karaf.apache.org/"><b>Apache Karaf</b></a> supports <a href="http://karaf.apache.org/manual/2.1.99-SNAPSHOT/users-guide/using-console.html">Felix GoGo console out-of-the box</a>, along with <a href="http://karaf.apache.org/manual/2.1.99-SNAPSHOT/users-guide/remote-console.html">MINA SSH server integration</a>. This blog post's focus would be <a href="http://eclipse.org/virgo/"><b>Eclipse Virgo Web Server (Eclipse Virgo Kernel)</b></a>, and how we can leverage the same CLI support, along with SSH, in Eclipse Virgo. Now this article would be very short if I were using Apache Karaf, because it already had the right ingredients built-in and well-integrated: Felix GoGo, SSH server, Apache Aries as OSGi Blueprint implementation. I hope these features will also be in Eclipse Virgo soon (here's hoping)! :-)<p /> Modular Java programming uses OSGi technology, if you aren't yet familiar with OSGi I recommend reading <a href="http://www.amazon.com/gp/product/1933988916/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=1933988916"><b>OSGi in Action: Creating Modular Applications in Java</b></a>.<p /> <b>Enhancing Eclipse Virgo with Felix GoGo and SSH</b><p />I hope Virgo will soon have built-in support for Felix GoGo and SSH server, but for now, we must do this ourselves.<p /><b>Hristo Iliev</b> from SAP has written an excellent tutorial on <a href="http://hsiliev.blogspot.com/2011/06/apache-gogo-and-ssh-support-in-eclipse.html">how to enable Felix GoGo and SSH support in Eclipse Virgo</a>, so I'll pretty much just paste it here :-)<br /> <h3 class="western">Step 1: Download Gogo shell</h3> <p class="western">To run the Gogo you will need three bundles from <a href="http://felix.apache.org/site/downloads.cgi"><b>Apache Felix downloads</b></a>:</p> <ul><li><p class="western" style="margin-bottom: 0cm;"><a href="http://apache.igor.onlinedirect.bg//felix/org.apache.felix.gogo.runtime-0.8.0.jar"><b>Gogo Runtime</b></a></p> </li><li><p class="western" style="margin-bottom: 0cm;"><a href="http://apache.igor.onlinedirect.bg//felix/org.apache.felix.gogo.shell-0.8.0.jar"><b>Gogo Shell</b></a></p> </li><li><p class="western"><a href="http://apache.igor.onlinedirect.bg//felix/org.apache.felix.gogo.command-0.8.0.jar"><b>Gogo Command</b></a></p> </li></ul> <h3 class="western">Step 2: Equinox and RFC-147 </h3> <p class="western">To enable RFC-147 integration in Equinox you will need some console supportability features that are provided by the <a href="http://eclipse.org/equinox/incubator/console/index.php"><b>Console supportability</b></a> project in Equinox Incubator.</p> <p class="western">20110611 build is fine for me. Download the <a href="http://www.eclipse.org/downloads/download.php?file=/equinox/drops/N20110611-2000/equinox-incubator-feature-N20110611-2000.zip"><b>Incubator build</b></a> that includes the bundle we'll need. Extract only the supportability JAR (<b>org.eclipse.equinox.console.supportability</b>) from plugins folder.</p> <h3 class="western">Step 3: Enabling SSH Support</h3> <p class="western">First download Apache Mina's <a href="http://www.apache.org/dyn/closer.cgi/mina/sshd/0.5.0/apache-sshd-0.5.0.zip"><b>binary distribution</b></a> for SSHD, and then from the lib directory of the archive extract these two bundles:</p> <ul><li><p class="western" style="margin-bottom: 0cm;"><b>sshd-core</b> </p> </li><li><p class="western"><b>mina-core</b> </p> </li></ul> <h3 class="western">Step 4: Setting up Virgo</h3> <ul><li><p class="western" style="margin-bottom: 0cm;">Copy the console supportability bundle (<b>org.eclipse.equinox.console.supportability) </b>in <b>lib </b>directory</p> </li><li><p class="western" style="margin-bottom: 0cm;">Place the rest of the bundles (3xGogo, SSHD and Mina) in <b>lib/kernel</b> directory </p> </li><li><p class="western">Edit <b>config/org.eclipse.virgo.kernel.userregion.properties</b> file and add the bundles to base bundles list: </p> </li></ul> <blockquote style="border: none; padding: 0cm;">baseBundles = \<br /> file:lib/kernel/org.eclipse.virgo.kernel.userregion-3.0.0.M05.jar@start,\<br /> file:lib/kernel/org.eclipse.virgo.kernel.osgicommand-3.0.0.M05.jar@start,\<br /> <b>file:lib/kernel/mina-core-2.0.1.jar@start,\<br /> file:lib/kernel/org.apache.felix.gogo.command-0.8.0.jar@start,\<br /> file:lib/kernel/org.apache.felix.gogo.runtime-0.8.0.jar@start,\<br /> file:lib/kernel/org.apache.felix.gogo.shell-0.8.0.jar@start,\<br /> file:lib/org.eclipse.equinox.console.supportability_1.0.0.N20110608-2000.jar@start,\<br /> file:lib/kernel/sshd-core-0.5.0.jar@start</b> </blockquote> <ul><li><p class="western" style="border: none; padding: 0cm;">In <b>lib/org.eclipse.virgo.kernel.launch.properties</b> remove the old Virgo shell by deleting the line org.eclipse.virgo.osgi.console.telnet.hook.TelnetHookConfigurator. The hooks entry should look like this afterwards:<p />osgi.hook.configurators.include=\<br /> org.eclipse.virgo.osgi.extensions.equinox.hooks.ExtensionsHookConfigurator </p> </li></ul> <ul><li><p class="western" style="border: none; padding: 0cm;">Delete (or move outside lib) the bundle <b>lib/org.eclipse.virgo.osgi.console-3.0.0.M05.jar</b> </p> </li></ul> <h3 class="western">Step 5: Configuring Virgo</h3> <ul><li><p class="western" style="border: none; padding: 0cm;">Comment or delete <b>osgi.console</b> property in <b>lib/org.eclipse.virgo.kernel.launch.properties</b>: (should already be commented)</p> </li></ul> <blockquote style="border: none; padding: 0cm;">#osgi.console=2401</blockquote> <ul><li><p class="western" style="border: none; padding: 0cm;">Add the following property entries to <b>lib/org.eclipse.virgo.kernel.launch.properties</b>: </p> </li></ul> <blockquote style="border: none; padding: 0cm;">osgi.console.ssh=2422<br />osgi.console.enable.builtin=false<br />osgi.console.ssh.useDefaultSecureStorage=true</blockquote> <ul><li><p class="western" style="border: none; padding: 0cm;">Add in <b>config/org.eclipse.virgo.kernel.authentication.config</b> file JAAS configuration for the SSH: </p> </li></ul> <blockquote style="margin-bottom: 0cm; border: none; padding: 0cm;">equinox_console { </blockquote> <blockquote style="margin-bottom: 0cm; border: none; padding: 0cm;"> org.eclipse.equinox.console.jaas.SecureStorageLoginModule REQUIRED; </blockquote> <blockquote style="border: none; padding: 0cm;">};</blockquote> <ul><li><p class="western" style="border: none; padding: 0cm;">Edit the <b>bin/dmk.bat</b> file to add <b>org.eclipse.equinox.console.jaas.file</b> and <b>ssh.server.keystore</b> VM system properties. After the changes the file should look as follows: </p> </li></ul> <blockquote style="border: none; padding: 0cm;">set KERNEL_JAVA_PARMS=%KERNEL_JAVA_PARMS% -Dorg.eclipse.virgo.kernel.authentication.file="%CONFIG_DIR%\org.eclipse.virgo.kernel.users.properties" <br /><b>set KERNEL_JAVA_PARMS=%KERNEL_JAVA_PARMS% -Dorg.eclipse.equinox.console.jaas.file="%CONFIG_DIR%/store"<br />set KERNEL_JAVA_PARMS=%KERNEL_JAVA_PARMS% -Dssh.server.keystore="%CONFIG_DIR%/hostkey.ser"</b>set KERNEL_JAVA_PARMS=%KERNEL_JAVA_PARMS% -Djava.io.tmpdir="%TMP_DIR%" </blockquote> <ul><li><p class="western" style="border: none; padding: 0cm;">Edit <b>bin/dmk.sh </b><span style="font-weight: normal;">as follows</span>: (following is in diff format)</p> </li></ul> <div class="CodeRay"> <div class="code"><pre>diff --git a/bin/dmk.sh b/bin/dmk.sh index 5f8112b..4122d55 100755 --- a/bin/dmk.sh +++ b/bin/dmk.sh @@ -174,6 +174,8 @@ then -XX:HeapDumpPath=$KERNEL_HOME/serviceability/heap_dump.hprof \ -Djava.security.auth.login.config=$CONFIG_DIR/org.eclipse.virgo.ker -Dorg.eclipse.virgo.kernel.authentication.file=$CONFIG_DIR/org.ecli + -Dorg.eclipse.equinox.console.jaas.file=$CONFIG_DIR/store \ + -Dssh.server.keystore=$CONFIG_DIR/hostkey.ser \ -Djava.io.tmpdir=$TMP_DIR \ -Dorg.eclipse.virgo.kernel.home=$KERNEL_HOME \ -classpath $CLASSPATH \</pre></div> </div> <h3 class="western">Step 6: Connecting to Virgo</h3> <p class="western">Fire your favourite SSH client on the specified port (2422 for instance).</p><p class="western"><span style="font-family: courier new,monospace;">$ ssh -p2422 equinox@localhost</span><br /></p> <ul><li><p class="western" style="margin-bottom: 0cm; border: none; padding: 0cm;"> Login with the default user and password (equinox/equinox) </p> </li><li><p class="western" style="border: none; padding: 0cm;">Create a new user and password (roles are optional)</p> </li></ul> <h3 class="western">Step 7: Fun with Gogo</h3> <p class="western">Check the features provided by GoGo and Equinox Console Supportability<b>:</b></p> <ul><li><p class="western" style="margin-bottom: 0cm; border: none; padding: 0cm;"> Tab Completion (...doesn't yet work for me... any suggestions?) / Line editing </p> </li><li><p class="western" style="margin-bottom: 0cm; border: none; padding: 0cm;"> Built-in commands like lb, ls, cat to name a few </p> </li><li><p class="western" style="margin-bottom: 0cm; border: none; padding: 0cm;"> Create some scripts </p> </li><li><p class="western" style="border: none; padding: 0cm;">Create some commands using <a href="http://felix.apache.org/site/rfc-147-overview.html"><b>RFC-147</b></a> </p> </li></ul> <h3 class="western">Step 8: Disconnecting</h3> <p class="western" style="margin-bottom: 0cm; border: none; padding: 0cm;"> <b>Do not use "exit"</b> since this will exit Virgo/OSGi framework. To end the session you'll have to close the console/SSH window. There is no command to close the session currently.</p> <br /><b>Create the Felix GoGo Command Implementation Project</b><p />You'll need <b>Eclipse IDE for RCP Development</b> distribution or PDE plug-in for this. Create a new Eclipse PDE Plug-in project with the following class:<p /> <span style="font-family: courier new,monospace;">package gogosimple1;</span><p /><span style="font-family: courier new,monospace;">public class SimpleCommand {</span><p /> <span style="font-family: courier new,monospace;"> public void walk() {</span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> System.out.println("walking");</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> }</span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> </span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> public void talk() {</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> System.out.println("talking");</span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> }</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;">}</span><p />Simple isn't it? And no external dependency on Felix GoGo API whatsoever (at this point).<p />I will use OSGi Blueprint to publish that class as a service and register a command scope to Felix GoGo. Create <b>src/OSGI-INF/blueprint/gogosimple1.xml</b> file with the following:<p /> <span style="font-family: courier new,monospace;"><blueprint xmlns="<a href="http://www.osgi.org/xmlns/blueprint/v1.0.0">http://www.osgi.org/xmlns/blueprint/v1.0.0</a>"</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> xmlns:xsi="<a href="http://www.w3.org/2001/XMLSchema-instance">http://www.w3.org/2001/XMLSchema-instance</a>"</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> xsi:schemaLocation="<a href="http://www.osgi.org/xmlns/blueprint/v1.0.0">http://www.osgi.org/xmlns/blueprint/v1.0.0</a> <a href="http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd</a>"></span><p /> <span style="font-family: courier new,monospace;"> <bean id="simpleCommand" class="gogosimple1.SimpleCommand" /></span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> <service ref="simpleCommand" auto-export="all-classes"></span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> <service-properties></span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> <entry key="osgi.command.scope"><value>simple</value></entry></span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> <entry key="osgi.command.function"></span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> <array value-type="java.lang.String"></span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> <value>walk</value></span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> <value>talk</value></span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> </array></span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> </entry></span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> </service-properties></span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> </service></span><p /><span style="font-family: courier new,monospace;"></blueprint></span><p /> For your convenience, the <a href="https://github.com/ceefour/gogosimple1">gogosimple1 example project is available on GitHub</a>.<p /><b>Installing Eclipse Gemini Blueprint on Virgo Web Server<br /></b><br /> Virgo 3.0 milestones shipped with (and <a href="http://wiki.eclipse.org/Virgo/Future#Virgo_Plan">unfortunately, will still ship</a>) the ancient Spring-DM 1.2.1, so to enable OSGi Blueprint we need to install a OSGi Blueprint implementation by ourselves.<p /> Fortunately Eclipse Gemini Blueprint seems to work just fine.<br /> <p class="western"><a href="http://www.eclipse.org/gemini/blueprint/download/">Download Eclipse Gemini Blueprint</a>.</p> <p class="western">Copy the following files to Virgo's <b>pickup/</b> folder: (in order)<br /></p> <ol><li><p class="western" style="margin-bottom: 0cm; line-height: 100%;"> gemini-blueprint-core-1.0.0.M1.jar </p> </li><li><p class="western" style="margin-bottom: 0cm; line-height: 100%;"> gemini-blueprint-extender-1.0.0.M1.jar </p> </li><li><p class="western" style="margin-bottom: 0cm; line-height: 100%;"> gemini-blueprint-io-1.0.0.M1.jar </p> </li></ol> <b>Alternative: Apache Aries Blueprint</b><p />Actually, I originally used <a href="http://aries.apache.org/modules/blueprint.html">Apache Aries Blueprint</a> implementation for this experiment.<p /> <a href="http://aries.apache.org/downloads/currentrelease.html">Download Apache Aries Blueprint</a>.<br /> <p class="western">Put these bundles in <b>pickup/</b> folder: (in order)</p> <ol><li><p class="western" style="margin-bottom: 0cm; line-height: 100%;"> com.springsource.org.objectweb.asm-3.1.0.jar </p> </li><li><p class="western" style="margin-bottom: 0cm; line-height: 100%;"> com.springsource.org.objectweb.asm.commons-3.1.0.jar </p> </li><li><p class="western" style="margin-bottom: 0cm; line-height: 100%;"> com.springsource.org.objectweb.asm.tree-3.1.0.jar </p> </li><li><p class="western" style="margin-bottom: 0cm; line-height: 100%;"> org.apache.aries.util-0.3.jar </p> </li><li><p class="western" style="margin-bottom: 0cm; line-height: 100%;"> org.apache.aries.proxy-0.3.jar </p> </li><li><p class="western" style="margin-bottom: 0cm; line-height: 100%;"> org.apache.aries.blueprint-0.3.jar </p> </li></ol><b>Trying the Commands</b><p /><b>Build</b> the simplegogo1 project (right click the project > Export... > Deployable plug-ins and fragments), and copy the resulting bundle JAR to Virgo's pickup/ folder.<br /> If you don't feel like compiling, you can <a href="https://github.com/ceefour/gogosimple1/tree/master/dist/plugins">download prebuilt gogosimple1 JAR here</a> (hint: it's very small, just 12 KB).<br />Virgo should install it, and Blueprint should register SimpleCommand bean as <b>OSGi service</b>:<p /> <span style="font-family: courier new,monospace;">g! <b>lb | grep -i gogo</b></span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> 44|Active | 1|Apache Felix Gogo Command (0.8.0)</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> 45|Active | 1|Apache Felix Gogo Runtime (0.8.0)</span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> 46|Active | 1|Apache Felix Gogo Shell (0.8.0)</span><br style="font-family: courier new,monospace;" /> <b><span style="font-family: courier new,monospace;"> 128|Active | 1|Gogosimple1 (1.0.0.qualifier)</span></b><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;">true<br /> </span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;">g! <b>b 128</b></span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;">gogosimple1_1.0.0.qualifier [128]</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> Id=128, Status=ACTIVE Data Root=/home/ceefour/project/Soluvas/web-as/virgo-tomcat-server-3.0.0.M05/work/osgi/configuration/org.eclipse.osgi/bundles/128/data</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> "Registered Services"</span><br style="font-family: courier new,monospace;" /><b><span style="font-family: courier new,monospace;"> {gogosimple1.SimpleCommand}={osgi.command.scope=simple, osgi.command.function=[walk,talk], <a href="http://org.eclipse.gemini.blueprint.bean.name">org.eclipse.gemini.blueprint.bean.name</a>=simpleCommand, osgi.service.blueprint.compname=simpleCommand, Bundle-SymbolicName=gogosimple1, Bundle-Version=1.0.0.qualifier, <a href="http://service.id">service.id</a>=269}</span><br style="font-family: courier new,monospace;" /> </b><span style="font-family: courier new,monospace;"> {org.osgi.service.blueprint.container.BlueprintContainer}={Bundle-SymbolicName=gogosimple1, Bundle-Version=1.0.0.qualifier, osgi.blueprint.container.version=1.0.0.qualifier, osgi.blueprint.container.symbolicname=gogosimple1, <a href="http://service.id">service.id</a>=270}</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> {org.eclipse.gemini.blueprint.context.DelegatedExecutionOsgiBundleApplicationContext, org.eclipse.gemini.blueprint.context.ConfigurableOsgiBundleApplicationContext, org.springframework.context.ConfigurableApplicationContext, org.springframework.context.ApplicationContext, org.springframework.context.Lifecycle, org.springframework.beans.factory.ListableBeanFactory, org.springframework.beans.factory.HierarchicalBeanFactory, org.springframework.context.MessageSource, org.springframework.context.ApplicationEventPublisher, org.springframework.core.io.support.ResourcePatternResolver, org.springframework.beans.factory.BeanFactory, org.springframework.core.io.ResourceLoader, org.springframework.beans.factory.DisposableBean}={<a href="http://org.springframework.context.service.name">org.springframework.context.service.name</a>=gogosimple1, Bundle-SymbolicName=gogosimple1, Bundle-Version=1.0.0.qualifier, <a href="http://service.id">service.id</a>=271}</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> Services in use:</span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> {org.xml.sax.EntityResolver}={<a href="http://spring.osgi.core.bundle.id">spring.osgi.core.bundle.id</a>=130, spring.osgi.core.bundle.timestamp=1308232521056, <a href="http://service.id">service.id</a>=249}</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> {org.springframework.beans.factory.xml.NamespaceHandlerResolver}={<a href="http://spring.osgi.core.bundle.id">spring.osgi.core.bundle.id</a>=130, spring.osgi.core.bundle.timestamp=1308232521056, <a href="http://service.id">service.id</a>=248}</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> {org.osgi.service.packageadmin.PackageAdmin}={service.ranking=2147483647, service.pid=0.org.eclipse.osgi.framework.internal.core.PackageAdminImpl, service.vendor=Eclipse.org - Equinox, <a href="http://service.id">service.id</a>=1}</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> No exported packages</span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> Imported packages</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> org.osgi.framework; version="1.6.0"<org.eclipse.osgi_3.7.0.v20110224 [0]></span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> No fragment bundles</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> Named class space</span><br style="font-family: courier new,monospace;" /><span style="font-family: courier new,monospace;"> gogosimple1; bundle-version="1.0.0.qualifier"[provided]</span><br style="font-family: courier new,monospace;" /> <span style="font-family: courier new,monospace;"> No required bundles</span><p />You can try GoGo commands you just installed:<p />g! <b>simple:walk</b><br />walking<br />g! <b>simple:talk</b><br /> talking<p />"help" command is also available to list all available commands:<p />g! <b>help</b><br />equinox:addRoles<br />equinox:addUser<br />equinox:b<br />equinox:bundle<br />equinox:bundles<br />equinox:classSpaces<br /> equinox:close<br />equinox:deleteUser<br />equinox:exec<br />equinox:exit<br />equinox:fork<br />equinox:gc<br />equinox:getPackages<br />equinox:getprop<br />equinox:h<br />equinox:headers<br />equinox:help<br />equinox:i<br />equinox:init<br />equinox:install<br /> equinox:listUsers<br />equinox:p<br />equinox:packages<br />equinox:pr<br />equinox:profilelog<br />equinox:props<br />equinox:r<br />equinox:refresh<br />equinox:removeRoles<br />equinox:requiredBundles<br />equinox:resetPassword<br />equinox:s<br /> equinox:se<br />equinox:services<br />equinox:setPassword<br />equinox:setbsl<br />equinox:setfwsl<br />equinox:setibsl<br />equinox:setp<br />equinox:setprop<br />equinox:shutdown<br />equinox:sl<br />equinox:ss<br />equinox:ssh<br />equinox:sta<br /> equinox:start<br />equinox:status<br />equinox:sto<br />equinox:stop<br />equinox:t<br />equinox:telnet<br />equinox:threads<br />equinox:un<br />equinox:uninstall<br />equinox:up<br />equinox:update<br />felix:bundlelevel<br />felix:cd<br />felix:frameworklevel<br /> felix:headers<br />felix:help<br />felix:inspect<br />felix:install<br />felix:lb<br />felix:log<br />felix:ls<br />felix:refresh<br />felix:resolve<br />felix:start<br />felix:stop<br />felix:uninstall<br />felix:update<br />felix:which<br />gogo:cat<br /> gogo:each<br />gogo:echo<br />gogo:format<br />gogo:getopt<br />gogo:gosh<br />gogo:grep<br />gogo:not<br />gogo:set<br />gogo:sh<br />gogo:source<br />gogo:tac<br />gogo:telnetd<br />gogo:type<br />gogo:until<br />obr:deploy<br />obr:info<br />obr:javadoc<br /> obr:list<br />obr:repos<br />obr:source<br />simple:talk<br />simple:walk<p /><b>Using Virgo Tools Eclipse IDE Plugin to Launch Eclipse Virgo Web Server</b><p />Building project and manually copying the JAR is boring, not to mention it's not helpful during development/debugging.<br /> Thankfully there is <b>Virgo Tools</b>, which has been part of <b>SpringSource Tool Suite.</b> You can install it in your Eclipse IDE using one of the following update sites (depending on your preference of stability, I use the "milestone" one, and I am not having problems):<br /> <ul type="DISC"><li><p class="western"><code class="western"><a href="http://dist.springsource.com/release/TOOLS/update/e3.6/">http://dist.springsource.com/release/TOOLS/update/e3.6/</a></code> </p> </li><li><p class="western"><code class="western"><a href="http://dist.springsource.com/milestone/TOOLS/update/e3.6/">http://dist.springsource.com/milestone/TOOLS/update/e3.6/</a></code> </p> </li><li><p class="western"><code class="western"><a href="http://dist.springsource.com/snapshot/TOOLS/nightly/e3.6/">http://dist.springsource.com/snapshot/TOOLS/nightly/e3.6/</a></code> </p> </li></ul> <p class="western">Choose: <b>Core / dm Server Tools</b><span style="font-weight: normal;"> (as of Virgo Tools 2.7.0-M2)</span></p> Open the Servers view (Shift+Alt+Q Q, choose Servers), then add a new server, pick Eclipse Virgo Web Server.<p />Before launching, you need to edit the launch configuration to enable SSH server.<br />Double-click Eclipse Virgo Web Server in the Servers view, then click "Edit launch configuration".<br /> <p class="western"><span style="font-weight: normal;">On </span><b>Arguments > VM arguments</b><span style="font-weight: normal;">, add the following:</span></p> <div class="CodeRay"> <div class="code"><pre>-Dorg.eclipse.equinox.console.jaas.file="$CONFIG_DIR/store" -Dssh.server.keystore="$CONFIG_DIR/hostkey.ser"</pre></div> </div> <p class="western"> <span style="font-weight: normal;">You need to </span><i><span style="font-weight: normal;">manually</span></i><span style="font-weight: normal;"> replace </span><b>$CONFIG_DIR</b><span style="font-weight: normal;"> above with the Virgo Web Server's </span><b>config</b><span style="font-weight: normal;"> folder.</span></p> Now you can launch Eclipse Virgo Web Server from Eclipse IDE, develop & debug, inspect its configuration, deploy artifacts, etc. right inside the Eclipse IDE.<p /><b>Deploying PDE Plug-in Projects into Eclipse Virgo Web Server</b><p /> Currently Virgo Tools is integrated with WTP, but not with PDE. Which means:<br /> <ul><li><p class="western"><span style="font-weight: normal;">Eclipse plugin projects cannot be deployed straightforwardly into Virgo Web Server using Virgo Tools plugin</span></p> </li><li><p class="western"><span style="font-weight: normal;">What can be deployed are VWS-specific OSGi Plan project and OSGi Bundle project. You can right click a "plain" Eclipse plugin project > Spring Tools > Add OSGi Bundle project nature to convert it to VWS OSGi Bundle project.</span></p> </li><li><p class="western"><span style="font-weight: normal;">OSGi Bundle nature has its own way of generating MANIFEST.MF, it doesn't use Eclipse PDE's META-INF/MANIFEST.MF file.</span></p> </li></ul> <p class="western"><span style="font-weight: normal;">State as of 13 June 2011: (<a href="http://www.eclipse.org/forums/index.php/m/683363/">source</a>)</span></p> <p class="western" style="margin-left: 1.27cm;"><span style="font-weight: normal;">Unfortunately there is no integration of STS and PDE. The </span><b>Libra project is doing a great job at integrating WTP and PDE and looks like a great way of developing OSGi standard bundles</b><span style="font-weight: normal;">, although it doesn't yet have a way to launch Virgo. The Virgo tooling, contributed to Eclipse from STS, is also developing, is able to launch Virgo (of course), and we hope to align it with Libra in due course.<p />For now, why not </span><b>ditch the PDE manifest and run with STS and Maven</b><span style="font-weight: normal;">? That's the approach we took in the Greenpages sample application which you may like to refer to. See also <a href="http://www.eclipse.org/virgo/documentation/">"Creating an Application with Virgo"</a> which goes through the Greenpages sample in some detail.</span></p>Better tooling for OSGi, Eclipse Virgo, Eclipse Gemini, and integration with PDE are goals of <a href="http://www.eclipse.org/libra/">Eclipse Libra project</a>. Maybe you can help? :-)<p /> To deploy gogosimple1 using Virgo Tools, first you need to enable the OSGi bundle project nature: right-click on the project, click Spring Tools > Add OSGi Bundle project nature.<p />Then right-click on the Virgo Web Server in Servers view, click Add and Remove, now you can choose gogosimple1 to deploy the project as artifact to Virgo.<p /> By doing the steps above, the development cycle with Virgo and Eclipse IDE becomes <i>very convenient,</i> anytime you change the project's source code, resource files, etc. Virgo will immediately pick it up. You can also launch Eclipse Virgo in debug mode and have all Eclipse IDE debugging tools at your fingertips!<p /> <b>Recommended Resources</b><p />For more in-depth explanation on OSGi, I highly recommend <a href="http://www.amazon.com/gp/product/1933988916/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=1933988916"><b>OSGi in Action: Creating Modular Applications in Java</b></a>.</div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com20tag:blogger.com,1999:blog-2436480061461152965.post-84828807991681023412011-04-28T14:28:00.001-07:002011-04-28T14:47:35.181-07:00Hot Deploy OSGi CLI Commands in Apache Karaf with Eclipse PDE<div class='posterous_autopost'><b><a href="http://felix.apache.org/site/apache-felix-gogo.html">Apache Felix GoGo</a></b> is a really cool <b>command line interface</b> (<b>CLI</b>, aka <b>shell</b> aka <b>console</b>) <b>framework</b> that runs in an <b>OSGi runtime</b>.<div> <b><a href="http://karaf.apache.org">Apache Karaf</a></b> (based on <b>Apache Felix</b> OSGi runtime) just so happens to include Felix GoGo built-in, and the <a href="http://karaf.apache.org/manual/2.1.99-SNAPSHOT/developers-guide/extending-console.html">console is very extensible</a> too, making it easy to add more commands at your will.</div> <p /><div><b>Karaf / Felix GoGo Features</b></div><p /><div>A very nice feature of Apache Karaf is it has a <b><a href="http://karaf.apache.org/manual/2.1.99-SNAPSHOT/users-guide/remote-console.html">built-in SSH server</a></b>, so you can literally and practically just ssh to the Karaf server :</div> <p /><div><div>ceefour@annafi:/opt/apache-karaf-2.2.0$ ssh -p 8102 karaf@localhost</div><div>karaf@localhost's password: </div> <div> __ __ ____ </div><div> / //_/____ __________ _/ __/ </div> <div> / ,< / __ `/ ___/ __ `/ /_ </div><div> / /| |/ /_/ / / / /_/ / __/ </div> <div> /_/ |_|\__,_/_/ \__,_/_/ </div><p /> <div> Apache Karaf (2.2.0)</div><p /><div>Hit '<tab>' for a list of available commands</div> <div>and '[cmd] --help' for help on a specific command.</div><div>Hit '<ctrl-d>' or 'osgi:shutdown' to shutdown Karaf.</div> <p /><div>karaf@bipposhell> erp:hello</div><div>Hello Ibrahim!</div> </div><p /><div>Default credentials is user=karaf, password=karaf .</div><div>And even (this I just found out!) the amazing thing is you can <i>run a command directly from ssh</i> :</div><p /><div><div> $ ssh -p 8102 karaf@localhost erp:hello</div><div>karaf@localhost's password: </div> <div>Hello Ibrahim!</div></div><p /><div>Isn't it amazing? What's more, the Karaf/Felix GoGo command line also supports <i>POSIX/GNU-style option arguments, Tab completion,</i> and <i>UNIX-like pipes</i>! (yes, pipes, where you can do 'grep' and stuff)</div> <p /><div>The other, and paramountly useful feature, is the ability to <b>hot deploy</b> <i>exploded</i> OSGi bundles! Now hot deploy in itself is really useful, the ability to deploy exploded bundles is even more fantastic. We'll see about that soon.</div> <p /><div><b>What About Virgo?</b></div><p /><div>Some of you may (almost) cry that I didn't mention <b><a href="http://www.eclipse.org/virgo/">Eclipse Virgo Kernel</a></b>. Which is, of course, a direct competitor to Apache Karaf.</div> <div>I haven't used Virgo yet (but I should try it one day), so because I have used Karaf in the past so for now I'm using Karaf, and I know it has a cool built-in CLI ;-)</div><div>I think the steps below can be adapted to Virgo with little (or not so little?) work. Let me know if you have tips, ideas, etc. :-) I will appreciate it a lot.</div> <p /><div><b>Developing CLI on Felix GoGo</b></div><p /><div>For my next Bippo Shell project I plan to use Felix GoGo as the CLI framework. I'll share my first steps experience on <b>developing CLI Commands with Karaf and Felix GoGo</b> with you.</div> <div>To get started, I first <b><a href="http://karaf.apache.org/">download Apache Karaf OSGi runtime</a></b>.</div><p /><div>Felix GoGo looks for command services exposed by other OSGi bundles. To develop this OSGi bundle, my IDE of choice happens to be... (surprise!) <b>Eclipse IDE</b>. ;-)</div> <div>Now there are several approaches to develop OSGi bundles in Eclipse IDE, but the path I want to take right now is using <b>Eclipse PDE (Plug-in Development Environment)</b>. At least for developing the bundle (i.e. Eclipse plugin) itself, not for running/launching the runtime (yet...)</div> <p /><div>I assume you are already somewhat familiar with Eclipse IDE and Eclipse PDE. If not, it's not too late to reach enlightenment ;-) get <b><a href="http://www.amazon.com/gp/product/0321603788/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=0321603788">Eclipse Rich Client Platform (2nd Edition) book</a></b>.</div> <p /><div><b>Creating the Target Platform</b></div><p /><div>The first thing I need to do before developing anything (for you guys familiar with Eclipse RCP would already know this: any Eclipse-based project for that matter) is <b>prepare the target platform</b>.</div> <div>The target platform contains all the OSGi bundles (Eclipse plugins) that are <i>already</i> in the target runtime environment. In this case: Felix Karaf.</div><p /><div>So, copy these files from Apache Karaf distribution (hint: look for lib/ and system/ folders) to another folder. These little commands may help you:</div> <p /><div>mkdir -vp ~/felix-karaf-target-platform/<br />cp -v lib/karaf-jaas-boot.jar ~/felix-karaf-target-platform<br />find system -iname '*.jar' -exec cp -v '{}' ~/felix-karaf-target-platform \;<br /> </div><p /><div>Then I created a target platform in Eclipse PDE, and add content based on the directory that contains the Karaf JARs, then activate the target platform.</div><p /><div><b>Creating the Eclipse Plugin Project</b></div> <p /><div>Although Karaf only needs an OSGi bundle, I decided to create an Eclipse plugin project. Why not, since an Eclipse plugin is always an OSGi bundle anyway! And it might be useful if I want to use my plugin projects in an Eclipse RCP project, for example.</div> <p /><div>First I create an Eclipse plugin project, then set the <b>Import-Package</b> in META-INF/MANIFEST.MF to:</div><p /><div><div>Import-Package: org.apache.felix.gogo.commands;version="0.6.1",</div> <div> org.apache.karaf.shell.console;version="2.2.0",</div><div> org.osgi.framework;version="1.3.0"</div> <p /></div><div>This allows me to refer to the Felix GoGo API classes and interfaces.</div><p /><div><b>Writing the Command Implementation</b></div> <p /><div>Thanks to annotations in similar spirit of JAX-RS, writing the CLI command implementation class is simple:</div><p /><div><div>package id.co.bippo.shell;</div> <p /><div>import org.apache.felix.gogo.commands.Command;</div> <div>import org.apache.karaf.shell.console.OsgiCommandSupport;</div><p /><div>@Command(scope="erp", name="hello", description="Says hello")</div><div>public class ErpCommand extends OsgiCommandSupport {</div> <p /><div><span class="Apple-tab-span" style=""> </span>/* (non-Javadoc)</div> <div><span class="Apple-tab-span" style=""> </span> * @see org.apache.karaf.shell.console.AbstractAction#doExecute()</div><div><span class="Apple-tab-span" style=""> </span> */</div> <div><span class="Apple-tab-span" style=""> </span>@Override</div><div><span class="Apple-tab-span" style=""> </span>protected Object doExecute() throws Exception {</div> <div><span class="Apple-tab-span" style=""> </span>System.out.println("Hello Ibrahim!");</div><div><span class="Apple-tab-span" style=""> </span>return null;</div> <div><span class="Apple-tab-span" style=""> </span>}</div><p /><div>}</div></div><p /><div>I think the annotations and what this class does is pretty much self-explanatory.</div><p /><div><b>Registering the Command Service using OSGi Blueprint</b></div><p /><div>To register the command service implementation, <b><a href="http://www.slideshare.net/gnodet/osgi-blueprint-services-1622424">OSGi Blueprint</a></b> is used (although not mandatory, you can use manual OSGi wiring if you want, but why do it the hard way?)</div> <div>Apache Karaf uses <b><a href="http://aries.apache.org/modules/blueprint.html">Apache Aries Blueprint</a></b> as the OSGi Blueprint implementation, but I believe it should work with Eclipse Gemini Blueprint as well.</div> <p /><div>Put this in <b>OSGI-INF/blueprint/myshell.xml</b> (actual filename doesn't matter):</div><p /><div><div><blueprint xmlns="<a href="http://www.osgi.org/xmlns/blueprint/v1.0.0">http://www.osgi.org/xmlns/blueprint/v1.0.0</a>"></div> <p /><div> <command-bundle xmlns="<a href="http://karaf.apache.org/xmlns/shell/v1.0.0">http://karaf.apache.org/xmlns/shell/v1.0.0</a>"></div> <div> <command name="erp/hello"></div><div> <action class="id.co.bippo.shell.ErpCommand"/></div> <div> </command></div><div> </command-bundle></div> <p /><div></blueprint></div></div><p /> <div>This registers the command that can be executed by typing: <b>erp:hello</b></div><p /><div>I wondered why the command name is repeated again here. It turns out that the Blueprint declaration actually registers the command under the specified name, and the class annotations are only used for usage help, i.e. when typing: <b>erp:hello --help</b></div> <p /><div><b>Deploying the Command Bundle/Plugin</b></div><p /><div>I'm not yet using fancy launcher PDE feature here, just old-school style:</div><div> <ol><li>Export the plugin as a binary JAR bundle to a folder</li><li>Copy the plugin JAR to Karaf's <b>deploy/</b> folder</li></ol><div>Karaf will detect it and register the command:</div></div><p /><div><div> karaf@bipposhell> la</div><div>START LEVEL 100 , List Threshold: 0</div> <div> ID State Blueprint Level Name</div><div>[ 0] [Active ] [ ] [ 0] System Bundle (3.0.8)</div> <div>[ 1] [Active ] [ ] [ 5] OPS4J Pax Url - wrap: (1.2.5)<br />...</div><div>[ 40] [Active ] [Created ] [ 30] Apache Karaf :: Deployer :: Blueprint (2.2.0)</div> <div>[ 41] [Active ] [Created ] [ 30] Apache Karaf :: Shell :: ConfigAdmin Commands (2.2.0)</div><div><b /><div> [ 42] [Active ] [Created ] [ 60] Bippo Shell (1.0.0.qualifier)</div></div><p /><div>karaf@bipposhell> erp:hello</div> <div>Hello Ibrahim!</div><p /><div>karaf@bipposhell> erp:hello --help</div> <div>DESCRIPTION</div><div> erp:hello</div><p /><div> Says hello</div><p /><div>SYNTAX</div> <div> erp:hello [options]</div><p /><div>OPTIONS</div> <div> --help</div><div> Display this help message</div> <p /><div><b>Hot Deploying Eclipse Plugins</b></div></div><p /><div>Karaf has very useful ability to do <b>hot deploy</b> of OSGi bundles during development. (actually, being OSGi, it can perform hot deployment anytime, not just "during development")</div> <div><b>osgi:install</b> and <b>dev:watch</b> Karaf commands will help you on this.</div><p /><div>But I prefer the low-tech solution:</div><p /><div><div>ceefour@annafi:~/project/bipponext/workspace/id.co.bippo.shell$ ln -s $PWD/bin /opt/apache-karaf-2.2.0/instances/bipposhell/deploy/id.co.bippo.shell</div> </div><p /><div>This creates a symlink to the <b>${project_loc}/bin</b> folder inside the Karaf's <b>deploy/</b> folder.</div><div>You may want to also copy the META-INF/MANIFEST.MF file. If not, it will still work in most cases, because Karaf is very smart to wrap non-OSGi "bundles" to be OSGi-friendly.</div> <div>This magic works because Karaf supports "exploded bundles", i.e. OSGi bundles not packaged in a JAR, but exploded as a directory structure.</div><p /><div><div>karaf@bipposhell> erp:hello</div> <div>Hello Ibrahim!</div></div><div>(......edit the source in Eclipse, then Save...and......)</div><div><div>karaf@bipposhell> erp:hello</div> <div>Hello Yudha!</div></div><p /><div>No need to restart, nothing! You don't even need to build/package the JAR!</div><div>Pretty nifty, eh? :-)</div> <p /><div><i>Note</i>: While doing this, I uncovered <a href="https://issues.apache.org/jira/browse/KARAF-602">bug KARAF-602</a>.</div><div><i>Karaf Tip</i>: If something goes wrong, <b>tail:log</b>, <b>log:set</b>, and <b>dev:show-tree</b> Karaf commands are your friends. :-)</div> <p /><div><b>What's Next ?</b></div><p /><div>As demonstrated, OSGi development <i>can</i> be easy and in some cases even <i>easier</i> than "plain" Java.</div> <p /><div>Actually I've managed to do a <b>headless build</b> using <b><a href="http://www.eclipse.org/proposals/tycho/">Eclipse Tycho</a></b> (formerly <b><a href="http://tycho.sonatype.org">Sonatype Tycho Maven plugin</a></b>), so I hope to write about this in a later post.</div> <p /><div>To learn more about Eclipse plugins, I recommend the <b><a href="http://www.amazon.com/gp/product/0321603788/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=0321603788">Eclipse Rich Client Platform (2nd Edition) book</a></b>.</div> <div>For a thorough explanation in developing OSGi applications, check out the recently released <b><a href="http://www.amazon.com/gp/product/1933988916/ref=as_li_ss_tl?ie=UTF8&tag=eclipsedriven-20&linkCode=as2&camp=217145&creative=399349&creativeASIN=1933988916">OSGi in Action, Creating Modular Applications in Java</a></b>.</div></div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com9tag:blogger.com,1999:blog-2436480061461152965.post-34513190861155687632011-01-02T11:27:00.001-08:002011-04-28T14:48:08.866-07:00Unit Testing EMF Models with EasyMock<div class='posterous_autopost'> During <b>unit testing</b>, I use <b><a href="easymock.org">EasyMock</a></b> to <b>mock</b> an <b>EMF-generated interface</b> (I don't plan on persisting the interface nor its implementation).<p /> Example usage is like this:<p /> <tt>protected void setUp() {</tt><br /> <tt> initXmi();</tt><br /> <tt> voucher = AbispulsaFactory.eINSTANCE.createVoucher();</tt><br /> <tt> voucher.setCode("I10");</tt><br /> <tt> voucher.setPrice(BigDecimal.valueOf(11500));</tt><br /> <tt> voucher.setCost(BigDecimal.valueOf(9900));</tt><br /> <tt> owner = new DummyOwner();</tt><br /> <b><tt> fixture.setRefillListener(createMock(RefillListener.class));</tt><br /> <tt>}</tt><p /> refillListener will be used somewhere in the implementation :<p /> <tt>...</tt><br /> <tt>refill.setVoucher(voucher);</tt><br /> <tt>DealerImpl.this.getRefills().add(refill);</tt><br /> <tt>beforeSave(refill);</tt><br /> <tt>try {</tt><br /> <tt> refill.eResource().save(null);</tt><br /> <tt>} catch (IOException e) {</tt><br /> <tt> throw new AbispulsaException(e);</tt><br /> <tt>}</tt><br /> <b><tt>getRefillListener().created(refill);</tt><br /> <tt>return refill;</tt><p /> The intention is to use a <b>mock object</b> for <b>refillListener</b> attribute/interface so I don't have to provide a real object.<p /> But I got this error:<p /> <tt>java.lang.ClassCastException: $Proxy0 cannot be cast to org.eclipse.emf.ecore.InternalEObject</tt><br /> <tt> at org.eclipse.emf.ecore.impl.EStructuralFeatureImpl$InternalSettingDelegateSingleEObject.dynamicGet(EStructuralFeatureImpl.java:2337)</tt><br /> <tt> at org.eclipse.emf.ecore.impl.BasicEObjectImpl.eDynamicGet(BasicEObjectImpl.java:1055)</tt><br /> <tt> at com.abispulsa.impl.DealerImpl.getRefillListener(DealerImpl.java:135)</tt><br /> <tt> at com.abispulsa.impl.DealerImpl$RefillTemplate.create(DealerImpl.java:85)</tt><br /> <tt> at com.abispulsa.impl.DealerImpl.refill(DealerImpl.java:288)</tt><br /> <tt> at com.abispulsa.tests.DealerTest.testRefill__Voucher_String_RefillOwner(DealerTest.java:351)</tt><br /> <tt> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)</tt><br /> <tt> at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)</tt><br /> <tt> at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)</tt><br /> <tt> at java.lang.reflect.Method.invoke(Method.java:597)</tt><br /> <tt> at junit.framework.TestCase.runTest(TestCase.java:168)</tt><br /> <tt> at junit.framework.TestCase.runBare(TestCase.java:134)</tt><br /> <tt> at junit.framework.TestResult$1.protect(TestResult.java:110)</tt><br /> <tt> at junit.framework.TestResult.runProtected(TestResult.java:128)</tt><br /> <tt> at junit.framework.TestResult.run(TestResult.java:113)</tt><br /> <tt> at junit.framework.TestCase.run(TestCase.java:124)</tt><br /> <tt> at junit.framework.TestSuite.runTest(TestSuite.java:232)</tt><br /> <tt> at junit.framework.TestSuite.run(TestSuite.java:227)</tt><br /> <tt> at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:83)</tt><br /> <tt> at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)</tt><br /> <tt> at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)</tt><br /> <tt> at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)</tt><br /> <tt> at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)</tt><br /> <tt> at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)</tt><br /> <tt> at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)</tt><p /> The solution is to mark the attribute as <b>Resolve Proxies = false</b>. (In addition, I also mark Transient = true)<p /> I'm not sure how this affects persistence mechanism such as <b>Teneo</b> or <b>CDO</b>, and if this is the proper way or not. If you know the proper way please advise. Thanks. </b></b></div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com15tag:blogger.com,1999:blog-2436480061461152965.post-31563073580077149842011-01-01T13:53:00.001-08:002011-04-28T14:48:31.345-07:00POJO Remoting over Camel-XMPP within Eclipse App<div class='posterous_autopost'> I spent a lot of hours trying to make <b><a href="http://spring-java-ee.blogspot.com/2010/12/remote-observer-pattern-with-publish.html">Remote Observer Pattern with Camel over XMPP</a></b> work inside my <b>Eclipse RCP Application</b>, so here's my guide to make it run successfully.<p /> <h3> Getting Camel </h3> <br /> Getting <b><a href="http://camel.apache.org/">Apache Camel</a></b> itself to run is the easiest part, because Camel is already packaged as OSGi bundles, also available from the Maven Central repository.<p /> Here are the artifacts that I needed: <ol type="1"> <li value="1" type="1"><b>org.apache.camel:camel-core:2.5.0</b> (dependency of the ones below) </li><li value="2" type="1"><b>org.apache.camel:camel-blueprint:2.5.0</b> </li><li value="3" type="1"><b>org.apache.camel:camel-eclipse:2.5.0</b> </li><li value="4" type="1"><b>org.apache.camel:camel-xmpp:2.5.0</b> </li><li value="5" type="1"><b>org.apache.camel:camel-xstream:2.5.0</b> </li></ol> These will pull additional dependencies, here are the JARs:<p /> <tt>camel-blueprint-2.5.0.jar</tt><br /> <tt>camel-core-2.5.0.jar</tt><br /> <tt>camel-eclipse-2.5.0.jar</tt><br /> <tt>camel-xmpp-2.5.0.jar</tt><br /> <tt>camel-xstream-2.5.0.jar</tt><br /> <tt>commons-management-1.0.jar</tt><br /> <tt>javax.xml_1.3.4.v201005080400.jar</tt><br /> <tt>javax.xml.stream_1.0.1.v201004272200.jar</tt><br /> <tt>jettison-1.2.jar</tt><br /> <tt>org.apache.aries.blueprint-0.2-incubating.jar</tt><br /> <tt>org.apache.servicemix.bundles.xmlpull-1.1.3.1_1.jar</tt><br /> <tt>org.apache.servicemix.bundles.xpp3-1.1.4c_4.jar</tt><br /> <tt>org.apache.servicemix.bundles.xstream-1.3_4.jar</tt><p /> You'll notice that it includes the <b><a href="http://incubator.apache.org/aries/blueprint.html">Apache Aries Blueprint container</a></b>.<br /> Camel can also use Blueprint service to run in OSGi. (It seems <a href="http://camel.apache.org/camel-eclipse.html">Camel can work in Eclipse</a> without Blueprint, but I haven't tried it. Besides I think Blueprint is a nice touch, hehe..)<br /> It's nice to know that Apache Aries Blueprint works well within Eclipse/Equinox.<br /> (I was tempted to use <b><a href="http://www.eclipse.org/blueprint/">Eclipse Gemini Blueprint</a></b> but it seems "not ready yet", and I suspect it brings on Spring Framework dependencies, which I'm not interested at the moment.)<p /> I need to replace some of the dependencies with OSGi bundles from: <ul> <li><a href="http://repo2.maven.org/maven2/org/apache/servicemix/specs/">Apache ServiceMix Specs Repository</a> </li><li><a href="http://repo2.maven.org/maven2/org/apache/servicemix/bundles/">Apache ServiceMix Bundles Repository</a> </li><li><a href="http://download.eclipse.org/tools/orbit/downloads/">Eclipse Orbit Repository</a> </li></ul> <br /> Note that I've left out Smack JARs there, for reasons later below.<p /> Hint: To know which OSGi-ready dependencies are available, check Camel for Karaf features list here :<br /> <a href="http://repo1.maven.org/maven2/org/apache/camel/karaf/apache-camel/2.5.0/">http://repo1.maven.org/maven2/org/apache/camel/karaf/apache-camel/2.5.0/</a><p /> <h3> Starting Apache Camel Bundles </h3> <br /> <b>Apache Camel</b> and the <b>Apache Aries Blueprint</b> needs to be explicitly started, so after copying these files to the target platform, I need to go the Eclipse Launch configuration and mark these bundles as "start". I'm not sure which exact bundles need to be started, but I just start all camel-* bundles and all its dependencies and Apache Aries Blueprint.<p /> If there is a trouble, it might be useful to tweak the start order, but I hope it's not necessary.<p /> <h3> Using Camel with Blueprint </h3> There are several ways to use <a href="http://camel.apache.org/camel-eclipse.html">Camel inside Eclipse</a>, the traditional way without Blueprint is like this:<p /> <tt>PackageScanClassResolver eclipseResolver = new EclipsePackageScanClassResolver();</tt><br /> <tt>CamelContext context = new DefaultCamelContext();</tt><br /> <tt>context.setPackageScanClassResolver(eclipseResolver);</tt><p /> To use Camel from a <b>Blueprint beans XML file</b>, create <tt>OSGI-INF/blueprint/config.xml</tt> file (directly below your plugin project folder, not inside the src folder) like this:<p /> <tt><?xml version="1.0" encoding="UTF-8"?></tt><br /> <tt><blueprint xmlns="<a href="http://www.osgi.org/xmlns/blueprint/v1.0.0">http://www.osgi.org/xmlns/blueprint/v1.0.0</a>"</tt><br /> <tt> xmlns:xsi="<a href="http://www.w3.org/2001/XMLSchema-instance">http://www.w3.org/2001/XMLSchema-instance</a>"</tt><br /> <tt> xsi:schemaLocation="<a href="http://www.osgi.org/xmlns/blueprint/v1.0.0">http://www.osgi.org/xmlns/blueprint/v1.0.0</a> <a href="http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd">http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd</a></tt><br /> <tt> <a href="http://camel.apache.org/schema/blueprint">http://camel.apache.org/schema/blueprint</a> <a href="http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">http://camel.apache.org/schema/blueprint/camel-blueprint.xsd</a>"></tt><p /> <tt> <camelContext xmlns="<a href="http://camel.apache.org/schema/blueprint">http://camel.apache.org/schema/blueprint</a>" id="camelContext" autoStartup="true"></tt><br /> <tt> <route></tt><br /> <tt> <from uri="direct:invoice" /></tt><br /> <tt> <to uri="seda:invoice.async" /></tt><br /> <tt> </route></tt><br /> <tt> </camelContext></tt><br /> <tt> </tt><br /> <tt> <bean id="invoicing" class="id.co.bippo.usexmpp.Invoicing"</tt><br /> <tt> init-method="init" destroy-method="destroy"></tt><br /> <tt> <property name="camelContext" ref="camelContext" /></tt><br /> <tt> </bean></tt><br /> <tt></blueprint></tt><p /> Note that <b>autoStartup</b> is true by default, you can change it to false.<p /> The <b>CamelContext</b> instance will be injected into the class by Blueprint, I created a class like this:<p /> <tt>import org.apache.camel.CamelContext;</tt><br /> <tt>import org.apache.camel.builder.ProxyBuilder;</tt><br /> <tt>import org.apache.camel.builder.RouteBuilder;</tt><br /> <tt>import org.apache.camel.component.eclipse.EclipsePackageScanClassResolver;</tt><br /> <tt>import org.slf4j.Logger;</tt><br /> <tt>import org.slf4j.LoggerFactory;</tt><p /> <tt>public class Invoicing {</tt><p /> <tt> private CamelContext camelContext;</tt><br /> <tt> private Logger logger = LoggerFactory.getLogger(getClass());</tt><br /> <tt> protected InvoiceListener loggerInvoiceListener = new LoggerInvoiceListener();</tt><br /> <tt> </tt><br /> <tt> public void init() throws Exception {</tt><br /> <tt> camelContext.setPackageScanClassResolver(new EclipsePackageScanClassResolver());</tt><br /> <tt> camelContext.addRoutes(new RouteBuilder() {</tt><br /> <tt> </tt><br /> <tt> @Override</tt><br /> <tt> public void configure() throws Exception {</tt><br /> <tt>// .. configure additional routes ...</tt><br /> <tt> }</tt><br /> <tt> });</tt><br /> <tt> camelContext.start();</tt><br /> <tt> </tt><br /> <tt> InvoiceListener invoiceListener = new ProxyBuilder(camelContext)</tt><br /> <tt> .endpoint("direct:invoice").build(InvoiceListener.class);</tt><br /> <tt> invoiceListener.invoiceCreated(123, "Bippo Indonesia");</tt><br /> <tt> }</tt><br /> <tt> </tt><br /> <tt> public void destroy() throws Exception {</tt><br /> <tt> camelContext.stop();</tt><br /> <tt> }</tt><p /> <tt> public void setCamelContext(CamelContext camelContext) {</tt><br /> <tt> this.camelContext = camelContext;</tt><br /> <tt> }</tt><p /> <tt>}</tt><p /> The simple application seems to run fine without setting <a href="http://camel.apache.org/camel-eclipse.html">EclipsePackageScanClassResolver</a>, but I guess it's safer to use it.<p /> Another way create a CamelContext using Blueprint XML is by instantiating it:<p /> <tt><bean id="camelContext" class="org.apache.camel.blueprint.BlueprintCamelContext"></tt><br /> <tt> <property name="bundleContext" ref="blueprintBundleContext" /></tt><br /> <tt> <property name="blueprintContainer" ref="blueprintContainer" /></tt><br /> <tt> <property name="packageScanClassResolver"></tt><br /> <tt> <bean class="org.apache.camel.component.eclipse.EclipsePackageScanClassResolver" /></tt><br /> <tt> </property></tt><br /> <tt></bean></tt><p /> Using the factory bean directly:<p /> <tt> <bean id="camelFactory" class="org.apache.camel.blueprint.CamelContextFactoryBean"></tt><br /> <tt> <property name="bundleContext" ref="blueprintBundleContext" /></tt><br /> <tt> <property name="blueprintContainer" ref="blueprintContainer" /></tt><br /> <tt> <property name="autoStartup" value="false" /></tt><br /> <tt></bean></tt><br /> <tt><bean id="camelContext" factory-ref="camelFactory" factory-method="getContext" /></tt><p /> <h3> Xstream Dependencies </h3> <b><a href="http://xstream.codehaus.org/">Xstream</a></b> is needed to <b>marshal/serialize</b> objects to/from <b>XML representation</b> and/or <b>JSON</b>.<p /> Xstream requires several dependencies: <ul> <li>javax.xml </li><li>javax.stream (aka StaX API) </li><li>xmlpull </li><li>xpp3 (aka xml.pull.mxp1) </li><li>Jettison </li></ul> <br /> These are available from Apache ServiceMix repositories (above).<p /> <h3> Making Xstream work with OSGi </h3> Camel has a shortcut way of marshalling/unmarshalling objects with Xstream :<p /> <tt>from("direct:invoice-json").marshal().json().("log:invoice");</tt><br /> <tt>from("direct:invoice-xstream").marshal().xstream().("log:invoice");</tt><br /> <tt>from("seda:json").unmarshal().json().("log:unmarshal");</tt><br /> <tt>from("seda:xml").unmarshal().xstream().("log:unmarshal");</tt><p /> However with OSGi we need to set the classloader which is used to resolve marshalled classes. The configuration becomes:<p /> <tt>import org.apache.camel.dataformat.xstream.XStreamDataFormat;</tt><br /> <tt>import com.thoughtworks.xstream.XStream;</tt><br /> <tt>...</tt><br /> <tt>camelContext.addRoutes(new RouteBuilder() {</tt><br /> <tt>@Override</tt><br /> <tt>public void configure() throws Exception {</tt><br /> <tt>XStream xstream = new XStream();</tt><br /> <tt><b>xstream.setClassLoader(getClass().getClassLoader());</b></tt><br /> <tt>XStreamDataFormat xstreamDataFormat = new XStreamDataFormat(xstream);</tt><p /> <tt>from("direct:invoice-xstream")</tt><tt><b>.marshal(xstreamDataFormat)</b></tt><tt>.("log:invoice");</tt><br /> <tt>from("seda:xml")</tt><tt><b>.unmarshal(xstreamDataFormat)</b></tt><tt>.("log:unmarshal");</tt><br /> <tt>}</tt><br /> <tt>});</tt><p /> To use the JSON format with Xstream, we need to configure the Xstream driver:<p /> <tt>import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;</tt><br /> <tt>...</tt><br /> <tt>xstreamDataFormat.setXstreamDriver(new JettisonMappedXmlDriver());</tt><p /> <h3> OSGi-fying Smack </h3> <br /> In the plain Java world, we'll be done. But not so with Eclipse and OSGi.<p /> I had the most trouble with the Smack library. The repackaged Smack library from Apache ServiceMix also didn't just work due to <a href="https://issues.apache.org/jira/browse/SMX4-724">missing META-INF/smack.providers file</a> and <b>XMPPConnection</b> not registering <b>ServiceDiscoveryManager</b>, which seem to happen because of <b>static { ... }</b> statements.<p /> One problem is this code from Smack:<p /> public static Collection<String> getServiceNames(XMPPConnection connection) throws XMPPException {<br /> final List<String> answer = new ArrayList<String>();<br /> <b> ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection);</b><br /> DiscoverItems items = discoManager.discoverItems(connection.getServiceName());<br /> for (Iterator<DiscoverItems.Item> it = items.getItems(); it.hasNext();) {<p /> discoManager returns null, because the map of instances is empty.<p /> The static code block here is not being called within OSGi runtime:<p /> // Create a new ServiceDiscoveryManager on every established connection<br /> static {<br /> XMPPConnection.addConnectionCreationListener(new ConnectionCreationListener() {<br /> public void connectionCreated(XMPPConnection connection) {<br /> new ServiceDiscoveryManager(connection);<br /> }<br /> });<br /> }<p /> So I had to tweak Smack and published the changes to <b><a href="https://github.com/ceefour/smack">my Smack repository on GitHub</a></b>.<p /> I've also published the tweaked Smack 3.1.0 library as Eclipse plugin to <a href="http://oss.bippo.co.id/p2/">Bippo OSS p2 Repository</a>.<p /> You can also download the bundle directly from: <a href="http://oss.bippo.co.id/p2/features/id.co.bippo.oss.smack_3.1.0.201101020232.jar">http://oss.bippo.co.id/p2/features/id.co.bippo.oss.smack_3.1.0.201101020232.jar</a><p /> Note that my Smack bundle now requires <b>SLF4j API for logging</b>.<p /> Tip: A nice way to debug Smack is by adding <tt>-Dsmack.debugEnabled=true</tt> to VM launch options.<p /> I also learned several (re-)packaging tips with Eclipse plugins: <ul> <li>make sure to add "." in Runtime classpath when adding other classpaths (Apache ServiceMix repackaged Smack library includes other dependencies in folder containing .class files) </li><li>Workarounds for missing files can potentially be done using OSGi Fragment bundles, for example to augment </li></ul><tt>META-INF/smack.providers</tt> if so desired. <br /> <h3> The App </h3> Here's how the <b>Camel XMPP</b> test <b>Eclipse app</b> looks like:<p /> <tt>import org.apache.camel.CamelContext;</tt><br /> <tt>import org.apache.camel.builder.ProxyBuilder;</tt><br /> <tt>import org.apache.camel.builder.RouteBuilder;</tt><br /> <tt>import org.apache.camel.component.eclipse.EclipsePackageScanClassResolver;</tt><br /> <tt>import org.apache.camel.dataformat.xstream.XStreamDataFormat;</tt><br /> <tt>import org.apache.camel.model.dataformat.JsonDataFormat;</tt><br /> <tt>import org.apache.camel.model.dataformat.JsonLibrary;</tt><br /> <tt>import org.slf4j.Logger;</tt><br /> <tt>import org.slf4j.LoggerFactory;</tt><p /> <tt>import com.thoughtworks.xstream.XStream;</tt><br /> <tt>import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;</tt><p /> <tt>public class Invoicing {</tt><p /> <tt> private CamelContext camelContext;</tt><br /> <tt> private Logger logger = LoggerFactory.getLogger(getClass());</tt><br /> <tt> protected InvoiceListener loggerInvoiceListener = new LoggerInvoiceListener();</tt><br /> <tt> </tt><br /> <tt> public void init() throws Exception {</tt><br /> <tt> logger.info("Invoicing started");</tt><br /> <tt> camelContext.setPackageScanClassResolver(new EclipsePackageScanClassResolver());</tt><br /> <tt> camelContext.addRoutes(new RouteBuilder() {</tt><br /> <tt> </tt><br /> <tt> @Override</tt><br /> <tt> public void configure() throws Exception {</tt><br /> <tt> JsonDataFormat dataFormat = new JsonDataFormat(JsonLibrary.XStream);</tt><br /> <tt> XStream xstream = new XStream();</tt><br /> <tt> xstream.setClassLoader(getClass().getClassLoader());</tt><br /> <tt> XStreamDataFormat xstreamDataFormat = new XStreamDataFormat(xstream);</tt><br /> <tt> xstreamDataFormat.setXstreamDriver(new JettisonMappedXmlDriver());</tt><br /> <tt> </tt><br /> <tt> final String xmppUri = "xmpp://abispulsabot@geulis.local/?room=invoice&password=test";</tt><br /> <tt> from("direct:invoice").marshal(xstreamDataFormat).threads().multicast()</tt><br /> <tt> .to(xmppUri)</tt><br /> <tt> .to("log:xmpp-output");</tt><br /> <tt> from(xmppUri).threads().multicast()</tt><br /> <tt> .to("log:xmpp-input")</tt><br /> <tt> .to("seda:loggerInvoiceBean");</tt><br /> <tt> from("seda:loggerInvoiceBean").unmarshal(xstreamDataFormat).bean(loggerInvoiceListener);</tt><br /> <tt> }</tt><br /> <tt> });</tt><br /> <tt> camelContext.start();</tt><br /> <tt> </tt><br /> <tt> InvoiceListener invoiceListener = new ProxyBuilder(camelContext)</tt><br /> <tt> .endpoint("direct:invoice").build(InvoiceListener.class);</tt><br /> <tt> invoiceListener.invoiceCreated(123, "Bippo Indonesia");</tt><br /> <tt> }</tt><br /> <tt> </tt><br /> <tt> public void destroy() throws Exception {</tt><br /> <tt> camelContext.stop();</tt><br /> <tt> logger.info("Invoicing stopped");</tt><br /> <tt> }</tt><p /> <tt> public void setCamelContext(CamelContext camelContext) {</tt><br /> <tt> this.camelContext = camelContext;</tt><br /> <tt> }</tt><p /> <tt>}</tt><p /> Log output:<p /> <tt>04:41:04.302 [Blueprint Extender: 2] WARN o.a.a.b.c.BlueprintContainerImpl - Bundle id.co.bippo.usexmpp is waiting for namespace handlers [(&(objectClass=org.apache.aries.blueprint.NamespaceHandler)(osgi.service.blueprint.namespace=<a href="http://camel.apache.org/schema/blueprint))">http://camel.apache.org/schema/blueprint))</a>]</tt><br /> <tt>04:41:04.301 [Start Level Event Dispatcher] INFO org.apache.camel.impl.osgi.Activator - Camel activator starting</tt><br /> <tt>04:41:04.318 [Start Level Event Dispatcher] INFO org.apache.camel.impl.osgi.Activator - Camel activator started</tt><br /> <tt>04:41:05.683 [Blueprint Extender: 2] INFO org.apache.camel.impl.osgi.Activator - Found 13 @Converter classes to load</tt><br /> <tt>04:41:05.707 [Blueprint Extender: 2] INFO o.a.c.b.BlueprintCamelContext - Starting Apache Camel as property ShouldStartContext is true</tt><br /> <tt>04:41:05.708 [Blueprint Extender: 2] INFO o.a.camel.impl.DefaultCamelContext - Apache Camel 2.5.0 (CamelContext: camelContext) is starting</tt><br /> <tt>04:41:05.708 [Blueprint Extender: 2] INFO o.a.camel.impl.DefaultCamelContext - JMX enabled. Using ManagedManagementStrategy.</tt><br /> <tt>04:41:05.711 [Blueprint Extender: 2] WARN o.a.camel.impl.DefaultCamelContext - Could not find needed classes for JMX lifecycle strategy. Needed class is in spring-context.jar using Spring 2.5 or newer (spring-jmx.jar using Spring 2.0.x). NoClassDefFoundError: org/springframework/jmx/export/metadata/JmxAttributeSource</tt><br /> <tt>04:41:05.711 [Blueprint Extender: 2] WARN o.a.camel.impl.DefaultCamelContext - Cannot use JMX. Fallback to using DefaultManagementStrategy (non JMX).</tt><br /> <tt>04:41:05.717 [Blueprint Extender: 2] INFO o.a.camel.impl.DefaultCamelContext - Total 0 routes, of which 0 is started.</tt><br /> <tt>04:41:05.718 [Blueprint Extender: 2] INFO o.a.camel.impl.DefaultCamelContext - Apache Camel 2.5.0 (CamelContext: camelContext) started in 0.010 seconds</tt><br /> <tt>04:41:05.718 [Blueprint Extender: 2] INFO id.co.bippo.usexmpp.Invoicing - Invoicing started</tt><br /> <tt>04:41:06.613 [Smack Packet Reader (0)] ERROR org.jivesoftware.smack.PacketReader - Empty IQ packet! </iq></tt><br /> <tt>04:41:06.715 [Blueprint Extender: 2] INFO o.a.c.c.xmpp.XmppGroupChatProducer - Joined room: <a href="mailto:invoice@conference.geulis">invoice@conference.geulis</a> as: abispulsabot</tt><br /> <tt>04:41:06.729 [Blueprint Extender: 2] INFO o.a.camel.impl.DefaultCamelContext - Route: route1 started and consuming from: Endpoint[direct://invoice]</tt><br /> <tt>04:41:06.779 [Blueprint Extender: 2] INFO o.a.c.component.xmpp.XmppConsumer - Joined room: <a href="mailto:invoice@conference.geulis">invoice@conference.geulis</a> as: abispulsabot</tt><br /> <tt>04:41:06.780 [Blueprint Extender: 2] INFO o.a.camel.impl.DefaultCamelContext - Route: route2 started and consuming from: Endpoint[xmpp://<a href="mailto:abispulsabot@geulis.local">abispulsabot@geulis.local</a>/?password=******&room=invoice]</tt><br /> <tt>04:41:06.805 [Blueprint Extender: 2] INFO o.a.camel.impl.DefaultCamelContext - Route: route3 started and consuming from: Endpoint[seda://loggerInvoiceBean]</tt><br /> <tt>04:41:06.806 [Blueprint Extender: 2] INFO o.a.camel.impl.DefaultCamelContext - Apache Camel 2.5.0 (CamelContext: camelContext) is starting</tt><br /> <tt>04:41:06.806 [Blueprint Extender: 2] INFO o.a.camel.impl.DefaultCamelContext - Total 3 routes, of which 3 is started.</tt><br /> <tt>04:41:06.806 [Blueprint Extender: 2] INFO o.a.camel.impl.DefaultCamelContext - Apache Camel 2.5.0 (CamelContext: camelContext) started in 0.000 seconds</tt><br /> <tt>04:41:06.846 [Camel Thread 1 - Threads] INFO xmpp-output - Exchange[ExchangePattern:InOut, BodyType:byte[], Body:{"org.apache.camel.component.bean.BeanInvocation":{"org.apache.camel.component.bean.MethodBean":{"name":"invoiceCreated","type":"id.co.bippo.usexmpp.InvoiceListener","parameterTypes":{"java-class":["int","java.lang.String"]}},"object-array":{"int":123,"string":"Bippo Indonesia"}}}]</tt><br /> <tt>04:41:06.858 [Camel Thread 2 - Threads] INFO xmpp-input - Exchange[ExchangePattern:InOnly, BodyType:String, Body:{"org.apache.camel.component.bean.BeanInvocation":{"org.apache.camel.component.bean.MethodBean":{"name":"invoiceCreated","type":"id.co.bippo.usexmpp.InvoiceListener","parameterTypes":{"java-class":["int","java.lang.String"]}},"object-array":{"int":123,"string":"Bippo Indonesia"}}}]</tt><br /> <tt>04:41:06.880 [Camel Thread 0 - seda://loggerInvoiceBean] INFO i.c.b.usexmpp.LoggerInvoiceListener - Invoice #123 name: Bippo Indonesia created</tt><br /> <tt>04:41:08.380 [Camel Thread 0 - seda://loggerInvoiceBean] INFO i.c.b.usexmpp.LoggerInvoiceListener - 123/Bippo Indonesia done!</tt><p /> <h3> <b>Conclusion</b> </h3> <br /> <b>Apache Camel</b> is <b>unobtrusive</b> way to add <b>flexibility of routing, messaging, and integration patterns</b> with <b>XMPP</b> or <b><a href="http://camel.apache.org/components.html">other connectors supported by Camel</a></b>. <br /> (you can create your own connectors if you want...)<p /> I highly suggest the <b><a href="http://www.amazon.com/gp/product/1935182366?ie=UTF8&tag=springjavaee-20&linkCode=as2&camp=1789&creative=390957&creativeASIN=1935182366">Camel in Action Book</a></b> for the <i>best in-depth guide and examples</i> for <b>using Camel to develop your applications</b> more <b>productively</b>. </div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com9tag:blogger.com,1999:blog-2436480061461152965.post-31175704560699830802010-12-29T19:48:00.001-08:002011-04-28T14:47:47.804-07:00Activating the EMF Edit ItemProvider AdapterFactory-s (with CDO)<div class='posterous_autopost'> I used to have a problem with <b>CDO Editor UI</b>: it does not use my <b>EMF Edit</b> <b>models' ItemProvider </b>implementation (like <b>label formatting</b>, <b>image/icon</b>, and so on).<p /> Today I found the reason, I changed the model's <b>nsURI</b> and did not change all references to it.<p /> The relevant portion is <b>plugin.xml</b> for <b>your.model.edit</b> plugin :<p /> <tt> <extension point="org.eclipse.emf.edit.itemProviderAdapterFactories"></tt><br /> <tt> <factory</tt><br /> <tt> uri="<a href="http://www.abispulsa.com/model/1.0">http://www.abispulsa.com/model/1.0</a>"</tt><br /> <tt> class="com.abispulsa.provider.AbispulsaItemProviderAdapterFactory"</tt><br /> <tt> supportedTypes=</tt><br /> <tt> "org.eclipse.emf.edit.provider.IEditingDomainItemProvider</tt><br /> <tt> org.eclipse.emf.edit.provider.IStructuredItemContentProvider</tt><br /> <tt> org.eclipse.emf.edit.provider.ITreeItemContentProvider</tt><br /> <tt> org.eclipse.emf.edit.provider.IItemLabelProvider</tt><br /> <tt> org.eclipse.emf.edit.provider.IItemPropertySource"/></tt><br /> <tt> </extension></tt><p /> The way to <a href="http://eclipsedriven.blogspot.com/2010/12/reusing-emfedit-generated-itemproviders.html">get ComposedAdapterFactory I previously blogged</a> turned out to be incorrect:<p /> <tt>public class AbispulsaContentProvider extends AdapterFactoryContentProvider {</tt><p /> <tt> private static AdapterFactory adapterFactory;</tt><br /> <tt> </tt><br /> <tt> static {</tt><br /> <tt> adapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {</tt><br /> <tt> new ResourceItemProviderAdapterFactory(),</tt><br /> <tt> new AbispulsaItemProviderAdapterFactory(),</tt><br /> <tt> new ReflectiveItemProviderAdapterFactory()</tt><br /> <tt> });</tt><br /> <tt> }</tt><br /> <tt> </tt><br /> <tt> public AbispulsaContentProvider() {</tt><br /> <tt> super(adapterFactory);</tt><br /> <tt> }</tt><br /> <tt> </tt><br /> <tt>}</tt><p /> The correct way is like this:<p /> <tt>public class AbispulsaContentProvider extends AdapterFactoryContentProvider {</tt><p /> <tt> private static ComposedAdapterFactory adapterFactory;</tt><br /> <tt> </tt><br /> <tt> static {</tt><br /> <tt> Registry registry = EMFEditPlugin.getComposedAdapterFactoryDescriptorRegistry();</tt><br /> <tt> adapterFactory = new ComposedAdapterFactory(registry);</tt><br /> <tt> adapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory());</tt><br /> <tt> adapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());</tt><br /> <tt> }</tt><br /> <tt> </tt><br /> <tt> public AbispulsaContentProvider() {</tt><br /> <tt> super(adapterFactory);</tt><br /> <tt> }</tt><br /> <tt> </tt><br /> <tt>}</tt><p /> Although then you should move the <b>ComposedAdapterFactory</b> creation to another singleton class so it can be reused from both <b>ContentProvider</b> and <b>LabelProvider</b>.<p /> Note that for above to work, the extension <tt>org.eclipse.emf.edit.itemProviderAdapterFactories</tt> must also be set properly.<p /> This is actually not <b>CDO</b> specific, but applies to all <b>EMF Edit / Editor UI</b> in general.<br /> But it highlights a very important CDO feature: it reuses your EMF Edit ItemProvider implementations! :-) </div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com2tag:blogger.com,1999:blog-2436480061461152965.post-19811948125044074632010-12-29T18:42:00.001-08:002011-01-01T06:54:44.191-08:00Persisting EMF Objects to RDBMS with Teneo and Hibernate<div class="posterous_autopost">In my project, <b><a href="http://www.eclipse.org/modeling/">EMF Objects</a></b> are now being <b>persisted</b> in a <b><a href="http://wiki.eclipse.org/CDO">CDO Model Repository</a></b>. It used to use "plain <a href="http://wiki.eclipse.org/Teneo">Teneo</a>", but before scraping the code I'd like to share it in my blog so in case I want to use Teneo directly again I won't be in trouble. ;-)<br />
<br />
<b><a href="http://wiki.eclipse.org/Teneo">Teneo</a></b> is a way to <b>persist EMF Objects</b> in a <b>relational database</b> using <b>Hibernate</b> and <b>JPA annotations</b> (you can also use <b>EclipseLink</b>, but I think Hibernate is better supported by Teneo).<br />
<br />
JPA annotations are generated automatically by Teneo but you can configure the JPA mapping/annotations manually by using Teneo's JPA <b>model annotations</b> (not JPA Java annotations) on the <b>EMF metamodel</b>.<br />
<h3>Connecting to the Hibernate EMF Data Store </h3><tt>private void initHibernate() {</tt><br />
<tt> // Set the database information, Environment is org.hibernate.cfg.Environment</tt><br />
<tt> final Properties props = new Properties();</tt><br />
<tt> </tt><br />
<tt>// props.setProperty(Environment.DRIVER, "org.hsqldb.jdbcDriver");</tt><br />
<tt>// props.setProperty(Environment.USER, "sa");</tt><br />
<tt>// //props.setProperty(Environment.URL, "jdbc:hsqldb:file:/tmp/hsqldb");</tt><br />
<tt>// props.setProperty(Environment.URL, "jdbc:hsqldb:mem:abispulsamodel");</tt><br />
<tt>// props.setProperty(Environment.PASS, "");</tt><br />
<tt>// props.setProperty(Environment.DIALECT, org.hibernate.dialect.HSQLDialect.class.getName());</tt><br />
<tt> props.setProperty(Environment.DRIVER, "com.mysql.jdbc.Driver");</tt><br />
<tt> props.setProperty(Environment.USER, "root");</tt><br />
<tt> props.setProperty(Environment.URL, "jdbc:mysql://127.0.0.1:3306/abispulsamodel");</tt><br />
<tt> props.setProperty(Environment.PASS, "");</tt><br />
<tt> props.setProperty(Environment.DIALECT, MySQLInnoDBDialect.class.getName());</tt><br />
<tt> // Set cascade policy for containment and non-containment references</tt><br />
<tt> props.setProperty(PersistenceOptions.CASCADE_POLICY_ON_CONTAINMENT, "ALL");</tt><br />
<tt> props.setProperty(PersistenceOptions.CASCADE_POLICY_ON_NON_CONTAINMENT, </tt><br />
<tt> "MERGE,PERSIST,REFRESH");</tt><br />
<tt> </tt><br />
<tt> // Create and register the datastore, it will be accessible using</tt><br />
<tt> // this EMF Resource URI: hibernate://?dsname=abispulsa</tt><br />
<tt> hbds = HbHelper.INSTANCE.createRegisterDataStore("abispulsa");</tt><br />
<tt> // Set the EPackages that will be used with this data store</tt><br />
<tt> hbds.setEPackages(new EPackage[]{AbispulsaPackage.eINSTANCE});</tt><br />
<tt> hbds.setProperties(props);</tt><br />
<tt> hbds.initialize();</tt><br />
<tt>}</tt><br />
<br />
Note: the above syntax is for Teneo 1.1.2. For Teneo 1.2.0, <b>setProperties()</b> becomes <b>setDataStoreProperties()</b>.<br />
<br />
As you can see, configuring data store properties is just like with "plain" Hibernate.<br />
<br />
You can change the DBMS backend easily by simply changing a few lines.<br />
And of course you can externalize the configuration to a .properties file or .xml or whatever suits you best.<br />
<h3>Using the Hibernate EMF Resource </h3>Simple load:<br />
<br />
<tt>HibernateResource resource = new HibernateResource(URI.createURI("hibernate://?dsname=abispulsa"));</tt><br />
<tt>resource.load(null);</tt><br />
<br />
Load with query, get specific EMF Object classes:<br />
<tt>HibernateResource resource = new HibernateResource(</tt><br />
<tt> URI.createURI("hibernate://?dsname=abispulsa&query1=FROM Organization"));</tt><br />
<tt>resource.load(null);</tt><br />
<br />
After you have a Resource, it's pretty much standard EMF / Resource stuff from here just like regular XMI-based resources.<br />
<br />
"Copy and paste" (aka <b>import</b>) <b>EMF Objects</b> from an <b>XMI file</b> to <b>Hibernate Resource</b>:<br />
<br />
<tt>ResourceSet rset = new ResourceSetImpl();</tt><br />
<tt>Resource xmiResource = rset.createResource(URI.createFileURI("/path/to/model/Catalog.xmi"));</tt><br />
<tt>try {</tt><br />
<tt> xmiResource.load(null);</tt><br />
<tt> log.info("XMI Resource has " + xmiResource.getContents().size() + " objects.");</tt><br />
<tt> for (EObject obj : xmiResource.getContents()) {</tt><br />
<tt> log.info(obj.toString());</tt><br />
<tt> }</tt><br />
<tt> Collection<EObject> copies = EcoreUtil.copyAll(xmiResource.getContents());</tt><br />
<tt> resource.getContents().addAll( copies );</tt><br />
<tt> resource.save(null);</tt><br />
<tt>} catch (Exception e) {</tt><br />
<tt> log.error("Error", e);</tt><br />
<tt> throw new RuntimeException(e);</tt><br />
<tt>}</tt><br />
<br />
Each time you open a Hibernate resource, a new Hibernate session is created by Teneo, which is fine for most purposes.<br />
<br />
But you can change this by using your own session manager for Teneo.<br />
<h3>Disconnecting from the Data Store </h3><tt>void closeHibernate() {</tt><br />
<tt> if (hbds != null) {</tt><br />
<tt> hbds.close();</tt><br />
<tt> HbHelper.INSTANCE.deRegisterDataStore(hbds);</tt><br />
<tt> hbds = null;</tt><br />
<tt> }</tt><br />
<tt>}</tt><br />
<h3>Comparison with CDO </h3>With CDO it's almost the same, but with CDO there's a need to explicitly <b>openTransaction()</b> before editing objects in a CDO resource.<br />
<br />
After manipulating a resource, either <b>resource.save()</b> or <b>transaction.commit()</b> will persist the changes.<br />
<br />
<b>Connect to CDO Server</b><br />
<tt>private static final String CONNECTION_ADDRESS = "tcp://localhost:2036";</tt><br />
<tt>private static final String REPO_NAME = "abispulsabisnis";</tt><br />
<tt>...</tt><br />
<tt>log.info("Connecting to CDO to {} using repo {}", CONNECTION_ADDRESS, REPO_NAME);</tt><br />
<tt>org.eclipse.net4j.connector.IConnector connector = Net4jUtil.getConnector(IPluginContainer.INSTANCE, CONNECTION_ADDRESS);</tt><br />
<tt>// Create configuration</tt><br />
<tt>org.eclipse.emf.cdo.net4j.CDOSessionConfiguration sessionConfiguration = CDONet4jUtil.createSessionConfiguration();</tt><br />
<tt>sessionConfiguration.setConnector(connector);</tt><br />
<tt>sessionConfiguration.setRepositoryName(REPO_NAME);</tt><br />
<tt>org.eclipse.emf.cdo.session.CDOSession session = sessionConfiguration.openSession();</tt><br />
<tt>session.getPackageRegistry().putEPackage(AbispulsaPackage.eINSTANCE);</tt><br />
<br />
CDO is designed to be used over a network using TCP connector. But it's possible to run the CDO server and CDO client on the same VM instance, and a JVM Net4j connector is provided.<br />
<br />
<b>Open a Resource</b><br />
<tt>org.eclipse.emf.cdo.transaction.CDOTransaction transaction = session.openTransaction();</tt><br />
<tt>org.eclipse.emf.cdo.eresource.CDOResource resource = transaction.getResource("/organization");</tt><br />
<br />
<b>Save Changes</b><br />
<tt>try {</tt><br />
<tt>//resource.save(null); // You can also do this (non-CDO aware)</tt><br />
<tt>transaction.commit(); // or this (CDO-aware)</tt><br />
<tt>} catch (Exception e) {</tt><br />
<tt>...</tt><br />
<tt>}</tt><br />
<br />
<b>Update: </b>As CDO committer <b>Eike Stepper</b> points out, CDO's resource.save() simply delegates to the underlying CDO Transaction's commit().</div><div class="posterous_autopost"><br />
<b>Close CDO Session</b><br />
<tt>session.close();</tt><br />
<br />
That's it! I originally wanted to cover only <b>Teneo</b> but you got <b>CDO</b> Crash Course as a bonus. ;-) </div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com15tag:blogger.com,1999:blog-2436480061461152965.post-13636790820617338452010-12-29T15:04:00.001-08:002011-04-28T14:48:08.866-07:00Building a Workspace Resources-powered Common Navigator Framework (CNF) View<div class='posterous_autopost'> <b><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/guide/cnf.htm">Common Navigator Framework (CNF) View</a></b> is a very useful and flexible component to display <b>structure</b> of any <b>object</b> in an <b>Eclipse RCP</b> Application.<p /> There are two ways to use CNF : <ol type="1"> <li value="1" type="1">Displaying Eclipse <b>Resources</b> inside <b>Workspace</b> </li><li value="2" type="1">Displaying custom content (non-resources) </li></ol> The good news is the two ways can be mixed.<br /> If you use <b>Resources</b> you can extend it with "custom content" underneath (using <b>Navigator Content Extensions</b> aka <b>NCEs</b>).<p /> The bad news is even on the current milestone of <b>Eclipse</b> Indigo 3.7 it's still not straightforward to use <b>CNF</b> with <b>Resources</b>.<br /> Again the good news is that there is a solution. :-)<p /> I'll focus on displaying <b>workspace resources</b> (folders and files inside the <b>workspace physical directory</b>) in CNF because it's not obvious how to do it inside <b>Eclipse RCP</b>.<br /> Note that if you use the IDE Application (at least the <b>IDE WorkbenchAdvisor</b>) it just works by default. But in Eclipse RCP you have to do some preparatory work.<p /> The steps are outlined in <b><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/guide/cnf_steps_content.htm">Eclipse Help > Common Navigator Framework > Content and Action Binding</a></b>, as follows.<p /> Please note, that using CNF inside of your own RCP requires three additional steps.<p /> <ol type="1"> <li value="1" type="1">Add the </li></ol><tt>org.eclipse.ui.ide</tt> as dependent plug-in <li value="2" type="1">To get the resource workspace root (</li><tt>IWorkspaceRoot</tt>) as default page input, override the <tt><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/ui/application/WorkbenchAdvisor.html#getDefaultPageInput">WorkbenchAdvisor.getDefaultPageInput()</a></tt> method in your WorkbenchAdvisor: <br /> <div class="CodeRay"> <div class="code"><pre>public IAdaptable getDefaultPageInput() { return ResourcesPlugin.getWorkspace().getRoot(); }</pre></div> </div> <li value="3" type="1">Hook the workbench adapters correctly before they are used, so add this code to the </li><tt><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/ui/application/WorkbenchAdvisor.html#initialize">WorkbenchAdvisor.initialize(IWorkbenchConfigurer)</a></tt> method: <br /> <div class="CodeRay"> <div class="code"><pre>public void initialize(IWorkbenchConfigurer configurer) { IDE.registerAdapters(); }</pre></div> </div> <br /> This will display <b>workspace resources</b> with <b>labels</b> but there are no <b>icons</b> yet.<p /> To display icons you still need to "steal" the internal <b>declareWorkbenchImages()</b> from <a href="http://dev.eclipse.org/viewcvs/viewvc.cgi/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEWorkbenchAdvisor.java?view=annotate">org/eclipse/ui/internal/ide/application/IDEWorkbenchAdvisor.java</a> in <b>org.eclipse.ide.ui.application</b> plugin.<p /> So the complete relevant parts become like this:<p /> <tt>import org.eclipse.core.resources.IWorkspaceRoot;</tt><br /> <tt>import org.eclipse.core.resources.ResourcesPlugin;</tt><br /> <tt>import org.eclipse.core.runtime.FileLocator;</tt><br /> <tt>import org.eclipse.core.runtime.IAdaptable;</tt><br /> <tt>import org.eclipse.core.runtime.Path;</tt><br /> <tt>import org.eclipse.core.runtime.Platform;</tt><br /> <tt>import org.eclipse.jface.resource.ImageDescriptor;</tt><br /> <tt>import org.eclipse.ui.application.IWorkbenchConfigurer;</tt><br /> <tt>import org.eclipse.ui.application.IWorkbenchWindowConfigurer;</tt><br /> <tt>import org.eclipse.ui.application.WorkbenchAdvisor;</tt><br /> <tt>import org.eclipse.ui.application.WorkbenchWindowAdvisor;</tt><br /> <tt>import org.eclipse.ui.ide.IDE;</tt><br /> <tt>import org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages;</tt><br /> <tt>import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin;</tt><br /> <tt>import org.osgi.framework.Bundle;</tt><br /> <tt>...</tt><br /> <tt> @Override</tt><br /> <tt> public void initialize(IWorkbenchConfigurer configurer) {</tt><br /> <tt> super.initialize(configurer);</tt><br /> <tt> //configurer.setSaveAndRestore(true); // if you want</tt><br /> <tt> IDE.registerAdapters();</tt><br /> <tt> declareWorkbenchImages();</tt><br /> <tt> }</tt><br /> <tt>...</tt><br /> <tt> /**</tt><br /> <tt> * Declares all IDE-specific workbench images. This includes both "shared"</tt><br /> <tt> * images (named in {@link IDE.SharedImages}) and internal images (named in</tt><br /> <tt> * {@link org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages}).</tt><br /> <tt> * </tt><br /> <tt> * @see IWorkbenchConfigurer#declareImage</tt><br /> <tt> */</tt><br /> <tt> private void declareWorkbenchImages() {</tt><p /> <tt> final String ICONS_PATH = "$nl$/icons/full/";//$NON-NLS-1$</tt><br /> <tt> final String PATH_ELOCALTOOL = ICONS_PATH + "elcl16/"; // Enabled //$NON-NLS-1$</tt><p /> <tt> // toolbar</tt><br /> <tt> // icons.</tt><br /> <tt> final String PATH_DLOCALTOOL = ICONS_PATH + "dlcl16/"; // Disabled //$NON-NLS-1$</tt><br /> <tt> // //$NON-NLS-1$</tt><br /> <tt> // toolbar</tt><br /> <tt> // icons.</tt><br /> <tt> final String PATH_ETOOL = ICONS_PATH + "etool16/"; // Enabled toolbar //$NON-NLS-1$</tt><br /> <tt> // //$NON-NLS-1$</tt><br /> <tt> // icons.</tt><br /> <tt> final String PATH_DTOOL = ICONS_PATH + "dtool16/"; // Disabled toolbar //$NON-NLS-1$</tt><br /> <tt> // //$NON-NLS-1$</tt><br /> <tt> // icons.</tt><br /> <tt> final String PATH_OBJECT = ICONS_PATH + "obj16/"; // Model object //$NON-NLS-1$</tt><br /> <tt> // //$NON-NLS-1$</tt><br /> <tt> // icons</tt><br /> <tt> final String PATH_WIZBAN = ICONS_PATH + "wizban/"; // Wizard //$NON-NLS-1$</tt><br /> <tt> // //$NON-NLS-1$</tt><br /> <tt> // icons</tt><p /> <tt> // View icons</tt><br /> <tt> final String PATH_EVIEW= ICONS_PATH + "eview16/"; //$NON-NLS-1$</tt><p /> <br /> <tt> Bundle ideBundle = Platform.getBundle(IDEWorkbenchPlugin.IDE_WORKBENCH);</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC, PATH_ETOOL</tt><br /> <tt> + "build_exec.gif", false); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_HOVER,</tt><br /> <tt> PATH_ETOOL + "build_exec.gif", false); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_DISABLED,</tt><br /> <tt> PATH_DTOOL + "build_exec.gif", false); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC, PATH_ETOOL</tt><br /> <tt> + "search_src.gif", false); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_HOVER,</tt><br /> <tt> PATH_ETOOL + "search_src.gif", false); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_DISABLED,</tt><br /> <tt> PATH_DTOOL + "search_src.gif", false); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_NEXT_NAV, PATH_ETOOL</tt><br /> <tt> + "next_nav.gif", false); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_PREVIOUS_NAV, PATH_ETOOL</tt><br /> <tt> + "prev_nav.gif", false); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_WIZBAN_NEWPRJ_WIZ, PATH_WIZBAN</tt><br /> <tt> + "newprj_wiz.png", false); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFOLDER_WIZ,</tt><br /> <tt> PATH_WIZBAN + "newfolder_wiz.png", false); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFILE_WIZ, PATH_WIZBAN</tt><br /> <tt> + "newfile_wiz.png", false); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTDIR_WIZ,</tt><br /> <tt> PATH_WIZBAN + "importdir_wiz.png", false); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTZIP_WIZ,</tt><br /> <tt> PATH_WIZBAN + "importzip_wiz.png", false); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTDIR_WIZ,</tt><br /> <tt> PATH_WIZBAN + "exportdir_wiz.png", false); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTZIP_WIZ,</tt><br /> <tt> PATH_WIZBAN + "exportzip_wiz.png", false); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_WIZBAN_RESOURCEWORKINGSET_WIZ,</tt><br /> <tt> PATH_WIZBAN + "workset_wiz.png", false); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_DLGBAN_SAVEAS_DLG, PATH_WIZBAN</tt><br /> <tt> + "saveas_wiz.png", false); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_DLGBAN_QUICKFIX_DLG, PATH_WIZBAN</tt><br /> <tt> + "quick_fix.png", false); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJ_PROJECT,</tt><br /> <tt> PATH_OBJECT + "prj_obj.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDE.SharedImages.IMG_OBJ_PROJECT_CLOSED, PATH_OBJECT</tt><br /> <tt> + "cprj_obj.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OPEN_MARKER,</tt><br /> <tt> PATH_ELOCALTOOL + "gotoobj_tsk.gif", true); //$NON-NLS-1$</tt><p /> <br /> <tt> // Quick fix icons</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ELCL_QUICK_FIX_ENABLED,</tt><br /> <tt> PATH_ELOCALTOOL + "smartmode_co.gif", true); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_DLCL_QUICK_FIX_DISABLED,</tt><br /> <tt> PATH_DLOCALTOOL + "smartmode_co.gif", true); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_OBJS_FIXABLE_WARNING,</tt><br /> <tt> PATH_OBJECT + "quickfix_warning_obj.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_OBJS_FIXABLE_ERROR,</tt><br /> <tt> PATH_OBJECT + "quickfix_error_obj.gif", true); //$NON-NLS-1$</tt><p /> <br /> <tt> // task objects</tt><br /> <tt> // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_HPRIO_TSK,</tt><br /> <tt> // PATH_OBJECT+"hprio_tsk.gif");</tt><br /> <tt> // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_MPRIO_TSK,</tt><br /> <tt> // PATH_OBJECT+"mprio_tsk.gif");</tt><br /> <tt> // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_LPRIO_TSK,</tt><br /> <tt> // PATH_OBJECT+"lprio_tsk.gif");</tt><p /> <tt> declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_TASK_TSK,</tt><br /> <tt> PATH_OBJECT + "taskmrk_tsk.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_BKMRK_TSK,</tt><br /> <tt> PATH_OBJECT + "bkmrk_tsk.gif", true); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_OBJS_COMPLETE_TSK, PATH_OBJECT</tt><br /> <tt> + "complete_tsk.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_OBJS_INCOMPLETE_TSK, PATH_OBJECT</tt><br /> <tt> + "incomplete_tsk.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_ITEM, PATH_OBJECT</tt><br /> <tt> + "welcome_item.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_BANNER, PATH_OBJECT</tt><br /> <tt> + "welcome_banner.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_OBJS_ERROR_PATH, PATH_OBJECT</tt><br /> <tt> + "error_tsk.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_OBJS_WARNING_PATH, PATH_OBJECT</tt><br /> <tt> + "warn_tsk.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_OBJS_INFO_PATH, PATH_OBJECT</tt><br /> <tt> + "info_tsk.gif", true); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_LCL_FLAT_LAYOUT, PATH_ELOCALTOOL</tt><br /> <tt> + "flatLayout.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_LCL_HIERARCHICAL_LAYOUT,</tt><br /> <tt> PATH_ELOCALTOOL + "hierarchicalLayout.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEM_CATEGORY,</tt><br /> <tt> PATH_ETOOL + "problem_category.gif", true); //$NON-NLS-1$</tt><p /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEMS_VIEW,</tt><br /> <tt> PATH_EVIEW + "problems_view.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEMS_VIEW_ERROR,</tt><br /> <tt> PATH_EVIEW + "problems_view_error.gif", true); //$NON-NLS-1$</tt><br /> <tt> declareWorkbenchImage(ideBundle,</tt><br /> <tt> IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEMS_VIEW_WARNING,</tt><br /> <tt> PATH_EVIEW + "problems_view_warning.gif", true); //$NON-NLS-1$</tt><p /> <tt> // synchronization indicator objects</tt><br /> <tt> // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_WBET_STAT,</tt><br /> <tt> // PATH_OVERLAY+"wbet_stat.gif");</tt><br /> <tt> // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_SBET_STAT,</tt><br /> <tt> // PATH_OVERLAY+"sbet_stat.gif");</tt><br /> <tt> // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_CONFLICT_STAT,</tt><br /> <tt> // PATH_OVERLAY+"conflict_stat.gif");</tt><p /> <tt> // content locality indicator objects</tt><br /> <tt> // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_NOTLOCAL_STAT,</tt><br /> <tt> // PATH_STAT+"notlocal_stat.gif");</tt><br /> <tt> // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_LOCAL_STAT,</tt><br /> <tt> // PATH_STAT+"local_stat.gif");</tt><br /> <tt> // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_FILLLOCAL_STAT,</tt><br /> <tt> // PATH_STAT+"filllocal_stat.gif");</tt><br /> <tt> }</tt><p /> <tt> /**</tt><br /> <tt> * Declares an IDE-specific workbench image.</tt><br /> <tt> * </tt><br /> <tt> * @param symbolicName</tt><br /> <tt> * the symbolic name of the image</tt><br /> <tt> * @param path</tt><br /> <tt> * the path of the image file; this path is relative to the base</tt><br /> <tt> * of the IDE plug-in</tt><br /> <tt> * @param shared</tt><br /> <tt> * <code>true</code> if this is a shared image, and</tt><br /> <tt> * <code>false</code> if this is not a shared image</tt><br /> <tt> * @see IWorkbenchConfigurer#declareImage</tt><br /> <tt> */</tt><br /> <tt> private void declareWorkbenchImage(Bundle ideBundle, String symbolicName,</tt><br /> <tt> String path, boolean shared) {</tt><br /> <tt> URL url = FileLocator.find(ideBundle, new Path(path), null);</tt><br /> <tt> ImageDescriptor desc = ImageDescriptor.createFromURL(url);</tt><br /> <tt> getWorkbenchConfigurer().declareImage(symbolicName, desc, shared);</tt><br /> <tt> }</tt><p /> Additional resources: <ul> <li><a href="http://dev.eclipse.org/blogs/francis/2008/05/27/magic-required-to-use-the-common-navigator-in-an-rcp-application/">Magic Required to use the Common Navigator in an RCP Application that uses Resources</a> </li><li><a href="http://scribbledideas.blogspot.com/2006/07/pdf-versions-now-available.html">Building a Common Navigator Framework viewer</a> </li></ul> </div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com6tag:blogger.com,1999:blog-2436480061461152965.post-32128647357028534822010-12-29T13:48:00.001-08:002011-04-28T14:48:08.867-07:00Types of Applications You Can Create with Eclipse Platform<div class='posterous_autopost'> The <b>Eclipse Platform</b> provides many <b>tools </b>and <b>frameworks</b> to build your application. They are flexible so you can do it in several ways according to your requirements.<p /> Here are the most common application types:<p /> <h4> 1. OSGi (Equinox) Application </h4> Using the bare minimum <b>OSGi runtime Equinox</b> (or your favorite OSGi runtime).<br /> You can use OSGi services and OSGi bundles, but not loading the <b>Eclipse Runtime (org.eclipse.core.runtime)</b>.<p /> <h4> 2. Eclipse Console Application </h4> Using <b>Eclipse Runtime (org.eclipse.core.runtime</b>), but without any UI bundles.<br /> You can actually do a lot of things in this mode.<p /> <h4> 3. Server/Daemon-style Eclipse Console Application </h4> This is actually just an Eclipse Console application, but that does not exit and continue running in the background.<br /> Daemon applications usually started with the <b>-noExit</b> command line argument or configuration.<p /> <h4> 4. <a href="http://www.eclipse.org/home/categories/rcp.php">Eclipse Rich Client Platform (RCP)</a> for Desktop Applications </h4> The most typical <b>Eclipse application</b> for the <b>desktop</b>.<br /> Uses the <b>org.eclipse.rcp</b> plugin.<br /> With features like perspectives, views, Common Navigator Framework, editors, action bars (toolbars), status line, menus, popup menus, ...<br /> It offers the <b>rich plugins </b>and functionality provided by Eclipse framework, and manageable overhead (disk space, memory usage, and performance).<br /> A well designed <b>Eclipse RCP application</b> starts just as <b>fast</b> as a hand-coded Swing application!<br /> Don't let the (slow) startup time of Eclipse Java IDE fool you...<p /> <h4> 5. <a href="http://www.eclipse.org/downloads/">Eclipse IDE Application</a> </h4> The most representative example is the <b>Eclipse Java IDE</b> itself.<br /> Uses <b>org.eclipse.ui.ide</b> plugin and other plugins, including <b>org.eclipse.jdt.ui</b>, <b>org.eclipse.team.ui</b>.<br /> Most application developers do not use these plugins, for smaller disk and memory footprint requirements.<br /> However, just to let you know that you can use them if you want. :-)<p /> <h4> 6. <a href="http://www.eclipse.org/rap/">Eclipse Rich Ajax Platform (RAP)</a> for Web Applications </h4> The new kid on the block, but is already very potential and is gaining momentum <i>very fast</i>.<br /> Uses <b>org.eclipse.rap.ui</b> plugin.<br /> RAP applications can be single sourced so that it runs on both Eclipse RCP and Eclipse RAP runtimes, however you need to be aware that RAP applications are multi-user / multi-session, where RCP applications are single user.<br /> The most common Eclipse UI plugins are supported by RAP runtime (including EMF Editor UI), and more (Draw2d, Common Navigator Framework aka CNF) are still coming soon.<p /> <h4> 7. <a href="http://www.eclipse.org/riena/">Eclipse Riena RCP Application</a> </h4> Framework for multi-tier / distributed application development.<br /> Write your plugins / application components in an uniform approach.<br /> Then deploy them in any side, whether in server or client-side.<br /> <b>Eclipse Riena</b> applications have at least two deployment artifacts, the one in client and the one in server.<br /> However, components do not care about the deployment, you write the components using the same framework.<br /> Riena abstracts the details of the deployment.<p /> <h4> 8. <a href="http://www.eclipse.org/riena/">Eclipse Riena RAP Application</a> </h4> The exact same application that you can write on Eclipse Riena RCP, can be deployed as a web application using RAP frontend!<p /> <h4> 9. Standalone Java application </h4> Contrary to what you might have believed, many Eclipse projects that bears the prefix "org.eclipse" in its name <b>do not require running within Eclipse Runtime</b>.<br /> For example, <b>EMF (Eclipse Modeling Framework) </b>runs fine within a standard Java static main() method.<br /> You just need to configure several stuff, that are normally done automatically using Eclipse extension mechanism. ;-) </div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com8tag:blogger.com,1999:blog-2436480061461152965.post-11209406215973356162010-12-29T11:09:00.001-08:002010-12-29T13:15:43.428-08:00Reusing EMF.Edit-generated ItemProviders for implementing Eclipse UI IContentProvider<div class="posterous_autopost"><img height="444" src="http://posterous.com/getfile/files.posterous.com/eclipsedriven/B4haZSOUWHSXLKqPEmERhACQeFrz2pG4mOCpcnheNxNO6GIbAhc9SZH81vs3/CNF-Navigator-view-EMF.png" width="326" /> <br />
<br />
<b>Eclipse Platform</b> allows us to be more productive by reusing many parts of the <b>framework</b>.<br />
<br />
For example, whether we want to display content in a standard <b><a href="http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/viewers/TreeViewer.html">TreeViewer SWT widget</a></b> or the much more versatile <b><a href="http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/cnf.htm">Common Navigator Framework (CNF)</a></b>, we can <i>reuse</i> the same objects for both.<br />
<br />
We need to provide a <b>model</b> (the object itself) and two <b>presenters: </b>an <b><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/viewers/class-use/ITreeContentProvider.html">ITreeContentProvider</a></b> (which extends the generic <b><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/viewers/IContentProvider.html">IContentProvider</a></b>) and an <b><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/jface/viewers/ILabelProvider.html">ILabelProvider</a></b>. [Note: I just realized that this is the real world example of the often hailed <b>MVP pattern aka Model-View-Presenter</b>!]<br />
<br />
While you <i>can</i> implement a <b>content provider</b> and <b>label provider</b> yourself, if you're like me it's sooo much easier if you just <i>reuse</i> Eclipse's <b><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/ui/model/WorkbenchContentProvider.html">BaseWorkbenchContentProvider</a></b> and <b><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/ui/model/WorkbenchLabelProvider.html">WorkbenchLabelProvider</a></b> and then make your objects adaptable to <b>IWorkbenchAdapter</b>.<br />
<h3>Overview </h3><br />
There are three steps to make this happen: <br />
<ol type="1"><li type="1" value="1">Implement <b>IWorkbenchAdapter</b> for your model class </li>
<li type="1" value="2">Create a <b><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/IAdapterFactory.html">org.eclipse.core.runtime.IAdapterFactory</a></b> implementation that can create <b>IWorkbenchAdapter</b> adapters for your model classes </li>
<li type="1" value="3">Register your <b>IAdapterFactory</b> to the Eclipse Platform adapter manager </li>
</ol>As noted in the post title, I have a surprise shortcut for you! ;-)<br />
<br />
But just to make things interesting, I'll describe in detail how <i>laborious</i> it still is to adapt to <b>IWorkbenchAdapter</b> even by reusing <b>BaseWorkbenchContentProvider</b> and <b>WorkbenchLabelProvider</b> for your models. <br />
<h3>Implementing IWorkbenchAdapter </h3><br />
Here is one way to implement <b><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/ui/model/WorkbenchAdapter.html">IWorkbenchAdapter</a></b> :<br />
<br />
<tt>import org.eclipse.jface.resource.ImageDescriptor;</tt><br />
<tt>import org.eclipse.ui.model.IWorkbenchAdapter;</tt><br />
<tt>import org.eclipse.ui.plugin.AbstractUIPlugin;</tt><br />
<tt>import com.abispulsa.Contact;</tt><br />
<tt>public class ContactWorkbenchAdapter implements IWorkbenchAdapter {</tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public Object[] getChildren(Object o) {</tt><br />
<tt> return ((Contact)o).getMobileNumbers().toArray();</tt><br />
<tt> }</tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public ImageDescriptor getImageDescriptor(Object object) {</tt><br />
<tt> return AbstractUIPlugin.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/user.png");</tt><br />
<tt> }</tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public String getLabel(Object o) {</tt><br />
<tt> return ((Contact)o).getName();</tt><br />
<tt> }</tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public Object getParent(Object o) {</tt><br />
<tt> return ((Contact)o).eContainer();</tt><br />
<tt> }</tt><br />
<tt>}</tt><br />
<br />
To be honest, I think the name <b>IWorkbenchAdapter</b> is confusing. It would be clearer if the interface name is <b>IWorkbenchContent</b>, simply for the fact that <b>BaseWorkbenchContentProvider</b> expects the objects to be an instance of <b>IWorkbenchAdapter</b>. The fact that we should <b>adapt</b> our objects to <b>IWorkbenchAdapter</b> instead of implementing <b>IWorkbenchAdapter</b> interface directly in our objects in another matter. In other words, it's possible to use <b>BaseWorkbenchContentProvider</b> with our objects *without* making our objects "adapt" to anything else, as long as it implements <b>IWorkbenchAdapter</b>.<br />
<br />
The above is pretty much a manual way to implement a <b>IWorkbenchAdapter</b> for model class. And of course, you have to do that for <b>*every*</b> model class you have. One adapter per model class. That's scary, huh?! Unless you have a predefined interface or base class... but I have a better idea (surprise! but later on below..)<br />
<h3>Creating the IAdapterFactory </h3><br />
This is the less painful step, implement <b><a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/core/runtime/IAdapterFactory.html">IAdapterFactory</a></b> that can create <b>IWorkbenchAdapter</b> for your objects :<br />
<br />
<tt>import org.eclipse.core.runtime.IAdapterFactory;</tt><br />
<tt>import org.eclipse.ui.model.IWorkbenchAdapter;</tt><br />
<tt>import com.abispulsa.Contact;</tt><br />
<tt>import com.abispulsa.MobileNumber;</tt><br />
<tt>import com.abispulsa.Organization;</tt><br />
<tt>public class AbisPulsaAdapterFactory implements IAdapterFactory {</tt><br />
<tt> private ContactWorkbenchAdapter contactAdapter = new ContactWorkbenchAdapter();</tt><br />
<tt> private OrganizationWorkbenchAdapter organizationAdapter = new OrganizationWorkbenchAdapter();</tt><br />
<tt> private MobileNumberWorkbenchAdapter mobileNumberAdapter = new MobileNumberWorkbenchAdapter();</tt><br />
<tt> </tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, java.lang.Class)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public Object getAdapter(Object adaptableObject, Class adapterType) {</tt><br />
<tt> if (adapterType == IWorkbenchAdapter.class) {</tt><br />
<tt> if (adaptableObject instanceof Contact)</tt><br />
<tt> return contactAdapter;</tt><br />
<tt> else if (adaptableObject instanceof Organization)</tt><br />
<tt> return organizationAdapter;</tt><br />
<tt> else if (adaptableObject instanceof MobileNumber)</tt><br />
<tt> return mobileNumberAdapter;</tt><br />
<tt> }</tt><br />
<tt> return null;</tt><br />
<tt> }</tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.core.runtime.IAdapterFactory#getAdapterList()</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public Class[] getAdapterList() {</tt><br />
<tt> return new Class[] { IWorkbenchAdapter.class };</tt><br />
<tt> }</tt><br />
<tt>}</tt><br />
<h3>Register the Factory for the Model Classes </h3>For each model class that can be adapted to <b>IWorkbenchAdapter</b> using your adapter factory, register it to the Eclipse Platform :<br />
<br />
<tt>import org.eclipse.core.runtime.Platform;</tt><br />
<tt>import com.abispulsa.Contact;</tt><br />
<tt>...</tt><br />
<tt>private AbisPulsaAdapterFactory adapterFactory = new AbisPulsaAdapterFactory();</tt><br />
<tt>...</tt><br />
<tt>Platform.getAdapterManager().registerAdapters(adapterFactory, Contact.class);</tt><br />
<h3>Using BaseWorkbenchContentProvider in the TreeViewer </h3>It's actually pretty standard stuff, but in case I have someone new to SWT/Eclipse/RCP/RAP ecosystem, this should provided a quick orientation to connect the above stuff:<br />
<br />
<tt>import org.eclipse.jface.viewers.TreeViewer;</tt><br />
<tt>import org.eclipse.swt.SWT;</tt><br />
<tt>import org.eclipse.ui.model.BaseWorkbenchContentProvider;</tt><br />
<tt>import org.eclipse.ui.model.WorkbenchLabelProvider;</tt><br />
<tt>import com.abispulsa.AbispulsaFactory;</tt><br />
<tt>...</tt><br />
<tt>treeViewer = new TreeViewer(parent, SWT.BORDER | SWT.MULTI | SWT.V_SCROLL);</tt><br />
<tt>getSite().setSelectionProvider(treeViewer);</tt><br />
<tt>treeViewer.setLabelProvider(new WorkbenchLabelProvider());</tt><br />
<tt>treeViewer.setContentProvider(new BaseWorkbenchContentProvider());</tt><br />
<tt>...</tt><br />
<tt>Contact contact = AbispulsaFactory.eINSTANCE.createContact();</tt><br />
<tt>contact.setName("Hendy Irawan");</tt><br />
<tt>treeViewer.setInput(contact);</tt><br />
Done!!!<br />
<h3>Reflecting Back </h3><br />
WHOA....! A lot of stuff just to get something displayed on a tree viewer!<br />
Actually, it's not that bad and it's quite powerful. Eclipse Platform provides : <br />
<ul><li>Your objects can now be displayed in pretty much any kind of exotic view possible. <b>BaseWorkbenchContentProvider</b> and <b>WorkbenchLabelProvider</b> will handle all that for you, consistently. </li>
<li>Your objects can, of course, be displayed inside a <b>Common Navigator Framework (CNF)</b> view, which can display several objects using a flexible combination of content providers, actions, popup menus, etc. </li>
</ul>With other (non-Eclipse) frameworks, with the same amount of code you'd still be a looooong way to achieving the flexibility that Eclipse Platform provides.<br />
Eclipse FTW!<br />
<h3>But wait... EMF Comes to the Rescue </h3><br />
I haven't even written the main content of my post yet ! Hehe...<br />
<br />
Some of you may notice that I used <b>EMF</b> to generate my model classes above. (Hurray!)<br />
<br />
While happily hacking my <b>IWorkbenchAdapter</b> <i>adapters</i> (see the confusion? it's not cool to write "IWorkbenchAdapter adapter"), I noticed that I have to set the image for the model, however I already had the images provided by the generated EMF-Edit providers. This practically makes the image on my workbench model different from the one used by EMF Editors.<br />
<br />
Here's the <b>generated</b> *EMF* Item Provider :<br />
<br />
<tt>/**</tt><br />
<tt> * This is the item provider adapter for a {@link com.abispulsa.Organization} object.</tt><br />
<tt> * <!-- begin-user-doc --></tt><br />
<tt> * <!-- end-user-doc --></tt><br />
<tt> * @generated</tt><br />
<tt> */</tt><br />
<tt>public class OrganizationItemProvider</tt><br />
<tt> extends ItemProviderAdapter</tt><br />
<tt> implements</tt><br />
<tt> IEditingDomainItemProvider,</tt><br />
<tt> IStructuredItemContentProvider,</tt><br />
<tt> ITreeItemContentProvider,</tt><br />
<tt> IItemLabelProvider,</tt><br />
<tt> IItemPropertySource {</tt><br />
<tt>...</tt><br />
<tt> /**</tt><br />
<tt> * This returns Organization.png.</tt><br />
<tt> * <!-- begin-user-doc --></tt><br />
<tt> * <!-- end-user-doc --></tt><br />
<tt> * @generated NOT</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public Object getImage(Object object) {</tt><br />
<tt> return overlayImage(object, getResourceLocator().getImage("full/obj16/Organization.png"));</tt><br />
<tt> }</tt><br />
<br />
Hey.... I can <b>reuse</b> that!<br />
<br />
So I change this hardcoded piece :<br />
<br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public ImageDescriptor getImageDescriptor(Object object) {</tt><br />
<tt> return AbstractUIPlugin.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/organization.png");</tt><br />
<tt> }</tt><br />
<br />
Into something more consistent, delegate the stuff to my EMF Edit provider:<br />
<br />
<tt>private OrganizationItemProvider itemProvider = (OrganizationItemProvider) new AbispulsaItemProviderAdapterFactory().createOrganizationAdapter();</tt><br />
<tt>...</tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public ImageDescriptor getImageDescriptor(Object object) {</tt><br />
<tt> return ImageDescriptor.createFromURL((URL) itemProvider.getImage(object));</tt><br />
<tt> }</tt><br />
<br />
When I did that, I noticed that <b>EMF.Edit's ItemProvider</b> provides other methods, like <b>getChildren()</b>, <b>getText()</b>, etc. that I can use... So I hack away and change the <b>IWorkbenchAdapter</b> implementation :<br />
<br />
<tt>public class OrganizationWorkbenchAdapter implements IWorkbenchAdapter {</tt><br />
<tt> private OrganizationItemProvider itemProvider = (OrganizationItemProvider) new AbispulsaItemProviderAdapterFactory().createOrganizationAdapter();</tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public Object[] getChildren(Object o) {</tt><br />
<tt> return itemProvider.getChildren(o).toArray();</tt><br />
<tt> //return ((Organization)o).getContacts().toArray();</tt><br />
<tt> }</tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public ImageDescriptor getImageDescriptor(Object object) {</tt><br />
<tt> return ImageDescriptor.createFromURL((URL) itemProvider.getImage(object));</tt><br />
<tt> }</tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public String getLabel(Object o) {</tt><br />
<tt> return itemProvider.getText(o);</tt><br />
<tt> //return ((Organization)o).getName();</tt><br />
<tt> }</tt><br />
<tt> /* (non-Javadoc)</tt><br />
<tt> * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object)</tt><br />
<tt> */</tt><br />
<tt> @Override</tt><br />
<tt> public Object getParent(Object o) {</tt><br />
<tt> return itemProvider.getParent(o);</tt><br />
<tt> //return ResourcesPlugin.getWorkspace().getRoot();</tt><br />
<tt> }</tt><br />
<tt>}</tt><br />
<br />
I put the original method implementations there so you can compare. The original implementations are so tightly coupled to the model structure, the EMF.Edit-powered ones are more generic.<br />
<br />
Then I noticed that I practically has no dependency to the <i>original</i> model (<b>Organization</b> class), because I can change :<br />
<br />
<tt>private OrganizationItemProvider itemProvider = (OrganizationItemProvider) new AbispulsaItemProviderAdapterFactory().createOrganizationAdapter();</tt><br />
into something like:<br />
<tt>AbispulsaItemProviderAdapterFactory factory = new AbispulsaItemProviderAdapterFactory();</tt><br />
<tt>ITreeItemContentProvider treeItemProvider = (ITreeItemContentProvider) factory.adapt(object, ITreeItemContentProvider.class);</tt><br />
<tt>IItemLabelProvider labelProvider = (IItemLabelProvider) factory.adapt(object, IItemLabelProvider.class);</tt><br />
<br />
Be surprised that right now I don't have any <i>mention</i> of the original model's class name <i>anywhere</i>!<br />
<h3>Content Provider Wars Episode V: The EMF Strikes Back! </h3>Unfortunately the <b>ITreeItemContentProvider</b> etc. interfaces are EMF's, and it's unusable by SWT and other Eclipse UI views, which requires <b>IWorkbenchAdapter</b> or <b>IContentProvider</b> and <b>ILabelProvider</b>.<br />
<br />
Then something struck me: come on, since so much infrastructure has been provided by EMF, surely EMF wouldn't leave out something so obvious: a <b>content provider</b> and a <b>label provider</b>!<br />
<br />
Enter the warlords: <br />
<ul><li><b><a href="http://help.eclipse.org/galileo/index.jsp?topic=/org.eclipse.emf.doc/references/javadoc/org/eclipse/emf/edit/ui/provider/AdapterFactoryContentProvider.html">org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider</a></b> </li>
<li><b><a href="http://help.eclipse.org/galileo/topic/org.eclipse.emf.doc/references/javadoc/org/eclipse/emf/edit/ui/provider/AdapterFactoryLabelProvider.html">org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider</a></b> </li>
</ul><br />
These providers are contained within the plugin <b>org.eclipse.emf.edit.ui</b>.<br />
We only need to subclass these providers and provide the our own list of <b><a href="http://help.eclipse.org/galileo/index.jsp?topic=/org.eclipse.emf.doc/references/javadoc/org/eclipse/emf/common/notify/AdapterFactory.html">org.eclipse.emf.common.notify.AdapterFactory</a></b> objects :<br />
<br />
<b>Our EMF-powered Content Provider :</b><br />
<br />
<tt>import org.eclipse.emf.common.notify.AdapterFactory;</tt><br />
<tt>import org.eclipse.emf.edit.provider.ComposedAdapterFactory;</tt><br />
<tt>import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;</tt><br />
<tt>import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;</tt><br />
<tt>import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;</tt><br />
<tt>import com.abispulsa.provider.AbispulsaItemProviderAdapterFactory;</tt><br />
<tt>public class AbispulsaContentProvider extends AdapterFactoryContentProvider {</tt><br />
<tt> private static AdapterFactory adapterFactory;</tt><br />
<tt> </tt><br />
<tt> static {</tt><br />
<tt> adapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {</tt><br />
<tt> new ResourceItemProviderAdapterFactory(),</tt><br />
<tt> new AbispulsaItemProviderAdapterFactory(),</tt><br />
<tt> new ReflectiveItemProviderAdapterFactory()</tt><br />
<tt> });</tt><br />
<tt> }</tt><br />
<tt> </tt><br />
<tt> public AbispulsaContentProvider() {</tt><br />
<tt> super(adapterFactory);</tt><br />
<tt> }</tt><br />
<tt> </tt><br />
<tt>}</tt><br />
<br />
<b>Our EMF-powered Label Provider :</b> (<i>identical</i> code to the above!)<br />
<br />
<tt>import org.eclipse.emf.common.notify.AdapterFactory;</tt><br />
<tt>import org.eclipse.emf.edit.provider.ComposedAdapterFactory;</tt><br />
<tt>import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;</tt><br />
<tt>import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;</tt><br />
<tt>import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider;</tt><br />
<tt>import com.abispulsa.provider.AbispulsaItemProviderAdapterFactory;</tt><br />
<tt>public class AbispulsaLabelProvider extends AdapterFactoryLabelProvider {</tt><br />
<tt> private static AdapterFactory adapterFactory;</tt><br />
<tt> </tt><br />
<tt> static {</tt><br />
<tt> adapterFactory = new ComposedAdapterFactory(new AdapterFactory[] {</tt><br />
<tt> new ResourceItemProviderAdapterFactory(),</tt><br />
<tt> new AbispulsaItemProviderAdapterFactory(),</tt><br />
<tt> new ReflectiveItemProviderAdapterFactory()</tt><br />
<tt> });</tt><br />
<tt> }</tt><br />
<tt> </tt><br />
<tt> /**</tt><br />
<tt> * @param adapterFactory</tt><br />
<tt> */</tt><br />
<tt> public AbispulsaLabelProvider() {</tt><br />
<tt> super(adapterFactory);</tt><br />
<tt> }</tt><br />
<tt>}</tt><br />
<h3>Using Our EMF-powered Content and Label Providers </h3>For an SWT TreeViewer :<br />
<br />
<tt>treeViewer.setLabelProvider(new AbispulsaLabelProvider());</tt><br />
<tt>treeViewer.setContentProvider(new AbispulsaContentProvider());</tt><br />
<br />
Right now it's safe to throw all those painfully coded <b>IWorkbenchAdapter</b>, UI <b>AdapterFactory</b>'s, and the adapter factory registration code to the dumpster. ;-)<br />
<br />
Super quick, huh? :-)<br />
<br />
A nice side effect is you don't rely on any "workbench" stuff... But of course now you rely on EMF Edit UI :-)<br />
<br />
Another nice side effect is the content/label providers actually can be used with any <b>EMF EObject</b>, due to <b><a href="http://help.eclipse.org/galileo/topic/org.eclipse.emf.doc/references/javadoc/org/eclipse/emf/edit/provider/ReflectiveItemProviderAdapterFactory.html">ReflectiveItemProviderAdapterFactory</a></b> magic. :-)<br />
<h3>EMF-powered Content in Common Navigator Framework (CNF) View </h3>Enough with the Star Wars talk, it's time to show how beautiful that concise piece of code working inside the powerful <b>Common Navigator Framework (CNF) View</b>.<br />
<br />
Declaratively of course!<br />
<br />
Here's the relevant parts in <b>plugin.xml</b>. The XML codes below might seem scary to readers who aren't familiar yet with how <b>Eclipse plugin extensions</b> work, but in reality there's no XML typing involved at all! All these extensions are editable using PDE's Extensions visual editor.<br />
<br />
Declare the <b>CNF view part</b>:<br />
<br />
<tt> <extension point="org.eclipse.ui.views"></tt><br />
<tt> <category id="com.abispulsa.ui.abispulsa_category" name="AbisPulsa"></tt><br />
<tt> </category></tt><br />
<tt> <view allowMultiple="false" category="com.abispulsa.ui.abispulsa_category"</tt><br />
<tt> class="org.eclipse.ui.navigator.CommonNavigator" icon="icons/contacts.ico"</tt><br />
<tt> id="com.abispulsa.ui.contactsNav_view" name="Contacts Nav"</tt><br />
<tt> restorable="true"></tt><br />
<tt> </view></tt><br />
<tt> </extension></tt><br />
<br />
Put the view in perspective: (in this case, all perspectives)<br />
<br />
<tt> <extension point="org.eclipse.ui.perspectiveExtensions"></tt><br />
<tt> <perspectiveExtension targetID="*"></tt><br />
<tt> <view id="com.abispulsa.ui.contactsNav_view" minimized="false"</tt><br />
<tt> ratio="0.4" relationship="left" relative="org.eclipse.ui.editorss"></tt><br />
<tt> </view></tt><br />
<tt> </perspectiveExtension></tt><br />
<tt> </extension></tt><br />
<br />
Declare our <b>NCE (Navigator Content Extension)</b>, which uses our <b>EMF</b>-powered <b>content</b> and <b>label providers</b>, handling all <b>EObject</b> instances:<br />
<br />
<tt> <extension point="org.eclipse.ui.navigator.navigatorContent"></tt><br />
<tt> <navigatorContent id="com.abispulsa.ui.modelContent" name="Model"</tt><br />
<tt> contentProvider="com.abispulsa.presentation.AbispulsaContentProvider"</tt><br />
<tt> labelProvider="com.abispulsa.presentation.AbispulsaLabelProvider"></tt><br />
<tt> <triggerPoints></tt><br />
<tt> <instanceof value="org.eclipse.emf.ecore.EObject"></tt><br />
<tt> </instanceof></tt><br />
<tt> </triggerPoints></tt><br />
<tt> </navigatorContent></tt><br />
<tt> </extension></tt><br />
<br />
Then <b>bind</b> our <b>CNF navigator view</b> with our <b>NCE</b> :<br />
<br />
<tt> <extension point="org.eclipse.ui.navigator.viewer"></tt><br />
<tt> <viewerContentBinding viewerId="com.abispulsa.ui.contactsNav_view"></tt><br />
<tt> <includes></tt><br />
<tt> <contentExtension pattern="com.abispulsa.ui.modelContent" /></tt><br />
<tt> </includes></tt><br />
<tt> </viewerContentBinding></tt><br />
<tt> </extension></tt><br />
<br />
Done!<br />
<br />
The real beauty of the <b>EMF enhanced solution</b> is you actually have to set it up only once. From now on all changes you do to your models will be reflected <b>directly</b> in the <b>UI content/label providers</b> and all the views that use your <b>EMF models</b>. How cool is that? ;-)<br />
<br />
PS. For your viewing pleasure, here's the resulting <b>Common Navigator Framework (CNF) view</b>, powered by <b>EMF models</b>. :-)<br />
<br />
P.P.S. As a bonus, for those who notice, these EMF models are powered by <b><a href="http://www.eclipse.org/cdo/">CDO Model Repository</a></b> ! ;-)<br />
<br />
Additional resources:<br />
<ul><li> <a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/guide/cnf.htm">Eclipse Help - Common Navigator Framework</a><span style="font-size: small;"> </span></li>
<li><span style="font-size: small;"><a href="http://dev.eclipse.org/blogs/francis/2008/05/27/magic-required-to-use-the-common-navigator-in-an-rcp-application/" rel="bookmark" title="Permanent Link to Magic Required to use the Common Navigator in an RCP Application that uses Resources">Magic Required to use the Common Navigator in an RCP Application that uses Resources</a></span></li>
<li><span style="font-size: small;"><a href="http://www.ibm.com/developerworks/library/os-eclipse-emf/">Build an Eclipse plug-in to navigate content in an EMF model</a> </span></li>
</ul></div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com6tag:blogger.com,1999:blog-2436480061461152965.post-31467884505141681142010-12-28T12:42:00.001-08:002011-04-28T14:48:31.346-07:00Logging with SLF4J and LogBack in Eclipse Application (with Hibernate)<div class='posterous_autopost'> During my <b>EMF persistence</b> adventures with <b>CDO 3.0</b>, <b>Teneo 1.1.2</b>, and <b>Hibernate 3.3.2</b> there's something bugging me: The Hibernate bundle from Elver.org includes several out-of-context dependencies <b>SLF4J API</b>, SLF4J NOP implementation, SLF4J Log4j implementation and <b>Log4j</b>.<p /> Bundling logging JARs is already strange enough, but the provided Log4j is of version 1.2.8 while SLF4j Log4j 1.5.8 requires at least Log4j 1.2.12. Even more is it bundles SLF4j NOP implementation, why???<p /> Since it's embedded, it makes it impossible for me to use the embedded SLF4j and Log4j for my own purposes.<p /> So I decided to fix things a bit. This is quite unproductive, but I guess it's an okay exercise.<p /> First I have to delete several files from the exploded org.hibernate_3.3.2.201005082216 plugin: <ol type="1"> <li value="1" type="1">slf4j-api-*.jar </li><li value="2" type="1">slf4j-log4j-*.jar </li><li value="3" type="1">slf4j-nop-*.jar </li><li value="4" type="1">log4j-*.jar </li></ol> Deleting these files "saves" about 400 KB of space. :-) Not bad.<p /> Then I have to change the META-INF/MANIFEST.MF of the Hibernate plugin:<p /> Edit the Bundle-ClassPath from:<p /> <tt>Bundle-ClassPath: antlr-2.7.6.jar,c3p0-0.9.1.jar,commons-collections-3</tt><br /> <tt> .1.jar,ehcache-1.2.3.jar,hibernate3.jar,hibernate-annotations.jar,hib</tt><br /> <tt> ernate-commons-annotations.jar,hibernate-entitymanager.jar,javassist-</tt><br /> <tt> 3.11.0.GA.jar,jta-1.1.jar,log4j-1.2.8.jar,slf4j-api-1.5.8.jar,slf4j-l</tt><br /> <tt> og4j12-1.5.8.jar,slf4j-nop-1.5.8.jar</tt><p /> To:<p /> <tt>Bundle-ClassPath: antlr-2.7.6.jar,c3p0-0.9.1.jar,commons-collections-3 </tt><br /> <tt> .1.jar,ehcache-1.2.3.jar,hibernate3.jar,hibernate-annotations.jar,hib </tt><br /> <tt> ernate-commons-annotations.jar,hibernate-entitymanager.jar,javassist- </tt><br /> <tt> 3.11.0.GA.jar,jta-1.1.jar</tt><p /> With the unnecessary cruft removed, it's time to add SLF4j and Logback plugins to the target platform. <a href="http://download.eclipse.org/tools/orbit/downloads/drops/S20101204061544/">Get these from Orbit</a>: <ol type="1"> <li value="1" type="1">ch.qos.logback.classic_0.9.24.v20100831-0715.jar </li><li value="2" type="1">ch.qos.logback.core_0.9.24.v20100831-0715.jar </li><li value="3" type="1">ch.qos.logback.slf4j_0.9.24.v20100831-0715.jar </li><li value="4" type="1">org.slf4j.api_1.6.1.v20100831-0715.jar </li><li value="5" type="1">org.slf4j.jcl_1.6.1.v20100831-0715.jar </li></ol> <br /> In total they are 619 KB. Not bad for a shared logging system. :-)<p /> Now remove/exclude <b>org.apache.commons.logging</b> plugin from your target platform because <b>org.slf4j.jcl</b> plugin replaces it.<p /> Done! That's it. <b>SLF4j</b> and <b>Logback</b> providing nice <b>logging implementation</b> inside the <b>Eclipse platform application</b>.<p /> A nice side effect is that <b>log4j.properties</b> is no longer needed, because Logback works fine without configuration.<br /> But you can also <a href="http://logback.qos.ch/manual/joran.html">configure Logback</a>.<p /> Also checkout <a href="http://ekkescorner.wordpress.com/blog-series/osgi-apps/">Logging in OSGi Applications by ekkes-corner</a> for more thorough explanation. </div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com4tag:blogger.com,1999:blog-2436480061461152965.post-3389569569085524252010-12-28T09:58:00.001-08:002010-12-28T10:30:14.493-08:00CDO 3.0 with Hibernate Experiences<div class="posterous_autopost"><b>CDO</b> allows efficient access to <b>EMF repositories</b> over a network, with pluggable <b>persistence backends</b> including <b>Hibernate</b>, which under the hood uses <b>Teneo</b> for its mapping.<br />
<br />
CDO 3.0 is released. The newer CDO 4.0 is still in development.<br />
<br />
CDO 4.0 should work with Teneo 1.1.2 and Hibernate 3.6.<br />
<br />
With CDO 3.0, I'm quite confused which one I should use: <br />
<ul><li type="1" value="1">Teneo 1.1.2 + Hibernate 3.3.2 </li>
<li type="1" value="2">Teneo 1.2.0 + Hibernate 3.3.2 </li>
<li type="1" value="3">Teneo 1.2.0 + Hibernate 3.6 </li>
</ul>It seemed the third option is easily out, because for example, CDO 3.0's CDOPropertyGetter, does not support the new members of Hibernate 3.6 Getter interface, which is getMember() :<br />
<tt>import org.hibernate.HibernateException;</tt><br />
<tt>import org.hibernate.engine.SessionImplementor;</tt><br />
<tt>import org.hibernate.property.Getter;</tt><br />
<tt>import java.lang.reflect.Method;</tt><br />
<tt>import java.util.Map;</tt><br />
<tt>/**</tt><br />
<tt> * TODO How does this differ from {@link CDORevisionSetter}? Both needed?</tt><br />
<tt> * </tt><br />
<tt> * @author Martin Taal</tt><br />
<tt> */</tt><br />
<tt>public class CDOPropertyGetter extends CDOPropertyHandler implements Getter</tt><br />
<br />
(from <a href="http://dev.eclipse.org/viewcvs/viewvc.cgi/org.eclipse.emf/org.eclipse.emf.cdo/plugins/org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/tuplizer/CDOPropertyGetter.java?view=annotate&root=Modeling_Project&pathrev=R3_0_maintenance"> org.eclipse.emf.cdo.server.hibernate/src/org/eclipse/emf/cdo/server/internal/hibernate/tuplizer/CDOPropertyGetter.java</a>)<br />
<br />
CDO Wiki said CDO 3.0 should work with Teneo 1.2.0 where Teneo wiki said CDO 3.0 only works with Teneo 1.1.2.<br />
<br />
I'm now running CDO 3.0 example with Teneo 1.2.0 but with Hibernate 3.3.2 instead of Hibernate 3.6. And it <b>seems</b> to work fine (I haven't tested thoroughly). I guess it's not about the Teneo version but the Hibernate version? (Meaning Teneo 1.2.0 is able to work with either Hibernate 3.3.2 or Hibernate 3.6? I'm not sure yet though.)<br />
<br />
Another strange thing is that Teneo 1.2.0 plugins from Teneo 1.2.0 update site is versioned as 1.1.2.v201011240719, not 1.2.0 ...? (the EMF Teneo features are versioned as 1.2.0... which signifies the confusion). Am I doing something wrong or getting from the wrong update site?<br />
<br />
Unfortunately the Eclipse newsgroup seems to be down on me right now so I can't post there so I posted it to my blog, hoping that this will provide some information on others wanting to taste CDO. :-)<br />
<br />
Another tip: Previously I got an error:<br />
<br />
<div style="font-family: "Courier New",Courier,monospace;">IllegalStateException: Not able to update container columns of CDOResource with id OID:http://www.eclipse.org/emf/CDO/Eresource/2.0.0#CDOResource#1</div><br />
Turned out it was due to me putting several duplicate Teneo plugins of different versions. (common sense, I know.)<br />
<br />
I hope <b>Eike Stepper</b> or <b>Martin Taal</b> steps in (pun not intended) to clarify more on this.<br />
<br />
I'll update this post when more information arrives. :)<br />
<br />
<b>Update:</b> It seems my suspicion is incorrect, when trying to run the example CDO Client, I got the following error:<br />
<br />
<div style="font-family: Arial,Helvetica,sans-serif;"> !ENTRY org.eclipse.osgi 2 0 2010-12-29 01:25:07.835<br />
!MESSAGE One or more bundles are not resolved because the following root constraints are not resolved:<br />
!SUBENTRY 1 org.eclipse.osgi 2 0 2010-12-29 01:25:07.835<br />
<b>!MESSAGE Bundle reference:file:/home/ceefour/project/AbisPulsa/p2/Teneo/org.eclipse.emf.teneo.hibernate_1.1.2.v201011240719.jar was not resolved.<br />
!SUBENTRY 2 org.eclipse.emf.teneo.hibernate 2 0 2010-12-29 01:25:07.835<br />
!MESSAGE Missing imported package org.hibernate.engine.query_[3.6.0,4.0.0).</b>!SUBENTRY 1 org.eclipse.osgi 2 0 2010-12-29 01:25:07.836<br />
!MESSAGE Bundle reference:file:/home/ceefour/project/AbisPulsa/workspace/.metadata/.plugins/org.eclipse.pde.core/.bundle_pool/plugins/org.eclipse.emf.query.ocl_2.0.0.v20091215-1624.jar was not resolved.<br />
!SUBENTRY 2 org.eclipse.emf.query.ocl 2 0 2010-12-29 01:25:07.836<br />
!MESSAGE Missing required bundle org.eclipse.ocl_[3.0.0,4.0.0).</div><br />
So it seems Teneo 1.2.0 (which actually uses the JAR version <b>1.1.2.v201011240719</b>) requires at least Hibernate 3.6.<br />
Since CDO 3.0 Hibernate Store only implements Hibernate 3.3.2 API, which means that it's impossible to use CDO 3.0 with Teneo 1.2.0, only with Teneo 1.1.2. (so Teneo wiki is correct, CDO wiki is wrong..?)</div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com3tag:blogger.com,1999:blog-2436480061461152965.post-11265739626203250752010-12-27T17:34:00.001-08:002011-04-28T14:48:08.867-07:00Querying EMF Models using Model Query<div class='posterous_autopost'> <b>Querying</b> i.e. <b>retrieving</b> the <b>list</b> of <b><a href="wiki.eclipse.org/EMF">EMF</a></b> objects that meet a specific <b>criteria</b> by only <b>navigating</b> the <b>object graph</b>, <b>finding</b> the right objects, plus loops and if-elses... is inconvenient! Not to mention the <b>performance</b> of the <b>lookup</b> would be very <b>slow</b> on large models.<p /> Fortunately there are several <b>solutions</b> (actually, alternatives, since there are still some restrictions) for this: <ol type="1"> <li value="1" type="1"><b><a href="http://www.eclipse.org/modeling/emf/?project=query">EMF Model Query</a></b> </li><li value="2" type="1"><b><a href="http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.emf.query.doc/references/examples/oclQueryExample.html">EMF Model Query</a></b> with <b>OCL constraint language</b> </li><li value="3" type="1"><b>HQL (Hibernate Query Language)</b> with <b>CDO / Teneo</b> and <b>Hibernate Data Store</b> </li></ol> <br /> <h3> Using EMF Model Query </h3> For now I'll only show you how it's done with <a href="http://www.eclipse.org/modeling/emf/?project=query">EMF Model Query</a> because it's the only one I've tried at this point. ;-)<p /> I'm currently porting one of my PHP apps to <b>Eclipse Platform</b> and <b>EMF</b>.<p /> Here's how it looks:<p /> <tt> /**</tt><br /> <tt> * Finds a refill that is in Refill::STATUS_POSTED status matching this code.</tt><br /> <tt> * @param string $voucher_code</tt><br /> <tt> * @param string $mobile</tt><br /> <tt> * @model</tt><br /> <tt> */</tt><br /> <tt> public Refill lookupPosted(String voucherCode, String destNumber) {</tt><br /> <tt> EObjectAttributeValueCondition statusCondition = new EObjectAttributeValueCondition(</tt><br /> <tt> AbispulsaPackage.Literals.REFILL__STATUS,</tt><br /> <tt> new ObjectInstanceCondition(RefillStatus.POSTED));</tt><br /> <tt> EObjectReferenceValueCondition voucherCondition = new EObjectReferenceValueCondition(</tt><br /> <tt> AbispulsaPackage.Literals.REFILL__VOUCHER,</tt><br /> <tt> new EObjectAttributeValueCondition(</tt><br /> <tt> AbispulsaPackage.Literals.VOUCHER__CODE, new StringValue(</tt><br /> <tt> voucherCode)));</tt><br /> <tt> EObjectAttributeValueCondition destNumberCond = new EObjectAttributeValueCondition(</tt><br /> <tt> AbispulsaPackage.Literals.REFILL__DEST_NUMBER, new StringValue(</tt><br /> <tt> destNumber));</tt><br /> <tt> SELECT statement = new SELECT(1, new FROM(this), new WHERE(</tt><br /> <tt> statusCondition.AND(voucherCondition).AND(destNumberCond)));</tt><br /> <tt> IQueryResult result = statement.execute();</tt><br /> <tt> if (!result.isEmpty())</tt><br /> <tt> return (Refill)result.iterator().next();</tt><br /> <tt> else</tt><br /> <tt> return null;</tt><br /> <tt> }</tt><p /> If you think the above code is verbose... Yeah, it certainly is! :-(<p /> Here's the original code in PHP :<p /> <tt> Refill refill = Doctrine_Query::create()->from('Refill')</tt><br /> <tt> ->where('status = ?', self::STATUS_POSTED)</tt><br /> <tt> ->andWhere('voucher_code = ?', $voucher_code)</tt><br /> <tt> ->andWhere('dest_number = ?', $mobile)</tt><br /> <tt> ->limit(1)->fetchOne();</tt><p /> Much more compact. Not to mention it's definitely much faster than EMF Query since the PHP code directly accesses the backend storage (MySQL) with all the indexing goodies of Relational DBMS.<p /> However, it's still a good way than coding your own loops and ifs. For small models and if you don't mind verbosity, plain <b>EMF Model Query</b> perfect and very flexible.<p /> <h3> EMF Model Query with OCL </h3> <b>EMF Model Query</b> with <b>OCL</b> works just like the plain EMF Model Query, but with a much nicer and widely used <b>standard constraint syntax</b>.<p /> A simple OCL is :<p /> <tt>self.name = 'Bob'</tt><p /> Expressing the same criteria in plain EMF Model Query would be much longer.<p /> Typical code looks like this (from EMF Model Query guide):<p /> <tt>Resource myResource = ... // get the resource</tt> <div class="CodeRay"> <div class="code"><pre></pre></div> </div> <tt>OCL ocl = org.eclipse.ocl.ecore.OCL.newInstance();</tt> <tt>Condition condition = new BooleanOCLCondition<EClassifier, EClass, EObject>(</tt> <tt> ocl.getEnvironment(),</tt> <tt> "self.books->collect(b : Book | b.category)->asSet()->size() > 2",</tt> <tt> EXTLibraryPackage.Literals.WRITER);</tt> <tt>SELECT statement = new SELECT(SELECT.UNBOUNDED, false,</tt> <tt> new FROM(myResource.getContents()), new WHERE(condition),</tt> <tt> new NullProgressMonitor());</tt> <tt>IQueryResult results = statement.execute();</tt> <tt> </tt> <tt>// do something with the results</tt> <tt>selectInEditor(results);</tt> <br /> See more <a href="http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.emf.query.doc/references/examples/oclQueryExample.html">EMF Model Query with OCL Examples</a>.<br /> <br /> <h3> HQL on CDO/Teneo + Hibernate </h3> This one is sort of cheating, because it doesn't <i>really</i> use EMF capabilities at all, but under the hood delegates to the underlying persistence storage which is <b>Hibernate</b> which in turn is backed by a relational database (such as <b>MySQL, HSQLDB, PostgreSQL</b>, you name it).<br /> <br /> The objects returned by Hibernate are then "converted" into EMF Objects. Actually they're already EMF Objects (of course!) but I suspect CDO/Teneo does additional processing like attaching the objects to the proper <b>EMF Resource</b>.<br /> <br /> The code is much more compact and developer-friendly: (especially if you're a Hibernate developer)<br /> <br /> <tt>CDOSession session = openSession();</tt> <div class="CodeRay"> <div class="code"><pre></pre></div> </div> <tt>CDOTransaction transaction = session.openTransaction();</tt> <tt> </tt> <div class="CodeRay"> <div class="code"><pre></pre></div> </div> <tt>CDOQuery cdoQuery = transaction.createQuery("hql", "from Product where vat=:vat");</tt> <tt>cdoQuery.setParameter("vat", VAT.VAT15);</tt> <tt>List<Product> products = cdoQuery.getResult(Product.class);</tt> <div class="CodeRay"> <div class="code"><pre></pre></div> </div> <tt>transaction.commit();</tt> <br /> Cool, huh? Compact, easy, and strongly typed! And it supports <b>parameterized queries</b>! And results are <b>fast</b>!<p /> More about <b><a href="http://wiki.eclipse.org/CDO/Hibernate_Store/HQL">HQL Support on CDO Eclipsepedia Wiki</a></b>.<p /> <h3> Conclusion </h3> I think using HSQL is the best solution from both the performance standpoint and ease of use.<p /> However it requires to use a data store that supports a query language, such as the Hibernate Data Store (I think the CDO Objectivity data store should also support querying).<p /> In theory, CDO supports pluggable query handler so that CDO Data Stores are free to implement their own optimized query language (with parameter support!) However whether this applies in practice (for other data stores), I still have to find out. :-) </div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com8tag:blogger.com,1999:blog-2436480061461152965.post-47214151526442605792010-12-26T05:54:00.001-08:002011-04-28T14:49:23.998-07:00How to Dump/Inspect Object or Variable in Java<div class='posterous_autopost'> <b>Scala (console)</b> has a very useful feature to <b>inspect or dump variables / object values :</b><p /> <tt>scala> def b = Map("name" -> "Yudha", "age" -> 27)</tt><br /> <tt>b: scala.collection.immutable.Map[java.lang.String,Any]</tt><p /> <tt>scala> b</tt><br /> <tt>res1: scala.collection.immutable.Map[java.lang.String,Any] = Map((name,Yudha), (age,27))</tt><p /> Inside our application, especially in <b>Java programming language</b> (although the techniques below obviously works with any <b>JVM language</b> like <b>Scala</b> and <b>Groovy</b>) sometimes we want to <b>inspect/dump the content of an object/value</b>. Probably for <b>debugging</b> or <b>logging</b> purposes.<p /> My two favorite techniques is just to <b>serialize the Java object to JSON</b> and/or <b>XML</b>. An added benefit is that it's possible to <b>deserialize</b> the <b>dumped object representation</b> back to an actual object if you want.<p /> <h3> JSON Serialization with Jackson </h3> Depend on Jackson (using Maven):<br /> <tt> <dependency></tt><br /> <tt> <groupId>org.codehaus.jackson</groupId></tt><br /> <tt> <artifactId>jackson-mapper-asl</artifactId></tt><br /> <tt> <version>1.6.3</version></tt><br /> <tt> </dependency></tt><br /> Then use it:<br /> <tt>import org.codehaus.jackson.JsonGenerationException;</tt><br /> <tt>import org.codehaus.jackson.map.JsonMappingException;</tt><br /> <tt>import org.codehaus.jackson.map.ObjectMapper;</tt><br /> <tt>import org.codehaus.jackson.map.SerializationConfig;</tt><br /> <tt>import org.slf4j.Logger;</tt><br /> <tt>import org.slf4j.LoggerFactory;</tt><p /> <tt>..</tt><br /> <tt> Logger logger = LoggerFactory.getLogger(getClass());</tt><br /> <tt> </tt><br /> <tt> @Test</tt><br /> <tt> public void level() throws ServiceException, JsonGenerationException, JsonMappingException, IOException {</tt><br /> <tt> MagentoServiceLocator locator = new MagentoServiceLocator();</tt><br /> <tt> Mage_Api_Model_Server_HandlerPortType port = locator.getMage_Api_Model_Server_HandlerPort();</tt><br /> <tt> String sessionId = port.login("...", "...");</tt><br /> <tt> logger.info(String.format("Session ID = %s", sessionId));</tt><br /> <tt> Map[] categories = (Map[]) port.call(sessionId, "catalog_category.level", new Object[] { null, null, 2 } );</tt><br /> <tt><b> ObjectMapper mapper = new ObjectMapper();</b></tt><br /> <tt><b> mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);</b></tt><br /> <tt><b> logger.info( mapper.writeValueAsString(categories) ); </b></tt><br /> <tt> }</tt><p /> Example output :<p /> <tt>6883 [main] INFO id.co.bippo.shop.magentoclient.AppTest - [ {</tt><br /> <tt> "position" : "1",</tt><br /> <tt> "level" : "2",</tt><br /> <tt> "is_active" : "1",</tt><br /> <tt> "name" : "Gamis",</tt><br /> <tt> "category_id" : "3",</tt><br /> <tt> "parent_id" : 2</tt><br /> <tt>}, {</tt><br /> <tt> "position" : "2",</tt><br /> <tt> "level" : "2",</tt><br /> <tt> "is_active" : "1",</tt><br /> <tt> "name" : "Celana",</tt><br /> <tt> "category_id" : "5",</tt><br /> <tt> "parent_id" : 2</tt><br /> <tt>} ]</tt><p /> <h3> XML Serialization with XStream </h3> As a pre-note, <b>XStream</b> can also handle <b>JSON</b> with either <b>Jettison</b> or its own JSON driver, however people usually prefer <b>Jackson</b> than XStream for JSON serialization.<p /> Maven dependency for XStream:<br /> <tt> <dependency></tt><br /> <tt> <groupId>xstream</groupId></tt><br /> <tt> <artifactId>xstream</artifactId></tt><br /> <tt> <version>1.2.2</version></tt><br /> <tt> </dependency></tt><br /> Use it:<br /> <tt>import java.io.IOException;</tt><br /> <tt>import java.rmi.RemoteException;</tt><br /> <tt>import java.util.Map;</tt><p /> <tt>import javax.xml.rpc.ServiceException;</tt><p /> <tt>import org.junit.Test;</tt><br /> <tt>import org.slf4j.Logger;</tt><br /> <tt>import org.slf4j.LoggerFactory;</tt><p /> <tt>import com.thoughtworks.xstream.XStream;</tt><br /> <tt>...</tt><br /> <tt> @Test</tt><br /> <tt> public void infoXml() throws ServiceException, RemoteException {</tt><br /> <tt> MagentoServiceLocator locator = new MagentoServiceLocator();</tt><br /> <tt> Mage_Api_Model_Server_HandlerPortType port = locator.getMage_Api_Model_Server_HandlerPort();</tt><br /> <tt> String sessionId = port.login("...", "...");</tt><br /> <tt> logger.info(String.format("Session ID = %s", sessionId));</tt><br /> <tt> Map category = (Map) port.call(sessionId, "catalog_category.info",</tt><br /> <tt> new Object[] { 3 } );</tt><br /> <tt><b> XStream xstream = new XStream();</b></tt><br /> <tt><b> logger.info( xstream.toXML(category) );</b></tt><br /> <tt> }</tt><p /> Sample output:<p /> <tt>5949 [main] INFO id.co.bippo.shop.magentoclient.AppTest - <map></tt><br /> <tt> <entry></tt><br /> <tt> <string>position</string></tt><br /> <tt> <string>1</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>custom_design</string></tt><br /> <tt> <string></string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>custom_use_parent_settings</string></tt><br /> <tt> <string>0</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>custom_layout_update</string></tt><br /> <tt> <string></string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>include_in_menu</string></tt><br /> <tt> <string>1</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>custom_apply_to_products</string></tt><br /> <tt> <string>0</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>meta_keywords</string></tt><br /> <tt> <string>gamis, busana muslim</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>available_sort_by</string></tt><br /> <tt> <string></string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>url_path</string></tt><br /> <tt> <string>gamis.html</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>children</string></tt><br /> <tt> <string></string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>landing_page</string></tt><br /> <tt> <null/></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>display_mode</string></tt><br /> <tt> <string>PRODUCTS</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>level</string></tt><br /> <tt> <string>2</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>description</string></tt><br /> <tt> <string>Gamis untuk muslimah</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>name</string></tt><br /> <tt> <string>Gamis</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>path</string></tt><br /> <tt> <string>1/2/3</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>created_at</string></tt><br /> <tt> <string>2010-12-24 11:37:41</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>children_count</string></tt><br /> <tt> <string>0</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>is_anchor</string></tt><br /> <tt> <string>1</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>url_key</string></tt><br /> <tt> <string>gamis</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>parent_id</string></tt><br /> <tt> <int>2</int></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>filter_price_range</string></tt><br /> <tt> <null/></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>all_children</string></tt><br /> <tt> <string>3</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>is_active</string></tt><br /> <tt> <string>1</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>page_layout</string></tt><br /> <tt> <string></string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>image</string></tt><br /> <tt> <null/></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>category_id</string></tt><br /> <tt> <string>3</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>default_sort_by</string></tt><br /> <tt> <null/></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>custom_design_from</string></tt><br /> <tt> <null/></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>updated_at</string></tt><br /> <tt> <string>2010-12-24 11:37:41</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>meta_description</string></tt><br /> <tt> <string>Jual baju gamis untuk muslim</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>custom_design_to</string></tt><br /> <tt> <null/></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>path_in_store</string></tt><br /> <tt> <null/></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>meta_title</string></tt><br /> <tt> <string>Gamis</string></tt><br /> <tt> </entry></tt><br /> <tt> <entry></tt><br /> <tt> <string>increment_id</string></tt><br /> <tt> <null/></tt><br /> <tt> </entry></tt><br /> <tt></map></tt><p /> Which one is better?<p /> I personally prefer <b>JSON</b>, but fortunately, you always have a choice. :-) </div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com2tag:blogger.com,1999:blog-2436480061461152965.post-10770501516488802212010-12-26T04:32:00.001-08:002011-04-28T14:49:23.999-07:00Maven 3.0 does not support *Maven* 1.0 (legacy) repository<div class='posterous_autopost'><div>Today I was <a href="https://cwiki.apache.org/MAVEN/maven-3x-compatibility-notes.html#Maven3.xCompatibilityNotes-LegacystyleRepositories">frustrated by this</a> :</div><p /><blockquote class="gmail_quote" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; border-left-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; padding-left: 1ex;"> <b>Maven 3.x</b> no longer supports repositories using <tt><b><layout>legacy</layout></b></tt>. Users that need to access <b>repositories</b> created with <b>Maven 1.x</b> are advised to use a <a href="http://maven.apache.org/repository-management.html" class="external-link" rel="nofollow">repository manager</a> that is capable of providing a Maven 2.x compatible view of the legacy repository.</blockquote> <p /><div><span style="line-height: 17px;">WHAT???</span></div> <p /><div><span style="line-height: 17px;">Even Maven's very own competitor, <a href="http://ant.apache.org/ivy//history/2.2.0/resolver/ibiblio.html">Ivy's ibiblio resolver supports Maven 1.0 legacy repositories</a>.</span><br /> </div><p /><div><span style="line-height: 17px;">Sonatype could simply change their suggestion to "</span>Users that need to access repositories created with Maven 1.x are advised to use <b><a href="http://ant.apache.org/ivy">Apache Ivy</a></b>."</div> <p /><div>I understand the need to force users to use the new technology, but not by disabling access to older technology without providing a practical alternative. At least wait for your competitors to stop supporting your legacy product before you yourself do it.</div> <p /><div><a href="http://download.java.net/maven/1">java.net Maven 1 repository</a> contains artifacts that I need at times, now I have to hunt the artifacts or do workarounds that was simply solved using <b>repository</b> <b>layout=legacy</b>. Argh!</div></div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com8tag:blogger.com,1999:blog-2436480061461152965.post-12696289265175465292010-12-24T10:20:00.001-08:002011-04-28T14:48:54.868-07:00Saving the xsi:schemaLocation into EMF XMI file<div class='posterous_autopost'> Previously I've shared <a href="http://eclipsedriven.blogspot.com/2010/12/solving-inside-eclipse-ide.html">how to fix loading an <b>EMF XMI file</b> within <b>Eclipse IDE</b> that has no <b>xsi:schemaLocation</b></a>.<p /> The reason why it happened to me was everytime <b>XMIResourceImpl.save()</b> is called, it removes the <b>xsi:schemaLocation</b> from the saved XMI file. Which made the file not openable from Eclipse IDE again. I want to get rid of this problem once and for all!<p /> <a href="http://www.eclipse.org/forums/index.php?t=msg&th=202291">Thanks to <b>Ed Merks</b></a>, there are three ways to solve this problem: <ol type="1"> <li value="1" type="1">Install the <b>plugin</b> containing your <b>EMF-generated package</b> to the Eclipse IDE. This seems to be the proper way to do it. Better yet, you can install the <b>generated EMF.Edit</b> and <b>EMF.Editor plugins</b> so you can experience the real editor with all the features (including icons) you've prepared. </li><li value="2" type="1">Use <b>XMIResource.OPTION_SCHEMA_LOCATION</b> to serialize the <b>xsi:schemaLocation</b>. I'll detail this below. </li><li value="3" type="1">Change the package's <b>nsURI</b> so that it points to the real, absolute path of the Ecore file (or the metamodel, I think you can use an <b>XML Schema/XSD</b> or <b>UML file</b> as well). This can be a <b>file:// URI</b> or a <b>http://</b> <b>URI</b> (I guess <b>platform:/</b> <b>URIs</b> would work as well). </li></ol> <br /> Since the first and third option is self-explanatory, here's how to do the second option.<p /> <h3> Using XMIResource.OPTION_SCHEMA_LOCATION </h3> <br /> Open your <b>*PackageImpl</b> class and override the <b>createResource()</b> method to specify the actual schema (Ecore metamodel) location:<p /> <tt> @Override</tt><br /> <tt> protected Resource createResource(String uri) {</tt><br /> <tt> return super.createResource("file:/home/ceefour/project/AbisPulsa/workspace/com.abispulsa.model/src/model/abispulsa.ecore");</tt><br /> <tt> }</tt><p /> The method is not there so you have to create it (press <b>Ctrl+Space</b> on an empty space to bring up the code assist).<p /> Now when doing <b>resource.save()</b>, you need to set the <b>XMIResource.OPTION_SCHEMA_LOCATION</b> to true :<p /> <tt>log.info(String.format("Saving resource: %s", getCatalog().eResource().getURI()));</tt><br /> <tt>HashMap<String, Object> opts = new HashMap<String, Object>();</tt><br /> <tt>opts.put(XMIResource.OPTION_SCHEMA_LOCATION, true);</tt><br /> <tt>getCatalog().eResource().save(opts);</tt><p /> Now it saves the <b>xsi:schemaLocation</b> to the <b>XMI file</b>. :-)<p /> Also sourced from EMF Wiki: <a href="http://wiki.eclipse.org/index.php/EMF/FAQ#How_can_I_ensure_that_an_xsi:schemaLocation_is_serialized_for_my_packages.3F">http://wiki.eclipse.org/index.php/EMF/FAQ#How_can_I_ensure_that_an_xsi:schemaLocation_is_serialized_for_my_packages.3F</a> </div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com6tag:blogger.com,1999:blog-2436480061461152965.post-57604453753270295032010-12-23T22:06:00.001-08:002011-04-28T14:48:31.346-07:00Creating a Daemon-style Eclipse "headless" Application<div class='posterous_autopost'> A <b>daemon</b> application is an application that does not exit immediately and does not wait for any input/interaction from the user. Instead, it is (usually) providing a <b>network service</b> or by being a <b>server</b>. Apache Tomcat servlet container is one such <b>daemon</b>.<p /> To run a daemon-style application in <b>Eclipse runtime</b>, it is tempting to just use the standard infinite loop:<p /> <tt>import org.eclipse.equinox.app.IApplication;</tt><br /> ...<p /> <tt>public class Application implements IApplication {</tt><p /> <tt> @Override</tt><br /> <tt> public Object start(IApplicationContext context) throws Exception {</tt><br /> <tt>// ... start services here (in another thread) ...</tt><br /> <tt> while (true) {</tt><br /> <tt> try {</tt><br /> <tt> Thread.sleep(1000);</tt><br /> <tt> } catch (InterruptedException e) {</tt><br /> <tt> break;</tt><br /> <tt> }</tt><br /> <tt> }</tt><br /> <tt> return IApplication.EXIT_OK;</tt><br /> <tt> }</tt><p /> While this technique works, I believe the better way is just to immediately return normally. And use Eclipse's <b><tt>-noExit</tt> launch argument to let Eclipse know that we're still doing things in the background.<p /> So the start() method can be just like this:<p /> <tt> @Override</tt><br /> <tt> public Object start(IApplicationContext context) throws Exception {</tt><br /> <tt> final ResourceSetImpl resourceSet = new ResourceSetImpl();</tt><br /> <tt> Resource dealerResource = resourceSet</tt><br /> <tt> .createResource(URI</tt><br /> <tt> .createFileURI("/home/ceefour/project/AbisPulsa/workspace/com.abispulsa.model/src/model/YahooDealer.xmi"));</tt><br /> <tt> dealerResource.load(null);</tt><br /> <tt> dealer = (YahooDealerImpl) dealerResource.getContents().get(0);</tt><p /> <tt> dealer.signon();</tt><br /> <tt> </tt><br /> <tt> Resource catalogRes = resourceSet.createResource(URI.createFileURI("/home/ceefour/project/AbisPulsa/workspace/com.abispulsa.model/src/model/Catalog.xmi"));</tt><br /> <tt> catalogRes.load(null);</tt><br /> <tt> Catalog catalog = (Catalog) catalogRes.getContents().get(0);</tt><br /> <tt> dealer.setCatalog(catalog);</tt><br /> <tt> </tt><br /> <tt> return IApplication.EXIT_OK;</tt><br /> <tt> }</tt><p /> Then edit the application's <b>Launch Configuration</b> and add the following to program arguments:<p /> <tt>-noExit -consoleLog</tt><p /> You may also want to add the OSGi console (argument <tt>-console</tt>):<p /> <tt>-noExit -consoleLog -console</tt><p /> Let's run 'em servers. :-) </b></div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com6tag:blogger.com,1999:blog-2436480061461152965.post-64199833165355130932010-12-23T17:51:00.001-08:002011-04-28T14:48:54.869-07:00Solving inside Eclipse IDE: org.eclipse.emf.ecore.xmi.PackageNotFoundException: Package with uri 'http://*' not found<div class='posterous_autopost'><a href='http://posterous.com/getfile/files.posterous.com/eclipsedriven/RPmdeOBUBs5hQ5but2ykQpu78PPAUBjdA39dRLeZAVoRhfFkLnLS7DcesPKQ/emf-model-editor-generic.png'><img src="http://posterous.com/getfile/files.posterous.com/eclipsedriven/neeMCv7B6k7gS1aGa12UkEplPPXlsINZ1ZKh5mxTbWDAfistIXHZIARxSlh4/emf-model-editor-generic.png.scaled.500.jpg" width="500" height="341"/></a> <p> When opening an <b>XMI</b> file (<b>EMF model</b>) inside the <b>Eclipse Modeling IDE</b> you may sometimes encounter the following error:<p /> <tt>org.eclipse.emf.ecore.resource.impl.ResourceSetImpl$1DiagnosticWrappedException: org.eclipse.emf.ecore.xmi.PackageNotFoundException: Package with uri '<a href="http://www.abispulsa.com/model/1.0'">http://www.abispulsa.com/model/1.0'</a> not found. (platform:/resource/com.abispulsa.model/src/model/Catalog.xmi, 2, 126)</tt><br /> <tt> at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.handleDemandLoadException(ResourceSetImpl.java:315)</tt><br /> <tt> at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.demandLoadHelper(ResourceSetImpl.java:274)</tt><br /> <tt> at org.eclipse.emf.ecore.resource.impl.ResourceSetImpl.getResource(ResourceSetImpl.java:397)</tt><br /> <tt> at org.eclipse.emf.ecore.presentation.EcoreEditor.createModelGen(EcoreEditor.java:1062)</tt><br /> <tt> at org.eclipse.emf.ecore.presentation.EcoreEditor.createModel(EcoreEditor.java:1082)</tt><br /> <tt> at org.eclipse.emf.ecore.presentation.EcoreEditor.createPages(EcoreEditor.java:1147)</tt><br /> <tt>...</tt><p /> When this happens in <b>your application</b>, you can <a href="http://eclipsedriven.blogspot.com/2010/12/fixing-emfecorexmi-resource-loading.html">register the package to fix it</a>.<p /> However in this case the error happens when you want to edit an XMI file (EMF model) <b>inside the Eclipse IDE</b>.<p /> To fix it open the file using the plain XML Editor, and change the line :<p /> <abispulsa:Catalog xmi:version="2.0" xmlns:xmi="<a href="http://www.omg.org/XMI">http://www.omg.org/XMI</a>" xmlns:abispulsa="<a href="http://www.abispulsa.com/model/1.0">http://www.abispulsa.com/model/1.0</a>"><p /> to point to the <b>metamodel's location</b> using <tt>xsi:schemaLocation</tt> :<p /> <tt><abispulsa:Catalog xmi:version="2.0" xmlns:xmi="<a href="http://www.omg.org/XMI">http://www.omg.org/XMI</a>" xmlns:xsi="<a href="http://www.w3.org/2001/XMLSchema-instance">http://www.w3.org/2001/XMLSchema-instance</a>" xmlns:abispulsa="<a href="http://www.abispulsa.com/model/1.0">http://www.abispulsa.com/model/1.0</a>" xsi:schemaLocation="<a href="http://www.abispulsa.com/model/1.0">http://www.abispulsa.com/model/1.0</a> abispulsa.ecore"></tt><p /> Now you can open the file in the <b>EMF Editor</b> successfully. </p></div>Hendyhttp://www.blogger.com/profile/05192845149798446052noreply@blogger.com4