tag:blogger.com,1999:blog-36271027424388985052024-03-16T08:11:01.346+03:00Wintermute BlogA developer's blog.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.comBlogger52125tag:blogger.com,1999:blog-3627102742438898505.post-84887899590102201192011-11-08T19:50:00.005+03:002011-11-08T20:43:49.676+03:00Easy XSLT without installing anything<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://blog.typpz.com/wp-content/275px-xml.png"><img style="float:right; margin:0 0 10px 10px;cursor:pointer; cursor:hand;width: 275px; height: 313px;" src="http://blog.typpz.com/wp-content/275px-xml.png" alt="" border="0" /></a><br />Sometimes you need to apply an XSLT to some XML, and there are (at least) a couple of ways to do it:<br /><br />(a) Install a processor like Saxon and run it through the command line;<br />(b) Reference an XSLT from your XML file, and then open it in a browser;<br /><br />Both are somewhat problematic - the first one requires installing and running something, and the second requires modifications to the XML. I propose a useful and effective alternative, which I've been using successfully for quite a while. The idea is as following: you simply open a special HTML in a browser. This HTML contains a JavaScript that loads both XML and XSLT files, applies the transform and renders the result into its own DOM. Here is the HTML I use, it is really simple and works in both IE and Mozilla. All you need to do is replace the filenames (input.xml and template.xsl):<br /><code><pre><html><br /><head><br /><script><br />function loadXMLDoc(fname)<br />{<br /> var xmlDoc;<br /> if (window.ActiveXObject)<br /> {<br /> xmlDoc = new ActiveXObject("Microsoft.XMLDOM");<br /> }<br /> else if (document.implementation && document.implementation.createDocument)<br /> {<br /> xmlDoc = document.implementation.createDocument("","",null);<br /> }<br /> else<br /> {<br /> alert('Your browser cannot handle this script');<br /> }<br /> xmlDoc.async = false;<br /> xmlDoc.load(fname);<br /> return(xmlDoc);<br />}<br /><br />function displayResult()<br />{<br /> xml = loadXMLDoc("<b><font color="red">input.xml</font></b>");<br /> xsl = loadXMLDoc("<b><font color="red">template.xsl</font></b>");<br /> if (window.ActiveXObject)<br /> {<br /> ex = xml.transformNode(xsl);<br /> document.getElementById("content").innerHTML = ex;<br /> }<br /> else if (document.implementation && document.implementation.createDocument)<br /> {<br /> xsltProcessor=new XSLTProcessor();<br /> xsltProcessor.importStylesheet(xsl);<br /> resultDocument = xsltProcessor.transformToFragment(xml,document);<br /> document.getElementById("content").appendChild(resultDocument);<br /> }<br />}<br /></script><br /></head><br /><body onLoad="displayResult()"><br /> <div id="content" /><br /></body><br /></html></pre></code><br />A sample use case: you have an application which logs some data into an XML file at a given location. You write an XSLT that filters errors in this log, and adjust the filenames inside the HTML above. You save this HTML on your Desktop, and every time you open or refresh it, it shows you the current errors in the log. It is easier and more user-friendly than running BAT-files saving output of transform into some temporary HTML and then opening it.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-16829878008997618232011-01-14T22:57:00.003+02:002011-01-14T23:11:22.537+02:00Meet Flowkeeper, a new open-source software timer for Pomodoro Technique<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdXSy5mzq7xdewXK8f2tPY6BUKqFjEsMp2E04_B2juByrCrZPu56I2a7dyTvcvg4QJVyRQqkLMp4w7OGJcepfwNUm_6GR-RImJZ9fRHZKM9W4AZaKp7XiEPkFAZiqPCQCIl2DJCDnJdvA/s1600/screenshot1.png"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 200px; height: 162px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdXSy5mzq7xdewXK8f2tPY6BUKqFjEsMp2E04_B2juByrCrZPu56I2a7dyTvcvg4QJVyRQqkLMp4w7OGJcepfwNUm_6GR-RImJZ9fRHZKM9W4AZaKp7XiEPkFAZiqPCQCIl2DJCDnJdvA/s400/screenshot1.png" alt="" id="BLOGGER_PHOTO_ID_5562151249520348946" border="0" /></a>I want to present you the very first version of <a href="http://flowkeeper.org/">Flowkeeper</a> - yet another free software timer for <a href="http://www.pomodorotechnique.com/">Pomodoro Technique</a>. Initially it was designed as a desktop UI for <a href="http://code.google.com/p/pomodoro-server/">Pomodoro Server</a> - my another project supporting teamwork for PT. Despite the almost-ready-for-production state of the latter one, I decided to implement both online and offline modes in Flowkeeper, and release offline-only version first. So here it is, ready for <a href="http://flowkeeper.sourceforge.net/setup.exe">download</a> :)<br /><br />Some of its features:<br /><ul><li>Your plans are persistent and can be viewed after the program is closed;</li><li>Configurable timer sounds and system tray notifications;</li><li>Some basic single-day statistics;</li><li>Supported platforms: Windows (with installer), Linux KDE, Linux Gnome (no system tray), MacOS (reported, but no details yet);</li></ul>I am pretty ambitious about this project. Here are some of the features to be included in the next version, just to name a few:<br /><ul><li>New multiplatform installer;</li><li>Activity Inventory;</li><li>Better statistics;</li><li>Source code will be published on SourceForge;</li></ul>So, keep tuned and don't hesitate to contact me.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com25tag:blogger.com,1999:blog-3627102742438898505.post-197359061170105232010-06-30T17:15:00.002+03:002010-06-30T17:40:40.700+03:00Oracle driver in MavenThis is just a short reminder how to add Oracle driver as a dependency in POM.<br /><br />In pom.xml:<br /><br /><code><dependency><br /><groupid>com.oracle</groupid><br /><artifactid>driver</artifactid><br /><version>10.14</version><br /></dependency></code><br /><br />In command line:<br /><br /><code>mvn install:install-file -Dfile=PATH_TO_DRIVER/ojdbc14.jar -DgroupId=com.oracle -DartifactId=driver -Dversion=10.14 -Dpackaging=jar</code>Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-36044827880399030092010-04-17T22:55:00.007+03:002010-04-17T23:30:40.803+03:00Fluent Interface Inheritance<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://hungrygeek.com.au/wp-content/uploads/2009/08/body_language-101.jpg"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 173px; height: 173px;" src="http://hungrygeek.com.au/wp-content/uploads/2009/08/body_language-101.jpg" alt="" border="0" /></a><a href="http://martinfowler.com/bliki/FluentInterface.html">Fluent Interface</a> is a convenient way of writing DSLish code in the general-purpose programming language. Some of the examples are quite impressive. But there comes a problem when you try to reuse this pattern via inheritance, because the basic method chaining implementations do not actually support it very well. I will use a JPA example found in Wikipedia to demonstrate the problem:<br /><br /><pre><code>public Collection<Student> findByNameAgeGender(String name, int age, Gender gender) {<br /> return em.createNamedQuery("Student.findByNameAgeGender")<br /> .setParameter("name", name)<br /> .setParameter("age", age)<br /> .setParameter("gender", gender)<br /> .setFirstResult(1)<br /> .setMaxResults(30)<br /> .setHint("hintName", "hintValue")<br /> .getResultList();<br />}</code></pre><br />Looking at this code we can now create a naive Query implementation:<br /><br /><pre><code>public interface Query {<br /> Query setParameter (String name, Object value);<br /> Collection getResultList() throws SqlException;<br />}<br /><br />public class PreparedSqlQuery implements Query {<br /> protected Map params = new HashMap();<br /> public Query setParameter (String name, Object value) {<br /> params.add(name, value);<br /> return this;<br /> }<br /> public Collection getResultList() throws SqlException {<br /> // Execute query and return results<br /> }<br />}<br /><br />public class SimpleQueryUser {<br /> public static void main(String[] args) throws Exception {<br /> new PreparedSqlQuery().setParameter("name", "value").getResultList();<br /> }<br />}</code></pre><br />No problems at all, our Fluent Interface is clean and elegant. But what happens when you create a subclass for executing prepared statements? You don't want to implement all that multiple setXxx methods once again, and instead decide to inherit from PreparedSqlQuery:<br /><br /><pre><code>public class StoredProcedureCall extends PreparedSqlQuery {<br /> protected String outputParameter = null;<br /> // Stored procedures may have both input and output parameters<br /> public StoredProcedureCall setOutputParameter(String param) {<br /> outputParameter = param;<br /> }<br /> public Collection getResultList() throws SqlException {<br /> // Execute stored procedure and return results<br /> }<br />}<br /><br />public class SimpleQueryUser {<br />public static void main(String[] args) throws Exception {<br /> // This works fine<br /> new StoredProcedureCall().setOutputParameter("OUT").setParameter("name", "value").getResultList();<br /> // This produces compilation errors<br /> new StoredProcedureCall().setParameter("name", "value").setOutputParameter("OUT").getResultList();<br />}<br />}</code></pre><br />The second oneliner produces a compilation error because Query object returned by setParameter() does not actually have setOutputParameter() method. To address this problem we can use <a href="http://www.java-tips.org/java-se-tips/java.lang/covariant-return-types.html">return type covariance</a>, which is a language feature added to Java 5. Here goes a fixed version of our StoredProcedureCall:<br /><br /><pre><code>public class StoredProcedureCall extends PreparedSqlQuery {<br /> protected String outputParameter = null;<br /> public StoredProcedureCall setOutputParameter(String param) {<br /> outputParameter = param;<br /> }<br /> // Compiler allows us to use StoredProcedureCall instead of Query as the return type here because StoredProcedureCall implements Query<br /> public StoredProcedureCall setParameter (String name, Object value) {<br /> super.setParameter(name, value);<br /> return this;<br /> }<br /> public Collection getResultList() throws SqlException {<br /> // Execute stored procedure and return results<br /> }<br />}</code></pre><br />Actually you can see exactly the same approach used in the real-world Query implementations, like <a href="http://openjpa.apache.org/builds/latest/docs/javadoc/org/apache/openjpa/persistence/OpenJPAQuery.html#setParameter%28java.lang.String,%20java.lang.Object%29">OpenJPAQuery</a>. Now what if you don't want to override all that numerous setXxx methods in all subclasses just to change their return types? Here is my solution utilizing another Java 5 language feature - <a href="http://en.wikipedia.org/wiki/Generics_in_Java">Generics</a>:<br /><br /><pre><code>public interface Query <T extends Query >{<br /> T setParameter (String name, Object value);<br /> Collection getResultList() throws SqlException;<br />}<br /><br />public class PreparedSqlQuery<S extends PreparedSqlQuery> implements Query<S> {<br /> protected Map params = new HashMap();<br /> public S setParameter (String name, Object value) {<br /> params.add(name, value);<br /> return (S) this;<br /> }<br /> public Collection getResultList() throws SqlException {<br /> // Execute query and return results<br /> }<br /> // Hide constructor, use factory to ensure users won't try to do something like: new PreparedSqlQuery<StoredProcedureCall>().setOutputParameter<br />}<br /><br />// We make it generic too to enable further extension<br />public class StoredProcedureCall<R extends StoredProcedureCall> extends PreparedSqlQuery<StoredProcedureCall> {<br /> protected String outputParameter = null;<br /> public R setOutputParameter(String param) {<br /> outputParameter = param;<br /> return (R) this;<br /> }<br /> // Note we don't have to override setParameter here<br /> public Collection getResultList() throws SqlException {<br /> // Execute stored procedure and return results<br /> }<br />}<br /><br />public class EntityManager {<br /> public static StoredProcedureCall<StoredProcedureCall> createStoredProcCall() {<br /> return new StoredProcedureCall<StoredProcedureCall>();<br /> }<br /> public static PreparedSqlQuery<PreparedSqlQuery> createNamedQuery() {<br /> return new PreparedSqlQuery<PreparedSqlQuery>();<br /> }<br />}<br /><br />public class SimpleQueryUser {<br /> public static void main(String[] args) throws Exception {<br /> EntityManager.createStoredProcCall().setParameter("name", "value").setOutputParameter("OUT").getResultList();<br /> }<br />}</code></pre><br />All that trickery with generics is done to fool the compiler. Good thing about it is that no additional methods are created, that means there are no super calls, which can potentially improve runtime performance. Bad things are obvious too:<ol><li>It is not safe unless you are using factories to instantiate that builders;</li><li>It is definitely not obvious and looks ugly;</li></ol><br /><br />Disclaimer: this is a hack, never use it in your code :)Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com1tag:blogger.com,1999:blog-3627102742438898505.post-12612950778657453512010-01-29T15:19:00.012+02:002010-07-13T08:49:28.069+03:00WebSphere + GWT + CometIBM supports Reverse AJAX (AKA <a href="http://en.wikipedia.org/wiki/Comet_%28programming%29">Comet</a>) in a form of <a href="http://www.ibm.com/developerworks/websphere/techjournal/0802_haverlock/0802_haverlock.html">WebSphere Feature Pack for Web 2.0</a> for WebSphere Application Server 6.0+. It is shipped by default with the latest fix packs for WAS 6.1 and in all 7.0 versions. Actually this Feature Pack has a lot of nice Web 2.0 features, but at the moment we are interested in <a href="http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp?topic=/com.ibm.websphere.webmsg.help/docs/GettingStarted.html">Web Messaging Service</a> only.<br /><br />To install and enable FP you need to follow the <a href="http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp?topic=/com.ibm.websphere.web20fep.multiplatform.doc/info/welcome_nd.html">documentation</a>, it won't take long. As the result you will get the ability to push data to your client's web browser by publishing it to the special JMS topics. As an alternative, you may also use a more specialized API for doing this, but the Publisher object is stored in the Servlet context, therefore it can be somewhat tricky to access it from an arbitrary part of your application, which is not true with JMS.<br /><br />I assume that you have successfully installed and tested your Web Messaging, and now want to upgrade your <a href="http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp?topic=/com.ibm.websphere.web20fep.multiplatform.doc/info/welcome_nd.html">QuoteStreamer sample application</a> to GWT. For example, you have a widget and want its methods to be called when some data arrives from the backend.<br /><br />It is possible to use <a href="http://googlewebtoolkit.blogspot.com/2008/07/getting-to-really-know-gwt-part-1-jsni.html">JSNI</a> to achieve exactly this goal. The idea is simple: when you need to push some object to the client (for example, out of your Message Driven Bean), you first serialize it using <a href="http://code.google.com/intl/ru/webtoolkit/doc/1.6/DevGuideServerCommunication.html">GWT RPC mechanism</a>, then put the resulting string to the appropriate JMS Topic. This string is wrapped to JSON envelope, sent to the client via WFP and unwrapped there via Dojo. Your JavaScript listener should be triggered at this moment. But as we use GWT for all our JavaScript programming, we should implement that listener as JSNI snippet:<br /><pre><code>private native void initCallbackAndSubscribe (String someParam) /*-{<br /></code><code> if ($wnd.dojox) {<br />$wnd.dojox.cometd.subscribe("/test/" + someParam, this, </code><code>function (comet) {<br /></code><code> </code><code> </code><code> module.@com.test.MyCoolWidget::onReverseAjax(Ljava/lang/String;)(comet.data)</code><code><br /></code><code> </code><code> </code><code>}</code><code>);<br />}<br />}-*/;<br /><br />// This is a normal Java code<br />public void </code><code>onReverseAjax</code><code>(String msg) throws </code><code>SerializationException </code><code>{<br /></code><code> </code><code>SerializationStreamReader r = ((SerializationStreamFactory) svc).createStreamReader(msg);<br /></code><code> Object data = </code><code>r.readObject();<br /></code><code> // Do something with that data arrived<br /></code><code>}<br /></code></pre>As you see, the data is deserialized later in your normal Java code called by this listener. That's it. If you have initialized everything right (in your index.jsp or some similar place), this scheme should work fine. Now I will show you how to send data using GWT RPC. It should be simple via RPC.encodeResponseForSuccess method, but there is one problem - it requires SerializationPolicy object for the security reasons. This mechanism restricts serialized objects to the limited set described in *.gwt.rpc files, generated during compilation. Another problem is that there can be several such files. I haven't found a good way to get the SerializationPolicy other than by concatenating these files altogether:<br /><code></code><pre>public SerializationPolicy getSerializationPolicy() throws Exception {<br /><code> </code>String result = "";<br /><code> </code>for (File file : new File(PATH_TO_YOUR_GENERATED_STUFF).listFiles(new FilenameFilter() {<br /><code> </code><code> </code>public boolean accept(File dir, String name) {<br /><code> </code><code> </code><code> </code>return name.endsWith(".rpc");<br /><code> </code><code> </code>}<br /><code> </code>})) {<br /><code> </code><code> </code><code></code>result += (FileUtils.readFileToString(file));<br /><code> </code>}<br /><code> </code>return SerializationPolicyLoader<br /><code> </code><code> </code>.loadFromStream(new ByteArrayInputStream(result<br /><code> </code><code> </code><code> </code>.getBytes("UTF-8")), null);<br />}<br /><br />public void publishObject(String topic, Object obj, Method method) throws Exception {<br /><code> </code>String r = RPC.encodeResponseForSuccess(method, obj, getSerializationPolicy());<br /><code> </code>publishStringViaJms(topic, r.toString());<br />}</pre>I forgot to mention that RPC.encodeResponseForSuccess requires a Method instance, but it is used only to extract the return value type, so any method returning the object of the obj.getClass() type will do.<br /><br />In general, there are some good things about such WebSphere + GWT + Comet integration, the major one is that it uses WFP, which is a pretty powerful Comet server, compared to some custom-built solutions. For example (at least on WAS 7.0), it uses connection multiplexing, nio and all that cool stuff you expect from the modern web server. Also, it naturally integrates into your ESB, so you can use it in your Process Server applications with ease. Some limitations are obvious too, for example it won't work in GWT Hosted Mode with Tomcat. Also it seems to me that this should be implemented somehow simpler, but I was unable to find it. So if you have any other ideas, please give me a clue.<br /><br />P.S.: Actually, there are some alternatives to this approach. For example you can use <a href="http://code.google.com/p/gwt-comet-streamhub/">gwt-comet-streamhub</a> to enable Reverse AJAX for your GWT applications. But it provides its own Comet server called <a style="font-style: italic;" href="http://www.stream-hub.com/">StreamHub</a> which is definitely not a part of the IBM WebSphere product line :)<br /><br />P.P.S.: The described method should work with other Comet-enabled J2EE servers like <a href="http://docs.codehaus.org/display/JETTY/Cometd+%28aka+Bayeux%29">Jetty</a> with minor modifications.<br /><br />P.P.P.S.: There is left one more issue with security. As for now, it is not really clear for me how to implement role-based security for Comet... I can think of using several update servlets at the same time, and it should allow me to use webapp security. But this issue definitely needs further investigation.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com10tag:blogger.com,1999:blog-3627102742438898505.post-53602308554318660702010-01-26T09:30:00.008+02:002010-01-28T09:14:47.258+02:00WADL toolbox<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpedNPsCk_Gbjl7P__1OswwYsrkribeur0clH6res3vKQacxxgRw-_5cTcCA7q4CzRDZAsl5sVlf2tZ4QX_Qsac4g_4KU8_4ZEplzqAfqSHVy0v1mkchbrPw9mjsRowXXXA5GX9bwrrkc/s1600-h/soapui.png"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 263px; height: 230px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpedNPsCk_Gbjl7P__1OswwYsrkribeur0clH6res3vKQacxxgRw-_5cTcCA7q4CzRDZAsl5sVlf2tZ4QX_Qsac4g_4KU8_4ZEplzqAfqSHVy0v1mkchbrPw9mjsRowXXXA5GX9bwrrkc/s400/soapui.png" alt="" id="BLOGGER_PHOTO_ID_5430959002461913394" border="0" /></a><a href="http://weblogs.java.net/blog/mhadley/archive/2005/05/introducing_wad.html">WADL</a> stands for Web Application Description Language and currently is in a state of <a href="http://www.w3.org/Submission/wadl/">W3C Member Submission</a>, that means at the early stage of W3C standardization process. The purpose of the "language" is similar to <a href="http://www.w3schools.com/WSDL/wsdl_intro.asp">WSDL</a>, with the orientation towards <a href="http://en.wikipedia.org/wiki/Representational_State_Transfer">RESTful APIs</a> in the first place. What is essential for me is that this language perfectly suits the task of <a href="http://code.google.com/p/pomodoro-server/">Pomodoro Server</a> API description. There are surprisingly few tools utilizing this format, I guess this is due to the immaturity of the language. Though I can name some of the most useful ones:<br /><ul><li><a href="https://jersey.dev.java.net/">Jersey</a>, a reference implementation of <a href="http://wikis.sun.com/display/Jersey/Overview+of+JAX-RS+1.0+Features">JAX-RS</a>. It is able to <a href="http://wikis.sun.com/display/Jersey/WADL">generate WADL</a> at run time based on the metadata found in your Java classes.</li><li><a href="http://www.soapui.org/">soapUI</a>, a great tool for testing web services. As one of its numerous new features in version 2.5 it can <a href="http://www.soapui.org/userguide/rest/index.html">utilize WADL</a> web service description and generate sample test cases automagically. It can even do the trick in the opposite direction, i. e. <a href="http://www.soapui.org/userguide/rest/inference.html">infer both WADL and XSD</a> from the existing test case structure! Unfortunately I've found some aspects of this generation somewhat <strike>buggy</strike> not really obvious. For example, it does not inherit template parameters for nested resources. And despite the ability to specify application-wide settings for HTTP Basic Auth, it does not get applied somehow, so it should be specified for each test case separately. Anyway, it is a great tool and I'm looking forward to see it in a bit more stable state.</li><li><a href="http://github.com/mnot/wadl_stylesheets/">WADL to HTML</a> is an advanced XSLT stylesheet to generate documentation from your WADLs. It seems that either my WADL is all wrong (then why it conforms to the <a href="https://wadl.dev.java.net/wadl200610-rddl.xhtml">WADL Schema</a>?), or my XSLT processors are broken (both <a href="http://saxon.sourceforge.net/">Saxon</a> and <a href="http://xml.apache.org/xalan-j/">Xalan</a> running on Windows and Linux), because I was unable to get any output. But they say it works for them, so I tend to think it is actually my own problem. Anyway, I've already wrote my own simple XSLTs and going to put it here in a matter of few days :)</li><li><a href="https://wadl.dev.java.net/wadl2java.html">wadl2java</a> generates a web service client based on WADL. It uses <a href="https://jaxb.dev.java.net/">JAXB</a> to create the necessary representations based on XSD referred in <span style="font-style: italic;">grammars</span> section.</li><li><a href="http://tomayac.de/rest-describe/latest/RestDescribe.html">REST Describe & Compile</a> is yet another client generator. It can infer WADL from the set of supplied request URLs, and then generate client code in several languages, namely Java and Python. The tool looks promising, but as for me, the UI is not intuitive enough. Also, you have no control over code generation, so good chances are that you will have to modify your clients manually after generation. <a href="http://docs.google.com/Doc?id=dgdcn6h3_38fz2vn5">Here</a> you can find a great deal of documentation for the tool and underlying concepts.<br /></li></ul>P. S.: You can find WADL for Pomodoro Server <a href="http://code.google.com/p/pomodoro-server/source/browse/trunk/src/pomodoro.wadl">here</a>. Also you can see some <a href="http://code.google.com/p/pomodoro-server/wiki/GeneratedAPI">generated documentation</a> in the <a href="http://code.google.com/projecthosting/">Google Code</a> <a href="http://code.google.com/p/support/wiki/WikiSyntax">wiki</a>.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com6tag:blogger.com,1999:blog-3627102742438898505.post-25388686910888488192009-12-23T12:56:00.004+02:002009-12-23T17:18:55.098+02:00Some useful JSR-168 resourcesWhile trying to figure out the essentials of portlet development, I found few useful resources on this topic:<br /><ul><li><a href="http://developers.sun.com/portalserver/reference/techart/jsr168/pb_whitepaper.pdf">Introduction to JSR 168—The Java Portlet Specification</a> (pdf)</li><li><a href="http://www.developer.com/article.php/10915_3366111_1/Understanding-the-Java-Portlet-Specification.htm">Understanding the Java Portlet Specification</a></li><li><a href="http://www.ibm.com/developerworks/java/library/j-jstl0415/">I18n in portlets</a><br /></li></ul>Here goes a mindmapped extract of those two:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimBfMjzVLrm6SE8rGdH_EMAYU9jbseLGbFJh55AabrDj0NUO3vk0-9BdjSc8_ECLS69r0kEq_ofqqbdZL4V7WQvwy_eI96SbuC8A3qR9axxoCKhQKdhniT2NENT0rbc3W-ixR3A0hc7jQ/s1600-h/JSR168+Portlets.png"><img style="cursor: pointer; width: 400px; height: 168px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimBfMjzVLrm6SE8rGdH_EMAYU9jbseLGbFJh55AabrDj0NUO3vk0-9BdjSc8_ECLS69r0kEq_ofqqbdZL4V7WQvwy_eI96SbuC8A3qR9axxoCKhQKdhniT2NENT0rbc3W-ixR3A0hc7jQ/s400/JSR168+Portlets.png" alt="" id="BLOGGER_PHOTO_ID_5418387639167082066" border="0" /></a>Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com1tag:blogger.com,1999:blog-3627102742438898505.post-75655236312097801372009-12-17T17:43:00.003+02:002009-12-17T17:47:44.588+02:00Using Spring in server side of GWTThanks to <a href="http://pgt.de/2009/07/17/non-invasive-gwt-and-spring-integration-reloaded/">this post</a>, I was able to use Spring in my GWT-RPC services. Though, I had to fix it a bit - use <code>autowireBeanProperties</code> instead of <code>autowireBean</code> (otherwise my services didn't get populated).Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-73541664059473645512009-11-07T19:36:00.008+02:002009-11-07T21:15:57.908+02:00Relational storage using JAXB, JPA and HyperJAXB<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPrK6LGo73Nty9UERpqGNDYg0Uutsf18rC4pUnbz94G8SLZu9yOsnmUc5mcS_K6FrC6GQcFDzJY3wpqCQ_8KgIvEVRrPQh3M8VkJfKAoYtdgJNmUfXsblpknq5r0ypPZQhEnp6EsHnY4Uq/s400/silver_bullet.jpg"><img style="margin: 0pt 10pt 10px 0px; float: left; cursor: pointer; width: 250px; height: 250px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPrK6LGo73Nty9UERpqGNDYg0Uutsf18rC4pUnbz94G8SLZu9yOsnmUc5mcS_K6FrC6GQcFDzJY3wpqCQ_8KgIvEVRrPQh3M8VkJfKAoYtdgJNmUfXsblpknq5r0ypPZQhEnp6EsHnY4Uq/s400/silver_bullet.jpg" alt="" border="0" /></a><span lang="EN-US">Let us imagine a situation when you are writing an application sitting on the ESB, or providing a web service enabled interface. In both cases you will probably end up having some XML Schema describing your data transfer format. Also, good chances are that you will have to have a RDBMS schema, too. Usually both are designed, implemented and documented separately. But when you write your application from scratch, you have a unique possibility to seriously reduce amount of work need to be done. Here I want to share some of my ideas how to achieve this.</span> <p class="MsoNormal"><span lang="EN-US">Few variants are possible. You can start with DDL, <a href="https://www.hibernate.org/hib_docs/tools/reference/en/html_single/#d0e1193">generate</a> JPA-annotated beans using <a href="https://www.hibernate.org/255.html">Hibernate Tools</a> for example, and then annotate it with some JAXB stuff (or use something not requiring annotations at all, like <a href="http://xstream.codehaus.org/">XStream</a>). You will be able to get an XSD using some JavaBeans-to-XSD tool (like you have in IBM Rational tools when generating WSDLs for given beans), or just generate a bunch of sample XML files by marshaling some test data and use any modern XML editor to <a href="http://www.dotkam.com/2008/05/28/generate-xsd-from-xml/">generate XSD by example</a>. But I must warn you that the XSD will be rather ugly, because usually you don't have too much control over the whole process, that's why the proper tooling is vital for success. Nevertheless, this approach is quite common and supported by many powerful tools, such as my favorite IBM Rational Software Architect 7.5.</span></p> <p class="MsoNormal"><span lang="EN-US">From the other hand, you can start with some UML and generate Java Beans, then put all the necessary annotations (both JAXB and JPA) inside. But you will have to do a double amount of work fixing the beans structure and hardcoding annotations for both technologies. Once again, you will need to fix the resulting XSD manually, that's for sure.</span></p> <p class="MsoNormal"><span lang="EN-US">What I prefer to do is design the data layer entirely in XSD (which maps to Java classes in a more natural way than DDL), then generate Java beans using JAXB or something similar, enrich it with JPA annotations and generate DDL as the result. Sounds pretty nice, but there are some problems, particularly at the JPA annotations stage. The major one is that you lose flexibility in XSD changing, because after each change your beans will be generated again and you will lose all your precious annotations (yes, you can keep JPA configuration in XML, but this approach has its own drawbacks, and it's beyond the scope of this post anyway). </span></p> <p class="MsoNormal"><span lang="EN-US">Fortunately there exists a tool which reduces (and in some cases can even eliminate it at all) the amount of the manual work at the JPA stage. It is implemented by <a href="http://lexicore.blogspot.com/">Aleksei Valikov</a> and called <a href="http://confluence.highsource.org/display/HJ3/Home">HyperJAXB 3</a> (version 1 is outdated and version 2 supports Hibernate instead of pure JPA). It is more-or-less a plugin set for <a href="http://java.sun.com/webservices/docs/1.6/jaxb/xjc.html">XJC</a>, adding JPA annotations to JAXB-generated beans. It is <a href="http://confluence.highsource.org/display/HJ3/Downloads">distributed</a> as an easy to use bundle containing everything (including build.xml) to start converting your XSDs to JAXB + JPA enabled Java Beans. </span></p> <p class="MsoNormal"><span lang="EN-US">Here comes the concepts of relationships and keys. The problem is, when not specified explicitly, HyperJAXB creates an autogenerated integer ID for every entity, which is not always what you really want to get. Let me explain. I want to automate not only the data schema creation, but also the API to access data. For example, when there comes an XML from the client, I don't want to check if the same piece of data is already present in my DB (in fact it can be a hell lot of work to do, especially when some complex, deeply nested XML structures are considered). I want the intelligent middleware to <a href="http://blog.xebia.com/2009/03/23/jpa-implementation-patterns-saving-detached-entities/">merge it automatically</a> with the existing data. But it won't happen by default, because of the autogenerated IDs, which are always issued new. So, if you will try to persist the same XML twice, it will put two copies in your DB, which is probably not what you expect to see. </span></p> <p class="MsoNormal"><span lang="EN-US">To fix this behavior, you need to explicitly specify the meaningful IDs for your entities. With HyperJAXB you can specify it directly in your XSD by marking some of the entity fields with the special XSD annotation in the same way you document your schemas (For details please consult <a href="http://confluence.highsource.org/display/HJ3/Documentation">HyperJAXB documentation</a>. Yes, you still can't use the XSD uniqueness constraints support because it's <a href="http://jira.highsource.org/browse/HJIII-13">a bit too hard</a> to parse by XJC-like tool). Same is true for the relationship options – for example, you can define one-to-many with all the possible JPA attributes, so it's quite flexible in case you need it. </span></p> <p class="MsoNormal"><span lang="EN-US">So far, so good. Things start being complicated when you have compound IDs (and I think it happens really soon in the real life applications). Fortunately HyperJAXB gained support for IdClass <a href="http://markmail.org/message/wp45bguix3ncddak">few weeks ago</a>. But here comes another problem of “cascading” IDs – when you have a complex primary key (in entity A) used as a foreign key in another entity (B), which at the same time is a part of its (B's) primary key. We had a brief <a href="http://markmail.org/message/2evf6nd2gbembgn3">discussion</a> on this topic with Aleksei (BTW, I want to say THANKS! for his great instant support for HyperJAXB and related stuff), and it seems to be too hard to implement in the foreseeable future due to some limitations of XJC extension mechanisms. So, the only way out is to tweak generated beans manually. Well, I think it is something like 20% of the job, and another 80% is done automatically.</span></p> <p class="MsoNormal"><span lang="EN-US">So, in the end the results are pretty sweet. You design your data structure as XML Schema (which itself can be generated based on XML samples). Then you put some metadata (mostly ID information) inside to make sure it is complete. Now you run HyperJAXB against your XSDs and fix the generated Java Beans in case you need to handle some “complex” situations. Then, using one of the tools (either Hibernate Tools or <a href="http://openjpa.apache.org/builds/1.0.2/apache-openjpa-1.0.2/docs/manual/ref_guide_mapping.html#ref_guide_ddl_examples">OpenJPA Mapping Tool</a>) you get DDL in a matter of seconds. After that you are able to (un)marshal your entities in a single line of code using JAXB, and load / merge them from / to any RDBMS in a single LOC using JPA. That totally eliminates all the serialization and storage-related code, reducing complexity, improving reliability, bla-bla-bla :)</span></p> <p class="MsoNormal"><span lang="EN-US">P. S.: I must warn you that there are some issues when using this approach with different JPA providers. I've tried both Hibernate and OpenJPA and they seem to be not really compatible with each other for anything but the most trivial models. It worth a separate post and I'm not going to explain it here, just to make it short: I prefer OpenJPA over Hibernate nowadays, because I find it less restricting in defining relationships and complex IDs. Also, from the tooling point of view, those are pretty close.</span></p> <p class="MsoNormal"><span lang="EN-US">P. P. S.: HyperJAXB functionality is growing rapidly, so I won't be surprised to see the majority of the issues mentioned in this post addressed in the upcoming releases. I wish this project good luck and can't wait to see it gaining popularity in the JEE development community.</span></p> <p class="MsoNormal"><span lang="EN-US"><o:p></o:p>P. P. P. S.: Code samples and / or detailed tutorial will come later, because it is a considerable amount of work and I'm pretty busy nowadays. Though, if you have any questions regarding the aforementioned approach, please feel free to ask.</span></p>Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com2tag:blogger.com,1999:blog-3627102742438898505.post-79767950434297365012009-08-28T12:38:00.000+03:002009-08-28T12:39:25.467+03:00Web 3.0 Explained<object height="344" width="425"><param name="movie" value="http://www.youtube.com/v/Rd21qGeQbp0&hl=en&fs=1&rel=0"><param name="allowFullScreen" value="true"><param name="allowscriptaccess" value="always"><embed src="http://www.youtube.com/v/Rd21qGeQbp0&hl=en&fs=1&rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="344" width="425"></embed></object>Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-22132655060741356772009-08-17T13:43:00.009+03:002010-02-02T11:54:32.460+02:00Very Simple RESTful Web Services in Python<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.vanille-media.de/images/python.png"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 267px; height: 235px;" src="http://www.vanille-media.de/images/python.png" alt="" border="0" /></a>I was trying to find something for easy implementation of web services in any CGI language. Firstly I considered PHP. A lot of negative emotions, nothing really simple and a wasted day. Perl has some better support for the stuff, but I've chosen Python (mostly because I've got less experience in it and wanted to give it a try).<br /><br />Python has a CGI-like standard called <a href="http://en.wikipedia.org/wiki/Web_Server_Gateway_Interface">WSGI</a>, which makes Web Services implementation much easier. But still not that easy, as I want it. So, spent a day writing my own "library", which you can find <a href="http://code.google.com/p/python-very-simple-web-services/">here</a>, hosted on Google Code. Now you can write RESTful Web Services like this:<br /><pre class="prettyprint"><a name="Examples"><span class="lit">@url_pattern</span><span class="pun">(</span><span class="str">"/users/${username}/plans/${year}"</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[</span><span class="str">'GET'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'PUT'</span><span class="pun">])</span><span class="pln"><br /></span><span class="kwd">def</span><span class="pln"> get_plans </span><span class="pun">(</span><span class="pln">username</span><span class="pun">, year, request):</span><span class="pln"><br /></span> <span class="kwd">return</span><span class="pln"> </span><span class="str">"Inside get_plans('%s', '%s')"</span><span class="pln"> </span><span class="pun">%</span><span class="pln"> (username, year)<br /></span></a></pre>I'm gonna use it to implement a very first version of <a href="http://code.google.com/p/pomodoro-server/">Pomodoro Server</a>. Some brief impressions:<br /><ul><li>Python is very easy to learn and has a lot of great metaprogramming capabilities, very similar to Ruby. Great language, it's a pleasure to code it!<br /></li><li>Python's documentation is just awesome!</li><li>Some Web Frameworks provide the similar functionality, but it's painful to install and comes with a lot of other heavyweight features, such as MVC and ORM.<br /></li><li>Google Code is a nice place to host your projects, though there are some limitations (for example, wiki is very basic and not compatible with anything else).</li></ul>Update: I've just found a similar thing in Java, defined in <a href="https://jsr311.dev.java.net/">JSR-311: JAX-RS - Java API for RESTful Web Services</a> and implemented as <a href="https://jersey.dev.java.net/">Jersey</a>. Just a short sample from its <a href="http://docs.sun.com/app/docs/doc/820-4867?l=en">tutorial</a>:<pre>@Path("/users/{username}")<br />public class UserResource {<br /> @GET<br /> @Produces("text/xml")<br /> public String getUser(@PathParam("username") String userName) {<br /> }<br />}<br /></pre>Update2: found a very similar solution for Python called <a href="http://github.com/defnull/bottle#readme">Bottle</a>. Sample code:<br /><pre>@route('/hello/:name')<br />def hello_name(name):<br /> return 'Hello %s!' % name<br /><br />run(host='localhost', port=8080)</pre>Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com3tag:blogger.com,1999:blog-3627102742438898505.post-46697661626879375292009-08-05T16:57:00.000+03:002009-08-05T17:45:14.614+03:00DSL Killer Application<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://deesel.codecauldron.org/SayNoToXMLWebSize.png"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 200px; height: 213px;" src="http://deesel.codecauldron.org/SayNoToXMLWebSize.png" alt="" border="0" /></a>Now I want to point at a really mind-blowing tool called <a href="http://www.jetbrains.com/mps/">JetBrains Meta Programming System</a>. You see, some time ago to implement your own <a href="http://en.wikipedia.org/wiki/Domain-specific_language">Domain-Specific Language</a> it was necessary to develop a grammar, implement a parser (or use such tools as <a href="http://www.gnu.org/software/bison/">GNU Bison</a> for example) which normally operates in <a href="http://en.wikipedia.org/wiki/Simple_API_for_XML">XML SAX</a> manner and finally do something with the results obtained. Nowadays these new tools enable you to do virtually all this stuff in kinda type-safe way. It strictly watches at all your manipulations and alerts violations.<br /><br />The whole process somehow resembles XDoclet and XSLT a bit, but it's much more reliable, because of the advanced IDEA editor, which takes into account all the necessary constraints, disallowing to misprint something. Still (due to verbose syntax) logical errors at source generation stage are highly possible. Good news is that you can use the whole power of modularity in this process to reduce such risks. Maybe the most striking thing about it is that its own language to define grammar and generate artifacts is also a DSL implemented in the same environment!<br /><br />You can download JetBrains MPS <a href="http://www.jetbrains.com/mps/download/index.html">here</a> for free and try <a href="http://www.jetbrains.com/mps/docs/tutorial.html">a tutorial</a>. It will take some time to get used to the tool's UI. Its strangeness is in fact that there's a pretty complex tree representation behind a text you see on the screen, and it contains a lot of meta-information displayed as special markup signs, different colors, etc. So you shouldn't think of it as of a plain old source code anymore (to see what I'm talking about, just look at XML files inside a simple test project).<br /><br />There are some alternatives to this environment, namely <a href="http://msdn.microsoft.com/en-us/library/bb126235.aspx">Microsoft Visual Studio Domain-Specific Language Tools</a> and <a href="http://www.metacase.com/products.html">MetaEdit+</a>, though I haven't tried any.<br /><br />P.S.: Thanks to <a href="http://www.martinfowler.com/bliki/">Martin Fowler's bliki</a> for the link - it's a really visionary source of information! Here you can also watch his <a href="http://www.infoq.com/presentations/domain-specific-languages">video</a> on this topic.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-51394105655377085472009-07-14T15:26:00.001+03:002009-07-14T16:17:02.043+03:00Code City<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.inf.unisi.ch/phd/wettel/pics/codecity_screenshot.png"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 355px; height: 224px;" src="http://www.inf.unisi.ch/phd/wettel/pics/codecity_screenshot.png" alt="" border="0" /></a>Today I was reading a book about software metrics called "<a href="http://www.springer.com/computer/programming/book/978-3-540-24429-5">Object-Oriented Metrics in Practice</a>" by <a href="http://www.inf.unisi.ch/faculty/lanza/publications.html">Michele Lanza</a> and <a href="http://www.cs.utt.ro/%7Eradum/papers.html">Radu Marinescu</a>. It's abouth things like "Design Harmony" and so on.<br /><br />Some of the ideas are debatable, for example <a href="http://books.google.com.by/books?id=gdLbgnaMaa0C&lpg=PA46&ots=sZsSyPJtrL&dq=%22Object-Oriented%20Metrics%20in%20Practice%22%20harmony%20poem&hl=en&pg=PA46">on page 46</a> they say:<blockquote style="font-style: italic;">...you cannot understand the beauty of a painting by measuring its frame or understand the depth of a <em>poem</em> by counting the lines...<br /><br />...metrics can help to evaluate and improve designs, but those have to be meaningful metrics that are put in a context of design harmony...<br /></blockquote>But at the same time it's obvious that you won't see the beauty of the poem looking on it's grammar, syntax or verse structure (these are more-or-less analogues for software design metrics), without actually reading and understanding the sense. That's why my conclusion is that for creating a <span style="font-style: italic;">harmonious</span> software design it's necessary (but not sufficient) for the metrics to be <span style="font-style: italic;">harmonious, </span>too<span style="font-style: italic;">.</span><br /><br />What I liked most of all was the concept of visualizing software projects as cities. The metaphor includes classes as buildings and packages as districts. It is implemented in a tool called <a href="http://moose.unibe.ch/tools/codecity">CodeCity</a>. Some results of its work can be seen on <a href="http://www.inf.unisi.ch/phd/wettel/activities.html">Richard Wettel's page</a>, who actually wrote it. Here is just one of them:<br /><br /><object style="margin: 0px;" type="application/x-shockwave-flash" data="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=wettel-icse09doc-090520041413-phpapp01&stripped_title=visual-exploration-of-largescale-evolving-software" height="355" width="425"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=wettel-icse09doc-090520041413-phpapp01&stripped_title=visual-exploration-of-largescale-evolving-software"><param name="allowFullScreen" value="true"><param name="allowScriptAccess" value="always"></object><br /><br />Though, there are few things which I think can make it even better:<br /><ul><li>It would be great to see a color scheme based on the developers responsible for changes, for example, using <a href="http://svnbook.red-bean.com/en/1.0/re02.html">svn blame</a> (it can be useful for both "normal" and "timeline" views).</li><li>Building base should be Sqrt(NOA), not just NOA - it will look more realistic. It also should have an option to scale building height to Log(NOM).</li><li>Color scheme should be configurable - for example, it's hard to see some "outdated" buildings on the dark backgrounds.</li></ul>Nevertheless, thank you very much for giving really interesting food for my mind :)Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com1tag:blogger.com,1999:blog-3627102742438898505.post-11155544678763291862009-07-13T15:43:00.001+03:002009-08-12T16:11:57.456+03:00A way to stress-test GUI<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.tecacentre.net/funpics/funpics/super_elephant.jpg"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 175px; height: 269px;" src="http://www.tecacentre.net/funpics/funpics/super_elephant.jpg" alt="" border="0" /></a>What to do if you need to test the performance of your client-server application, which is not on the Web? For example, some kind of Lotus Notes one. The problem is - you can't even execute several Lotus instances on a single machine, so it's quite a tricky task to simulate multiple simultaneous users.<br /><br />Virtualization is what you can use in this case. There's an example of working stress testing system consisting of <a href="http://en.wikipedia.org/wiki/Citrix_XenApp">Citrix XenApp</a> terminal server running 10 Lotus Notes instances (eating 50 Mb of RAM each), and a load generator running IBM Rational Performance Tester (which is just a very advanced point-and-click thing), which can safely simulate 20 - 40 concurrent users.<br /><br />Major bottlenecks of this setup are:<br /><ol><li>RAM on Terminal Server (it's better to use 64 bit solution)</li><li>Network bandwidth (at least 1 Gbit Ethernet)</li><li>It takes much longer (up to 4 times) to implement and debug such test cases, compared to usual web testing scenarios<br /></li></ol>Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-38360220150448087792009-07-13T13:01:00.000+03:002009-07-13T13:09:56.787+03:00XML Appliances<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www-01.ibm.com/software/integration/datapower/images/xi50_mainframe.jpg"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 245px; height: 82px;" src="http://www-01.ibm.com/software/integration/datapower/images/xi50_mainframe.jpg" alt="" border="0" /></a>I was surprised to find some hardware XML processors, called <a href="http://en.wikipedia.org/wiki/XML_appliance">XML Appliances</a>. They can do XSLT,validation, encryption and many more. Implemented as a separate network device it can be useful in SOA.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-55692506575125758932009-06-07T23:25:00.000+03:002009-06-07T23:48:52.333+03:00Things we should add to our build processI want to try some of these and see what happens:<br /><ol> <li>Use <a href="http://sonar.codehaus.org/">Sonar</a> for tracking various metrics (it uses all well-known Open Source tools like <a href="http://pmd.sourceforge.net/">PMD</a>, <a href="http://checkstyle.sourceforge.net/">Checkstyle</a>, etc. and <a href="http://sonar.codehaus.org/a-new-hudson-plugin-for-a-closer-integration-with-sonar/">compatible</a> with <a href="https://hudson.dev.java.net/">Hudson</a>, which we already use in our projects);</li> <li>Use <a href="http://clirr.sourceforge.net/clirr-core/index.html">Cirr</a> to document public API changes (to know when something really important has changed);</li> <li>Use <a href="http://innig.net/macker/faq.html#goodfor">Macker</a> (btw, its' <a href="http://innig.net/macker/faq.html">FAQ</a> is really informative, thanks!) to keep dependencies between different modules under control. It first requires <a href="http://innig.net/macker/guide/basic.html#rules">some modeling</a>, after which it can break builds in case someone breaks convention;</li> </ol> Also, what I want to do in the nearest future is some kind of <span style="font-style: italic;">retrospective metrics analysis</span> tool. It will consist of (maybe) set of shell scripts checking out revisions from Subversion repository, compiling code, running <a href="http://www.clarkware.com/software/JDepend.html">JDepend</a> and <a href="http://java-source.net/open-source/code-analyzers">others</a> against it, applying some XSLTs to its results to combine it altogether into single big XML, putting it then into some kind of 3D storage (Revision * Metric * Module), like OLAP cube in order to analyse it. Theoretically, such tool can generate a great amount of information, and what's interesting is how useful it could actually be.<br /><br />The idea was inspired by one of the reports on <a href="http://www.infopark.by/main.aspx?uid=2536">Software Engineering Forum 2009</a> (link in Russian).Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-88940751349248367922009-06-07T22:22:00.000+03:002009-06-07T22:38:31.177+03:00TeamCity by JetBrains: yet another great CI solutionAfter some excitement about Rational Team Concert features, here comes another one about <a href="http://www.jetbrains.com/teamcity/index.html">TeamCity</a>, by <a href="http://www.jetbrains.com/">JetBrains</a>, the same company which gave us <a href="http://www.jetbrains.com/idea/index.html">IntelliJ IDEA</a> and <a href="http://www.jetbrains.com/resharper/index.html">ReShaper</a>.<br /><br />So, TeamCity is an all-included solution supporting <a href="http://www.jetbrains.com/teamcity/features/index.html">tons of really advanced features</a>, among which there are:<br /><ul> <li><a href="http://www.jetbrains.com/teamcity/features/continuous_integration.html#Remote_Build_Run_and_Pre-Tested_Commit">Remote Run and Pre-tested commit</a></li> <li><a href="http://www.jetbrains.com/teamcity/features/build_grid.html">Build Grid</a> (just awesome!)</li> <li><a href="http://www.jetbrains.com/teamcity/features/code_quality.html#Code_Coverage_Inspections_and_Duplicates_Search">Code Coverage, Inspections and Duplicates Search</a></li> <li><a href="http://www.jetbrains.com/teamcity/features/continuous_integration.html#Build_Progress_and_Estimation">Build Progress and Estimation</a></li> <li><a class="improved" href="http://www.jetbrains.com/teamcity/features/continuous_integration.html#Notifications">Notifications</a> (of all kinds)</li> </ul> You can try and actually <a href="http://www.jetbrains.com/teamcity/buy/edition_comparison.html">use it for free</a> until your project is huge enough. It takes just 225 Mb to <a href="http://www.jetbrains.com/teamcity/download/index.html">download</a> and 3 minutes to install.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-81537807675957867172009-06-05T18:10:00.000+03:002009-06-07T22:39:38.199+03:00Nice short article about IBM Rational Team Concert<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://spicytunas.com/wp-content/uploads/2008/07/teamwork.jpg"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 200px; height: 188px;" src="http://spicytunas.com/wp-content/uploads/2008/07/teamwork.jpg" alt="" border="0" /></a><a href="http://jazz.net/forums/viewtopic.php?t=4383">Here it is</a>. Seems that RTC is even better than I thought... Among cool things mentioned, there are:<br /><ul><li>Support for Agile methodology out of the box</li><li>Original approach to SCM, based on the concept of "streams" (which are essentially branches)</li><li>Advanced build system, automatically collecting all supporting artifacts, such as change sets, fixed defects, etc.</li><li>A lot of great documentation (like <a href="http://jazz.net/library/article/41">Getting Started with Jazz Source Control</a>)<br /></li></ul>What I can add from my personal experience, is that the Major Huge Advantage of RTC is that you get all these features integrated altogether out of the box. Installed it today on Windows 2003 Server - it took just 15 minutes (!) to install and configure a complete team collaboration solution. It normally takes few days to configure something like Trac + Subversion + Hudson, and even longer if you'd like to replace Trac with Redmine or Bugzilla on Linux, etc.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com2tag:blogger.com,1999:blog-3627102742438898505.post-49287965219205973622009-06-05T12:11:00.000+03:002009-06-05T12:24:24.140+03:00Client JavaScript data storageCross-browser data storage made easy using <a href="http://pablotron.org/software/persist-js/">PersistJS</a>. It supports a lot of different ways of storing data on client browser, falling back to plain old cookies, when necessary. The library itself is really minimalistic and standalone (opposed to <a href="http://www.dojotoolkit.org/node/115">Dojo Storage</a>, for example). Also, <a href="http://taffydb.com/index.cfm?oa=faq&q=clientStorage">it can be integrated</a> with another lightweight runtime storage solution called "<a href="http://taffydb.com/index.cfm">Taffy DB</a>", which will enable almost SQL-like queries in JavaScript. Looks very nice to store some user-related info, session data, etc.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com2tag:blogger.com,1999:blog-3627102742438898505.post-32694021679831587912009-05-31T14:37:00.000+03:002009-05-31T15:05:10.197+03:00Ultimate Pomodoro?<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://farm3.static.flickr.com/2252/2300694417_2f1818ede8.jpg?v=0"><img style="margin: 0pt 10px 10px 0pt; float: right; cursor: pointer; width: 170px; height: 250px;" src="http://farm3.static.flickr.com/2252/2300694417_2f1818ede8.jpg?v=0" alt="" border="0" /></a>What is the major problem of using the <a href="http://wintermuteblog.blogspot.com/2009/02/pomodoro-technique.html">Pomodoro Technique</a>? I think it's a <a href="http://metalelf0dev.blogspot.com/2009/05/shared-pomodoro-to-share-or-not-to.html">sharing one</a>. You see, when you're "in the middle of a pomodoro", no one really cares - your team mates will go on interrupting you anyway!<br /><br />So, you need to synchronize it somehow. Setting a single timer per team is a bad idea, for the obvious reasons. What I suggest to do is to invent some kind of a Pomodoro Server and track your current status there (team-wise or project-wise). Of course, the policy should be invented for all team members to check this status before interrupting another person.<br /><br />Sample feature list:<br /><ul><li>Notification when required person's pomodoro has finished</li><li>Integration with your <strike>corporate</strike> favourite IM client as a plugin</li><li>Integration with popular issue tracking systems via plugins</li><li>Speach recognition to simplify items entry</li><li>Flexible hotkeys for all frequent actions to make things even easier</li><li>Statistics gathering to analyze and boost personal productivity</li></ul>Hope to find some time to figure it out in a further detail... Frankly speaking, already tried some of the most popular open source voice recognition tools. It seems that either my microphone, or my English, or these tools, or its' settings, or alltogether are too bad to recognize anything but "one [pause] two". With 25% accuracy. Sigh...Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com4tag:blogger.com,1999:blog-3627102742438898505.post-2148320718887504452009-05-12T23:57:00.000+03:002009-05-13T00:04:24.624+03:00About codingI want to separate two different kinds of coding.<br /><br />First is handwriting code from scratch, which is a good old not-reusable-bad-to-support approach. It gives me a great pleasure, because I know what to expect and when I solve a Task, it's clear what I've done to complete it. I mean, it's all so clear and predictable, that nothing can spoil your pleasure of getting things done. I used to code this way somewhere about seven years ago writting C++ utils in MS Visual Studio 7.<br /><br />Nowadays, things are different. I'm developing Important Enterprise Java Applications, and the <strike>coding</strike> development sequence looks like this:<br /><br /><ol><li>Find an appropriate Library or Framework.</li><li>Choose the one which either the trendiest one (in this case all your further actions will be more painful), or the wider used one (of course, in this case everyone will think that you're starting to lose your grip on modern hi-tech).</li><li>Try it on some sample application (though, this stage will not give you any useful information at all).</li><li>Try to plug it to your existing application, consisting of Frameworks, Libraries and running on Servers. Configure it (that's where you start hating both XML and lazy developers who suddenly appear not being able to write any meaningful documentation at all). That's where the vast majority of time gets spent.</li><li>Fix all bugs and side effects brought by that all-new yet-another Framework. You won't be able to spot all of them fast enough of course, so this stage virtually never ends.</li></ol><br />So, in this world the pleasure of getting things done is usually comes at step 4, when you finally manage to squeeze your brand new part into the puzzle. But that's not it - it has nothing in common with the first Predictable Big Pleasure, and this makes a huge problem for me. Somehow, programming (which is such an interesting and exciting thing in fact!) doesn't make me happy anymore - it became just a job.<br /><br />And exactly that's why I'm really looking forward to start coding in Ruby, to see if it's that good, as they say.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-83343901012727158182009-04-07T13:57:00.000+03:002009-04-07T14:11:53.226+03:00web.xml security limitationsIt appears that web.xml security is kinda useless in the real world - it has few severe limitations:<br /><ul><li>This will not work at all (several wildcards):<br /><code><url-pattern>/stations/*/departure/*</url-pattern></code><br /><br /></li><li>This will not work as expected, because only one security constraint will be checked (both constraints work separately):<br /><code><br /> <security-constraint><br /> <display-name>Station 14 constraint</display-name><br /> <web-resource-collection><br /> <web-resource-name>All station 14'th resources</web-resource-name><br /> <url-pattern><b>/stations/14/*</b></url-pattern><br /> </web-resource-collection><br /> <auth-constraint><br /> <role-name>STATION_14</role-name><br /> </auth-constraint><br /> </security-constraint><br /><br /> <security-constraint><br /> <display-name>View arrival constraint</display-name><br /> <web-resource-collection><br /> <web-resource-name>View arrival page</web-resource-name><br /> <url-pattern><b>/stations/14/arrival/*</b></url-pattern><br /></web-resource-collection><br /> <auth-constraint><br /> <role-name>VIEW_ARRIVAL</role-name><br /> </auth-constraint><br /> </security-constraint><br /></code></li></ul>Had to spend three days to figure it out :( Now I'm going to investigate <a href="http://static.springframework.org/spring-security/site/">Spring Security</a> (AKA <a href="http://www.acegisecurity.org/">Acegi Security</a>).Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-76111551038232140262009-03-20T15:47:00.000+02:002009-06-07T22:43:26.420+03:00Mercurial vs Subversion<a href="http://www.selenic.com/mercurial/wiki/">Mercurial</a> is yet another version control system. According to its' <a href="http://hgbook.red-bean.com/hgbook.pdf">book</a>, here are the main differences to Subversion:<br /><ul><li>Mercurial is distributed, i. e. each Mercurial client has his own complete copy of repository, including whole history. That's the main difference to Subversion, which is client-server, so other differences can be considered as side-effects of this.</li><li>Merge is much better in Mercurial, which makes it easier to use branches (always a big pain with Subversion). This is really required, because it's the main mechanism of sharing code between developers.</li><li>Performance - Mercurial is in general somewhat faster.</li><li>Space - Mercurial is more efficient when consuming disk space.</li><li>3rd-party integration - Subversion has more integration means, at least now.</li><li>Locks - Subversion provides locking mechanism, which is suitable for working with large binary files. Mercurial is not that efficient in this respect, also due to its distributed nature.</li><li>Import/Export - Mercurial is able to import and export data from Subversion, CVS, git and others. It makes it easier to migrate to.</li></ul>A simple way to start with Mercurial (at least for Windows users) is to download <a href="http://freefr.dl.sourceforge.net/sourceforge/tortoisehg/TortoiseHg-0.7.exe">TortoiseHg installer for Windows</a> (all included).<br /><br />Also, <a href="http://doc.bazaar-vcs.org/latest/en/user-guide/index.html">Bazaar</a> should be considered as an alternative (<a href="http://sayspy.blogspot.com/2006/11/bazaar-vs-mercurial-unscientific.html">here</a> is a good article comparing and explaining those). Yes, Mercurial appears to be much simpler in install and more than 2 times faster than Bazaar for nearly all operations.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-33132790997412462382009-03-20T11:17:00.000+02:002009-06-07T22:43:25.060+03:00Complete stack of RedmineWhen installing <a href="http://www.redmine.org/wiki/redmine/Features">Redmine</a> (which seems to be a nice alternative to <a href="http://trac.edgewall.org/">Trac</a>), I found a site where you can <a href="http://bitnami.org/stacks/">download complete stacks</a> of different open source software, like <a href="http://bitnami.org/stack/lampstack">LAMP</a>, <a href="http://bitnami.org/stack/trac">Trac</a>, etc. Should be very useful when one needs a basic fast installation. Versions there are more-or-less fresh (i. e. 0.10.4 for Trac and 0.8.1 for Redmine).Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com0tag:blogger.com,1999:blog-3627102742438898505.post-85150333773224201752009-03-02T17:43:00.000+02:002009-06-07T22:43:46.637+03:00Trac + Subversion installation on WindowsHere I'll describe a 20 minute procedure of <a href="http://trac.edgewall.org/">Trac</a> + <a href="http://subversion.tigris.org/">Subversion</a> (the latest versions) installation for 32 bit Windows (successfully tested under Windows XP and Windows 2003 Server). It's really simple and straightforward.<br /><br />I assume the following directory structure (my project is called "rw"):<br /><br /><code>D:\projects<br />D:\projects\trac<br />D:\projects\trac\rw<br />D:\projects\repos<br />D:\projects\repos\rw<br />D:\projects\tools<br />D:\projects\tools\Python25<br />D:\projects\tools\svn-win32-1.5.5</code><br /><br />Well, let's get started!<br /><br />1. <span style="font-weight: bold;">Subversion server setup</span><br /><br />There are two ways to install SVN server. I tried both of them and both work fine, so it's up to you which one to choose.<br /><br />1.1.1. Download <a href="http://www.visualsvn.com/files/VisualSVN-Server-1.6.3.msi">VisualSVN server</a><br />1.1.2. Download <a href="http://subversion.tigris.org/files/documents/15/44589/svn-win32-1.5.5.zip">SVN binaries</a> (also see complete <a href="http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=8100">downloads list</a>)<br />1.1.3. Download <a href="http://subversion.tigris.org/files/documents/15/44591/svn-win32-1.5.5_py.zip">Python bindings for Subversion</a> (at the same place)<br />1.1.4. Install VisualSVN to any location, set repositories location to <code>d:\projects\repos</code> (you can accept defaults for all other options)<br />1.1.5. Create a repository (you can use management console for this) named "rw" and check a special checkbox to create a standard folder structure (<span style="font-style: italic;">trunk</span>, <span style="font-style: italic;">tags</span>, etc)<br />1.1.5. Create a new user for accessing repository. Now you can browse it using this URL and newly created login: <a href="https://localhost:8443/svn/rw/">https://localhost:8443/svn/rw/</a><br /><br />OR<br /><br />1.2.1. Download <a href="http://subversion.tigris.org/files/documents/15/44589/svn-win32-1.5.5.zip">SVN server</a> from tigris.org (also see complete <a href="http://subversion.tigris.org/servlets/ProjectDocumentList?folderID=8100">downloads list</a>)<br />1.2.2. Download <a href="http://subversion.tigris.org/files/documents/15/44591/svn-win32-1.5.5_py.zip">Python bindings for Subversion</a> (at the same place)<br />1.2.3. Unzip SVN server to <code>d:\projects\tools</code>, optionally add its <span style="font-style: italic;">bin </span>folder to <span style="font-style: italic;">PATH</span><br />1.2.4. Create a repository named "rw" (<code>svnadmin create d:\projects\repos\rw</code>) and create a standard folder structure (trunk, tags, etc) manually<br />1.2.5. Create a new user for accessing repository (modify <code>D:\projects\repos\rw\conf\passwd</code> and uncomment a line in <code>D:\projects\repos\rw\conf\svnserve.conf</code>). Now you can browse it using this URL and newly created login: <a href="svn://localhost/rw/">svn://localhost/rw/</a><br />1.2.6. You can start the server like this: <code>svnserve.exe -r d:\projects\repos -d</code><br /><br /><span style="font-weight: bold;">TODO</span>: See how it can be tunelled through HTTPS, also see how it can be run as a service.<br /><br />2. <span style="font-weight: bold;">Trac setup</span><br /><br />2.1. Download <a href="http://ftp.edgewall.com/pub/trac/Trac-0.11.3.win32.exe">Trac for windows installer</a> (see complete <a href="http://trac.edgewall.org/wiki/TracDownload">downloads section</a>)<br />2.2. Download <a href="http://www.python.org/ftp/python/2.5/python-2.5.msi">Python 2.5 installer</a> (details <a href="http://trac.edgewall.org/wiki/TracOnWindows/Python2.5">here</a>, if needed)<br />2.3. Download <a href="http://ftp.edgewall.com/pub/genshi/Genshi-0.5.1.win32-py2.5.exe">genshi installer</a> for Python 2.5<br />2.4. Download <a href="http://pypi.python.org/packages/2.5/s/setuptools/setuptools-0.6c9.win32-py2.5.exe#md5=602d06054ec1165e995ae54ac30884d7">setuptools</a> (instructions available <a href="http://pypi.python.org/pypi/setuptools/#windows">here</a>)<br />2.5. Install Python, for example to <code>d:\Python25</code><br />2.6. (Optionally) add <code>d:\Python25</code> to <span style="font-style: italic;">PATH</span> system environment variable<br />2.7. Install setuptools, genshi and Trac to default locations<br /><br />3. <span style="font-weight: bold;">Integration with Subversion</span> (this solution is kinda ugly hack. I assume it could be achieved much easier, though I was unable to find how exactly):<br /><br />3.1. Unzip <code>svn-win32-1.5.5_py.zip</code> to <code>d:\Python25\Lib\site-packages</code><br />3.2. Unzip <code>svn-win32-1.5.5.zip\svn-win32-1.5.5\bin</code> to <code>d:\Python25\Lib\site-packages\libsvn</code><br />3.3. <code>copy d:\Python25\Lib\site-packages\libsvn\*.dll d:\Python25\Lib\site-packages\libsvn\*.pyd</code><br />3.4. <code>mkdir d:\projects\trac\rw</code><br />3.5. Execute this: <code>trac-admin d:\projects\trac\rw initenv</code>, enter the project name and <code>d:\projects\repos\rw</code> when asked for Subversion repository location. Leave all other values default.<br />3.7. To test installation just execute this: <code>tracd --port 8000 d:\projects\trac\rw</code><br /><br />4. <span style="font-weight: bold;">Setup authentication</span> (see <a href="http://trac.edgewall.org/wiki/TracOnWindows/Python2.5">instructions</a>)<br /><br />4.1. Create a new file named <code>trac-digest.py</code> and fill it with code from <a href="http://trac.edgewall.org/wiki/TracStandalone">this page</a><br />4.2. Create an administrator user (user "<span style="font-style: italic;">adm</span>" with password "<span style="font-style: italic;">adm</span>"): <code>python trac-digest.py -u adm -p adm >> d:\projects\trac\rw\digest.txt</code><br />4.3. Give that user all permissions: <code>trac-admin d:\projects\trac\rw permission add adm TRAC_ADMIN</code><br />4.4. Run this to test everything: <code>tracd -p 80 --auth=rw,trac\rw\digest.txt,trac trac\rw</code><br /><br />Now you should be able to see the Admin tab if you log in with "<span style="font-style: italic;">adm</span>" / "<span style="font-style: italic;">adm</span>" (see <a href="http://localhost:8000/rw">http://localhost/rw</a>)<br /><br />4.5. Adjust attachment limit in <code>trac.ini</code>:<br /><br /><code>[attachment]<br />max_size = 262144000</code><br />(it's 250 Mb)<br /><br />5. <span style="font-weight: bold;">Enable automatic ticket control via Subversion comments.</span> See <a href="http://trac.edgewall.org/wiki/TracFaq#can-trac-automatically-update-a-ticket-when-i-commit-a-changeset">instuctions</a>:<br />Download <code>trac-post-commit-hook</code> and <code>trac-post-commit-hook.cmd</code> from <a href="http://trac.edgewall.org/browser/branches/0.11-stable/contrib">here</a> and follow instructions in <code>trac-post-commit-hook.cmd</code>.<br />Place it to <code>d:\projects\repos\rw\hooks</code> and modify <span style="font-style: italic;">.cmd</span> file like that:<br /><code>SET TRAC_ENV=D:\projects\trac\rw</code><br /><br />6. <span style="font-weight: bold;">Update. Use Subversion authentication in Trac. </span><br />Using <a href="http://trac-hacks.org/wiki/AccountManagerPlugin">AccountManagerPlugin</a> we can work with Trac users fast and easy. I'll describe the simplest and unsecure way of setting it up.<br /><br />6.1. <code>easy_install http://trac-hacks.org/svn/accountmanagerplugin/trunk</code><br />6.2. Go to Trac Admin tab and enable Account Manager Plugin and the following modules:<br /><ul><li>AccountManagerAdminPage</li><li>AccountManager</li><li>AbstractPasswordFileStore</li><li>HttpAuthStore</li><li>AccountChangeListener</li><li>AccountChangeNotificationAdminPanel</li><li>SvnServePasswordStore</li><li>AccountModule</li><li>LoginModule</li><li>RegistrationModule</li></ul>(it will make your life much easier allowing not to login into that Windoze box to change some passwords).<br />6.3. Now you'll need to add the following line to [components] section of trac.ini (it will disable HTTP authentication):<br /><code>trac.web.auth.LoginModule = disabled</code><br />6.4. Go to Accounts / Configuration (see left menu in Admin mode) and enter your passwd filename into <span style="font-style: italic;">SvnServePasswordStore</span> box (i. e. <code>D:\projects\repos\rw\conf\passwd</code>)<br />6.5. Now you can login as <span style="font-style: italic;">adm</span> (don't forget to add this user to your Subversion's <span style="font-style: italic;">passwd</span> file) and add / remove Trac users via new menu items in Admin tab. The best thing about it is that now all your changes will be reflected in Subversion configuration, so this could be considered as the common place of manipulating users for your development environment.<br />6.6. IMPORTANT. Now <a href="http://trac-hacks.org/wiki/AccountManagerPlugin#DisableHTTPauthentication">you can't use --auth</a> when starting <span style="font-style: italic;">tracd</span>. So, my command line is simply <code>tools\Python25\Scripts\tracd.exe -p 80 trac\rw</code><br /><br />Now you can customize all the necessary settings, first of all authorization. Also there available a lot of useful plugins for Trac, see <a href="http://trac-hacks.org/">Trac hacks site</a>. What I'm going to do next is install <a href="http://maven-proxy.codehaus.org/">Maven proxy</a> and <a href="https://hudson.dev.java.net/">Hudson</a> continuous integration solution.<br /><br />P.S. The best thing about this installation is that it can be done once and then packed into a handy (in my case 80 Mb) redistributable ZIP file and use it everywhere. The only issue in this case is that you will need to install Python anyway (because of some shared DLLs), but during installation you can choose the existing <code>D:\projects\tools\Python25</code> directory and it won't override your changes.Constantinehttp://www.blogger.com/profile/01268291797043527406noreply@blogger.com7