<?xml version="1.0" encoding="iso-8859-1"?>

<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
  xmlns:admin="http://webns.net/mvcb/"
  xmlns:cc="http://web.resource.org/cc/"
  xmlns="http://purl.org/rss/1.0/">

<channel rdf:about="http://blogs.codehaus.org/people/vmassol//archives/think_tank.html">
<title>Vincent Massol Think Tank - Think tank</title>
<link>http://blogs.codehaus.org/people/vmassol//archives/think_tank.html</link>
<description></description>
<dc:language>en-us</dc:language>
<dc:creator></dc:creator>
<dc:date>2007-12-19T10:25:25+00:00</dc:date>
<admin:generatorAgent rdf:resource="http://www.movabletype.org/?v=2.661" />

<items>
<rdf:Seq><rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001647_great_new_searcharchiving_service_markmail.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001371_checking_for_since.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001370_easy_unit_testing_of_file_operations.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001369_intellib.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001338_wanted_wiki_features.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001324_ensuring_binary_compatibility.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001314_documentationdriven_development.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001218_welcome_to_the_matrix.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001209_database_manipulation_framework_wanted.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001132_cds_vs_yds.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001063_clirr_rocks.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001036_id_love_reusable_ant_tasks.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001007_increasing_open_source_project_contributions.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/001006_distributed_build.html" />
<rdf:li rdf:resource="http://blogs.codehaus.org/people/vmassol/archives/000953_binary_dependency_builds.html" />
</rdf:Seq>
</items>

</channel>

<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001647_great_new_searcharchiving_service_markmail.html">
<title>Great new search/archiving service: Markmail</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001647_great_new_searcharchiving_service_markmail.html</link>
<description><![CDATA[<p>Jason Hunter was kind enough to set up a <a href="http://xwiki.markmail.org">Markmail site for XWiki</a>. <a href="http://markmail.org/">Markmail</a> is a mailing list archiving tool with a powerful search feature.</p>
<p>What sets it apart from other such tool IMO is the UI and speed/quality of search. I especially like the ability to see who's sending the most mails to a list and the nice syntax coloring display of emails (in addition to the thread view). Another nice feature is that emails are indexed a few seconds after they have been received by the list (compared to several hours with other tools). I love it :)
</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2007-12-19T10:25:25+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001371_checking_for_since.html">
<title>Checking for @since</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001371_checking_for_since.html</link>
<description><![CDATA[<p>It would be nice if there were a tool that could verify that you have correctly added <code>@since</code> tags for methods added in the current version. It would do this by checking against the previous release.</p>
<p>This tool could be based on <a href="http://clirr.sourceforge.net/">Clirr</a> or <a href="http://www.jdiff.org/">JDiff</a> for example. It would also have an option to fail the build if there are new methods without a <code>@since</code> tag.</p>
<p>Do you know if such a tool exists?</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2006-07-18T08:19:54+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001370_easy_unit_testing_of_file_operations.html">
<title>Easy unit testing of File operations</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001370_easy_unit_testing_of_file_operations.html</link>
<description><![CDATA[<p>The experience that I'm relating here is part of an exploratory refactoring that I'm currently doing on the <a href="http://cargo.codehaus.org">Cargo</a> code base. 
  Till now we were using Java File objects for representing J2EE archives or container installation and configuration directories. This is ok but it makes unit testing a little bit complex when it comes
to unit testing File operations. The reason is that you need to define a location on your local file system where you're going to read/write files to, clean up the files, etc.</p>

<p>Here's a method we had (it expands a JAR file):</p>

<div style="width:100%; overflow:auto; border-style: solid; border-width: 1px"><pre><code language="java">
    public void expandToPath(String path) throws IOException
    {
        File workDir = new File(path);
        JarInputStream inputStream = getContentAsStream();
        
        byte[] buffer = new byte[40960];
        
        ZipEntry entry;
        while ((entry = inputStream.getNextEntry()) != null)
        {
            String entryName = entry.getName();
            entryName = entryName.replace('/', File.separatorChar);
            
            String outFileName = workDir.getPath() + File.separator + entryName;
            File outFile = new File(outFileName);
            
            if (outFileName.endsWith("/") || outFileName.endsWith("\\"))
            {
                outFile.mkdirs();
            }
            else
            {
                if (!outFile.getParentFile().exists())
                {
                    outFile.getParentFile().mkdirs();
                }
                
                if (!outFile.exists())
                {
                    outFile.createNewFile();
                }
                
                FileOutputStream out = new FileOutputStream(outFile);
                int read;
                while ((read = inputStream.read(buffer)) > 0)
                {
                    out.write(buffer, 0, read);
                }
                
                out.close();
            }
        }
        inputStream.close();
    }
</code></pre></div>

<p>Here's how I've transformed the method by removing all <code>File</code> operations and instead introducing a <code>FileHandler</code> interface with the following methods, equivalent to the <code>File</code> ones:</p>
<ul>
  <li>append(URI, String): appends a suffix to a URI</li>
  <li>mkdirs(URI): create directories for the URI</li>
  <li>exists(URI): return true if the URI exists</li>
  <li>createFile(URI): create a file</li>
  <li>getOutputStream(URI): get an output stream for the passed URI</li>
</ul>

<div style="width:100%; overflow:auto; border-style: solid; border-width: 1px"><pre><code language="java">
    public void expandToPath(URI path) throws IOException
    {
        JarInputStream inputStream = getContentAsStream();

        byte[] buffer = new byte[40960];

        ZipEntry entry;
        while ((entry = inputStream.getNextEntry()) != null)
        {
            String entryName = entry.getName();

            URI outFile = getFileHandler().append(path, entryName);

            if (outFile.toString().endsWith("/"))
            {
                getFileHandler().mkdirs(outFile);
            }
            else
            {
                if (!getFileHandler().exists(getFileHandler().getParent(outFile)))
                {
                    getFileHandler().mkdirs(getFileHandler().getParent(outFile));
                }

                if (!getFileHandler().exists(outFile))
                {
                    getFileHandler().createFile(outFile);
                }

                OutputStream out = getFileHandler().getOutputStream(outFile);
                int read;
                while ((read = inputStream.read(buffer)) > 0)
                {
                    out.write(buffer, 0, read);
                }

                out.close();
            }
        }
        inputStream.close();
    }
</code></pre></div>
  
<p>The interesting part comes now. Because it was a bit hard to create a unit test for the original <code>expandToPath</code> method nobody had done it. It would have involved passing a test JAR but more difficult it would
  have involved passing a target directory where the JAR would be expanded. This is not easy as the location of this target dir would depend from where the tests is executed and making it work seamlessly from both a build tool 
and from your IDE is not trivial. Here comes <a href="http://jakarta.apache.org/commons/vfs/">VFS</a> to help us. By implementing the <code>FileHandler</code> interface using VFS, we can now write the following unit test:</p>
  
<div style="width:100%; overflow:auto; border-style: solid; border-width: 1px"><pre><code language="java">
    public void testExpandToPath() throws Exception
    {
        URI jarURI = new URI("ram:///test.jar");

        FileObject testJar = VFS.getManager().resolveFile(jarURI.toString());
        ZipOutputStream zos = new ZipOutputStream(testJar.getContent().getOutputStream());
        ZipEntry zipEntry = new ZipEntry("rootResource.txt");
        zos.putNextEntry(zipEntry);
        zos.write("Some content".getBytes());
        zos.closeEntry();
        zos.close();

        DefaultJarArchive jarArchive = new DefaultJarArchive(jarURI);
        jarArchive.setFileHandler(new VFSFileHandler());

        jarArchive.expandToPath(new URI("ram:///test"));

        // Verify that the rootResource.txt file has been correctly expanded
        FileObject rootResource = VFS.getManager().resolveFile("ram:///test/rootResource.txt");
        assertTrue(rootResource.exists());
    }
</code></pre></div>

<p>Notice the use of the "ram:" URI scheme. This one of the <a href="http://jakarta.apache.org/commons/vfs/filesystems.html">many filesystems</a> supported by VFS and it means that all file operations will happen in a virtual file system in memory. 
  Also note that VFS doesn't currently support creating Zip files so we're using the JDK's <code>ZipOutputStream</code> API.
The nice thing is that as this test operates in memory there's no need to define a target location on the file system.</p>

<p>The other nice thing is that by introducing VFS to this <code>expandToPath()</code> method it's now possible to expand a JAR to any file system supported by VFS. We could thus expand to a FTP server, to a WebDAV repository, to an HTTP URL, to a remote machine using SSH, etc. All
this without changing a line to our code. Nice isn't it?</p>

  ]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2006-07-17T13:52:39+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001369_intellib.html">
<title>IntelliB</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001369_intellib.html</link>
<description><![CDATA[<p>(<b>Updated 2006-07-14</b>: Added section on discovering modules and added disclaimer at the end)</p>

<p>IntelliJ IDEA has revolutioned the IDE landscape by adding "intelligence" to IDEs. 
  A few days ago I did a thought experiment by asking myself the following question "how feasible would it be to build a project without knowing any meta-data about it?".
In other words, is it possible for a build tool to be intelligent enough to build a project without build files nor POMs. Said differently, is it possible to figure out a project's POM automatically?
Let's review some required typical meta-data information and see how they could be guessed.</p>

<h3>Source locations</h3>

<p>It is possible to guess where sources are by looking for <code>*.java</code> files (for Java projects - The same applies for other project types). Now we still need to differentiate main sources from test sources 
but that's also relatively easy to do. We can check for classes extending JUnit's TestCase for example or the TestNG equivalent, or any other well-known testing framework.</p>

<p>Note: An interesting thing here is that to be intelligent we'd need the help of the community to add new rules to the discovery process. For example imagine that a new testing framework appears; we'd need to add 
it to the Test Discovery Rules. Thus, this type of intelligent build system would need to rely a lot on the community and thus would need to get its data from an online repository that could be edited by the community. </p>

<h3>Dependencies</h3>

<p>How do we detect project dependencies? One relatively way is to parse the sources that we have found above and find all external imports. Then query ibiblio to find matching package names (this information is present 
in Maven POMs on ibiblio). Now for guessing the version, there's no easy magic. A first approach would be to get the latest released version of the dependencies we've found.</p>

<h3>Project type</h3>

<p>Project types can easily be guessed by looking at some files. For example if a <code>web.xml</code> file is present then it's a WAR project, if an <code>application.xml</code> one is found then it's an EAR project, if a <code>jnlp</code> file is found then it's a JNLP project, etc.</p>
  
<h3>SCM</h3>

<p>SCM can easily be guessed by looking for special files on the filesystem of the project. For example we would look for <code>.cvs</code> directories for SCV and for <code>.svn</code> files for Subversion, etc</p>
  
<h3>Developers</h3>

<p>Once we got the SCM URL we can then query the SCM to get the list of all developers.</p>

<h3>Project name</h3>

<p>The project name could be the name of the top level directory and the version could be set arbitrarily to 1.0. Actually we could even check ibiblio to see if the project is already on ibiblio, get the latest version there 
  and increase the minor number by one as a first order guess. Another strategy would be to query the SCM and look for tags and deduce existing versions by parsing those tags (there are some usual conventions for naming 
tags so it should be possible to make a good guess).</p>

<h3>Modules and artifacts</h3>

<p>Discovering the different modules of a project is probably one of the hardest thing to do. If you look at different projects in the wild I believe there are not that many directory structures out there. Maybe 10-15. Thus it 
  should be possible to register knowledge of these structures and let the tool discover which ones matches the closest with the project at hand. This would also allow to deduce the different artifacts that have to be generated.
  Of course it won't be perfect as there are projects which generate several artifacts and which may be in the same module. Again it's a question of doing 80% of the job and leaving 20% to be done manually.

<h3>Additional information</h3>

<p>Of course, the information found above are just guesses. In most cases they could be correct but of course we would need to offer a way for the user to edit them and to add any missing information.</p>

<h3>Conclusion</h3>

<p>I believe it should be possible to create such an intelligent meta-build project which could be used to generate files for one of the existing build system such as Maven, Ant, etc. For example it could create an internal POM file on 
which Maven could then be executed to produce the build results. At a minimum such a tool could be used to convert existing projects to Maven. I wonder how intelligent it could be but I guess it could go pretty far.</p>

<p>Disclaimer: Of course, such a tool would be bad from a conventions stand point. One of the great strength of Maven has been to standardize the directory structure of projects. I can go to any Maven project and I know exactly where 
stuff will, what will be generated, etc.</p>
  
<p>Are there other information which you think could be guessed automatically? Can you think of better algorithms to guess some of the information shown above?</p>


  ]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2006-07-13T09:54:49+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001338_wanted_wiki_features.html">
<title>Wanted wiki features</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001338_wanted_wiki_features.html</link>
<description><![CDATA[<p>Current wikis are great. However when used as development wikis I have found some limitations which are hampering their use. Please note that my experience is based on using <a href="http://atlassian.com/software/confluence/">Confluence</a> and <a href="http://www.xwiki.org/">XWiki</a> and other wikis may support some of the features mentioned below. Here's my top wishlist for development wikis and for Confluence and XWiki in particular:
<ul>
  <li><b>Moderated wikis</b>. Right now there are only two choices for a wiki: either they are open and anyone can edit a page or they are closed wikis and you need to register and get the rights to make modifications. For example most spaces on the <a href="http://docs.codehaus.org/">Codehaus wiki</a> are closed. 
    They were initially open but vandalism was too high and we had to close them. This is hampering documentation contributions. A moderated wiki would alleviate this: when the page is saved, an email would be sent to a list of moderators for the space for
    approval of rejection (either by responding to a certain email address as for mailing list moderation or by clicking on a link in the email). Ideally, clicking on the validation link in the email would open the page in a browser with the modifications 
  highlighted so that the moderator could make some changes before clicking on the save button.</li>
  <li><b>Anonymous edits</b>. Although this feature already exists, I'd like wikis to add 2 fields when anonymously editing a topic: a user name and an email address. The idea is make it even easier to contrinute to a wiki. If the wiki is moderated as
    explained above, moderators would receive an email. The idea of the username and email is to allow the moderator/community to discuss with the contributor if need be and to give him credits. These 2 fields would obviously be optional and there
  should be a text on the page explaining that the email will not get displayed on the wiki and that filling the fields will allow credits/acknowledgment to be given.</li>
  <li><b>Diff notifications</b>. Most wikis allow some form of space watch but the wikis I have used still do not offer the possibility to send notifications in a text diff format (wiki markup diff is good enough). For a development wiki, the idea is to send
  diff notifications to the development mailing list so that all developers are aware of wiki page modifications.</li>
  <li><b>Daily notifications</b>. This is also supported in some wikis but what I would like is the ability to watch a single space and to aggregate changes in that space (using the diff notification format mentioned above). Please note that
    Confluence does not support this as it requires you to modify all other spaces permissions so that the user doing the watch has no view rights on the other spaces, which is not usable for example on wikis such as the one on Codehaus which have hundreds of spaces.
  <li><b>In place comments</b>. The idea is again to lower contribution by allowing wiki users to highlight a portion of text in their browser and to associate a comment with it (like a post-it). There would be an option to turn on/off these comments. It's easier
  for a user to highlight a line and put a comment like "I don't understand this sentence" or fix a typo rather than have to use current the type of comments at the bottom of a page. Note that this is similar to how word processors such as Word allow adding comments to a document.</li>
  <li><b>Patch handling</b>. I'd like the ability to make modifications to a page and then instead of saving, have the ability to click on a "Generate patch" button which would generate a text file in wiki markup diff format. Then there would need to be a "Apply patch" action
  that can be done on a page. This would allow using wikis for project development web sites and allow contributors to provide documentation patches along with code patches. This is currently a big pain when using a wiki as a project development web site.</li>
</ul>
<p>I have quite a few other suggestions for improvements but I feel those are the major ones when it comes to using a wiki as a project development wiki. Let's hope wiki vendors are listening... :-). Are these also on your wish list?</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2006-03-17T10:07:41+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001324_ensuring_binary_compatibility.html">
<title>Ensuring binary compatibility</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001324_ensuring_binary_compatibility.html</link>
<description><![CDATA[<p>I see 2 use cases where ensuring <a href="http://blogs.codehaus.org/people/vmassol/archives/000098_evolving_javabased_apis_the_nightmare_of_binary_compatibility.html">binary compatibility</a> is a must:</p>
<ul>
<li>When you're developing a framework, i.e. a piece of software meant to be used at an API level by other developers. In that case, breaking binary compatibility is not something to do lightly.</li>
<li>When working in a large team it's common to define "interface" projects that represent the contracts to be followed by the different teams. In that case breaking the binary compatibility in an "interface" project is something that has to be planned and organized.</li>
</ul>
<h3>Enforcing binary compatibility in the build</h3>
<p>The automated build is a nice place to enforce binary compatibility as the build is something executed by the indiviudal developers before checking-in and it's also executed by the continuous integration build. 
Thus any binary incompatibility can be quickly discovered. Or course this doesn't replace tests which can also help discover breakages. However the problem is that with all the nice refactoring IDEs we have now, it's easy
to refactor the tests at the same time as the code and thus introducing a binary incompatibility is not always noticed.</p>
<p>A good strategy to discover an incompatibility is to compare the current code with the latest released code. This is what <a href="http://blogs.codehaus.org/people/vmassol/archives/001063_clirr_rocks.html">Clirr</a> is doing. 
Clirr currently sports an Ant and Maven1 integration. The good news is that there's a Maven2 plugin in the work (more on that when it's released). However using a tool is only good if there's a strategy behind it.</p>
<h3>Strategy for using Clirr</h3>
<p>Here is what I believe can be done to automate binary compatibility checks in the build:</p>
<ul>
<li>Start by organizing your packages so that you <a href="http://jakarta.apache.org/cactus/participating/apis.html">clearly demarcate the user-public API from the SPI from the internal implementations</a>. You'll probably want to fail the build only on the user-public API (and possibly on the SPI too but that's
probably a lower severity).</li>
<li>Use Clirr to make your build fail upon violation on the user-public API.</li>
<li>After discussing with the team and possibly with users, decide whether you wish to allow the binary incompatibility. Always consider going for a deprecation cycle. If you choose to allow the incompatibility, register it in an exception file that you
pass to Clirr so that it builds without choking on those errors (Note: I believe Clirr needs to be improved to better support exceptions not only at the file level but at the violation level).</li>
<li>When the release time comes, you'll have a nice file listing all the binary incompatibilities. Include it in the release notes so that your users know what to expect and even better, for each incompatibility add a description that explains
how to modify the user code to use the new version of the API.</li>
</ul>
<p>Note: On the <a href="http://cargo.codehaus.org">Cargo</a> project we've <a href="http://cargo.codehaus.org/Release+notes+for+Cargo+0.5">tried to do this</a>, even though there's still room for lots of improvement. Actually our main issue on Cargo is
not detecting binary incompatibilites but rather deciding to release a 1.0 version which would mean that from then forward we would aways look for a deprecation solution rather than break binary compatibility. We've always pushed back this 1.0 release 
because our API has been changing quite frequently but we're now nearing a 1.0 version. When that comes we'll turn Clirr on to fail the build upon breakage. I'll let you know how it goes...</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2006-02-12T13:48:56+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001314_documentationdriven_development.html">
<title>Documentation-Driven Development</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001314_documentationdriven_development.html</link>
<description><![CDATA[<p>I'm currently writing my third book and I'm starting to notice a pattern. Whenever I write a book about a tool/framework to which I have access to the sources, the code ends up being better.</p>
<p>The way I work goes like this: I start writing about a topic. If it's taking too long to explain it, I consider that something is wrong about the code. I modify the source code so that the 
document I'm writing has the minimal required size to explain the topic.</p>
<p>The good thing with a book is that what you're explaining has to be simple and not convulted which leads to this nice
effect of improving usability of your code. I get a bit of the same result when I write project documentation but not to the same level. This is probably simply because writing a book is a more
involved process, you dedicate more time to it and thus you want it to be as perfect as possible (and thus as readable as possible).</p>
<p>I guess nothing here is new. This is all about having a user of your code. Tests are "users" of your code and thus leads to better design. I guess documentation can also be a "user" of the code
and thus help improving it.</p>
<p>If you're writing some framework/tool, consider writing a book for it and if you're diligent in your writing your code will end up being better! As an added benefit your users will love you... :-)</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2006-01-21T08:55:37+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001218_welcome_to_the_matrix.html">
<title>Welcome to the Matrix!</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001218_welcome_to_the_matrix.html</link>
<description><![CDATA[<p>Amazon has released a beta of the <a href="http://www.mturk.com/mturk/welcome">Mechanical Turk</a>. It allows a program to programatically ask a question to a human and wait for the answer. Here's an example (copied from <a href="http://blog.outer-court.com/archive/2005-11-04-n69.html">Google Blogoscoped</a>):</p>
<pre><code language="java">
read (photo);
photoContainsHuman = callMechanicalTurk(photo);
if (photoContainsHuman == TRUE) {
  acceptPhoto;
}
else {
  rejectPhoto;
}
</code></pre>
<p>This is really like the Matrix except that the humans get paid a little bit of money (but in the end that's close to getting fed) and it's other humans that controlling the programs... until we have web services using other web services using the Mechanical Turk. Then who's controlling who is going to be hard to decide :-)</p>
<p>Source: <a href="http://blog.outer-court.com/archive/2005-11-04-n69.html">Google Blogoscoped</a>.</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2005-11-04T18:30:50+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001209_database_manipulation_framework_wanted.html">
<title>Database manipulation framework wanted</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001209_database_manipulation_framework_wanted.html</link>
<description><![CDATA[<p>I'm working on automating a J2EE build using Maven 2 and I'm in need of a Maven 2 plugin to do the following:</p>
<ol>
<li>load a database schema in the instance</li>
<li>load data in the instance</li>
<li>start/stop a database instance</li>
<li>ability to create an instance from scratch</li>
</ol>
<p>The ideal situation would be to find an existing Java framework that would already perform all or some of those steps. Then I could easily create a Maven 2 plugin wrapping it.
So far I haven't been able to find such a tool. If you know any please suggest them!</p>
<p>Here's what I have found so far below. Please note that I have probably made mistakes while filling this table and I'd be happy to be corrected...</p>
<table border="1">
  <th>
    <td>Load schema</td>
    <td>Load data</td>
    <td>Start/stop instance</td>
    <td>Create instance</td>
    <td>Comments</td>
  </th>
  <tr>
    <td><a href="http://dbunit.sourceforge.net">DBunit</a></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/forbidden.gif"/></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/add.gif"/></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/forbidden.gif"/></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/forbidden.gif"/></td>
    <td>&nbsp;</td>
  </tr>
  <tr>
    <td><a href="http://db.apache.org/ddlutils/">DDLUtils</a></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/add.gif"/></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/forbidden.gif"/></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/forbidden.gif"/></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/forbidden.gif"/></td>
    <td>I think DDLUtils is the old commons-sql project.</td>
  </tr>
  <tr>
    <td><a href="http://db.apache.org/derby/integrate/plugin_help/ij_toc.html">Derby ij</a></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/add.gif"/></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/add.gif"/></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/forbidden.gif"/></td>
    <td><img src="http://docs.codehaus.org/images/icons/emoticons/forbidden.gif"/></td>
    <td>ij 10.1.1.0 requires the db2jcc.jar which is not on Ibiblio. I need to check the license to see if it could be uploaded.</td>
  </tr>
</table>
<p>Again, let me know if you know some tools that are not listed here.</p>
<p>If no such tool exist, an idea I have would be to add support for databases in <a href="http://cargo.codehaus.org">Cargo</a>. Indeed Cargo is meant for manipulating any kind of containers.
It happens that the first type of container we've implemented are J2EE containers but it should work for any other type and the interfaces should remain the same.</p>
<p>WDYT?</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2005-10-26T18:49:41+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001132_cds_vs_yds.html">
<title>CDS vs YDS</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001132_cds_vs_yds.html</link>
<description><![CDATA[<p>I've just tried today <a href="http://www.copernic.com/en/products/desktop-search/index.html">Copernic Desktop Search</a> (CDS). I've been 
using <a href="">Yahoo Desktop Search</a> (YDS) for several months now and I'm very happy with it. It has some issues though like it's putting 
my laptop on its knees when it performs indexing, it has no Windows taskbar integration, etc. I wanted to see how CDS fared against YDS.</p>
<p>Here are my findings after one day of using CDS. Please note that this is definitely not long enough to have a definitive opinion on the 
topic but I thought I'd still share what I've learnt today.</p>
<h3>General opinion</h3>
<p>CDS is a very good desktop search. I was very impressed. It seemed perfect at first and then slowly I started finding some little
flaws compared to YDS. Still it is extremely good. It has all the features you'll find in YDS and Google Desktop Search (GDS).</p>
<h3>Pros of CDS vs YDS</h3>
<ul>
<li>Integration with Windows taskbar</li>
<li>Low resource for indexing. It is not slowing my laptop when indexing. That's very good!</li>
<li>Immediate scanning of new resources. If you receive an email for example, it is immediatly available for searching. No need to 
wait for the next indexing.</li>
</ul>
<h3>Cons of CDS vs YDS</h3>
<ul>
<li>No vertical layout for views (as there is in YDS). This means that you cannot fully the message being previewed</li>
<li>No "All" categories search. You have to choose the category you wish to search (emails, files, contacts, etc)</li>
<li>No as-you-type results</li>
<li>No possibility to choose the columns to display (for exemple email folders or email size). There are only a few basic columns</li>
<li>Slower to search and display items than YDS. It was very fast initially and it quickly became slow and very slow as indexed items increased</li>
<li>XML preview is using IE engine on Windows and thus there are lots of XML files that don't display correctly</li>
</ul>
<p>Some minor details:</p>
<ul>
<li>Delete key does not work to suppress an email</li>
<li>Cannot select different emails (to suppress them for example)</li>
</ul>
<h3>Conclusion</h3>
<p>If only it could have a better view layout and be faster to display results it would be perfect. Its killer features are really its CPU-friendly 
indexing for me and the immediate availability of new resources in searches.</p>
<p>I've just noticed that YDS has released verson 1.2beta yesterday and I'm installing it. For now, I'll still keep using YDS which is still my favorite. YMMV.</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2005-07-13T18:19:19+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001063_clirr_rocks.html">
<title>Clirr rocks!</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001063_clirr_rocks.html</link>
<description><![CDATA[<p><a href="http://clirr.sourceforge.net">Clirr</a> is one of these tools that would deserve to be known better. I have mentioned it several times in other posts but it's really the first
time I get to use it in real. It rocks! I'm about to release <a href="http://cargo.codehaus.org">Cargo</a> 0.5 and I wanted to get an exact list 
of the API modifications we have done compared to version 0.4.</p>
<p>Here's the kind of output Clirr gives (the full output is available <a href="http://cargo.codehaus.org/Downloads#clirr">here</a>):</p>
<div style="width:100%; overflow:auto; border-style: solid; border-width: 1px"><code><pre>
ERROR: 8001: org.codehaus.cargo.deployment.DefaultJarArchive: Class org.codehaus.cargo.deployment.DefaultJarArchive removed
INFO: 8000: org.codehaus.cargo.module.DefaultJarArchive: Class org.codehaus.cargo.module.DefaultJarArchive added
ERROR: 7002: org.codehaus.cargo.container.Container: Method 'public void addDeployable(org.codehaus.cargo.container.deployable.Deployable)' has been removed
INFO: 7011: org.codehaus.cargo.ant.ConfigurationElement: Method 'public void addConfiguredEar(org.codehaus.cargo.ant.EARElement)' has been added
INFO: 4000: org.codehaus.cargo.container.jetty.JettyStandaloneConfiguration: Added org.codehaus.cargo.container.configuration.StandaloneConfiguration to the set of implemented interfaces
ERROR: 7005: org.codehaus.cargo.container.Container: Parameter 1 of 'public void setConfiguration(org.codehaus.cargo.container.Configuration)' has changed its type to org.codehaus.cargo.container.configuration.Configuration
ERROR: 7006: org.codehaus.cargo.ant.ConfigurationElement: Return type of method 'public org.codehaus.cargo.container.Configuration createConfiguration(org.codehaus.cargo.container.Container)' has been changed to org.codehaus.cargo.container.configuration.Configuration
ERROR: 4001: org.codehaus.cargo.container.jetty.JettyStandaloneConfiguration: Removed org.codehaus.cargo.container.Configuration from the set of implemented interfaces
INFO: 7003: org.codehaus.cargo.container.spi.AbstractConfiguration: Method 'public void configure()' has been removed, but an inherited definition exists.
ERROR: 5001: org.codehaus.cargo.container.deployable.EAR: Removed org.codehaus.cargo.util.MonitoredObject from the list of superclasses
INFO: 5000: org.codehaus.cargo.container.deployable.EAR: Added org.codehaus.cargo.util.monitor.MonitoredObject to the list of superclasses
ERROR: 7012: org.codehaus.cargo.container.Container: Method 'public java.io.File getOutput()' has been added to an interface
INFO: 7010: org.codehaus.cargo.container.spi.AbstractContainer: Accessibility of method 'protected java.io.File getOutput()' has been increased from protected to public
INFO: 6000: org.codehaus.cargo.container.property.GeneralPropertySet: Added public field JVMARGS
</pre></code></div>
<p>Even though we're using JIRA with an Iteration-Driven Development strategy (IDD) it was still a very interesting exercise to verify that we had not missed any issue by running Clirr on the source code. 
In addition, it provides a more detailed view of what exactly has changed in term of API which our JIRA report does not provide.</p>
<p>The next step would be to use it to fail our build whenever someone introduces a public API break. It would be quite easy for us because we've cleanly separated non-public API from public APIs by using internal
packages (see the <a href="http://jakarta.apache.org/cactus/participating/apis.html">Cactus API design rule</a> to see what it means). Of course sometimes, you want to voluntariy add a breaking change. That's legitimate
 but it has to be controlled. The strategy would be to have the build fail and then if the change is voluntary to exclude it from Clirr.</p>
<p>Well done Lars!</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2005-04-30T15:22:11+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001036_id_love_reusable_ant_tasks.html">
<title>I&apos;d love reusable Ant tasks</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001036_id_love_reusable_ant_tasks.html</link>
<description><![CDATA[<p>Where is Ant heading in the future? I would be very interested to learn more about this. I've been using Ant for several years now and I've always been a happy user. However these days, I'm no longer using much the XML scripting side of Ant but I'm using heavily the Ant Java API; what I'm interested in are the Java Ant tasks.</p>
<p>I think this is really where the value of Ant is. All those years of implementing the base building block for a portable OS Java API have created a very useful Task set. I think every Java application that needs to do copying, deleting a directory, spawning a Java application, etc should use these tasks. There's no point in reinventing the wheel!</p>
<p>For example, you may think that deleting a directory is simple. But it's not so easy. Have a look at the Delete Ant task source code. You'll find portion of code like this one:</p>
<pre><code language="java">
/**
 * Accommodate Windows bug encountered in both Sun and IBM JDKs.
 * Others possible. If the delete does not work, call System.gc(),
 * wait a little and try again.
 */
private boolean delete(File f) {
    if (!f.delete()) {
        if (Os.isFamily("windows")) {
            System.gc();
        }
        try {
            Thread.sleep(DELETE_RETRY_SLEEP_MILLIS);
        } catch (InterruptedException ex) {
            // Ignore Exception
        }
        if (!f.delete()) {
            if (deleteOnExit) {
                int level = quiet ? Project.MSG_VERBOSE : Project.MSG_INFO;
                log("Failed to delete " + f + ", calling deleteOnExit."
                    + " This attempts to delete the file when the ant jvm"
                    + " has exited and might not succeed."
                    , level);
                f.deleteOnExit();
                return true;
            }
            return false;
        }
    }
    return true;
}
</code></pre>
<p>Would you have thought about this? Probably not and you would have been right not to as this only happens in some rare occasions. But when one of your users reports it, it's going to be darn difficult to identify and fix. Personally I'd rather depend on a stable and well tested library rather than recode it myself.</p>
<p>The problem is that the Ant tasks are a bit too much linked to the execution engine (the XML scripting engine). For example reusing an Ant tasks requires you to create a Project object. This in turn drags loggers, the Ant classloader (in some cases) and possibly other objects. I know it's possible to use Ant from Java (I've been doing it for a long time now) but I'd love it be even easier to do so.</p>
<p>Instead of writing:</p>
<pre><code language="java">
Project project = new Project();
Expander expander = project.createTask("unzip"); 
expander.setSrc(new File(zipfile)); 
expander.setDest(new File(destdir)); 
expander.execute();
</code></pre>
<p>I'd like to be able to write:</p>
<pre><code language="java">
Expand expand = new Expand();
expand.setSrc(new File(zipfile));
expand.setDest(new File(destdir));
expand.setLogger(myLogger);
expand.execute();
</code></pre>
<p>I don't want to see the get/setLocation, get/setTaskName(), get/setDescription() and in general all methods from Task.java.</p>
<p>What I'd love to see is Ant moving in the direction of providing completely reusable Tasks that have 0% dependencies on the Ant engine. This means that loggers, classloaders would be passed to the Ant task by the program who uses it.</p>
<p>I'd like to see Ant provide 2 distributable jars: one containing the XML scripting engine only and one containing all the pure java beans Ant tasks that can be reused in any Java application.</p>
<p>I'd like to see Ant separate into 2 subprojects: one for the XML scripting engine (let's call it engine) and one for the Ant tasks (let's call it tasks). The reason for the 2 projects is to ensure there's no dependency in the direction tasks->engine.</p>
<p>I'd like to see Maven2 use those completely reusable Ant tasks instead of recreating them (this is a wish I'm addressing to both projects, not just Ant! :-)).</p>
<p>I'd like to see those Ant tasks being a JSR and incorporated in a future version of the JDK, thus providing a higher level API that the best classes from the JDK.</p>
<p>Is that where Ant is heading today?</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2005-04-07T16:29:11+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001007_increasing_open_source_project_contributions.html">
<title>Increasing open source project contributions</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001007_increasing_open_source_project_contributions.html</link>
<description><![CDATA[<p>I may be dense but I've just realized today that there is a potentially simple way to increase participation to an open source project. That's always been one of the questions on my mind: how do I make my open source projects more successful? For me 
a successful open source project is one which has a rich developer community. How do I make this possible? There are of course several ideas to make this happen but the one that dawned on me this morning is that
the project has to reduce its complexity (by making it more modular for example).</p>
<p>Indeed, the barrier to participation is often due to the fact that a user who wants to participate will need to understand the whole design, how the different classes are entangled, what effect a change here will have on the rest of the project, etc.
Thus, if we make the project more modular a contributor who wants to participate will only need to understand the design of a given 'module'.</p>
<p>A 'module' would need to have some good-to-have characteristics:<p>
<ul>
<li>Very loose coupling with other modules</li>
<li>Clearly defined and *published* interfaces. There should be some documentation on the project's web site explaining them and a tutorial showing how to implement new modules (or swapping a module implementation by another one) for example.</li>
<li>Separate builds so that it's easy to build only the module (this can be alleviated if the master build is easy to use (i.e. no property tweaking necessary, it just builds - As it's the case with good Maven builds... ;-))</li>
<li>Separate documentation on the web site, so that the website itself is modular and the complexity of each module is hidden in that module's web site. Thus the top level web site would be quite simple only listing what the project does as a whole and listing the different modules</li>
</ul>
<p>Interestingly one way to implement the 'very loose coupling with other modules' characteristic is by using a Service Architecture. This can be done for example by using the Dependency Injection pattern and/or using a lightweight container - PicoContainer, Spring, etc).</p>
<p>This is probably obvious stuff but I've just realized that it's not only good design practices but that it'll also help open source projects attract more contributions. Of course that leads to another topic which is when to accept contributions and how to maintain them in the long run but that would be another discussion...</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2005-03-11T11:21:57+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/001006_distributed_build.html">
<title>Distributed build</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/001006_distributed_build.html</link>
<description><![CDATA[<p>Continuing with my current build-mania, I'd like to propose the idea of a distributed build architecture. I'd love to
see my favorite continuous integration tools (CruiseControl, DamageControl and Continuum in the future) support this notion in the future (I know they're thinking about it already!).</p>
<h3>So what is the need for a distributed build?</h3>
<p>I can see several use cases:</p>
<ul>
<li>building on several JDKs</li>
<li>building on different OS platforms</li>
<li>building with different environment setups (for example building with different application servers, different browsers, different databases, etc) to validate that a product integrates well with various environment setups</li>
<li>delegating the build load on several machines when the build starts to take too long (of course, the first solution should be to try to lighten your build as much as possible)</li>
</ul>
<h3>A proposed architecture</h3>
<p><b>Disclaimer</b>: this only ONE potential solution. There are lots of other solution probably even more valid than this one. Please feel free to add your ideas as comments to this post.</p>
<a href="http://www.codehaus.org/~vmassol/blog/distributed_build_archi.jpg"><img src="http://www.codehaus.org/~vmassol/blog/distributed_build_archi_small.jpg"/></a>
<p>It could work as follows:</p>
<ol>
<li>The central build machine (aka the build orchestrator) decides to start a build. The orchestrator can be one existing continuous integration tool like CruiseControl, DamageControl, etc.
They can trigger a build on anything they want: time-based, change-based, manual, continuously, etc. The orchestrator sends a build request to the space. The request contains all the information 
about the requested build (e.g. JDK to run on, OS to run on, App.Sever/DB/etc to run on)</li>
<li>The space holds all requests. It chould be a good idea to provide a browser to see pending requests (preferably using a simple HTTP browser so that people who wish to contribute can see what type of builds are required). 
In any case it's important that the space be transactional (Note: I'm not sure about the word "transactional". What I mean is that a request cannot be read by several build agents at the same time)</li>
<li>Build agents listen on space build requests objects that match their capabilities. Using Jini/Javaspace would be nice here because (among other things) agents would be able to easily listen to requests with Jini attributes (OS, JDK, etc). Once they read a request they start a local build and publish the result to the space as a Result object</li>
<li>The build orchestrator listens to Results object, and generate result reports, aggregating all results. Build results could contain anything required: result of the build, logs, generated artifacts, etc. The orchestrator gets the data from the Result object and perform usual build operations (publishing, build result notification).</li>
</ol>
<p>Of course there would be several details to sort out, like should we send 2 Requests object for each build need so that we can compare the results and only accept the result if they match, etc.</p>
<h3>Conclusion</h3>
<p>I think this type of distributed build could be especially interesting for open source projects in order to build an active community around a project. This would be yet another way in which people can contribute 
to an open source project: by lending some of their machine CPU to perform continuous integration builds of this project. This usually makes sense as open source projects may be low on hardware resources and lending 
some would help. Of course it also bring its challenge of security issues that would also need to be implemented...</p>
<p>Would you like such a distributed build system? I personally prefer this architecture over one where the orchestrator directly sends build requests to build agents as I find it more scalable and more flexible.</p>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2005-03-11T09:28:42+00:00</dc:date>
</item>
<item rdf:about="http://blogs.codehaus.org/people/vmassol/archives/000953_binary_dependency_builds.html">
<title>Binary Dependency Builds</title>
<link>http://blogs.codehaus.org/people/vmassol/archives/000953_binary_dependency_builds.html</link>
<description><![CDATA[<h3>The concept</h3>
<p>The typical local builds that developers run on their machines work by building the subproject they're working on but also all the dependent subprojects it requires. Usually, as building all dependent subprojects takes
a lot of time, the developer infrequently checks-out other project sources and build them on demand. His focus is on his subproject that he's making modifications to (and rightly so!). This strategy has the following 
drawbacks:</p>
<ul>
<li>Setting up the build on a new fresh machine is complex and takes time. Indeed you have to check out all the top level project sources and build all projecets one by one until you reach the subproject you're concerned with.</li>
<li>It doesn't scale too well. Your local build starts taking tens of minutes which does not encourage running it that often. And if you do, you don't rebuild all the subprojects even though there are probably lots of changes 
that have been made by other coworkers. Thus, you're increasing the possibility of an integration break (breaking your other coworkers when they integrate your changes).</li>
<li>When someone from another team inadverently breaks your project's build, you'll have to switch context (i.e. stop what you're doing) and help out to restore the master build. If this happens unfrequently, it's probably fine 
and even positive (as it increases team collaboration ;-)). However when it happens frequently (which is bound to happen as the team grows), you'll start suffering from it...</li>
</ul>
<p>Because of all these problems, I have been using a different approach on my current project for the past 2 years. This was mostly motivated by the fact that the project is a big project (close to 100 developers) and we were hitting the issues 
mentioned above. I have called this strategy "Binary Dependencies Build". If you're interested this is an approach I have presented both at <a href="http://www.pivolis.com/pdf/Enterprise_Builds_V1.0.pdf">TSSS2004</a> and 
at <a href="http://www.codehaus.org/~vmassol/blog/Maven%201.0%20-%2020041216.ppt">Javapolis 2004</a>.</p>
<p>Here is how it works (click on image for a larger picture):</p>
<a href="http://www.codehaus.org/~vmassol/blog/binary-dependencies-build.jpg"><img src="http://www.codehaus.org/~vmassol/blog/binary-dependencies-build-small.jpg"/></a>
<p>Imagine that you have a "trading" subproject that depends on 2 other subprojects ("partners" and "referenceData"). The idea is that your local build will NOT build them from sources but instead will download their <b>latest
version that work</b> from a remote artifact repository (a location where the result of the subproject build is located). In order to accelerate even further the build, the versions downloaded are stored locally. In our example, 
the latest "partners" jar is already available locally and is thus not downloaded but the "referenceData" one is not. It is downloaded and then stored locally. The "trading" subproject is built using these binary dependencies.</p>
<p>This is all fine but there is a burning question: How do I do continuous integration with such a system? Won't the binary dependencies be old versions when I get them? The solution to this is to have a continuous build
server that continuously build subprojects and puts their artifacts in the remote repository. Note that there are put in the repository <b>only</b> if their build passes with no errors. This ensure that there are always fresh
versions available and that they are as "good" as they can get.</p>
<h3>Doing it with Maven</h3>
<p>The good news is that this feature is built in Maven. Maven implements this support of artifact repositories (local and remote) and it supports the process of automatic download of artifacts not available in the local repository. 
Usually Maven will verify first in the local repository if the artifact's version exists and if so will use it. However, if an artifact's version contains the "SNAPSHOT" keyword, Maven will always check if there's is a more uptodate
artifact in the remote repository. This allows implementing easily the strategy defined above.</p>
<h3>Conclusion</h3>
<p>We've been very happy with this solution so far. I think there are 2 key points in making this work:</p>
<ul>
<li>A good build that provides assurance that the binary artifacts are working. Indeed we've experienced that our subproject build was not always good enough to qualify how "good" was a jar artifact. This was usually caused by the 
non-existence of automated functional tests which meant that even though the build was passing the jar was not working when executed on the developer's machine. The solution is of course to include integration/functional tests in
the build (at least the master CI build). </li>
<li>A quick master build. It's important that it generates fresh jar artifacts as quickly as possible so that CI can happen as often as possible.</li>
</ul>]]></description>
<dc:subject>Think tank</dc:subject>
<dc:creator>vmassol</dc:creator>
<dc:date>2005-01-12T10:44:45+00:00</dc:date>
</item>


</rdf:RDF>
