Showing posts with label GroovyMonkey. Show all posts
Showing posts with label GroovyMonkey. Show all posts
Sunday, November 23, 2008
Monday, June 16, 2008
Notes from Eclipse Democamp in Dallas, Texas
I am late writing this blog since the democamp actually occurred last monday and it is now a week later. I know my memory is a bit faulty, but I wanted to jot a few notes on things that stood out to me.
* Pulse is a cool tool that I really need to take the chance to learn and leverage. I think its ability to generate profiles is a killer app for a Eclipse developer like myself, since I need to switch between versions of Eclipse often. The stuff about being able to provision a workspace should be of great use on my next project. I mean talking to Mr.Williams about the feature convinces me that it is what you want Eclipse Teamsets and exported settings to be.
* You know you should really hold a democamp in a place that has an internet connection. I mean BJ's Brewhouse is a fine enough choice, but no wireless? I wonder how long it will be that any restaurant that has a meeting room will put in wireless internet, it is really starting to become necessary. Todd Williams from Genuitec had a devil of a time demoing what appears to be a really cool product, Pulse. The trouble is that Pulse is based on the Eclipse P2 provisioning platform, which is meant to attend to the Update Manager's legion of flaws. So without an internet connection, there aint alot to demo is there? I think Chris Aniszczyk must have thought of this since the Austin Democamp will be held at Nokia's offices near Stubb's BBQ. Btw, nice choice Chris, Stubb's is an Austin landmark. I would have chosen to do Chuy's or the Hula Hut somehow, but hey, Stubb's is pretty damn cool too.
* I need to come up with a set of cool example scripts so that people get why a monkey scripting tool is so cool. This is a problem of my own making, I see the advantage, particularly coming from the Eclipse plugin development side of the fence, but there aren't that many Eclipse plugin developers. Any help in this arena would be most appreciated. I don't want to see the blank stares again when I goto Eclipse Day at the Googleplex or do the Austin Democamp later on this month.
* Pulse is a cool tool that I really need to take the chance to learn and leverage. I think its ability to generate profiles is a killer app for a Eclipse developer like myself, since I need to switch between versions of Eclipse often. The stuff about being able to provision a workspace should be of great use on my next project. I mean talking to Mr.Williams about the feature convinces me that it is what you want Eclipse Teamsets and exported settings to be.
* You know you should really hold a democamp in a place that has an internet connection. I mean BJ's Brewhouse is a fine enough choice, but no wireless? I wonder how long it will be that any restaurant that has a meeting room will put in wireless internet, it is really starting to become necessary. Todd Williams from Genuitec had a devil of a time demoing what appears to be a really cool product, Pulse. The trouble is that Pulse is based on the Eclipse P2 provisioning platform, which is meant to attend to the Update Manager's legion of flaws. So without an internet connection, there aint alot to demo is there? I think Chris Aniszczyk must have thought of this since the Austin Democamp will be held at Nokia's offices near Stubb's BBQ. Btw, nice choice Chris, Stubb's is an Austin landmark. I would have chosen to do Chuy's or the Hula Hut somehow, but hey, Stubb's is pretty damn cool too.
* I need to come up with a set of cool example scripts so that people get why a monkey scripting tool is so cool. This is a problem of my own making, I see the advantage, particularly coming from the Eclipse plugin development side of the fence, but there aren't that many Eclipse plugin developers. Any help in this arena would be most appreciated. I don't want to see the blank stares again when I goto Eclipse Day at the Googleplex or do the Austin Democamp later on this month.
Wednesday, May 28, 2008
Eclipse Monkey - Extension Points it is
For the new Eclipse Monkey I was considering use of Declarative Services in OSGi since I was intending to allow at least the core set of plugins to be able to used within the context of just a OSGi container. I have decided to use extension points based on my research of the past couple of days.
Neil Bartlett has an excellent writeup of extension points versus OSGi services: http://neilbartlett.name/downloads/extensions_vs_services.pdf
My number one concern was allowing people using Equinox, Knopflerfish(really what a name right?), Apache Felix, or other OSGi container could leverage the basic ability to write scripts. This concern addressed by the fact that bundles working in the context of a pure OSGi container only has to add the following two Eclipse Equinox bundles: org.eclipse.equinox.registry and org.eclipse.equinox.common. I don't think this is a bad trade-off because of the following considerations: dynamic bundles and tooling.
The first consideration is dynamism of bundles, whether they can be hot swapped in or out. This is the number one reason to use OSGi Declarative Services, it is made to work with the fact that bundles could come up or down. I don't think this will be a major concern for monkey, extensions will be things like definitions of script metadata elements, DOM objects, etc... These are things where dynamic hot swapping of bundles is not really beneficial. Besides I always prefer to ask the Extension Registry for the list of available extensions and not cache them. Without caching references from extensions, the code will already allow for dynamic hot swapping. If performance becomes a major issue, well that is what the ExtensionTracker is for.
The second consideration is a major one for me. There is some tooling in Eclipse within the PDE for extensions. Actually there is a lot of tooling. Tooling support for OSGi services, much less Declarative Services, is pretty much non-existent.
So given the thought process outlined here, I think Eclipse Monkey is safe sticking with extension points. As long as there is an Eclipse, it will have to support Extension Points and Extensions. If there is no Eclipse, well there is no Monkey either right?
P.S.: During my research on this I have to say I was impressed with the Knopflerfish site and its Eclipse plugin. I was also almost impressed with the Ops4J Pax Cursor. Honestly, is it so hard to integrate with the PDE Knopflerfish developers? I mean do you need to create your own builder and nature? You guys are almost there, just hook into the PDE and provide an Eclipse recognizable Target Platform. That is it, a few days work maybe, and then voila your stuff will work and all the PDE tooling will work for you. Same complaint with the Pax Cursor people, y'all are further along than the Knopflerfish people in that you not only support multiple OSGi containers and integrate better with the Eclipse PDE, but could I just get an Eclipse recognizable Target Platform? I mean y'all are just one little step away. Get Chris Aniszczyk(I take back what I said about Knopflerfish's name earlier) or someone else on the PDE team to point you to how to address the last hurdle. I mean guys, what better way to get some Java developers(in particular Eclipse Plugin developers) to try out your stuff. Also I should not just beat up on the Knopflerfish and Pax Cursor guys, I mean are you listening Eclipse PDE people? Its great you can run your bundles on a launch configuration that says OSGi container, but is it really that cool given that the only container that works out of the box is the Equinox one?? I have worked with some of those containers in the past and I know this aint rocket science. Why don't I do it? As my grandmother used to say, "If wishes were horses, beggars would ride." My interests right now lie elsewhere.
Neil Bartlett has an excellent writeup of extension points versus OSGi services: http://neilbartlett.name/downloads/extensions_vs_services.pdf
My number one concern was allowing people using Equinox, Knopflerfish(really what a name right?), Apache Felix, or other OSGi container could leverage the basic ability to write scripts. This concern addressed by the fact that bundles working in the context of a pure OSGi container only has to add the following two Eclipse Equinox bundles: org.eclipse.equinox.registry and org.eclipse.equinox.common. I don't think this is a bad trade-off because of the following considerations: dynamic bundles and tooling.
The first consideration is dynamism of bundles, whether they can be hot swapped in or out. This is the number one reason to use OSGi Declarative Services, it is made to work with the fact that bundles could come up or down. I don't think this will be a major concern for monkey, extensions will be things like definitions of script metadata elements, DOM objects, etc... These are things where dynamic hot swapping of bundles is not really beneficial. Besides I always prefer to ask the Extension Registry for the list of available extensions and not cache them. Without caching references from extensions, the code will already allow for dynamic hot swapping. If performance becomes a major issue, well that is what the ExtensionTracker is for.
The second consideration is a major one for me. There is some tooling in Eclipse within the PDE for extensions. Actually there is a lot of tooling. Tooling support for OSGi services, much less Declarative Services, is pretty much non-existent.
So given the thought process outlined here, I think Eclipse Monkey is safe sticking with extension points. As long as there is an Eclipse, it will have to support Extension Points and Extensions. If there is no Eclipse, well there is no Monkey either right?
P.S.: During my research on this I have to say I was impressed with the Knopflerfish site and its Eclipse plugin. I was also almost impressed with the Ops4J Pax Cursor. Honestly, is it so hard to integrate with the PDE Knopflerfish developers? I mean do you need to create your own builder and nature? You guys are almost there, just hook into the PDE and provide an Eclipse recognizable Target Platform. That is it, a few days work maybe, and then voila your stuff will work and all the PDE tooling will work for you. Same complaint with the Pax Cursor people, y'all are further along than the Knopflerfish people in that you not only support multiple OSGi containers and integrate better with the Eclipse PDE, but could I just get an Eclipse recognizable Target Platform? I mean y'all are just one little step away. Get Chris Aniszczyk(I take back what I said about Knopflerfish's name earlier) or someone else on the PDE team to point you to how to address the last hurdle. I mean guys, what better way to get some Java developers(in particular Eclipse Plugin developers) to try out your stuff. Also I should not just beat up on the Knopflerfish and Pax Cursor guys, I mean are you listening Eclipse PDE people? Its great you can run your bundles on a launch configuration that says OSGi container, but is it really that cool given that the only container that works out of the box is the Equinox one?? I have worked with some of those containers in the past and I know this aint rocket science. Why don't I do it? As my grandmother used to say, "If wishes were horses, beggars would ride." My interests right now lie elsewhere.
Labels:
Eclipse Monkey,
EclipseMonkey,
GroovyMonkey,
Monkey
Tuesday, May 06, 2008
New Eclipse Monkey
As you may or may not know, I have been working on Groovy Monkey for some time now. I think it is a useful tool and has a number of benefits over its predecessor Eclipse Monkey. Right now I am hoping that I will have the opportunity to take over Eclipse Monkey and take all of Groovy Monkey's improvements and make them more available to everyone.
I am writing a proposal for this (its a work in progress), you can view it at Eclipse Monkey Proposal. I would love your comments and feedback. I really hope this will happen, I wrote Groovy Monkey because I wanted a tool like that and I think others will too.
I am writing a proposal for this (its a work in progress), you can view it at Eclipse Monkey Proposal. I would love your comments and feedback. I really hope this will happen, I wrote Groovy Monkey because I wanted a tool like that and I think others will too.
Labels:
EclipseMonkey,
GroovyMonkey,
Monkey
Friday, January 12, 2007
Install Eclipse Update Sites through a Firewall
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Download > Update Site
* Script-Path: /GroovyWebUtil/monkey/UpdateSiteDownload.em
* Kudos: ERVIN
* License: EPL 1.0
* Include: /GroovyWebUtil/commons-codec-1.3.jar
* Include: /GroovyWebUtil/commons-httpclient-3.0.1.jar
* Include: /GroovyWebUtil/commons-logging-1.0.3.jar
* Include-Bundle: org.apache.ant
*/
import java.io.*
import java.net.*
import org.apache.commons.httpclient.*
import org.apache.commons.httpclient.auth.*
import org.apache.commons.httpclient.methods.*
import org.apache.commons.io.*
import org.apache.commons.lang.*
import org.eclipse.core.resources.*
import org.eclipse.core.runtime.*
// Set these following 8 parameters
def featureName = 'GroovyEclipsePlugin'
def baseURL = 'http://dist.codehaus.org/groovy/distributions/update/'
def siteURL = baseURL + 'site.xml'
def baseDir = 'c:/plugins/' + featureName + '/eclipse/'
def proxyHost = 'xxxx'
def proxyPort = xx
def proxyUser = 'xxxx'
def proxyPassword = 'xxxx'
def client = new HttpClient()
client.params.setParameter( CredentialsProvider.PROVIDER, this )
client.hostConfiguration.setProxy( proxyHost, proxyPort )
FileUtils.forceMkdir( new File( baseDir ) )
FileUtils.touch( new File( baseDir + '.eclipseextension' ) )
def method = new GetMethod( siteURL )
client.executeMethod( method )
out.println 'status: ' + method.statusText + ' -> ' + siteURL
def input = method.responseBodyAsStream
def siteXML = new XmlSlurper().parse( input )
def features = siteXML.feature
def featureURLs = [ : ]
def versionMap = [ : ]
features.each
{ feature ->
if( !versionMap.containsKey( feature.@id.text() ) )
versionMap[ feature.@id.text() ] = feature.@version.text()
if( versionMap[ feature.@id.text() ].compareTo( feature.@version.text() ) > 0 )
return
featureURLs[ feature.@id.text() ] = feature.@url
}
def ant = new AntBuilder()
def plugins = []
featureURLs.values().each
{ featureURL ->
if( monitor.isCanceled() )
return
def newURL = new URL( baseURL + featureURL )
def featureFile = baseDir + featureURL
def destDir = StringUtils.substringBeforeLast( featureFile, '.' )
FileUtils.forceMkdir( new File( destDir ) )
FileUtils.copyURLToFile( newURL, new File( featureFile ) )
ant.unzip( src: featureFile, dest: destDir )
ant.delete( file: featureFile )
def featureXMLText = new File( destDir + '/feature.xml' ).text
def featureXML = new XmlSlurper().parseText( featureXMLText )
featureXML.plugin.depthFirst().collect{ plugins.add( it ) }
}
monitor.beginTask( 'Starting downloads', plugins.size() + 1 )
plugins.each
{ plugin ->
if( monitor.isCanceled() )
return
def pluginJar = "${plugin.@id}_${plugin.@version}.jar"
def newURL = new URL( baseURL + 'plugins/' + pluginJar )
def pluginFile = baseDir + 'plugins/' + pluginJar
def destDir = StringUtils.substringBeforeLast( pluginFile, '.' )
FileUtils.forceMkdir( new File( destDir ) )
if( new File( pluginFile ).exists() )
return
FileUtils.copyURLToFile( newURL, new File( pluginFile ) )
if( plugin.@unpack.text().trim().toLowerCase() != 'false' )
{
ant.unzip( src: pluginFile, dest: destDir )
ant.delete( file: pluginFile )
}
}
--- And burbled as it ran! ---
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Download > Update Site
* Script-Path: /GroovyMonkeyScripts/monkey/UpdateSiteDownload.gm
* Kudos: ERVIN
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include: /GroovyMonkeyScripts/commons-codec-1.3.jar
* Include: /GroovyMonkeyScripts/commons-httpclient-3.0.1.jar
* Include: /GroovyMonkeyScripts/commons-logging-1.1.jar
* Include-Bundle: org.apache.ant
*/
import java.io.*
import java.net.*
import org.apache.commons.httpclient.*
import org.apache.commons.httpclient.auth.*
import org.apache.commons.httpclient.methods.*
import org.apache.commons.io.*
import org.apache.commons.lang.*
import org.eclipse.core.resources.*
import org.eclipse.core.runtime.*
// Set these following 8 parameters
def featureName = 'GroovyEclipsePlugin'
def baseURL = 'http://dist.codehaus.org/groovy/distributions/update/'
def siteURL = baseURL + 'site.xml'
def baseDir = 'c:/plugins/' + featureName + '/eclipse/'
def proxyHost = 'xxxx'
def proxyPort = xx
def proxyUser = 'xxxx'
def proxyPassword = 'xxxx'
def client = new HttpClient()
client.params.setParameter( CredentialsProvider.PROVIDER, this )
client.hostConfiguration.setProxy( proxyHost, proxyPort )
FileUtils.forceMkdir( new File( baseDir ) )
FileUtils.touch( new File( baseDir + '.eclipseextension' ) )
def method = new GetMethod( siteURL )
client.executeMethod( method )
out.println 'status: ' + method.statusText + ' -> ' + siteURL
def input = method.responseBodyAsStream
def siteXML = new XmlSlurper().parse( input )
def features = siteXML.feature
def featureURLs = [ : ]
def versionMap = [ : ]
features.each
{ feature ->
if( !versionMap.containsKey( feature.@id.text() ) )
versionMap[ feature.@id.text() ] = feature.@version.text()
if( versionMap[ feature.@id.text() ].compareTo( feature.@version.text() ) > 0 )
return
featureURLs[ feature.@id.text() ] = feature.@url
}
def ant = new AntBuilder()
def plugins = []
featureURLs.values().each
{ featureURL ->
if( monitor.isCanceled() )
return
def newURL = new URL( baseURL + featureURL )
def featureFile = baseDir + featureURL
def destDir = StringUtils.substringBeforeLast( featureFile, '.' )
FileUtils.forceMkdir( new File( destDir ) )
FileUtils.copyURLToFile( newURL, new File( featureFile ) )
ant.unzip( src: featureFile, dest: destDir )
ant.delete( file: featureFile )
def featureXMLText = new File( destDir + '/feature.xml' ).text
def featureXML = new XmlSlurper().parseText( featureXMLText )
featureXML.plugin.depthFirst().collect{ plugins.add( it ) }
}
monitor.beginTask( 'Starting downloads', plugins.size() + 1 )
plugins.each
{ plugin ->
if( monitor.isCanceled() )
return
def pluginJar = "${plugin.@id}_${plugin.@version}.jar"
def newURL = new URL( baseURL + 'plugins/' + pluginJar )
def pluginFile = baseDir + 'plugins/' + pluginJar
def destDir = StringUtils.substringBeforeLast( pluginFile, '.' )
if( new File( pluginFile ).exists() )
return
FileUtils.copyURLToFile( newURL, new File( pluginFile ) )
out.println "${plugin.@id}: unpack=${plugin.@unpack.text()}"
if( plugin.@unpack.text().trim().toLowerCase() == 'false' )
return
FileUtils.forceMkdir( new File( destDir ) )
ant.unzip( src: pluginFile, dest: destDir )
ant.delete( file: pluginFile )
}
--- And burbled as it ran! ---
Want to be able to download eclipse plugins, but the firewall wont let you?
Having trouble getting the eclipse update mechanism to tunnel through the firewall, but port 80 is open through http?
If you have Groovy Monkey installed and include the commons-codec-1.3.jar, commons-httpclient-3.0.1.jar and commons-logging-1.0.3.jar libraries in your workspace, use the script above. Define the 8 parameters and the script should download the update package from the url that the update manager should connect to. The script then packages it as the appropriate set of plugins ( zipped or unzipped ) based on the feature.xml and then deploys it to a directory that you can define and creates an extension location that Eclipse can directly import.
In the script above I downloaded the Groovy Eclipse plugin from the update site and had it packaged into the /plugins/GroovyEclipse/eclipse directory as an extension location. I like to separate my plugins like this. The last step is after the script runs successfully goto "Help -> Software Updates -> Manage Configuration" and open the manage configuration dialog. Right click on the root node "Eclipse SDK" and select "Add -> Extension Location". Navigate to the location where the plugin was installed, in the above case /plugins/GroovyEclipse/eclipse, and then click ok. Eclipse will install the plugin and probably ask to restart. You are done...
Labels:
Eclipse,
Groovy,
GroovyMonkey,
Monkey
Tuesday, December 26, 2006
Version 0.4.0 is released
I have released the latest version of Groovy Monkey with the following new features:
I am also in the process of merging Groovy Monkey with the original Eclipse Monkey, so future work maybe accomplished over there.
Version 0.4.0 - December 24, 2006
- Added autocomplete for binding variables.
- Added method autocompletion for bound variables.
- Now can publish to clipboard directly from the editor.
- Added a "Save Script To" dialog to allow the user to select where in their workspace they want the script written.
I am also in the process of merging Groovy Monkey with the original Eclipse Monkey, so future work maybe accomplished over there.
Labels:
Eclipse,
Groovy,
GroovyMonkey
Tuesday, December 19, 2006
Quickly remove unwanted markers in your workspace.
I have been asked, "Groovy Monkey why?" Now there are the default answers of API exploration, Eclipse Automation and rapid prototyping, however, now I have a new one: "You think someone will bother to write a plugin to do that?" The following example highlights this feature.
Here is a quick script written whilest I was debugging the Groovy Eclipse Plugin. The problem I had was that the plugin left some markers lying around that I don't want to see anymore.
So you can use this script to parse through your Eclipse workspace and find all the problem markers and then choose to delete those that don't apply anymore. I think it is pretty useful when you are working on plugins or if you downloaded a particularly buggy plugin. Sure does beat shutting down Eclipse, then opening up the $workspace/.metadata/org.eclipse.core.resources/.projects/$projectname/ and then manually deleting any .markers files you find.
To import the script into Eclipse follow the following steps.
First you must have installed Groovy Monkey.
Secondly, then you must select all the text below ( starting with '--- Came wiffling...' and copy it into your clipboard on your browser (Ctrl-C usually works).
Now goto eclipse and under the Groovy Monkey menu select 'Paste New Script'.
Voila it will be incorporated into your workspace and can be run as 'Groovy Monkey > Remove Markers'. Enjoy.
Here is a quick script written whilest I was debugging the Groovy Eclipse Plugin. The problem I had was that the plugin left some markers lying around that I don't want to see anymore.
So you can use this script to parse through your Eclipse workspace and find all the problem markers and then choose to delete those that don't apply anymore. I think it is pretty useful when you are working on plugins or if you downloaded a particularly buggy plugin. Sure does beat shutting down Eclipse, then opening up the $workspace/.metadata/org.eclipse.core.resources/.projects/$projectname/ and then manually deleting any .markers files you find.
To import the script into Eclipse follow the following steps.
First you must have installed Groovy Monkey.
Secondly, then you must select all the text below ( starting with '--- Came wiffling...' and copy it into your clipboard on your browser (Ctrl-C usually works).
Now goto eclipse and under the Groovy Monkey menu select 'Paste New Script'.
Voila it will be incorporated into your workspace and can be run as 'Groovy Monkey > Remove Markers'. Enjoy.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Remove Markers
* Script-Path: /GroovyMonkeyScripts/monkey/removeMarkers.gm
* Kudos: ERVIN
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
*/
import org.eclipse.core.resources.*
out.clear()
workspace.root.projects.each
{ project ->
out.println "${project.name}"
project.findMarkers( null, true, IResource.DEPTH_INFINITE ).each
{ marker ->
out.println " ** ${marker.type} ${marker.id} ${marker.attributes}"
if( marker.type == 'org.codehaus.groovy.eclipse.groovyFailure' )
marker.delete()
}
}
--- And burbled as it ran! ---
Labels:
Eclipse,
Groovy,
GroovyMonkey
Monday, September 11, 2006
Version 0.3.0 of Groovy Monkey released
I am just putting all the packaging in place for the latest version of Groovy Monkey, version 0.3.0. By the way, I am not putting a whole lot of meaning behind the version numbers, 0.2.0 was chosen before since it was originally a port of Eclipse Monkey and it was at version 0.1.5 or something. The following is noteworthy:
Version 0.3.0 - September 11, 2006
- Changed the Groovy Monkey default project name to GroovyMonkeyScripts as per issue: [ 1536760 ] Change GroovyMonkeyExamples to GroovyMonkeyScripts
- Added an Include-Bundle dialog to the editor context menu, to the Package Explorer view and Navigator.
- Added Include and Include-Bundle dialogs to Groovy Editor context menu command set.
- Added a Java search, modeled on the PDE search, to the outline view to look for all the classes exposed from an included bundle in the set of exported packages. So if your 'External Plug-in Libraries' project is setup, you should see the classes.
- Added a Script-Path: metadata tag that is automatically maintained by Groovy Monkey, it is set to the workspace path of the script.
- The Script Outline view now has menu commands to switch between Flat/Hierarchical views of the binding.
- There are now error markers signifying some command errors with script metadata and some quick fixes to assist.
- There is now some content assist on the Script Metadata tags.
- A new submenu of the top level 'Groovy Monkey' has been added called 'Edit Script'. The Edit Script submenu reflects the top menu, but instead of launching the scripts, it opens the editor for you.
- Added Hippie completion to the editor.
- Added ability to map dom vars to different names for the local script, enables solution for variable name conflicts between DOM plugins.
Labels:
Eclipse,
Groovy,
GroovyMonkey
Tuesday, August 08, 2006
Groovy Monkey: Eclipse Icons Script Pt 4/4: Rapid Prototyping with DOM Plugins
In the first and second postings of this series I showed how to implement a script that checks out the icons folders from all of the Eclipse Projects from the CVS server at dev.eclipse.org. The third installment had to do with breaking up and using library scripts through the Runner DOM to create code reuse and refactoring. This installment will now attempt to do what the third did, but with a prototype DOM plugin developed right in the workspace of your current Eclipse instance. The part I think that is exciting is that we will write a plugin and then use some simple Groovy Monkey scripts to dynamically update the DOM from within the current running Eclipse instance without restarting the workbench. Before I continue, one note of caution, this is still a bit of a work in progress. The plugins that will be dynamically swapped out are the simple type that you would use as a DOM and shouldn't contain much in the way of state, unless you are going through the trouble to make sure that you can hotswap out your plugins. The subject of how to make your plugins hotswappable is a whole subject onto itself, so we are going to sidestep it by keeping the DOM plugins simple.
This article is going to take you step by step through an example using the getEclipseIcons.gm script that was developed in previous articles in this series and the IncludeLocalBundle PDE JUnit test case that I wrote in net.sf.groovyMonkey.tests.
So to begin lets start with the example script that was created before we modified it to use library scripts in the last posting.
We want to create a DOM that can wrap calls to Eclipse's CVS API and provide a simplified interface for our scripts. One definite advantage to doing this is that we can develop the DOM plugin using the PDE and all of the wonderous advantages of the JDT Editor like autocompletion and the like. Another is that while the Runner DOM is useful, there is nothing like being able to make a direct method call on an object for clarity.
The first step is that we need an Eclipse Project in our workspace in which to begin work on this new DOM. Of course this is a plugin project, so we use the 'New Plug-in Project' wizard. I name this project net.sf.groovyMonkey.dom.cvs since I am working on Groovy Monkey projects, however, you can name it whatever you like. I use all the default settings and avoid using the templates since Groovy Monkey does not have a wizard for creating a DOM Plugin Project. Of course, there is nothing that says that your DOM cannot have UI elements and therefore you might choose to use a template for your project, but at this time I am electing for simplicity.
After we have our project in the workspace, we must do some configuration work.
First make your plugin depend upon the net.sf.groovyMonkey plugin project by opening the Manifest editor and adding it to the list of required plugins.
Second add an extension of the extension point net.sf.groovyMonkey.dom using the Extensions page of the Manifest editor. Right click the net.sf.groovyMonkey.dom extension in the view and select 'New > updateSite'. This is what is used to display your DOM in the Outline view of the Groovy Monkey editor and the InstalledDOMs view. If you don't know exactly what it should be yet, this is fine, just select something that is likely to make sense. In my case I put in 'http://groovy-monkey.sourceforge.net/update/net.sf.groovyMonkey.dom.cvsdom'. To be honest I have yet to really use update sites to update my DOM plugins, so don't be surprised if it does not quite work. I tend to use the DOM plugins in my workspace and also have a default set of doms that come with Groovy Monkey included. If you try and it does/doesn't work, drop me a line ( jervin@completecomputing.com ) and let me know the results. As you will see from what follows, it may not be completely necessary.
Next right click the net.sf.groovyMonkey.dom node again and this time select 'New > dom'. Under this new node you see a few fields to fill in and we will go through them one by one.
I just made a strong claim, that you can already install this DOM in your workbench and use it. It is an even stronger claim given that I made the promise that I will show you how to do it dynamically and without restarting your workbench and without having to spawn a self hosted runtime workbench instance. To make this promise come true, we are going to have to do a few things.
1. Create a monkey folder, for a script I am going to provide, under the project, in my case net.sf.groovyMonkey.dom.cvs.
2. Create a lib folder for the library scripts I am going to provide.
3. Copy the following script into your clipboard and use the 'Groovy Monkey > Paste New Script' menu command to put it into your workspace. You are going to have to move it to your project under the monkey folder manually, perhaps there is an opportunity for an enhancement here. I put in under my net.sf.groovyMonkey.dom.cvs project under the monkey folder and called it installDOM.gm.
* Note: If you are not calling your project 'net.sf.groovyMonkey.dom.cvs' remember to change the plugin variable above to the correct project name.
** Note: Be real careful with bunderDOM. By default it wants to deploy plugins to '/tmp/deployedBundles/plugins' and will want to delete and recreate the directory each time you call createDeployDir() on it. I forgot about this and set the deploy dir to my eclipse install plugins directory and *really* regretted it.
*** Note: When you restart eclipse, since the path '/tmp/deployedBundles' is not an Eclipse Extension Location, it be loaded when eclipse is restarted. I think while you are developing a DOM plugin this could be a feature, however, if you want it to persist just copy it from the deploy directory ( default: '/tmp/deployedBundles/plugins' ) to an Eclipse Extension Location or easier yet, into your Eclipse Install plugins directory.
4. Copy the following script into the clipboard and use the 'Groovy Monkey > Paste New Script' menu command to place it into your workspace. Once again move it manually into the project under the lib folder. There is a difference here, since the script above makes a call on it by directly referencing it as uninstall.gm, it is important you name it as such or you go back and change the installDOM.gm script to refer to the correct library script. I put it into my net.sf.groovyMonkey.dom.cvs project under lib and with the name uninstall.gm.
5. Once again copy the following script into the clipboard and use the 'Groovy Monkey > Paste New Script' menu command to place it into your workspace. Move it manually into the project under the lib folder. Since the main script makes a call on it by directly referencing it as getBundleVersion.gm, it is important you name it as such or you go back and change the installDOM.gm script to refer to the correct library script. I put it into my net.sf.groovyMonkey.dom.cvs project under lib and with the name getBundleVersion.gm.
6. Just to be able to show that it works as expected, bring up the 'Installed DOMs' view by 'Window > Show View > Other > Groovy Monkey > Installed DOMs'. Look at the list, unless you have done some other work before following this script, it should not include an entry for your project, in my case no net.sf.groovyMonkey.dom.cvs DOM plugin installed.
Run the script installDOM.gm either by right click menu command 'Run Script' from the Groovy Monkey Editor or by 'Groovy Monkey > Install > CVS DOM'. Now go back to the 'Installed DOMs' view and it should be there. Go ahead and write a quick script to check it out if you like, of course we haven't added anything to it yet.
Well the initial idea is to replace sections of the original getEclipseIcons.gm script with method calls on this new CVS DOM object. So lets add a method called getKnownRepository that takes the arguments of the script that we used to refactor the script in the last article. To accomplish this we will have to add org.eclipse.team.cvs.core bundle as a Required Plug-in in the manifest editor. The code is as follows:
* Note: I am using Java 5.0 for this example and if you are running Groovy Monkey, your Eclipse instance needs to be run in Java 5.0 too. This should not be a large problem since Eclipse can be run with one JRE install, but your code that you are developing can be run with another. There are also all sorts of nice settings in the JDT to allow you to use Java 5.0, but force the code to be valid for 1.4, 1.3, etc...
** Note: Of course you can rewrite this to be 1.4 compliant if you wish.
*** Note: Lastly remember that since your DOM will not be invoked in a seperate thread ( i.e. Eclipse Job ), if you suspect that the operation will take too long, by all means pass in a progress monitor to the method and make use of it.
Now that is kind of neat, so lets use this. What we can't? The other version of the DOM was installed first? Well try running the 'Groovy Monkey > Install > CVS DOM' feature again. Now go back to the 'Installed DOMs' view, does it look the same? Open up the cvsDOM node, open up the CVSDOM class node and now check the listed methods. Your new getKnownRepository() method now shows up.
For people who have been working with Eclipse and developing plugins, this should pique your interest. We are using the OSGi runtime as it was intended, we hotswapped out our net.sf.groovyMonkey.dom.cvs plugin at runtime. If you keep the DOM plugin simple, this should work again and again, with no need to restart the workbench or having to mess with version numbers. To learn more about this goto OSGi and get a copy of the specification.
So now to use it in our script, we open up the getEclipseIcons.gm script in our Groovy Monkey Editor and then right click to bring up the menu option 'Add DOM to Script'. Select the new DOM to be added, in my case here it is net.sf.groovyMonkey.dom.cvs, and click ok. Now rewrite the following section of the script:
as:
The last part just makes sure that the value is not null as a check.
You can continue on to replace the other sections of code with the DOM. Down below is the final version of the class:
Now here is the final version of the script to the new DOM:
If we like the results, we can then deploy the DOM plugin for real to an update site for others to use. Once I figure out a little bit more of this blog API and figure out how to upload files, I will include the source files for the scripts and the net.sf.groovyMonkey.dom.cvs DOM plugin.
I hope once again this has proven helpful and will encourage you to experiment more with Eclipse using Groovy Monkey.
This article is going to take you step by step through an example using the getEclipseIcons.gm script that was developed in previous articles in this series and the IncludeLocalBundle PDE JUnit test case that I wrote in net.sf.groovyMonkey.tests.
So to begin lets start with the example script that was created before we modified it to use library scripts in the last posting.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.eclipse.core.resources.IProject
import org.eclipse.core.resources.IResource
import org.eclipse.core.runtime.SubProgressMonitor
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder
import org.eclipse.team.internal.ccvs.ui.operations.CheckoutIntoOperation
import org.eclipse.team.internal.ccvs.ui.operations.DisconnectOperation
// Here we find the desired repository location that has already been configured
// in the CVS Respository Explorer.
def plugin = CVSProviderPlugin.getPlugin()
def repositoryLoc
for( location in plugin.getKnownRepositories() )
{
if( monitor.isCanceled() )
return
if( location.getRootDirectory() == '/home/eclipse' )
repositoryLoc = location
}
// Here we query all the remote repository top level projects for sub-folders
// called icons and then store them into a list for later use.
def members = repositoryLoc.members( null, false, null )
monitor.beginTask( '', 2 * members.size() )
def iconFolders = []
for( member in members )
{
member.fetchChildren()
if( monitor.isCanceled() )
return
if( !member.childExists( 'icons' ) )
{
monitor.worked( 1 )
continue
}
def icons = member.getFolder( 'icons' )
iconFolders.add( icons )
monitor.worked( 1 )
}
// Check out those icon folders under a sub-folder of the target project called
// icons and place each remote icon folder in a sub-folder of icons that corresponds
// to its project name.
def targetProject = workspace.getRoot().getProject( 'GroovyMonkeyExamples' )
iconFolders.each
{ folder ->
if( monitor.isCanceled() )
return
def targetFolder = targetProject.getFolder( 'icons' ).getFolder( folder.getRemoteParent().getRepositoryRelativePath() )
new CheckoutIntoOperation( null, folder, targetFolder, true ).execute( new SubProgressMonitor( monitor, 1 ) )
monitor.worked( 1 )
}
// Clear off the CVS cruft.
jface.syncExec
{
new DisconnectOperation( null, [ targetProject ].toArray( new IProject[0] ), true ).run()
}
// Build the eclipse-icons.zip file using AntBuilder
def baseDir = targetProject.getFolder( 'icons' )
def destFile = targetProject.getFile( 'eclipse-icons.zip' )
if( destFile.exists() )
destFile.delete( true, null )
def ant = new AntBuilder()
ant.zip( basedir:"${baseDir.getRawLocation()}", destfile:"${destFile.getRawLocation()}" )
// Refresh the targetProject so that eclipse-icons.zip shows up in the Navigator and Package Explorer.
targetProject.refreshLocal( IResource.DEPTH_INFINITE, null )
monitor.done()
--- And burbled as it ran! ---
We want to create a DOM that can wrap calls to Eclipse's CVS API and provide a simplified interface for our scripts. One definite advantage to doing this is that we can develop the DOM plugin using the PDE and all of the wonderous advantages of the JDT Editor like autocompletion and the like. Another is that while the Runner DOM is useful, there is nothing like being able to make a direct method call on an object for clarity.
Setup DOM Plugin Project: net.sf.groovyMonkey.dom.cvs
The first step is that we need an Eclipse Project in our workspace in which to begin work on this new DOM. Of course this is a plugin project, so we use the 'New Plug-in Project' wizard. I name this project net.sf.groovyMonkey.dom.cvs since I am working on Groovy Monkey projects, however, you can name it whatever you like. I use all the default settings and avoid using the templates since Groovy Monkey does not have a wizard for creating a DOM Plugin Project. Of course, there is nothing that says that your DOM cannot have UI elements and therefore you might choose to use a template for your project, but at this time I am electing for simplicity.
After we have our project in the workspace, we must do some configuration work.
First make your plugin depend upon the net.sf.groovyMonkey plugin project by opening the Manifest editor and adding it to the list of required plugins.
Second add an extension of the extension point net.sf.groovyMonkey.dom using the Extensions page of the Manifest editor. Right click the net.sf.groovyMonkey.dom extension in the view and select 'New > updateSite'. This is what is used to display your DOM in the Outline view of the Groovy Monkey editor and the InstalledDOMs view. If you don't know exactly what it should be yet, this is fine, just select something that is likely to make sense. In my case I put in 'http://groovy-monkey.sourceforge.net/update/net.sf.groovyMonkey.dom.cvsdom'. To be honest I have yet to really use update sites to update my DOM plugins, so don't be surprised if it does not quite work. I tend to use the DOM plugins in my workspace and also have a default set of doms that come with Groovy Monkey included. If you try and it does/doesn't work, drop me a line ( jervin@completecomputing.com ) and let me know the results. As you will see from what follows, it may not be completely necessary.
Next right click the net.sf.groovyMonkey.dom node again and this time select 'New > dom'. Under this new node you see a few fields to fill in and we will go through them one by one.
- The first field is variableName and it is required. The variableName field is the name under which your dom will be put in the scripts binding, so it is important to select something easy to type and more importantly a name that is relatively unique. I know that the uniqueness part is a bit tricky and perhaps this is something that could use a tool to enhance or help. At the very least a warning in the Error Log that Eclipse has a DOM Plugins that define variables of the same name. Of course conflicting names may not be a problem if your scripts do not reference the DOMs with the same variable name. In this case I use cvsDOM. I am using the DOM postfix convention, so as to hopefully leave the binding and the script namespace relatively clean. Even if you should override the variable name inside your script's scope, you can use the bsf ( never override ) variable to access the BSFFunctions class which can allow you to look up the bound object by name.
- The next field specifies the class that will implement the IMonkeyDOMFactory interface to create instances of the DOM objects that the script will use. I click the class link and use the wizard to create a class called DOMFactory in the net.sf.groovymonkey.dom.cvsdom package that implements IMonkeyDOMFactory. I am not using a name like CVSDOMFactory since I think that the package that it exists in is obvious enough and I am not going to put another DOM object in that package, for now at least.
- The next field called 'id' is an optional field where you can put a human friendly name on this DOM, I think 'CVS DOM' is friendly enough.
- Finally there is the optional field called 'resource' and it is used to have you enter in the full class name of the object that the DOM Factory is supposed to return in the method getDOMRoot(). This field is preferred by the Groovy Monkey Outline page to assist the user in knowing what types and methods they have available. So it is highly recommended that you set it, since otherwise the Outline page content provider will call getDOMRoot() and perform reflection on it to determine what is being returned. It is kind of hard to know what to put in there before we create it, so we leave it blank for now.
Setup Project to be loaded at will in current Eclipse Workspace
I just made a strong claim, that you can already install this DOM in your workbench and use it. It is an even stronger claim given that I made the promise that I will show you how to do it dynamically and without restarting your workbench and without having to spawn a self hosted runtime workbench instance. To make this promise come true, we are going to have to do a few things.
1. Create a monkey folder, for a script I am going to provide, under the project, in my case net.sf.groovyMonkey.dom.cvs.
2. Create a lib folder for the library scripts I am going to provide.
3. Copy the following script into your clipboard and use the 'Groovy Monkey > Paste New Script' menu command to put it into your workspace. You are going to have to move it to your project under the monkey folder manually, perhaps there is an opportunity for an enhancement here. I put in under my net.sf.groovyMonkey.dom.cvs project under the monkey folder and called it installDOM.gm.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Install/Update > CVS DOM
* Kudos: James E. Ervin
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/net.sf.groovyMonkey.dom
*/
import java.io.File
import org.apache.commons.io.FileUtils
def plugin = 'net.sf.groovyMonkey.dom.cvs'
// If this bundle is already installed, remove it
runnerDOM.runScript( "${plugin}/lib/uninstall.gm", [ pluginToUninstall:plugin ] )
// Build and export the bundle jar
bundlerDOM.createDeployDir()
jface.syncExec
{
bundlerDOM.buildPluginJar( workspace.getRoot().getProject( plugin ) )
}
// Grab the current version of the plugin to be able to identify the jar file.
def bundleVersion = runnerDOM.runScript( "${plugin}/lib/getBundleVersion.gm", [ 'plugin':plugin ] )
// Install and start the new bundle.
def context = bundleDOM.context()
def installedBundle = context.installBundle( "file:" + bundlerDOM.getDeployDir() + "/plugins/" + plugin + "_" + bundleVersion + ".jar" )
installedBundle.start()
--- And burbled as it ran! ---
* Note: If you are not calling your project 'net.sf.groovyMonkey.dom.cvs' remember to change the plugin variable above to the correct project name.
** Note: Be real careful with bunderDOM. By default it wants to deploy plugins to '/tmp/deployedBundles/plugins' and will want to delete and recreate the directory each time you call createDeployDir() on it. I forgot about this and set the deploy dir to my eclipse install plugins directory and *really* regretted it.
*** Note: When you restart eclipse, since the path '/tmp/deployedBundles' is not an Eclipse Extension Location, it be loaded when eclipse is restarted. I think while you are developing a DOM plugin this could be a feature, however, if you want it to persist just copy it from the deploy directory ( default: '/tmp/deployedBundles/plugins' ) to an Eclipse Extension Location or easier yet, into your Eclipse Install plugins directory.
4. Copy the following script into the clipboard and use the 'Groovy Monkey > Paste New Script' menu command to place it into your workspace. Once again move it manually into the project under the lib folder. There is a difference here, since the script above makes a call on it by directly referencing it as uninstall.gm, it is important you name it as such or you go back and change the installDOM.gm script to refer to the correct library script. I put it into my net.sf.groovyMonkey.dom.cvs project under lib and with the name uninstall.gm.
--- Came wiffling through the eclipsey wood ---
/*
* Kudos: James E. Ervin
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/net.sf.groovyMonkey.dom
*/
import org.apache.commons.lang.Validate
Validate.notNull( bsf.lookupBean( 'pluginToUninstall' ), 'pluginToUninstall must be set' )
for( plugin in bundleDOM.context().getBundles() )
{
if( plugin.getSymbolicName().equals( pluginToUninstall ) )
plugin.uninstall()
}
--- And burbled as it ran! ---
5. Once again copy the following script into the clipboard and use the 'Groovy Monkey > Paste New Script' menu command to place it into your workspace. Move it manually into the project under the lib folder. Since the main script makes a call on it by directly referencing it as getBundleVersion.gm, it is important you name it as such or you go back and change the installDOM.gm script to refer to the correct library script. I put it into my net.sf.groovyMonkey.dom.cvs project under lib and with the name getBundleVersion.gm.
--- Came wiffling through the eclipsey wood ---
/*
* Kudos: James E. Ervin
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/net.sf.groovyMonkey.dom
*/
import java.util.jar.Manifest
import org.apache.commons.lang.Validate
Validate.notNull( bsf.lookupBean( 'plugin' ), 'plugin must be set' )
def file = workspace.getRoot().getProject( plugin ).getFile( 'META-INF/MANIFEST.MF' )
def input = file.getContents()
try
{
def manifest = new Manifest( input )
def attributes = manifest.getMainAttributes()
return attributes.getValue( 'Bundle-Version' )
}
finally
{
input.close()
}
--- And burbled as it ran! ---
6. Just to be able to show that it works as expected, bring up the 'Installed DOMs' view by 'Window > Show View > Other > Groovy Monkey > Installed DOMs'. Look at the list, unless you have done some other work before following this script, it should not include an entry for your project, in my case no net.sf.groovyMonkey.dom.cvs DOM plugin installed.
Run the script installDOM.gm either by right click menu command 'Run Script' from the Groovy Monkey Editor or by 'Groovy Monkey > Install > CVS DOM'. Now go back to the 'Installed DOMs' view and it should be there. Go ahead and write a quick script to check it out if you like, of course we haven't added anything to it yet.
Start work on CVS DOM object
Well the initial idea is to replace sections of the original getEclipseIcons.gm script with method calls on this new CVS DOM object. So lets add a method called getKnownRepository that takes the arguments of the script that we used to refactor the script in the last article. To accomplish this we will have to add org.eclipse.team.cvs.core bundle as a Required Plug-in in the manifest editor. The code is as follows:
package net.sf.groovymonkey.dom.cvsdom;
import static org.eclipse.team.internal.ccvs.core.CVSProviderPlugin.getPlugin;
import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
public class CVSDOM
{
public ICVSRepositoryLocation getKnownRepository( final String locationString )
{
for( ICVSRepositoryLocation location : getPlugin().getKnownRepositories() )
{
if( location.getLocation( true ).equals( locationString ) )
return location;
}
return null;
}
}
* Note: I am using Java 5.0 for this example and if you are running Groovy Monkey, your Eclipse instance needs to be run in Java 5.0 too. This should not be a large problem since Eclipse can be run with one JRE install, but your code that you are developing can be run with another. There are also all sorts of nice settings in the JDT to allow you to use Java 5.0, but force the code to be valid for 1.4, 1.3, etc...
** Note: Of course you can rewrite this to be 1.4 compliant if you wish.
*** Note: Lastly remember that since your DOM will not be invoked in a seperate thread ( i.e. Eclipse Job ), if you suspect that the operation will take too long, by all means pass in a progress monitor to the method and make use of it.
Now that is kind of neat, so lets use this. What we can't? The other version of the DOM was installed first? Well try running the 'Groovy Monkey > Install > CVS DOM' feature again. Now go back to the 'Installed DOMs' view, does it look the same? Open up the cvsDOM node, open up the CVSDOM class node and now check the listed methods. Your new getKnownRepository() method now shows up.
For people who have been working with Eclipse and developing plugins, this should pique your interest. We are using the OSGi runtime as it was intended, we hotswapped out our net.sf.groovyMonkey.dom.cvs plugin at runtime. If you keep the DOM plugin simple, this should work again and again, with no need to restart the workbench or having to mess with version numbers. To learn more about this goto OSGi and get a copy of the specification.
So now to use it in our script, we open up the getEclipseIcons.gm script in our Groovy Monkey Editor and then right click to bring up the menu option 'Add DOM to Script'. Select the new DOM to be added, in my case here it is net.sf.groovyMonkey.dom.cvs, and click ok. Now rewrite the following section of the script:
// Here we find the desired repository location that has already been configured
// in the CVS Respository Explorer.
def plugin = CVSProviderPlugin.getPlugin()
def repositoryLoc
for( location in plugin.getKnownRepositories() )
{
if( monitor.isCanceled() )
return
if( location.getRootDirectory() == '/home/eclipse' )
repositoryLoc = location
}
as:
// Here we find the desired repository location that has already been configured
// in the CVS Respository Explorer.
def cvsRepository = ':pserver:anonymous@dev.eclipse.org:/home/eclipse'
def repositoryLoc = cvsDOM.getKnownRepository( cvsRepository )
Validate.notNull( repositoryLoc, "Error could not find the repository ${cvsRepository}, has it been added to the CVS Repository Explorer?" )
The last part just makes sure that the value is not null as a check.
You can continue on to replace the other sections of code with the DOM. Down below is the final version of the class:
package net.sf.groovymonkey.dom.cvsdom;
import static org.eclipse.core.runtime.SubProgressMonitor.PREPEND_MAIN_LABEL_TO_SUBTASK;
import static org.eclipse.swt.widgets.Display.getCurrent;
import static org.eclipse.swt.widgets.Display.getDefault;
import static org.eclipse.team.internal.ccvs.core.CVSProviderPlugin.getPlugin;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRemoteResource;
import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFolder;
import org.eclipse.team.internal.ccvs.ui.operations.CheckoutIntoOperation;
import org.eclipse.team.internal.ccvs.ui.operations.DisconnectOperation;
public class CVSDOM
{
public ICVSRepositoryLocation getKnownRepository( final String locationString )
{
for( ICVSRepositoryLocation location : getPlugin().getKnownRepositories() )
{
if( location.getLocation( true ).equals( locationString ) )
return location;
}
return null;
}
public List<> getRepositoryResources( final IProgressMonitor progressMonitor,
final ICVSRepositoryLocation location,
final String subfolder )
throws CVSException
{
final IProgressMonitor monitor = progressMonitor == null ? new NullProgressMonitor() : progressMonitor;
final List<> folders = new ArrayList<>();
final ICVSRemoteResource[] members = location.members( null, false, null );
monitor.beginTask( "Getting Remote CVS Resources", members.length );
for( final ICVSRemoteResource member : members )
{
if( monitor.isCanceled() )
return null;
monitor.subTask( member.getName() );
final RemoteFolder folder = ( RemoteFolder )member;
folder.fetchChildren( new SubProgressMonitor( monitor, PREPEND_MAIN_LABEL_TO_SUBTASK ) );
if( StringUtils.isBlank( subfolder ) )
{
folders.add( member );
monitor.worked( 1 );
continue;
}
if( !folder.childExists( subfolder ) )
{
monitor.worked( 1 );
continue;
}
folders.add( ( ICVSRemoteResource )folder.getFolder( subfolder ) );
monitor.worked( 1 );
}
monitor.done();
return folders;
}
public CVSDOM checkOut( final IProgressMonitor progressMonitor,
final List<> remoteResources,
final IFolder target,
final boolean disconnect )
throws CVSException, InterruptedException, InvocationTargetException
{
final IProgressMonitor monitor = progressMonitor == null ? new NullProgressMonitor() : progressMonitor;
monitor.beginTask( "Checking out into target: " + target.getFullPath(), remoteResources.size() );
for( final ICVSRemoteResource folder : remoteResources )
{
if( monitor.isCanceled() )
return this;
if( !( folder instanceof ICVSRemoteFolder ) )
continue;
final IFolder targetFolder = target.getFolder( folder.getRemoteParent().getRepositoryRelativePath() );
new CheckoutIntoOperation( null, ( ICVSRemoteFolder )folder, targetFolder, true ).execute( new SubProgressMonitor( monitor, 1 ) );
monitor.worked( 1 );
}
if( !disconnect )
return this;
disconnect( target.getProject() );
monitor.done();
return this;
}
public CVSDOM disconnect( final IProject project )
{
if( getCurrent() == null )
{
final Runnable runnable = new Runnable()
{
public void run()
{
disconnect( project );
}
};
getDefault().syncExec( runnable );
return this;
}
try
{
new DisconnectOperation( null, new IProject[] { project }, true ).run();
}
catch( final InvocationTargetException e )
{
throw new RuntimeException( e );
}
catch( final InterruptedException e )
{
throw new RuntimeException( e );
}
return this;
}
}
Now here is the final version of the script to the new DOM:
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons > Refactored DOM
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
* DOM: http://groovy-monkey.sourceforge.net/update/net.sf.groovyMonkey.dom.cvs
*/
import org.apache.commons.lang.Validate
import org.eclipse.core.resources.IResource
import org.eclipse.core.runtime.SubProgressMonitor
// Here we find the desired repository location that has already been configured
// in the CVS Respository Explorer.
def cvsRepository = ':pserver:anonymous@dev.eclipse.org:/home/eclipse'
def repositoryLoc = cvsDOM.getKnownRepository( cvsRepository )
Validate.notNull( repositoryLoc, "Error could not find the repository ${cvsRepository}, has it been added to the CVS Repository Explorer?" )
// Here we query all the remote repository top level projects for sub-folders
// called icons and then store them into a list for later use.
def iconFolders = cvsDOM.getRepositoryResources( new SubProgressMonitor( monitor, 1 ), repositoryLoc, 'icons' )
if( iconFolders == null )
return
// Check out those icon folders under a sub-folder of the target project called
// icons and place each remote icon folder in a sub-folder of icons that corresponds
// to its project name.
def targetProject = workspace.getRoot().getProject( 'GroovyMonkeyExamples' )
cvsDOM.checkOut( new SubProgressMonitor( monitor, 1 ), iconFolders, targetProject.getFolder( 'icons' ), true )
// Build the eclipse-icons.zip file using AntBuilder
def baseDir = targetProject.getFolder( 'icons' )
def destFile = targetProject.getFile( 'eclipse-icons.zip' )
if( destFile.exists() )
destFile.delete( true, null )
def ant = new AntBuilder()
ant.zip( basedir:"${baseDir.getRawLocation()}", destfile:"${destFile.getRawLocation()}" )
// Refresh the targetProject so that eclipse-icons.zip shows up in the Navigator and Package Explorer.
targetProject.refreshLocal( IResource.DEPTH_INFINITE, null )
monitor.done()
--- And burbled as it ran! ---
If we like the results, we can then deploy the DOM plugin for real to an update site for others to use. Once I figure out a little bit more of this blog API and figure out how to upload files, I will include the source files for the scripts and the net.sf.groovyMonkey.dom.cvs DOM plugin.
I hope once again this has proven helpful and will encourage you to experiment more with Eclipse using Groovy Monkey.
Labels:
Eclipse,
Groovy,
GroovyMonkey
Monday, August 07, 2006
Groovy Monkey: Eclipse Icons Script Pt 3/4: Using library scripts
In my previous posts on the Eclipse Icons Script ( here and here ) I wrote a functional Groovy Monkey script that would query the remote Eclipse projects in CVS to get the contents of all their 'icons' folders. The script would then clean up the CVS team cruft and then package it all as a zip file called 'eclipse-icons.zip'. The next question to ask is, how can I reuse some of this work? I mean there are more than one occasion where I would like to check projects out of CVS and the like. You could cut and paste those sections in to new scripts. While cut and paste is a programmer's best friend, avoiding duplication should be a mantra followed under penalty of death. So why don't we investigate some reuse?
To faciliate reuse Groovy Monkey has two mechanisms. One is the Runner DOM which allows you to invoke other Groovy Monkey scripts in your workspace and the second is developing a DOM plugin that can be invoked from your Groovy Monkey script using the script binding. This blog entry will describe how to use the Runner DOM.
One of the big advantages of using monkey scripts to reuse code is that it can be shared far more easily than with plugins. A library script can be posted to the web directly in a blog or web page and just as easily shared. In fact if you compensate for the workspace paths and the script names, you can cut and paste these scripts and run them immediately within your Eclipse workbench. All this without having to run the Update Manager and probably having to restart Eclipse.
I am going to paste the original script with some comments delimiting the various steps the script does to accomplish its task. These delimited sections would seem to be good candidates for separate library scripts.
One note before we start breaking the script up into component library scripts, is the issue of where to store the scripts. In Groovy Monkey when a script is loaded under the monkey folder of a project and contains a 'Menu:' metadata tag that is not blank, only then will it show up in the 'Groovy Monkey' menu. This is important because library scripts often need parameters passed into them for them to work and there is no mechanism for setting parameters using an action kicked off by a menu item. So as long as the library script does not have a 'Menu:' metadata tag set, this requirement is satisfied. Since library scripts are not meant to be invoked from the menu, you are free to place them where you like in the workspace. I like to put them under a lib folder under my 'GroovyMonkeyExamples' project, but you are not held to this.
As I look at the first part of the script that gets the ICVSRepositoryLocation instance associated with dev.eclipse.org, I realize I really don't like how I did it. So I go ahead and pop off a script to test the getLocation() method output and see if it is in a form I like.
When running that script I found no real difference with the output format, but since the boolean parameter is named display, I will set it to true hoping that means it matches the string shown in the CVS Repository Explorer. I am going to use the above script as the model for the library script. I copy it into the 'GroovyMonkeyExamples/lib' folder and rename it getKnownRepository.gm. The first modification I make is to remove the 'Menu:' metadata tag.
With a library script usually you have to pass in some parameters and Groovy Monkey does this via the script binding when invoked. You can use the BSFFunctions class that is put in the binding under the variable name bsf to check if something has been put into the binding. In fact the following is probably a good practice just for documentation. I named the following script getKnownRepository.gm
So now we modify the original script to invoke it. We have made it clear that we must put in a value in the binding for cvsLocation. Here is how it is done.
Notice that we specify the path in the workspace to the script as a string and that we pass in a java.util.Map instance that specifies those values we wish to set/override in the targetted script's binding. The script is started within its own Eclipse Job and this script is stopped while the other waits to complete and give us a return value. This is all there is to it. Remember that you must specify the full workspace relative path to the target script, this could cause some trouble when you are renaming or moving scripts around in your workspace. It would be nice to include the sort of refactoring support that exists in Eclipse, but since Groovy Monkey is designed to support multiple scripting languages, this presents a bit of a challenge to implement.
Now we rewrite the next section to a script that takes an ICVSRepositoryLocation and a string called subfolder that can be set blank. It does the work of the original section of the script with the addition that if the subfolder parameter is set blank, the top level project is returned instead. This is so you can use the script to just check out all the remote projects. I called this script getRepositoryResources.gm.
Now here is the check out script, which I called checkOut.gm.
Now here is the script to build the zip file, which I called buildZip.gm.
Here is the original script, rewritten to use the library scripts. Note that your paths and script names could be different from these, adjust accordingly.
I hope you found this little exercise useful, because I have. For one I think there is some more work to do to remove some scaffolding, like maybe a convience validate DOM or method to reduce the need to keep calling Validate.notNull() for instance. I would also like to reduce some of the overhead in calling the Runner DOM, but I am not sure how yet to accomplish it.
To faciliate reuse Groovy Monkey has two mechanisms. One is the Runner DOM which allows you to invoke other Groovy Monkey scripts in your workspace and the second is developing a DOM plugin that can be invoked from your Groovy Monkey script using the script binding. This blog entry will describe how to use the Runner DOM.
One of the big advantages of using monkey scripts to reuse code is that it can be shared far more easily than with plugins. A library script can be posted to the web directly in a blog or web page and just as easily shared. In fact if you compensate for the workspace paths and the script names, you can cut and paste these scripts and run them immediately within your Eclipse workbench. All this without having to run the Update Manager and probably having to restart Eclipse.
I am going to paste the original script with some comments delimiting the various steps the script does to accomplish its task. These delimited sections would seem to be good candidates for separate library scripts.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.eclipse.core.resources.IProject
import org.eclipse.core.resources.IResource
import org.eclipse.core.runtime.SubProgressMonitor
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder
import org.eclipse.team.internal.ccvs.ui.operations.CheckoutIntoOperation
import org.eclipse.team.internal.ccvs.ui.operations.DisconnectOperation
// Here we find the desired repository location that has already been configured
// in the CVS Respository Explorer.
def plugin = CVSProviderPlugin.getPlugin()
def repositoryLoc
for( location in plugin.getKnownRepositories() )
{
if( monitor.isCanceled() )
return
if( location.getRootDirectory() == '/home/eclipse' )
repositoryLoc = location
}
// Here we query all the remote repository top level projects for sub-folders
// called icons and then store them into a list for later use.
def members = repositoryLoc.members( null, false, null )
monitor.beginTask( '', 2 * members.size() )
def iconFolders = []
for( member in members )
{
member.fetchChildren()
if( monitor.isCanceled() )
return
if( !member.childExists( 'icons' ) )
{
monitor.worked( 1 )
continue
}
def icons = member.getFolder( 'icons' )
iconFolders.add( icons )
monitor.worked( 1 )
}
// Check out those icon folders under a sub-folder of the target project called
// icons and place each remote icon folder in a sub-folder of icons that corresponds
// to its project name.
def targetProject = workspace.getRoot().getProject( 'GroovyMonkeyExamples' )
iconFolders.each
{ folder ->
if( monitor.isCanceled() )
return
def targetFolder = targetProject.getFolder( 'icons' ).getFolder( folder.getRemoteParent().getRepositoryRelativePath() )
new CheckoutIntoOperation( null, folder, targetFolder, true ).execute( new SubProgressMonitor( monitor, 1 ) )
monitor.worked( 1 )
}
// Clear off the CVS cruft.
jface.syncExec
{
new DisconnectOperation( null, [ targetProject ].toArray( new IProject[0] ), true ).run()
}
// Build the eclipse-icons.zip file using AntBuilder
def baseDir = targetProject.getFolder( 'icons' )
def destFile = targetProject.getFile( 'eclipse-icons.zip' )
if( destFile.exists() )
destFile.delete( true, null )
def ant = new AntBuilder()
ant.zip( basedir:"${baseDir.getRawLocation()}", destfile:"${destFile.getRawLocation()}" )
// Refresh the targetProject so that eclipse-icons.zip shows up in the Navigator and Package Explorer.
targetProject.refreshLocal( IResource.DEPTH_INFINITE, null )
monitor.done()
--- And burbled as it ran! ---
One note before we start breaking the script up into component library scripts, is the issue of where to store the scripts. In Groovy Monkey when a script is loaded under the monkey folder of a project and contains a 'Menu:' metadata tag that is not blank, only then will it show up in the 'Groovy Monkey' menu. This is important because library scripts often need parameters passed into them for them to work and there is no mechanism for setting parameters using an action kicked off by a menu item. So as long as the library script does not have a 'Menu:' metadata tag set, this requirement is satisfied. Since library scripts are not meant to be invoked from the menu, you are free to place them where you like in the workspace. I like to put them under a lib folder under my 'GroovyMonkeyExamples' project, but you are not held to this.
As I look at the first part of the script that gets the ICVSRepositoryLocation instance associated with dev.eclipse.org, I realize I really don't like how I did it. So I go ahead and pop off a script to test the getLocation() method output and see if it is in a form I like.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: List Repositories
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
*/
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
def plugin = CVSProviderPlugin.getPlugin()
for( location in plugin.getKnownRepositories() )
{
if( monitor.isCanceled() )
return
out.println 'location.getLocation( true ): ' + location.getLocation( true )
out.println 'location.getLocation( false ): ' + location.getLocation( false )
}
--- And burbled as it ran! ---
When running that script I found no real difference with the output format, but since the boolean parameter is named display, I will set it to true hoping that means it matches the string shown in the CVS Repository Explorer. I am going to use the above script as the model for the library script. I copy it into the 'GroovyMonkeyExamples/lib' folder and rename it getKnownRepository.gm. The first modification I make is to remove the 'Menu:' metadata tag.
With a library script usually you have to pass in some parameters and Groovy Monkey does this via the script binding when invoked. You can use the BSFFunctions class that is put in the binding under the variable name bsf to check if something has been put into the binding. In fact the following is probably a good practice just for documentation. I named the following script getKnownRepository.gm
--- Came wiffling through the eclipsey wood ---
/*
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
*/
import org.apache.commons.lang.Validate
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
Validate.notNull( bsf.lookupBean( 'cvsLocation' ), 'cvsLocation string describing the CVS location must be set' )
def plugin = CVSProviderPlugin.getPlugin()
for( location in plugin.getKnownRepositories() )
{
if( monitor.isCanceled() )
return
if( location.getLocation( true ) == "$cvsLocation" )
return location
}
return null
--- And burbled as it ran! ---
So now we modify the original script to invoke it. We have made it clear that we must put in a value in the binding for cvsLocation. Here is how it is done.
def map = [ cvsLocation : ':pserver:anonymous@dev.eclipse.org:/home/eclipse' ]
def repositoryLoc = runnerDOM.runScript( '/GroovyMonkeyExamples/lib/getKnownRepository.gm', map )
Notice that we specify the path in the workspace to the script as a string and that we pass in a java.util.Map instance that specifies those values we wish to set/override in the targetted script's binding. The script is started within its own Eclipse Job and this script is stopped while the other waits to complete and give us a return value. This is all there is to it. Remember that you must specify the full workspace relative path to the target script, this could cause some trouble when you are renaming or moving scripts around in your workspace. It would be nice to include the sort of refactoring support that exists in Eclipse, but since Groovy Monkey is designed to support multiple scripting languages, this presents a bit of a challenge to implement.
Now we rewrite the next section to a script that takes an ICVSRepositoryLocation and a string called subfolder that can be set blank. It does the work of the original section of the script with the addition that if the subfolder parameter is set blank, the top level project is returned instead. This is so you can use the script to just check out all the remote projects. I called this script getRepositoryResources.gm.
--- Came wiffling through the eclipsey wood ---
/*
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.apache.commons.lang.Validate
import org.apache.commons.lang.StringUtils
Validate.notNull( bsf.lookupBean( 'repositoryLoc' ), 'repositoryLoc must be set' )
Validate.notNull( bsf.lookupBean( 'subfolder' ), 'subfolder must be set' )
def members = repositoryLoc.members( null, false, null )
monitor.beginTask( 'Getting Remote CVS Resources', members.size() )
def folders = []
for( member in members )
{
if( monitor.isCanceled() )
return null
monitor.subTask( "${member.getName()}" )
member.fetchChildren()
if( StringUtils.isBlank( "$subfolder" ) )
{
folders.add( member )
monitor.worked( 1 )
continue
}
if( !member.childExists( "$subfolder" ) )
{
monitor.worked( 1 )
continue
}
def icons = member.getFolder( "$subfolder" )
folders.add( icons )
monitor.worked( 1 )
}
monitor.done()
return folders
--- And burbled as it ran! ---
Now here is the check out script, which I called checkOut.gm.
--- Came wiffling through the eclipsey wood ---
/*
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.apache.commons.lang.Validate
import org.apache.commons.lang.StringUtils
import org.eclipse.core.resources.IProject
import org.eclipse.core.resources.IResource
import org.eclipse.core.runtime.SubProgressMonitor
import org.eclipse.team.internal.ccvs.ui.operations.CheckoutIntoOperation
import org.eclipse.team.internal.ccvs.ui.operations.DisconnectOperation
Validate.notNull( bsf.lookupBean( 'remoteResources' ), 'remoteResources must be set' )
Validate.notNull( bsf.lookupBean( 'target' ), 'target must be set' )
Validate.notNull( bsf.lookupBean( 'disconnect' ), 'disconnect must be set' )
monitor.beginTask( 'Check out from CVS', remoteResources.size() )
remoteResources.each
{ folder ->
if( monitor.isCanceled() )
return
def targetFolder = target.getFolder( folder.getRemoteParent().getRepositoryRelativePath() )
new CheckoutIntoOperation( null, folder, targetFolder, true ).execute( new SubProgressMonitor( monitor, 1 ) )
monitor.worked( 1 )
}
if( disconnect )
{
// Clear off the CVS cruft.
jface.syncExec
{
new DisconnectOperation( null, [ target.getProject() ].toArray( new IProject[0] ), true ).run()
}
}
monitor.done()
--- And burbled as it ran! ---
Now here is the script to build the zip file, which I called buildZip.gm.
--- Came wiffling through the eclipsey wood ---
/*
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
*/
import org.apache.commons.lang.Validate
import org.apache.commons.lang.StringUtils
import org.eclipse.core.resources.IProject
import org.eclipse.core.resources.IResource
import org.eclipse.core.runtime.SubProgressMonitor
Validate.notNull( bsf.lookupBean( 'srcFolder' ), 'srcFolder must be set' )
Validate.notNull( bsf.lookupBean( 'destFolder' ), 'destFolder must be set' )
Validate.notNull( bsf.lookupBean( 'zipName' ), 'zipName must be set' )
Validate.notNull( bsf.lookupBean( 'replace' ), 'replace must be set' )
monitor.beginTask( 'Building Zip', -1 )
def destFile = destFolder.getFile( "$zipName" )
if( destFile.exists() )
{
if( replace == false )
{
monitor.done()
return
}
destFile.delete( true, null )
}
def ant = new AntBuilder()
ant.zip( basedir:"${srcFolder.getRawLocation()}",destfile:"${destFile.getRawLocation()}" )
// Refresh the targetProject so that eclipse-icons.zip shows up in the Navigator and Package Explorer.
destFolder.getProject().refreshLocal( IResource.DEPTH_INFINITE, null )
monitor.done()
--- And burbled as it ran! ---
Here is the original script, rewritten to use the library scripts. Note that your paths and script names could be different from these, adjust accordingly.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.eclipse.core.resources.IResource
import org.eclipse.core.runtime.SubProgressMonitor
def libDir = '/GroovyMonkeyExamples/lib/'
monitor.beginTask( 'Get Eclipse Icons', -1 )
// Here we find the desired repository location that has already been configured
// in the CVS Respository Explorer.
def map = [ cvsLocation : ':pserver:anonymous@dev.eclipse.org:/home/eclipse' ]
def repositoryLoc = runnerDOM.runScript( libDir + 'getKnownRepository.gm', map )
if( repositoryLoc == null )
throw new RuntimeException( 'Error repositoryLoc has not been configured correctly: ' + map )
// Here we query all the remote repository top level projects for sub-folders
// called icons and then store them into a list for later use.
map = [ 'repositoryLoc':repositoryLoc, subfolder:'icons' ]
def iconFolders = runnerDOM.runScript( libDir + 'getRepositoryResources.gm', map )
if( iconFolders == null monitor.isCanceled() )
return
// Check out those icon folders under a sub-folder of the target project called
// icons and place each remote icon folder in a sub-folder of icons that corresponds
// to its project name.
def targetProject = workspace.getRoot().getProject( 'GroovyMonkeyExamples' )
map = [ remoteResources:iconFolders, target:targetProject.getFolder( 'icons' ), disconnect:true ]
runnerDOM.runScript( libDir + 'checkOut.gm', map )
if( monitor.isCanceled() )
return
// Build the eclipse-icons.zip file using AntBuilder
map = [ srcFolder:targetProject.getFolder( 'icons' ), destFolder:targetProject, zipName: 'eclipse-icons.zip', replace:true ]
runnerDOM.runScript( libDir + 'buildZip.gm', map )
monitor.done()
--- And burbled as it ran! ---
I hope you found this little exercise useful, because I have. For one I think there is some more work to do to remove some scaffolding, like maybe a convience validate DOM or method to reduce the need to keep calling Validate.notNull() for instance. I would also like to reduce some of the overhead in calling the Runner DOM, but I am not sure how yet to accomplish it.
Labels:
Eclipse,
Groovy,
GroovyMonkey
Groovy Monkey: Eclipse Icons Script in Groovy Monkey Pt 2/4
In my previous post I began work on writing a script to checkout all the available icon files from the Eclipse Platform project as a test and demonstration of Groovy Monkey. I would like to continue on and begin demonstrating how you can rewrite some of that code to be reused as a Groovy Monkey library script or as a DOM plugin prototype. I would like to but after reading the original article that inspired me again, I realized I have a little more work to do. I need to remove all the CVS cruft from the workspace and then package the whole thing up as a zip file that could be published to a website.
Now before I dive into the subject, I would like to take a moment to point out a feature of Groovy Monkey that was inherited from Eclipse Monkey. In my previous posting I have a number of versions of the "Get Eclipse Icons" script and I have been working on this script from more than one location. It is easy in Groovy Monkey to grab scripts and to dump them into your workspace. Select the text of the script beginning with:
And ending with:
Copy into the clipboard and then go over to your Eclipse workbench and select "Groovy Monkey > Paste New Script". The script should be dumped into your workspace into the GroovyMonkeyExamples or Groovy Monkey Scripts project under the monkey folder. You can directly invoke it from the Groovy Monkey menu or can proceed to edit it.
Now back to the job at hand. We could just delete the CVS folders and files and then have Eclipse update the workspace, but there is a command in Eclipse that will disconnect a project from CVS and so why not try and use it?
I know there is a Disconnect popup menu command from the Package Explorer view and so I know to begin looking through the plugin.xml files of Eclipse plugins I suspect. From the previous work on this script I suspect the org.eclipse.team.cvs plugins, so lets start there. Since the command I would like to automate has a graphical component, I start with the org.eclipse.team.cvs.ui plugin. Since I know that Eclipse likes to use plugin/bundle.properties to put the names of UI commands in, I start there. Sure enough "UnmanageFolder.label=&Disconnect..." seems to be what I am looking for. In the plugin.xml this maps to the class org.eclipse.team.internal.ccvs.ui.actions.UnmanageAction. Lets start there.
In the UnmanageAction class, it seems as though all it does is do some configuration , then query the user to see if they are sure they wish to disconnect the project and then use the DisconnectOperation class to actually do the work. So it looks as though all we must do is to instanciate the DisconnectOperation and then invoke run, lets go for it.
One note, if you have already run the script and checked out the icons, it would probably be better to split this off into another script so as to avoid having to recheckout everything.
Now we have a script that checks out icons and then removes CVS cruft. So that just leaves us with the need to package it up as a zip. I don't know of any Eclipse code in particular to support this so I am going to use the Groovy Ant scripting support to do this quickly. Afterwords I am going to have the script tell the workspace to refresh itself.
Now we have a script that accomplishes the entirety of the original script. Now to some of you this script is probably pretty good and you would use it as written. Now me, with my ADD and lazy mind, does not like it. I would like to be able to reuse some of this code in other scripts and quite frankly it is too complex and ugly. So my next couple of blog entries are going to deal with this issue. First we are going to rewrite the script so that it can invoke other Groovy Monkey scripts and create reuse that way and second we are going to start work on doing a DOM plugin prototype that I think you will find more dynamic and fun that doing straight Eclipse plugin work. So hopefully you will stick around for them.
Now before I dive into the subject, I would like to take a moment to point out a feature of Groovy Monkey that was inherited from Eclipse Monkey. In my previous posting I have a number of versions of the "Get Eclipse Icons" script and I have been working on this script from more than one location. It is easy in Groovy Monkey to grab scripts and to dump them into your workspace. Select the text of the script beginning with:
--- Came wiffling through the eclipsey wood ---
And ending with:
--- And burbled as it ran! ---
Copy into the clipboard and then go over to your Eclipse workbench and select "Groovy Monkey > Paste New Script". The script should be dumped into your workspace into the GroovyMonkeyExamples or Groovy Monkey Scripts project under the monkey folder. You can directly invoke it from the Groovy Monkey menu or can proceed to edit it.
Now back to the job at hand. We could just delete the CVS folders and files and then have Eclipse update the workspace, but there is a command in Eclipse that will disconnect a project from CVS and so why not try and use it?
I know there is a Disconnect popup menu command from the Package Explorer view and so I know to begin looking through the plugin.xml files of Eclipse plugins I suspect. From the previous work on this script I suspect the org.eclipse.team.cvs plugins, so lets start there. Since the command I would like to automate has a graphical component, I start with the org.eclipse.team.cvs.ui plugin. Since I know that Eclipse likes to use plugin/bundle.properties to put the names of UI commands in, I start there. Sure enough "UnmanageFolder.label=&Disconnect..." seems to be what I am looking for. In the plugin.xml this maps to the class org.eclipse.team.internal.ccvs.ui.actions.UnmanageAction. Lets start there.
In the UnmanageAction class, it seems as though all it does is do some configuration , then query the user to see if they are sure they wish to disconnect the project and then use the DisconnectOperation class to actually do the work. So it looks as though all we must do is to instanciate the DisconnectOperation and then invoke run, lets go for it.
One note, if you have already run the script and checked out the icons, it would probably be better to split this off into another script so as to avoid having to recheckout everything.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.eclipse.core.resources.IProject
import org.eclipse.core.runtime.SubProgressMonitor
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder
import org.eclipse.team.internal.ccvs.ui.operations.CheckoutIntoOperation
import org.eclipse.team.internal.ccvs.ui.operations.DisconnectOperation
def plugin = CVSProviderPlugin.getPlugin()
def repositoryLoc
for( location in plugin.getKnownRepositories() )
{
if( monitor.isCanceled() )
return
if( location.getRootDirectory() == '/home/eclipse' )
repositoryLoc = location
}
def targetProject = workspace.getRoot().getProject( 'GroovyMonkeyExamples' )
def members = repositoryLoc.members( null, false, null )
monitor.beginTask( '', 2 * members.size() )
def iconFolders = []
for( member in members )
{
member.fetchChildren()
if( monitor.isCanceled() )
return
if( !member.childExists( 'icons' ) )
{
monitor.worked( 1 )
continue
}
def icons = member.getFolder( 'icons' )
iconFolders.add( icons )
monitor.worked( 1 )
}
iconFolders.each
{ folder ->
if( monitor.isCanceled() )
return
def targetFolder = targetProject.getFolder( 'icons' ).getFolder( folder.getRemoteParent().getRepositoryRelativePath() )
new CheckoutIntoOperation( null, folder, targetFolder, true ).execute( new SubProgressMonitor( monitor, 1 ) )
monitor.worked( 1 )
}
jface.syncExec
{
new DisconnectOperation( null, [ targetProject ].toArray( new IProject[0] ), true ).run()
}
monitor.done()
--- And burbled as it ran! ---
Now we have a script that checks out icons and then removes CVS cruft. So that just leaves us with the need to package it up as a zip. I don't know of any Eclipse code in particular to support this so I am going to use the Groovy Ant scripting support to do this quickly. Afterwords I am going to have the script tell the workspace to refresh itself.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.eclipse.core.resources.IProject
import org.eclipse.core.resources.IResource
import org.eclipse.core.runtime.SubProgressMonitor
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder
import org.eclipse.team.internal.ccvs.ui.operations.CheckoutIntoOperation
import org.eclipse.team.internal.ccvs.ui.operations.DisconnectOperation
def plugin = CVSProviderPlugin.getPlugin()
def repositoryLoc
for( location in plugin.getKnownRepositories() )
{
if( monitor.isCanceled() )
return
if( location.getRootDirectory() == '/home/eclipse' )
repositoryLoc = location
}
def targetProject = workspace.getRoot().getProject( 'GroovyMonkeyExamples' )
def members = repositoryLoc.members( null, false, null )
monitor.beginTask( '', 2 * members.size() )
def iconFolders = []
for( member in members )
{
member.fetchChildren()
if( monitor.isCanceled() )
return
if( !member.childExists( 'icons' ) )
{
monitor.worked( 1 )
continue
}
def icons = member.getFolder( 'icons' )
iconFolders.add( icons )
monitor.worked( 1 )
}
iconFolders.each
{ folder ->
if( monitor.isCanceled() )
return
def targetFolder = targetProject.getFolder( 'icons' ).getFolder( folder.getRemoteParent().getRepositoryRelativePath() )
new CheckoutIntoOperation( null, folder, targetFolder, true ).execute( new SubProgressMonitor( monitor, 1 ) )
monitor.worked( 1 )
}
jface.syncExec
{
new DisconnectOperation( null, [ targetProject ].toArray( new IProject[0] ), true ).run()
}
def baseDir = targetProject.getFolder( 'icons' )
def destFile = targetProject.getFile( 'eclipse-icons.zip' )
if( destFile.exists() )
destFile.delete( true, null )
def ant = new AntBuilder()
ant.zip( basedir:"${baseDir.getRawLocation()}", destfile:"${destFile.getRawLocation()}" )
targetProject.refreshLocal( IResource.DEPTH_INFINITE, null )
monitor.done()
--- And burbled as it ran! ---
Now we have a script that accomplishes the entirety of the original script. Now to some of you this script is probably pretty good and you would use it as written. Now me, with my ADD and lazy mind, does not like it. I would like to be able to reuse some of this code in other scripts and quite frankly it is too complex and ugly. So my next couple of blog entries are going to deal with this issue. First we are going to rewrite the script so that it can invoke other Groovy Monkey scripts and create reuse that way and second we are going to start work on doing a DOM plugin prototype that I think you will find more dynamic and fun that doing straight Eclipse plugin work. So hopefully you will stick around for them.
Labels:
Eclipse,
Groovy,
GroovyMonkey
Friday, August 04, 2006
Groovy Monkey: Eclipse Icons script in Groovy Monkey Pt 1/4
Since I have been working on Groovy Monkey, I have been trying to reuse the icons that are provided by the Eclipse Platform. Why? Well Eclipse does have its look and feel and where concepts in Groovy Monkey are similar to those in Eclipse, why not make it look similar? Besides I am lazy and the thought of creating a bunch of icons does not float my boat.
So I took particular interest in what Mr. Ben Walding posted. He wrote a script that would goto cvs and check out the icon folders from the Eclipse projects themselves and place them on your local system. This is something I have considered myself, though I thought more about using your local install of the Eclipse SDK and extracting them.
Still what he wrote was a Shell script and if it can work for you, use it. It did get me to thinking, why not try and rewrite it as a Groovy Monkey script so that you can run it in Eclipse and dump it into your workspace? This could be a really nice testcase for Groovy Monkey.
So I am going to be updating this post based on my results in attempting to write it in Groovy Monkey. I mean I am writing this as I am trying to write the script and so do not be surprised if there are a few dead ends and the like.
The first step is to setup Eclipse for this. We need to add the repository location :pserver:anonymous@dev.eclipse.org:/home/eclipse to the CVS Repository Explorer. Validate the connection by opening the HEAD branch under the explorer and browse around a bit just to make sure it is working. So now we should have access to the repositories to do a checkout.
The next step is a bit tricky since the Eclipse Team people have yet to publish a public API for CVS, so now we have to fumble around and find what we want. I start by looking at the Eclipse Help under the "Platform Plugin Developer Guide" and check out what is there under "Team Support." On that page I see the following blurb of text:
The specifics are not as important as the fact that I have found my first clue, use Ctrl-Shift-T to open the class CVSTeamProvider and check it out. As I scan the API and the documentation on Repository Providers, I realize that they are mapped directly to projects and I am not sure that is what I want to do. Still one thing did catch my eye as a first target of a Groovy Monkey script. The RepositoryProvider class has static methods to allow you to find out what Repository Providers exist currently in your workspace. So I create my new Groovy Monkey Script using the "File > New > Other > Groovy Monkey > New Groovy Monkey Script" menu item to start the wizard. I name it getEclipseIcons.gm and here it is.
In the Monkey output console I get the following:
So now I know that I have three providers, one for cvs, another one that the PDE is providing and my subversion provider.
Well the above script is interesting and does highlight in a simple way some of the power of Groovy Script, it does not get me closer to what I want. So I go open up the CVSRepositoryProvider in the Editor and use the "Show In > Package Explorer" pop-up menu command to show me where the code is located. I see it is in the org.eclipse.team.cvs.core plugin and I notice the CVSProviderPlugin class.
The CVSProviderPlugin class seems more interesting and I begin to explore by rewriting the script to the following:
The output in the console is:
Now this is looking like it can be useful.
On second thought, maybe not, as I scan the Eclipse Platform code a bit more, it really looks like what I want is in the org.eclipse.team.cvs.ui plugin and that I want to try out is the CheckoutIntoOperation. CheckoutIntoOperation requires that I
get some IRemoteFolder(s) and well the above script is useful after all. So I rewrite the script as follows:
I get the following output from running this modified script:
Now this is starting to look like the script described in Mr Walding's blog posting. We have a hint that we want to use the CheckoutIntoOperation and we now have access to the remote folders.
Since each of these CVS operations can take some time to complete, I need to have a way to allow the user to monitor progress and cancel the script if they desire. Therefore next step I realize I should do is start using the monitor variable. I also want to begin to check the individual eclipse projects for icons. I know, I think, that Eclipse CVS projects like to put all their icon files into a top level directory called icons, so I am going to check for them.
I get a surprising result from the script above, it doesn't work. I fumble about the API and notice an interesting method called fetchChildren(). I am guessing that information is cached in these classes only on request, so I add the call.
Well the script now does what I expected to. So to complete this exercise I collect those repository resources that correspond to icon folders and place them into a list called iconFolders and prepare to make the call to CheckoutIntoOperation. I did have one more intermediate step here, I tried to check out all the icons into the same set of folders, but the CheckoutIntoOperation would wipe the directory for each project. So we check them out into a project that already exists in my workspace called GroovyMonkeyExamples and into a folder called icons. We place the icon folders into subdirectories under icons that correspond to their remote repository path. To complete we instanciate the CheckoutIntoOperation and invoke its execute() method.
This now gets what we want. We check all the Eclipse projects for icons and get them all. It can be left for homework to try and automate the task to disconnect the project from CVS and to package it up into a zip archive.
There are a few notes to make here. First, this is a quick prototype and there may very well be far more efficient ways to accomplish this. In fact, you could write a seperate script to grab all the icon folders and print them out so that you can paste them into this script and check only those remote projects that have icons. A second note to make is, well this prototype only took a couple of hours to accomplish and that was with my less than ideal setup ( and I do mean less than ideal ). Imagine the time it would have taken using self hosting or deploying plugins. The last note is that of course it would be been easier to install cvs on my system and script commands to call it directly, but that would have been cheating since the whole point was to demonstrate how you could use Groovy Monkey to navigate the Eclipse Platform API world directly and easily.
So we do have a functional script, but it is not very asthetically pleasing ( at least to me ) and the ability to check out remote resources from CVS into my workspace is probably something I want to do again. So in future blog entries I want to demonstrate how we can split off some functions into seperate libraries scripts that can be reused or how you can begin to prototype some plugins by starting to write the function as DOM in your workspace and be able to runtime deploy it into your current Eclipse instance.
External Link
So I took particular interest in what Mr. Ben Walding posted. He wrote a script that would goto cvs and check out the icon folders from the Eclipse projects themselves and place them on your local system. This is something I have considered myself, though I thought more about using your local install of the Eclipse SDK and extracting them.
Still what he wrote was a Shell script and if it can work for you, use it. It did get me to thinking, why not try and rewrite it as a Groovy Monkey script so that you can run it in Eclipse and dump it into your workspace? This could be a really nice testcase for Groovy Monkey.
So I am going to be updating this post based on my results in attempting to write it in Groovy Monkey. I mean I am writing this as I am trying to write the script and so do not be surprised if there are a few dead ends and the like.
The first step is to setup Eclipse for this. We need to add the repository location :pserver:anonymous@dev.eclipse.org:/home/eclipse to the CVS Repository Explorer. Validate the connection by opening the HEAD branch under the explorer and browse around a bit just to make sure it is working. So now we should have access to the repositories to do a checkout.
The next step is a bit tricky since the Eclipse Team people have yet to publish a public API for CVS, so now we have to fumble around and find what we want. I start by looking at the Eclipse Help under the "Platform Plugin Developer Guide" and check out what is there under "Team Support." On that page I see the following blurb of text:
<extension
point="org.eclipse.team.core.repository">
<repository
class="org.eclipse.team.internal.ccvs.core.CVSTeamProvider"
id="org.eclipse.team.cvs.core.cvsprovider">
</repository>
</extension>
The specifics are not as important as the fact that I have found my first clue, use Ctrl-Shift-T to open the class CVSTeamProvider and check it out. As I scan the API and the documentation on Repository Providers, I realize that they are mapped directly to projects and I am not sure that is what I want to do. Still one thing did catch my eye as a first target of a Groovy Monkey script. The RepositoryProvider class has static methods to allow you to find out what Repository Providers exist currently in your workspace. So I create my new Groovy Monkey Script using the "File > New > Other > Groovy Monkey > New Groovy Monkey Script" menu item to start the wizard. I name it getEclipseIcons.gm and here it is.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.core
*/
import org.eclipse.team.core.RepositoryProvider
for( providerID in RepositoryProvider.getAllProviderTypeIds() )
{
out.println "providerID: ${providerID}"
}
--- And burbled as it ran! ---
In the Monkey output console I get the following:
providerID: org.eclipse.team.cvs.core.cvsnature
providerID: org.eclipse.pde.core.BinaryRepositoryProvider
providerID: org.polarion.team.svn.core.svnnature
So now I know that I have three providers, one for cvs, another one that the PDE is providing and my subversion provider.
Well the above script is interesting and does highlight in a simple way some of the power of Groovy Script, it does not get me closer to what I want. So I go open up the CVSRepositoryProvider in the Editor and use the "Show In > Package Explorer" pop-up menu command to show me where the code is located. I see it is in the org.eclipse.team.cvs.core plugin and I notice the CVSProviderPlugin class.
The CVSProviderPlugin class seems more interesting and I begin to explore by rewriting the script to the following:
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
*/
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
def plugin = CVSProviderPlugin.getPlugin()
for( location in plugin.getKnownRepositories() )
{
out.println "Repository Location: ${location.dump()}"
}
--- And burbled as it ran! ---
The output in the console is:
Repository Location: <org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation@63adab5c method=org.eclipse.team.internal.ccvs.core.connection.PServerConnectionMethod@262e12 user=anonymous password=null host=dev.eclipse.org port=0 root=/home/eclipse userFixed=true passwordFixed=false allowCaching=false serverPlatform=1 previousAuthenticationFailed=false>
Repository Location: <org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation@553087e method=org.eclipse.team.internal.ccvs.core.connection.PServerConnectionMethod@262e12 user=anonymous password=null host=dev.eclipse.org port=0 root=/cvsroot/technology userFixed=true passwordFixed=false allowCaching=false serverPlatform=0 previousAuthenticationFailed=false>
Repository Location: <org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation@6142ea28 method=org.eclipse.team.internal.ccvs.core.connection.ExtConnectionMethod@7b51d2 user=xxxxx password=null host=groovy-monkey.cvs.sourceforge.net port=0 root=/cvsroot/groovy-monkey userFixed=true passwordFixed=false allowCaching=false serverPlatform=0 previousAuthenticationFailed=false>
Now this is looking like it can be useful.
On second thought, maybe not, as I scan the Eclipse Platform code a bit more, it really looks like what I want is in the org.eclipse.team.cvs.ui plugin and that I want to try out is the CheckoutIntoOperation. CheckoutIntoOperation requires that I
get some IRemoteFolder(s) and well the above script is useful after all. So I rewrite the script as follows:
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
import org.eclipse.team.internal.ccvs.ui.operations.CheckoutIntoOperation
def plugin = CVSProviderPlugin.getPlugin()
def repositoryLoc
for( location in plugin.getKnownRepositories() )
{
out.println "Repository Location: ${location.getRootDirectory()}"
if( location.getRootDirectory() == '/home/eclipse' )
repositoryLoc = location
}
for( member in repositoryLoc.members( null, false, null ) )
{
out.println "Repository Path: ${member.getRepositoryRelativePath()}"
}
--- And burbled as it ran! ---
I get the following output from running this modified script:
Repository Location: /home/eclipse
Repository Location: /cvsroot/technology
Repository Location: /cvsroot/groovy-monkey
Repository Path: CVSROOT
Repository Path: cdt-contrib
Repository Path: eclipse-project-home
Repository Path: eclipse-project-website
Repository Path: equinox-incubator
Repository Path: jdt-core-home
Repository Path: jdt-debug-home
Repository Path: jdt-doc-home
Repository Path: jdt-ui-home
Repository Path: org.apache.ant
Repository Path: org.apache.lucene
Repository Path: org.apache.xerces
Repository Path: org.eclipse.ant.core
Repository Path: org.eclipse.ant.optional.junit
Repository Path: org.eclipse.ant.tests.core
Repository Path: org.eclipse.ant.tests.ui
Repository Path: org.eclipse.ant.ui
Repository Path: org.eclipse.compare
Repository Path: org.eclipse.compare.examples
Repository Path: org.eclipse.compare.examples.xml
Repository Path: org.eclipse.compare.tests
Repository Path: org.eclipse.core.applicationrunner
Repository Path: org.eclipse.core.boot
Repository Path: org.eclipse.core.commands
Repository Path: org.eclipse.core.components
Repository Path: org.eclipse.core.contenttype
Repository Path: org.eclipse.core.expressions
...
Now this is starting to look like the script described in Mr Walding's blog posting. We have a hint that we want to use the CheckoutIntoOperation and we now have access to the remote folders.
Since each of these CVS operations can take some time to complete, I need to have a way to allow the user to monitor progress and cancel the script if they desire. Therefore next step I realize I should do is start using the monitor variable. I also want to begin to check the individual eclipse projects for icons. I know, I think, that Eclipse CVS projects like to put all their icon files into a top level directory called icons, so I am going to check for them.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
import org.eclipse.team.internal.ccvs.ui.operations.CheckoutIntoOperation
def plugin = CVSProviderPlugin.getPlugin()
def repositoryLoc
for( location in plugin.getKnownRepositories() )
{
out.println "Repository Location: ${location.getRootDirectory()}"
if( location.getRootDirectory() == '/home/eclipse' )
repositoryLoc = location
}
monitor.beginTask( '', members.size() )
for( member in repositoryLoc.members( null, false, null ) )
{
if( monitor.isCanceled() )
return
if( !member.childExists( 'icons' ) )
{
monitor.worked( 1 )
continue
}
out.println "Repository Path: ${member.getRepositoryRelativePath()}"
}
monitor.done()
--- And burbled as it ran! ---
I get a surprising result from the script above, it doesn't work. I fumble about the API and notice an interesting method called fetchChildren(). I am guessing that information is cached in these classes only on request, so I add the call.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
import org.eclipse.team.internal.ccvs.ui.operations.CheckoutIntoOperation
def plugin = CVSProviderPlugin.getPlugin()
def repositoryLoc
for( location in plugin.getKnownRepositories() )
{
out.println "Repository Location: ${location.getRootDirectory()}"
if( location.getRootDirectory() == '/home/eclipse' )
repositoryLoc = location
}
monitor.beginTask( '', members.size() )
for( member in repositoryLoc.members( null, false, null ) )
{
member.fetchChildren()
if( monitor.isCanceled() )
return
if( !member.childExists( 'icons' ) )
{
monitor.worked( 1 )
continue
}
out.println "Repository Path: ${member.getRepositoryRelativePath()}"
}
monitor.done()
--- And burbled as it ran! ---
Well the script now does what I expected to. So to complete this exercise I collect those repository resources that correspond to icon folders and place them into a list called iconFolders and prepare to make the call to CheckoutIntoOperation. I did have one more intermediate step here, I tried to check out all the icons into the same set of folders, but the CheckoutIntoOperation would wipe the directory for each project. So we check them out into a project that already exists in my workspace called GroovyMonkeyExamples and into a folder called icons. We place the icon folders into subdirectories under icons that correspond to their remote repository path. To complete we instanciate the CheckoutIntoOperation and invoke its execute() method.
--- Came wiffling through the eclipsey wood ---
/*
* Menu: Get Eclipse Icons
* Kudos: ervinja
* License: EPL 1.0
* DOM: http://groovy-monkey.sourceforge.net/update/plugins/net.sf.groovyMonkey.dom
* Include-Bundle: org.eclipse.team.cvs.core
* Include-Bundle: org.eclipse.team.cvs.ui
*/
import org.eclipse.core.runtime.SubProgressMonitor
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin
import org.eclipse.team.internal.ccvs.core.ICVSRemoteFolder
import org.eclipse.team.internal.ccvs.ui.operations.CheckoutIntoOperation
def plugin = CVSProviderPlugin.getPlugin()
def repositoryLoc
for( location in plugin.getKnownRepositories() )
{
if( monitor.isCanceled() )
return
if( location.getRootDirectory() == '/home/eclipse' )
repositoryLoc = location
}
def targetProject = workspace.getRoot().getProject( 'GroovyMonkeyExamples' )
def members = repositoryLoc.members( null, false, null )
monitor.beginTask( '', 2 * members.size() )
def iconFolders = []
for( member in members )
{
member.fetchChildren()
if( monitor.isCanceled() )
return
if( !member.childExists( 'icons' ) )
{
monitor.worked( 1 )
continue
}
def icons = member.getFolder( 'icons' )
iconFolders.add( icons )
monitor.worked( 1 )
}
iconFolders.each
{ folder ->
if( monitor.isCanceled() )
return
def targetFolder = targetProject.getFolder( 'icons' ).getFolder( folder.getRemoteParent().getRepositoryRelativePath() )
new CheckoutIntoOperation( null, folder, targetFolder, true ).execute( new SubProgressMonitor( monitor, 1 ) )
monitor.worked( 1 )
}
monitor.done()
--- And burbled as it ran! ---
This now gets what we want. We check all the Eclipse projects for icons and get them all. It can be left for homework to try and automate the task to disconnect the project from CVS and to package it up into a zip archive.
There are a few notes to make here. First, this is a quick prototype and there may very well be far more efficient ways to accomplish this. In fact, you could write a seperate script to grab all the icon folders and print them out so that you can paste them into this script and check only those remote projects that have icons. A second note to make is, well this prototype only took a couple of hours to accomplish and that was with my less than ideal setup ( and I do mean less than ideal ). Imagine the time it would have taken using self hosting or deploying plugins. The last note is that of course it would be been easier to install cvs on my system and script commands to call it directly, but that would have been cheating since the whole point was to demonstrate how you could use Groovy Monkey to navigate the Eclipse Platform API world directly and easily.
So we do have a functional script, but it is not very asthetically pleasing ( at least to me ) and the ability to check out remote resources from CVS into my workspace is probably something I want to do again. So in future blog entries I want to demonstrate how we can split off some functions into seperate libraries scripts that can be reused or how you can begin to prototype some plugins by starting to write the function as DOM in your workspace and be able to runtime deploy it into your current Eclipse instance.
External Link
Labels:
Eclipse,
Groovy,
GroovyMonkey
Subscribe to:
Posts (Atom)