tag:blogger.com,1999:blog-17974869669244541742024-03-05T17:15:05.128-08:00blambi.blogspotBrice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.comBlogger10125tag:blogger.com,1999:blog-1797486966924454174.post-27161391878049710272011-04-11T23:28:00.000-07:002011-04-11T23:32:48.818-07:00Server Assist OverlaysTo follow on from the last blog post, I made a google code page for the server assist overlays. This is a demonstration of how to use GDAL to build geo-referenced images and pixel maps for user interaction on the fly.<br /><br />This allows for lots and lots of polygons/lines/points to be drawn and still allows the user to interact. This is useful for when your data is too large for the javascript overlays of google maps to handle.<br /><br />Take a look:<br /><br /><a href="http://code.google.com/p/server-assist-overlays/">http://code.google.com/p/server-assist-overlays/</a><br /><br/><br /><br/>Brice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.com0tag:blogger.com,1999:blog-1797486966924454174.post-54460275466491838042010-05-01T15:20:00.000-07:002011-01-24T02:43:42.054-08:00Making Geo-Referenced Images in Python with GDALOften it might be desirable to make an image to overlay on Google Maps instead of creating tiles or using markers or polygon/polyline overlays. The Geospatial Data Abstraction Library (GDAL) provides python bindings that can be used to reference points between geospace, at different projections, and pixel space.<br /><br /><br />First you will need to know the bounds of t<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmbQJ4rgLQYhsyJKT_QAPPMGlcjeFZAlWXxjE6dT1sJh_GOhf8fNLkakSkyKxt-5im61P-fkAc3myZiF_OMgT33EXK1YR-9ZUfMN_XHVPAlUO068lidRII8WoMMMEguGXDxncEnMkP6d8o/s1600/Screenshot-2.png"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 320px; height: 228px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmbQJ4rgLQYhsyJKT_QAPPMGlcjeFZAlWXxjE6dT1sJh_GOhf8fNLkakSkyKxt-5im61P-fkAc3myZiF_OMgT33EXK1YR-9ZUfMN_XHVPAlUO068lidRII8WoMMMEguGXDxncEnMkP6d8o/s320/Screenshot-2.png" alt="" id="BLOGGER_PHOTO_ID_5466452544752431842" border="0" /></a>he area you are targeting. For this example we will use temperature data from RWIS roadside sensors but we are only interested in Colorado. At the time of this writing you can right click on Google Maps and click "What's here?" and it will display the latitude and longitude in the search box.<br /><br /><br /><br /><code>upper_left = (41.172865, -109.260864)<br />lower_right = (36.926183,-101.867066)<br /></code><br /><br />We will need to setup the projections, this EPSG (900913, google) is known to work with Google Maps (and others):<br /><br /><code>def get_mercator_projection():<br /> proj = osr.SpatialReference()<br /> proj.ImportFromEPSG(900913)<br /> return proj<br /></code><br /><br />We will also need a projection for our input decimal latitude longitude values:<br /><br /><code>def get_default_projection():<br /> proj = osr.SpatialReference()<br /> proj.ImportFromEPSG(4326)<br /> return proj<br /></code><br /><br />We will also need a geotransform array that defines the origin and pixel size of the projection we want to use. This can be created with the boundaries we chose above and the width and height of the image we want to create, there should be a relationship between negative pixel sizes and the hemisphere you are mapping because the mercator origin is not at the top left of the earth, this code attempts to correct this but it might only work for north america:<br /><code> def get_geo_transform(upper_left, lower_right, width, height, transform):<br /> (ul_px, ul_py, z) = transform.TransformPoint(upper_left[1],upper_left[0])<br /> (lr_px, lr_py, z) = transform.TransformPoint(lower_right[1],lower_right[0])<br /> dx = abs(ul_px - lr_px)<br /> dy = abs(ul_py - lr_py)<br /> pixel_size_x = dx / float(width)<br /> pixel_size_y = dy / float(height)<br /> if ul_px < 0 and pixel_size_x < 0: # does this make sense?<br /> pixel_size_x *= -1 #this might only work for North America...<br /> if ul_py > 0 and pixel_size_y > 0:<br /> pixel_size_y *= -1<br /> return [ul_px,<br /> pixel_size_x,<br /> 0,<br /> ul_py, #these values are in a weird order but this is correct<br /> 0,<br /> pixel_size_y]<br /></code><br /><br />This geotransform will define the pixel to lat/lon transformation but we need an inverse of this to map our lat/lon values to a pixel on our image (this feature requires gdal-1.7.1 or greater):<br /><br /><code>merc_proj = get_mercator_projection()<br />def_proj = get_default_projection()<br />def_transform = osr.CoordinateTransformation(def_proj, merc_proj)<br />geo = get_geo_transform(upper_left, lower_right, width, height, def_transform)<br />inv_geo = gdal.InvGeoTransform(geo)[1]<br /></code><br /><br />Now we are ready to transform a lat/lon to pixel:<br /><br /><code>def get_pixel(lat, lon, inv_geo, transform):<br /> (gx,gy,gz) = transform.TransformPoint(lon,lat)<br /> (gx, gy) = gdal.ApplyGeoTransform(inv_geo, gx, gy)<br /> px = int(gx)<br /> py = int(gy)<br /> return (px,py)<br /></code><br /><br />Now we can bring it all together and read our temperature data to create an image using python PIL:<br /><br /><code>#!/usr/bin/env python<br /><br />from osgeo import osr<br />from osgeo import gdal<br />from PIL import Image<br />from PIL import ImageDraw<br />from PIL import ImageFont<br /><br />fontfile = "/usr/share/fonts/truetype/freefont/FreeSans.ttf"<br />fontsize = 10<br />input_data_file = "rwis_temp_20090107.0000.csv"<br />output_image = "rwis_temp_20090107.0000.png"<br />upper_left = (41.172865, -109.260864)<br />lower_right = (36.926183,-101.867066)<br />width = 1200<br />height = 1200<br /><br />class georef:<br /><br /> @staticmethod<br /> def main():<br /> gref = georef(upper_left, lower_right, width, height)<br /> f = open(input_data_file,"r")<br /> for l in f.readlines():<br /> ls = l.split(",")<br /> lat = float(ls[1])<br /> lon = float(ls[2])<br /> temp = int(ls[3])<br /> tempf = temp * (9.0/5.0) - 459.67<br /> if tempf < -20 or tempf > 100:<br /> continue<br /> gref.draw_text(lat,lon,tempf)<br /> gref.write_image(output_image)<br /><br /> def __init__(self, upper_left, lower_right, width, height):<br /> merc_proj = georef.get_mercator_projection()<br /> def_proj = georef.get_default_projection()<br /> self.def_transform = osr.CoordinateTransformation(def_proj, merc_proj)<br /> geo = georef.get_geo_transform(upper_left, lower_right, width, height, self.def_transform)<br /> self.inv_geo = gdal.InvGeoTransform(geo)[1]<br /> self.img = Image.new("RGBA",(width,height),(0,0,0,0))<br /> self.width = width<br /> self.height = height<br /><br /> def draw_text(self, lat, lon, value, color = (0,0,0,255)):<br /> (px,py) = georef.get_pixel(lat,lon,self.inv_geo,self.def_transform)<br /> if px > self.width or px < 0 or py > self.height or py < 0: # throw out anything outside our domain<br /> return<br /> if (0,0,0,0) != self.img.getpixel((px,py)): #skip stuff that is here already<br /> return<br /> dr = ImageDraw.Draw(self.img)<br /> font = ImageFont.truetype(fontfile,fontsize)<br /> dr.text((px,py), "%d" % value, font=font, fill=color)<br /> del dr<br /><br /> def write_image(self, fname):<br /> self.img.save(fname)<br /><br /> @staticmethod<br /> def get_mercator_projection():<br /> proj = osr.SpatialReference()<br /> proj.ImportFromEPSG(900913)<br /> return proj<br /><br /> @staticmethod<br /> def get_default_projection():<br /> proj = osr.SpatialReference()<br /> proj.ImportFromEPSG(4326)<br /> return proj<br /><br /> @staticmethod<br /> def get_geo_transform(upper_left, lower_right, width, height, transform):<br /> (ul_px, ul_py, z) = transform.TransformPoint(upper_left[1],upper_left[0])<br /> (lr_px, lr_py, z) = transform.TransformPoint(lower_right[1],lower_right[0])<br /> dx = abs(ul_px - lr_px)<br /> dy = abs(ul_py - lr_py)<br /> pixel_size_x = dx / float(width)<br /> pixel_size_y = dy / float(height)<br /> if ul_px < 0 and pixel_size_x < 0: # does this make sense?<br /> pixel_size_x *= -1 #this might only work for North America...<br /> if ul_py > 0 and pixel_size_y > 0:<br /> pixel_size_y *= -1<br /> return [ul_px,<br /> pixel_size_x,<br /> 0,<br /> ul_py, #these values are in a weird order but this is correct<br /> 0,<br /> pixel_size_y]<br /><br /> @staticmethod<br /> def get_pixel(lat, lon, inv_geo, transform):<br /> (gx,gy,gz) = transform.TransformPoint(lon,lat)<br /> (gx, gy) = gdal.ApplyGeoTransform(inv_geo, gx, gy)<br /> px = int(gx)<br /> py = int(gy)<br /> return (px,py)<br /><br /><br />if "__main__" == __name__:<br /> georef.main()<br /></code><br /><br />download the test data here: <a href="http://procrustes.googlecode.com/files/rwis_temp_20090107.0000.csv">rwis_temp_20090107.0000.csv</a>. This should result in an image that looks like this:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic-qVVpJthgWI_mKlrenzzZa0EPpBLvF431vKTCe5157rC6PqqVgBZBhZYmssL89Sq5vF5yBcuIHGhNcNJ-W7U6clx78TNu90xGixIp_O7__giHF_OWQqiB6mtQBPy68K_Re8w9Cfm79aB/s1600/rwis_temp_20090107.0000.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 320px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic-qVVpJthgWI_mKlrenzzZa0EPpBLvF431vKTCe5157rC6PqqVgBZBhZYmssL89Sq5vF5yBcuIHGhNcNJ-W7U6clx78TNu90xGixIp_O7__giHF_OWQqiB6mtQBPy68K_Re8w9Cfm79aB/s320/rwis_temp_20090107.0000.png" alt="" id="BLOGGER_PHOTO_ID_5466603577030524194" border="0" /></a><br /><br /><br />Now we can overlay this on a map using Google Maps API v3, see <a href="http://code.google.com/apis/maps/documentation/v3/overlays.html#CustomOverlays">this example for how to overlay images</a>. Notice that a bounding box in Google Maps is definded by lower left and upper right corners. Also, as you zoom in the resolution is not so nice. One could expand on this to support different zoom levels or even serve these images in real-time based on the bounds provided by the Google Maps API.<br /><br /><script type="text/javascript"><br /><br /> var _gaq = _gaq || [];<br /> _gaq.push(['_setAccount', 'UA-20941105-1']);<br /> _gaq.push(['_trackPageview']);<br /><br /> (function() {<br /> var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;<br /> ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';<br /> var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);<br /> })();<br /><br /></script><br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhezyi-A8KgTbqEEQaY2XZ7c8w6WlhiYYzsrqSIZNfEttPJodWjcsLgJj3-mwfU39aH73SiTIzxLXlrfjgFN5dixf-U4vW5CfsX_twGJJIp-A-Dv1ha-Ln3O2dvKJ12UuInkypzFbTmxIBx/s1600/Screenshot-4.png"><img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 256px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhezyi-A8KgTbqEEQaY2XZ7c8w6WlhiYYzsrqSIZNfEttPJodWjcsLgJj3-mwfU39aH73SiTIzxLXlrfjgFN5dixf-U4vW5CfsX_twGJJIp-A-Dv1ha-Ln3O2dvKJ12UuInkypzFbTmxIBx/s320/Screenshot-4.png" alt="" id="BLOGGER_PHOTO_ID_5466534281975620786" border="0" /></a>Brice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.com0tag:blogger.com,1999:blog-1797486966924454174.post-54984238525560665062007-07-30T17:19:00.000-07:002011-01-24T02:44:48.015-08:00Maven2 SWT builds<script type="text/javascript"><br /><br /> var _gaq = _gaq || [];<br /> _gaq.push(['_setAccount', 'UA-20941105-1']);<br /> _gaq.push(['_trackPageview']);<br /><br /> (function() {<br /> var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;<br /> ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';<br /> var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);<br /> })();<br /><br /></script><br /><br /><br />Building a SWT application with maven can be tricky. The public maven repositories don't have current versions of SWT. You must download the recent version for each platform that you need to support and place them in your own repository. Since SWT needs the native libraries for your platform, you will need to package those in individual jar (or zip) files and deploy those to your local repository. I recommend giving them the same artifactId as your Java SWT libraries with a suffix like '-native'. So for three platforms you should have six artifacts like the following.<br /><pre><br />swt/swt-win32/3.2.2/swt-win32-3.2.2.jar<br />swt/swt-win32-native/3.2.2/swt-win32-native-3.2.2.jar<br />swt/swt-linux-gtk/3.2.2/swt-linux-gtk-3.2.2.jar<br />swt/swt-linux-gtk-native/3.2.2/swt-linux-gtk-native-3.2.2.jar<br />swt/swt-macosx/3.2.2/swt-macosx-3.2.2.jar<br />swt/swt-macosx-native/3.2.2/swt-macosx-native-3.2.2.jar<br /></pre><br />Now you can use profiles to determine the proper dependency and the <a href="http://maven.apache.org/plugins/maven-dependency-plugin/unpack-mojo.html" target="blank">maven-dependency-plugin</a> to unpack the proper native libraries. First create a profile for each platform that you need to support in your pom.xml file and set properties defining the proper dependencies for each platform:<br /><pre><br /><profiles><br /> <profile><br /> <id>unix</id><br /> <activation><br /> <os><br /> <family>unix</family><br /> </os><br /> </activation><br /> <properties><br /> <swt.os-specific-dep>swt-linux-gtk</swt.os-specific-dep><br /> <swt.version>3.2.2</swt.version><br /> </properties><br /> </profile><br /> <profile><br /> <id>windows</id><br /> <activation><br /> <os><br /> <family>windows</family><br /> </os><br /> </activation><br /> <properties><br /> <swt.os-specific-dep>swt-win32</swt.os-specific-dep><br /> <swt.version>3.2.2</swt.version><br /> </properties><br /> </profile><br /> <profile><br /> <id>mac</id><br /> <activation><br /> <os><br /> <family>macosx</family><br /> </os><br /> </activation><br /> <properties><br /> <swt.os-specific-dep>swt-macosx</swt.os-specific-dep><br /> <swt.version>3.2.2</swt.version><br /> </properties><br /> </profile><br /></profiles><br /></pre><br />You can reference these properties in your pom for dependencies and for the unpack goal of the <a href="http://maven.apache.org/plugins/maven-dependency-plugin/unpack-mojo.html" target="blank">maven-dependency-plugin</a>. The example below uses the <a href="http://mojo.codehaus.org/appassembler/appassembler-maven-plugin" target="blank">appassembler-maven-plugin</a> output directory to unpack the native libraries. You can use the appassembler plugin to create scripts for launching your application and add extra JVM arguments which you will need to do.<br /><pre><br /><build><br /><plugins><br /> <plugin><br /> <groupId>org.codehaus.mojo</groupId><br /> <artifactId>appassembler-maven-plugin</artifactId><br /> <executions><br /> <execution><br /> <id>assemble</id><br /> <phase>package</phase><br /> <goals><br /> <goal>assemble</goal><br /> </goals><br /> <configuration><br /> <programs><br /> <program><br /> <mainClass>esrl.gsd.fsl.enroute.client.Client</mainClass><br /> <name>enroute-client</name><br /> </program><br /> </programs><br /> <extraJvmArguments>-Djava.library.path=lib</extraJvmArguments><br /> </configuration><br /> </execution><br /> </executions><br /> </plugin><br /><br /> <plugin><br /> <groupId>org.apache.maven.plugins</groupId><br /> <artifactId>maven-dependency-plugin</artifactId><br /> <executions><br /> <execution><br /> <id>unpack</id><br /> <phase>package</phase><br /> <goals><br /> <goal>unpack</goal><br /> </goals><br /> <configuration><br /> <artifactItems><br /> <artifactItem><br /> <groupId>swt</groupId><br /> <artifactId>${swt.os-specific-dep}-native</artifactId><br /> <version>${swt.version}</version><br /> <type>jar</type><br /> <overWrite>true</overWrite><br /> <outputDirectory><br /> ${project.build.directory}/appassembler/lib<br /> </outputDirectory><br /> </artifactItem><br /> </artifactItems><br /> </configuration><br /> </execution><br /> </executions><br /> </plugin><br /></plugins><br /></build><br /></pre><br />Notice the extra JVM argument given to the appassembler plugin and the output directory for the unpack configuration.<br /><pre><br />%> mvn package<br /></pre><br />This will unpack the proper native libraries in the lib directory of the assembled application, and create shell scripts for launching the application as well as copy all the other necessary dependencies to the assembled application. See the dependency and appassembler plugins for more configuration options.Brice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.com6tag:blogger.com,1999:blog-1797486966924454174.post-12761321876029542622007-07-03T13:13:00.000-07:002011-01-24T02:44:57.189-08:00Parallel Queries with PostgresThis is a little off topic from what I usually write about but just wanted to point this out. A project called <a href="http://pgpool.projects.postgresql.org/pgpool-II/en/" target="_blank">pgpool-II</a> is a tool for Postgres that allows you to run parallel queries (among other things) in a clustered environment. They haven't released anything yet, but you can check out <a href="http://pgpool.projects.postgresql.org/" target="_blank">pgpool-I</a> to check out some of the other benefits it provides.<br /><br /><br /><script type="text/javascript"><br /><br /> var _gaq = _gaq || [];<br /> _gaq.push(['_setAccount', 'UA-20941105-1']);<br /> _gaq.push(['_trackPageview']);<br /><br /> (function() {<br /> var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;<br /> ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';<br /> var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);<br /> })();<br /><br /></script>Brice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.com0tag:blogger.com,1999:blog-1797486966924454174.post-79280768883686003622007-05-03T08:53:00.000-07:002007-05-03T09:37:06.850-07:00More Venting about AnnotationsIt's been a while since I've posted here and I've been distracted by some other projects. For example check out <a href="http://www.liftweb.net">lift</a> (formerly scala with sails), a web development framework built with scala. But now its back to working on Openlims and finding myself frustrated with Java Annotations.<br />Java Annotations lack of support for inheritance can make for lots of code duplication. This is something to be aware of when designing something around the use of Annotations. Consider this example:<br /><pre><br />public @interface Observable {<br /> String name();<br />}<br />public @interface Setable {<br /> String name();<br />}<br /></pre><br />Ideally you would like these to be of the same type, so you don't have to type 'name' twice and so you can pass the instance annotation to a method that takes a common type. <br /><pre><br />public @interface Observable {<br /> String name();<br /><br /> public @interface Setable {<br /> //name is NOT a member of Setable<br /> //Setable is not a subtype of Observable<br /> }<br />}<br />public Class MyClass {<br /> public MyClass(Observable o) {}<br />}<br />public Class MyPersonalClass extends MyClass {<br /> public MyPersonalClass(Setable s)<br /> {<br /> super(s);//won't work<br /> }<br /> @Setable(name="rodney")//name is not a member of Setable<br /> public void annotatedMethod() {<br /> }<br />}<br /></pre><br />Unfortunately this is not currently possible and forces the developer to take caution to avoid code duplication. It becomes unclear the best way to avoid this. Wrapper objects can be used, but it is not as 'elegant'. Annotations are very useful, but perhaps should not be used in place of regular objects.Brice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.com0tag:blogger.com,1999:blog-1797486966924454174.post-49144431581668657712007-01-22T07:29:00.000-08:002007-01-22T08:13:18.008-08:00Using AspectJ with AnnotationsRecently I've been exploring annotations and how to use them with AspectJ. I've ran into a few things that I found to be odd, and may be due to the fact that annotations are still new to Java. Consider this example:<br /><pre><br />/* Required.java @Required annotation */<br />import java.lang.annotation.ElementType;<br />import java.lang.annotation.Retention;<br />import java.lang.annotation.RetentionPolicy;<br />import java.lang.annotation.Target;<br /><br />@Retention(RetentionPolicy.RUNTIME)<br />@Target(ElementType.METHOD)<br />public @interface Required {<br /> String[] names();<br />}<br /></pre><br /><pre><br />/*MyClass.java*/<br />import java.util.Map;<br />public class MyClass {<br /><br /> @Required(names = {"id","label"})<br /> public static void myMethod(Map<String,String> args,Object obj)<br /> {<br /> //id and label should be guaranteed not null<br /> }<br />}<br /><br /></pre><br />The following aspect with the required pointcut will match against the method above like one would expect.<br /><pre><br />/*CheckRequired.aj*/<br />import java.util.Map;<br /><br />public aspect CheckRequired {<br /><br /> protected pointcut required(Required required) :<br /> execution(@Required * *(Map<String,String>,..))<br /> && @annotation(required);<br /><br /> before(Required required) : required(required)<br /> {<br /> Map<String,String> args = (Map)thisJoinPoint.getArguments()[0];<br /> for (String name:required.names())<br /> {<br /> if (!args.containsKey(name))<br /> throw new IllegalArgumentException(name + " is a required parameter.");<br /> }<br /> }<br />}<br /></pre><br />Instead of getting the arguments from thisJoinPoint we would like the arguments to be part of the pointcut like the following:<br /><pre><br />/*CheckRequired.aj*/<br />import java.util.Map;<br />public aspect CheckRequired {<br /> protected pointcut required(Map<String,String> args, Required required) :<br /> execution(@Required * *(Map<String,String>,*))<br /> && @annotation(required)<br /> && args(args);<br /> before(Map<String,String> args, Required required) : required(args,required)<br /> {<br /> for (String name:required.names())<br /> {<br /> if (!args.containsKey(name))<br /> throw new IllegalArgumentException(name + " is a required parameter.");<br /> }<br /> }<br />}<br /></pre><br />But when this is compiled we get the warning "advice defined in CheckRequired has not been applied". Why is this? Changing the method from static to non-static doesn't make a difference. You'll notice that MyClass.myMethod takes a second argument that is never used in this example. Removing the argument will cause the second example of CheckRequired.aj to advise that pointcut. I came across this problem with a method that actually does need a second argument so that is why I included it in this example.Brice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.com0tag:blogger.com,1999:blog-1797486966924454174.post-69683761270286792632006-12-05T13:51:00.000-08:002007-07-11T08:36:29.877-07:00Building scala from maven - maven-scala-pluginI recently wanted to build scala code in maven. There was no plugin to do this but I did find a <a target="new" href="http://jira.codehaus.org/browse/PLX-263">patch</a> for maven. I didn't like this approach because maven is supposed to be plugin based and you should be able to extend it without changing it. So I wrote a quick and dirty little plugin <a target="new" href="http://millstone.iodp.tamu.edu/~blambi/maven-scala-plugin">http://millstone.iodp.tamu.edu/~blambi/maven-scala-plugin(docs)</a>. Update 7/11/07: The maven-scala-plugin is available on <a href="http://code.google.com/p/maven-scala-plugin">Google Code</a><br /><br />At first I wanted to use the api provided by scala to compile code. I used the scala eclipse plugin as an example, and got it working. When I ran it as a maven plugin I got a strange error: "object scala not found". I assumed this meant that the package scala wasn't available, due to the fact that the scala jar files are not included on the classpath when the jvm is started by maven. So next I tried creating a new ClassLoader and force loading every class in the scala libraries, but I received the same error.<br /><br />The simple way, I decided, was to run a new jvm with the proper classpath. This is what scalac does when it runs. So I simply setup the classpath and execute 'java scala.tools.nsc.Main'. It may be a naive implementation, but it works and I'm happy I can now compile my scala code using maven.Brice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.com4tag:blogger.com,1999:blog-1797486966924454174.post-48362571753370032282006-12-01T16:04:00.000-08:002006-12-05T14:42:08.160-08:00Maven 2 Quick GuideMaven 2 is a project management tool that you can use for building, deploying, configuring, documenting, dependency management, etc. It's not just for Java, it will also build many other languages and you can build a plugin to support any language you like.<br /><br />First create a new project:<br /><pre><br /> mvn archetype:create -DgroupId=my.org -DartifactId=myapp<br /></pre><br />groupId is whatever you want, it's sort of like a package name or namespace. artifactId is probably your application name, a directory with this value will be created and your final build will be named this value.<br /><br />Inside the directory just created you will find the src dir. If you are developing a java app, put all your code under src/main/java. There will be some generated code in src/main/java and src/test/java, you can delete it or ignore it.<br /><br />The easy way to get all your external jar files into your project is to set up your own repository. It can be a local dir, or be hosted with apache. Just make a directory somewhere and start putting jar files in your repository with this command:<br /><pre><br /> mvn deploy:deploy-file -DgroupId=mailapi -DartifactId=smtp \<br /> -Dversion=1.4 -Durl=file:///path/to/repo \<br /> -Dpackaging=jar -Dfile=smtp.jar<br /></pre><br />If you have ssh keys setup, you can use scp in the url also. <br />Now you have a repository you can reference in your project. Open pom.xml and add the new repository to your project:<br /><pre><br /><repositories><br /> <repository><br /> <name>myname</name><br /> <id>someid</id><br /> <url>file:///path/to/repo</url><br /> </repository><br /></repositories><br /></pre><br />Now you can reference your jar files as dependencies in pom.xml.<br /><pre><br /><dependency><br /> <groupId>mailapi</groupId><br /> <artifactId>smtp</artifactId><br /> <version>1.4</version><br /></dependency><br /></pre><br />If you omit version, it will use the newest version available.<br />Now to you can use simple commands to build your application:<br /><pre><br /> mvn compile<br /></pre><br />Only compiles the source code.<br /><pre><br /> mvn install<br /></pre><br />Compiles and packages your project then installs it in your local repository.<br /><pre><br /> mvn war:war<br /></pre><br />Builds a war file of your application, including everything from src/main/webapp and your packaged project.<br /><br />Creating a web site for your project is really easy, you can use an xml format (xdoc), that you can embed html into, or you can use a wiki like format (apt). You need a site.xml file, put it in src/site. Put your xml or apt files in src/site/xdoc and src/site/apt repectively. Here is an example site.xml and xdoc file:<br /><pre><br /><!-- site.xml --><br /><project name="My Project"><br /> <bannerLeft><br /> <name>banner</name><br /> <src>relative/path/to/image</src><br /> <href>http://my.home.page</href><br /> </bannerLeft><br /> <body><br /> <links><br /> <item name="orglink" href="http://my.org" target="_blank"/><br /> </links><br /> <menu name="About My Project"><br /> <item name="Introduction" href="index.html"/><br /> <item name="Getting Started" href="getting-started.html"/><br /> </menu><br /> </body><br /></project><br /><br /><!-- index.xml --><br /><document><br /> <properties><br /> <title>My Project</title><br /> <sub-title>Welcome to My Project</sub-title><br /> <authors><br /> <person name="Brice Lambi" email="bricelambi@my.org"/><br /> </authors><br /> </properties><br /> <body><br /> <section name="Welcome to My Project"><br /> <p>Maven is cool!</p><br /> </section><br /> </body><br /></document><br /></pre><br />You can build your site with the command:<br /><pre><br /> mvn site:site<br /></pre><br />Check out the maven documentation for more info: <a href="http://maven.apache.org">http://maven.apache.org</a>Brice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.com0tag:blogger.com,1999:blog-1797486966924454174.post-52494914044758025362006-12-01T07:57:00.000-08:002006-12-01T15:50:32.367-08:00Using AspectJ in web applicationsAspectJ is a great tool, and can make your life a lot easier when used properly. I have found several great uses for it in web application programming. Most web applications use a database connection and share that connection in a http session. Often a programmer will need to get the connection object from the session and check to make sure it isn't null, if it is try to instantiate it and catch any errors. Instead of putting this code everywhere you use the connection just use an aspect. The following example works well with a portlet application.<br /><br /><pre><br />public aspect SanityCheck {<br /> protected pointcut render(RenderRequest req,RenderResponse res) : <br /> execution(void GenericPortlet.do*(RenderRequest,RenderResponse)<br /> && args(req,res);<br /><br /> around(RenderRequest req, RenderResponse res) : render(req,res)<br /> {<br /> Object conn = req.getPortletSession().getAttribute("conn");<br /> if (conn == null)<br /> {<br /> try{<br /> conn = new MyConnection();<br /> req.getPortletSession().setAttribute("conn",conn,PortletSession.APPLICATION_SCOPE);<br /> }catch(Exception e){<br /> //handle error<br /> }<br /> }<br /> proceed(req,res);<br /> }<br />}<br /></pre><br /><br />Now you have all this logic in one place and there is no need to be concerned with this problem elsewhere in your code. You also have a reusable pointcut for all renders, notice it is protected so any aspects that extend this aspect can also advise this pointcut.<br /><br />Managing sessions can also be a pain sometimes. You might have one class that relies on session variables, but you want those to be cleared when a user navigates away from that page. Luckily we have the negation operator in AspectJ.<br /><br /><pre><br />public aspect ClearSession {<br /><br /> private List<String> varNames = new ArrayList<String>();<br /><br /> protected pointcut display(MyPortlet portlet, RenderRequest req) :<br /> execution(void MyPortlet.do*(RenderRequest,RenderResponse))<br /> && target(portlet)<br /> && args(req,*);<br /><br /> private pointcut notDisplay(RenderRequest req) :<br /> execution (void GenericPortlet.do*(RenderRequest,RenderResponse)<br /> && !execution(void MyPortlet.do*(RenderRequest,RenderResponse))<br /> && args(req,*);<br /><br /> //only do this after returning, if it threw an exception we probably don't want to bother<br /> after(MyPortlet portlet, RenderRequest req) returning : display(portlet,req)<br /> {<br /> //assumes MyPortlet has this method to retrieve a list of variable names it uses<br /> List<String> names = portlet.getVarNames();<br /> for (String x:names)<br /> {<br /> if (!varNames.contains(x))<br /> varNames.add(x);<br /> }<br /> }<br /><br /> before(RenderRequest req) : notDisplay(req)<br /> {<br /> for (String x:varNames)<br /> req.getPortletSession().removeAttribute(x,PortletSession.APPLICATION_SCOPE);<br /> varNames.clear();<br /> }<br />}<br /></pre><br /><br />This will get a list of variables to be cleared from the portlet, and then when anything other than this portlet executes, we clear those variables.Brice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.com0tag:blogger.com,1999:blog-1797486966924454174.post-32627842927535041272006-12-01T07:31:00.000-08:002006-12-01T09:40:11.464-08:00File find in JavaThe other day I wanted to search a directory and get a list of files that match a pattern. Basically the find command. I was very annoyed that Java didn't have anything like this built into the File API. So this little method does the trick. You might use it like<br /><br /><pre><br /> findFiles(new File("/home/blambi/workspace"),<br /> File.separator,<br /> ".*\\.class");<br /></pre><br /><br />to find all the class files in /home/blambi/workspace.<br /><br /><pre><br />/**<br /> * Recursively searches a directory for filenames matching pattern and <br /> * uses delim as the file seperator.<br /> * @param dir dir to search<br /> * @param delim file seperator<br /> * @param pattern string to match against<br /> * @return a list of strings that are the filenames that match pattern<br /> */<br />List<String> findFiles(File dir, String delim, String pattern)<br />{<br /> List<String> ls = new ArrayList<String>();<br /> String[] dirs = dir.list();<br /> for (String x:dirs)<br /> {<br /> File tmp = new File(dir,x);<br /> if (tmp.isDirectory())<br /> {<br /> List<String> fs = findFiles(tmp,delim,pattern);<br /> for (String y:fs)<br /> {<br /> ls.add(x + delim + y);<br /> }<br /> }<br /> else if (x.matches(pattern))<br /> {<br /> ls.add(x);<br /> }<br /> }<br /> return ls;<br />}<br /></pre>Brice Lambihttp://www.blogger.com/profile/15471911153127383453noreply@blogger.com0