Friday, October 16, 2009

Die MacRoman, DIE!!

OSX defaults to MacRoman for an encoding, so if you're using Maven, you might see messages like:
[WARNING] Using platform encoding (MacRoman actually) to copy
filtered resources, i.e. build is platform dependent
Simple enough, but it can cause you headaches. To change to UTF-8, just add the following properties to your pom.xml:
<properties>
...
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
...
</properties>


(edit: had a couple typos, encoding in the property name needs to start with a capital E - camelCase)

Extremely useful maven placeholder based on profile

Someone just showed me this example which solves one of the annoying parts of multiple devs working with multiple resource configurations (databases, mailservers, etc) for different environments (dev,test,prod,etc)

(in project pom)
<properties>
...
<project.build.sourceEncoding>ISO-8859-1</project.build.sourceEncoding>
<log4j.priority>DEBUG</log4j.priority>
<project.build.listener-resources-context>staging-listener-resources.xml</project.build.listener-resources-context>
</properties>
<profiles>
<profile>
<id>production</id>
<properties>
<log4j.priority>INFO</log4j.priority>
<project.build.listener-resources-context>prod-listener-resources.xml</project.build.listener-resources-context>
<maven.test.skip>true</maven.test.skip>
</properties>
</profile>
</profiles>
...

Then this goes in the build section:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>

Then in your spring context, you can import just like normal, but using a placeholder now:
<import resource="${project.build.listener-resources-context}"/>

So now a normal build uses the staging resources, and something like
mvn install -Pproduction
now uses the production resources.

Thursday, October 15, 2009

Removing chars from files in a directory

Some files got generated with symbols like the 'tm' symbol and the system consuming those files can't handle those characters... so we had to remove them.

To identify them:
find . | xargs perl -ne 'print if /[\176-\512]/' | perl -ne 'print "$1\n" if /(\d+),.*/'

To remove the "special" characters:
find . -type f -print0 | xargs -0 perl -p -i -e 's/[\176-\512]//g'

quick and dirty!

Monday, October 5, 2009

Global git config

Just some quick git options when hopping on a new box:

git config --global user.name "Your Name Comes Here"
git config --global user.email you@yourdomain.example.com
git config --global color.branch auto
git config --global color.diff auto
git config --global color.interactive auto
git config --global color.status auto

Tuesday, September 22, 2009

my perl-fu is weak =/

ok, it's been a LOOOONG time since I've had to uhm, I mean the opportunity to work in perl. This will be my catchall post for useful stuff...

For example:

The special $/ which can be used to slurp an entire file at once if you don't really need to loop over the lines.

sub getfile {
my $filename = shift;
open HANDLE, "< $filename" or die "Couldn't open `$filename': $!";
local $/ = undef; # Read entire file at once
$contents = <HANDLE>; # Return file as one single `line'
close HANDLE;
return $contents;
}

Wednesday, May 27, 2009

Sometimes it's the simplest things...

I pulled a svn repository (containing a ton of projects) onto a local filesystem using git-svn and wanted to run "git gc" on each project to see how much space I could free up. (1.1GB down to 800MB btw) - sure I could do it by hand or write a quick script, but how about one line on the CL?
for d in `ls -D`; do cd $d; git gc; cd ..; done
(Those are backticks by the way)

Tuesday, May 26, 2009

Installing RoR on Ubuntu 9.04

Mainly because I always forget... here's the quickest way to get Rails installed on a fresh Ubuntu box.
First, grab everything you need to install the native gems. Then most of the ruby stuff and mysql (it will ask you to choose a root password - don't forget what you pick)
sudo apt-get install build-essential
sudo apt-get install ruby ri rdoc mysql-server libmysql-ruby ruby1.8-dev irb1.8 libdbd-mysql-perl libdbi-perl libmysql-ruby1.8 libmysqlclient15off libnet-daemon-perl libplrpc-perl libreadline-ruby1.8 libruby1.8 mysql-client-5.0 mysql-common mysql-server-5.0 rdoc1.8 ri1.8 ruby1.8 irb libopenssl-ruby libopenssl-ruby1.8 libhtml-template-perl mysql-server-core-5.0 libmysqlclient-dev
Next, grab the latest rubygem from Rubyforge (http://rubyforge.org/projects/rubygems/)
wget http://rubyforge.org/frs/download.php/56227/rubygems-1.3.3.tgz
tar -zxvf rubygems-1.3.3.tgz
cd rubygems-1.3.3
sudo ruby setup.rb
At this point, do:
gem -v
Since this is a fresh install, you'll probably see something like:
slotito@ubuntu-desktop:~/rubygems-1.3.3$ gem -v
The program 'gem' can be found in the following packages:
* rubygems1.8
* rubygems1.9
Try: sudo apt-get install
bash: gem: command not found
If you do, create some symlinks:
sudo ln -s /usr/bin/gem1.8 /usr/local/bin/gem
sudo ln -s /usr/bin/ruby1.8 /usr/local/bin/ruby
sudo ln -s /usr/bin/rdoc1.8 /usr/local/bin/rdoc
sudo ln -s /usr/bin/ri1.8 /usr/local/bin/ri
sudo ln -s /usr/bin/irb1.8 /usr/local/bin/irb
Ok, now for rails and some goodies =)
sudo gem install rails
sudo gem install mysql
sudo apt-get install imagemagick
sudo gem install rmagick
sudo gem install mongrel

Wednesday, May 6, 2009

Installing AIR on Ubuntu 9.04

Go to http://get.adobe.com/air and download the bin file.
cd to whatever directory you downloaded the file into.
chmod 755 AdobeAIRInstaller.bin
sudo ./AdobeAIRInstaller.bin
It will pop up an installer program, click 'Accept', wait for it to finish and click 'Finish"
You're done, easy.

Getting gitjour working with Ubuntu

So... I'm attending railsconf2009 and EVERYONE is abuzz about gitjour so I figured I'd take a look at it. Only problem, it doesn't install out of the box on Ubunutu, you need to add some libs.
me@laptop:~$ sudo gem install dnssd gitjour
Building native extensions. This could take a while...
ERROR: Error installing dnssd:
ERROR: Failed to build gem native extension.

/usr/bin/ruby1.8 extconf.rb install dnssd gitjour
checking for DNSServiceRefSockFD() in -lmdns... no
checking for DNSServiceRefSockFD() in -ldns_sd... no
can't find rendezvous library
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of
necessary libraries and/or headers.
I couldn't find any resources on how to fix it, so here it goes:
me@laptop:~$ sudo apt-get install avahi-daemon avahi-utils libavahi-compat-libdnssd1 libavahi-compat-libdnssd-dev
me@laptop:~$ sudo gem install dnssd gitjour
Super easy, but couldn't find the solution anywhere... enjoy!

RailsConf 2009

Monday, April 6, 2009

Just reading up on BDD

I'm really starting to warm up to the idea of behavior driven development, but I'm not sure how it will work in practice. It supports the idea of YAGNI nicely (you ain't gonna need it) through the emphasis on building the simplest thing that passes the test or scenario.

Wish I had a simpler real-world project to test drive but the one I'm working on right now is in Flex... not that it's not possible to use rspec and FunFX, just that I'd like something more basic to learn on. Here's another FunFX link though http://peternic.blogspot.com/

If you're interested in rspec, there's a Pragmatic Programmer's book on it http://www.pragprog.com/titles/achbd/the-rspec-book - it's still in beta right now, but they'll sell you the pdf. (pragprog: an affiliate program would be nice, hint)

Wednesday, March 25, 2009

What's the difference between those who can't read and those who don't?

ANSWER: nothing

Saw that in a /. comment today, but have to admit I got a chuckle out of it.

In the same vein, I think I'm falling in love with LMGTFY.

Tuesday, March 3, 2009

Flex vs. Web Services (pt.2) SOAP faults

Today's fun with Flex came about because one of our developers was trying to figure out how to handle exceptions from a simple user authentication web service.

Basically, there's a few exceptions that can be thrown from the Java code (but this post applies to ANYONE using SOAP faults and flex). Those exceptions end up being wrapped in SOAP faults automatically (as defined in the WSDL).

The Flex Builder web service import wizard successfully builds the Actionscript classes and they seem fine.

Unfortunately, when you send a request that would result in an exception being thrown, you won't see your exception. Instead you'll be able to drill down in the Fault to the following somewhat cryptic message:

Error #2032: Stream Error.

What we discovered is that Flex can't see the body when ANY HTTP status code other than 200 is returned. (There's debate on that - see one of the Flex bugs) The "official" suggested workaround seems to be bastardize your web service so that it returns a 200 status code with a SOAP fault instead of the normal 500 status code. (This also implies you won't be able to use other published web services like Amazon's because they follow the standard with a status code of 500)

Anyhow, assuming you want to hack up your server running the web service and are using Java (we're using CXF, but this is applicable for a servlet container).

1) Create a class extending HttpServletResponseWrapper
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.apache.log4j.Logger;

public class FlexSoapFaultServletResponseWrapper extends HttpServletResponseWrapper {

static final Logger logger = Logger.getLogger(FlexSoapFaultServletResponseWrapper.class);

public FlexSoapFaultServletResponseWrapper(HttpServletResponse response) {
super(response);
}

@Override
public void setStatus(int statusCode) {
if (statusCode == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
super.setStatus(HttpServletResponse.SC_OK);
logger.debug("Converted status code 500 -> 200");
}
}

}
2) Create a servlet filter using that wrapper
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;

public class FlexSoapFaultHandlingHackFilter implements Filter {

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
FlexSoapFaultServletResponseWrapper wrapper = new FlexSoapFaultServletResponseWrapper(httpServletResponse);
chain.doFilter(request, wrapper);
}

@Override
public void init(FilterConfig config) throws ServletException {
}

@Override
public void destroy() {
}
}
3) Define the filter in your WEB-INF/web.xml file and associate it with your CXFServlet
...
<filter>
<filter-name>FlexSoapFaultHandlingHackFilter</filter-name>
<filter-class>com.donbest.services.FlexSoapFaultHandlingHackFilter</filter-class>
</filter>
...
<filter-mapping>
<filter-name>FlexSoapFaultHandlingHackFilter</filter-name>
<servlet-name>CXFServlet</servlet-name>
</filter-mapping>
...
<servlet>
<servlet-name>CXFServlet</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
...

That's it, now any SOAP fault will be translated to a HTTP status 200 and Flex will be able to handle it. (oh, this might have the much unwanted side-effect of masking any TRUE 500 errors)

Flex vs. Web Services (pt.1) enums

Yes, the title is Flex VERSUS web services (SOAP at least), since we just wasted far too much time trying to figure out some of the quirks.

1) Define a web services including something with an enum (I'm using Java, you use whatever you want)
2) Deploy your webservice. Your WSDL will contain something similar to:
<xs:simpleType name="fooEnum">
<xs:restriction base="xs:string">
<xs:enumeration value="GOOD"/>
<xs:enumeration value="BAD"/>
<xs:enumeration value="UGLY"/>
</xs:restriction>
</xs:simpleType>
3) Generate your actionscript classes from the WSDL. You'll end up with a fooEnum.as file containing
package generated.webservices
{
import mx.utils.ObjectProxy;
import flash.utils.ByteArray;
import mx.rpc.soap.types.*;
/**
* Wrapper class for a operation required type
*/

public class FooEnum
{
/**
* Constructor, initializes the type class
*/
public function FooEnum() {}

[Inspectable(category="Generated values", enumeration="GOOD,BAD,UGLY", type="String")]
public var fooEnum:String;public function toString():String
{
return fooEnum.toString();
}

}
}

4) Try to use the web service. Everything will appear to be fine, EXCEPT that the enum will ALWAYS be null.
If you trace the XML sent down from the server (even using Wireshark), it all looks good.
If you look at the action script classes, they look fine. Eventually you'll find http://bugs.adobe.com/jira/browse/SDK-14800 and some related bugs. Basically, it appears Flex doesn't handle the enums yet (as of SDK 3.x) and the easiest thing to do is change all your enums to Strings =(
Hope you didn't already have people using that web service...

Wednesday, February 18, 2009

rspec/cucumber

Time to catch up on some reading...

Behavior driven development (BDD) with rspec and cucumber - don't know much about it yet but PragProg has a new book in beta The RSpec Book and a link to getting that working with JRuby and Java http://wiki.github.com/aslakhellesoy/cucumber/jruby-and-java

Monday, February 9, 2009

Maven2 - BlazeDS, ActiveMQ and Flex (part2)

Assuming you followed along for "Maven2 - BlazeDS, ActiveMQ and Flex project structure"...

Go into your parent project and create the web module.
mvn archetype:create -DgroupId=mycompany -DartifactId=myproject-web -DarchetypeArtifactId=maven-archetype-webapp
That just created a new module under the parent called "myproject-web". Now edit the pom.xml in that newly created project and add in the dependencies. I'm using Spring and Hibernate at the moment.
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.6.ga</version>
</dependency>

Also fix the compiler so that it compiles to 1.6 (1.5 if you prefer).
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>

Ok, it should build at this point. Add your spring context (exercise to the reader) to the src/main/webapp/WEB-INF folder.
Edit the web.xml file so that the webapp knows to load your context.

Next up, a configuration module. Since we're using BlazeDS, there are a couple files that need to be compiled into the SWF as well as present in the webapp. Having them in two places and somehow remembering to keep them in sync is a pretty clear DRY violation. To get around that, we're going to put them in a module all on their own and then add that to the appropriate modules.
mvn archetype:create -DgroupId=mycompany -DartifactId=myapplication-config
We don't need the src/test or src/main/java directories, so delete them and create a src/main/resources directory.
Inside the src/main/resources directory, create the services-config.xml file we'll need. This sets up the channels we'll need for our AMF communication.
<?xml version="1.0" encoding="UTF-8"?>
<services-config>

<services>
<service-include file-path="messaging-config.xml" />
</services>

<security />

<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
</properties>
</channel-definition>
<channel-definition id="my-streaming-amf" class="mx.messaging.channels.StreamingAMFChannel">
<endpoint url="/messagebroker/streamingamf" class="flex.messaging.endpoints.StreamingAMFEndpoint"/>
<properties>
<idle-timeout-minutes>0</idle-timeout-minutes>
</properties>
</channel-definition>

<channel-definition id="my-secure-amf" class="mx.messaging.channels.SecureAMFChannel">
<endpoint uri="https://{server.name}:9100/{context.root}/messagebroker/amfsecure" class="flex.messaging.endpoints.SecureAMFEndpoint"/>
</channel-definition>

<channel-definition id="my-polling-amf" class="mx.messaging.channels.AMFChannel">
<endpoint uri="http://{server.name}:{server.port}/{context.root}/messagebroker/amfpolling" class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>true</polling-enabled>
<polling-interval-seconds>8</polling-interval-seconds>
</properties>
</channel-definition>

<channel-definition id="my-rtmp" class="mx.messaging.channels.RTMPChannel">
<endpoint uri="rtmp://{server.name}:2039" class="flex.messaging.endpoints.RTMPEndpoint"/>
<properties>
<idle-timeout-minutes>20</idle-timeout-minutes>
<client-to-server-maxbps>100K</client-to-server-maxbps>
<server-to-client-maxbps>100K</server-to-client-maxbps>
</properties>
</channel-definition>

<channel-definition id="my-http" class="mx.messaging.channels.HTTPChannel">
<endpoint uri="http://{server.name}:{server.port}/{context.root}/messagebroker/http" class="flex.messaging.endpoints.HTTPEndpoint"/>
</channel-definition>

<channel-definition id="my-secure-http" class="mx.messaging.channels.SecureHTTPChannel">
<endpoint uri="https://{server.name}:9100/{context.root}/messagebroker/httpsecure" class="flex.messaging.endpoints.SecureHTTPEndpoint"/>
</channel-definition>
</channels>

<logging>
<target class="flex.messaging.log.ConsoleTarget" level="Debug">
<properties>
<prefix>[Flex] </prefix>
<includeDate>true</includeDate>
<includeTime>true</includeTime>
<includeLevel>true</includeLevel>
<includeCategory>true</includeCategory>
</properties>
<filters>
<pattern>Endpoint.*</pattern>
<pattern>Service.*</pattern>
<pattern>Configuration</pattern>
</filters>
</target>
</logging>

<system>
<redeploy>
<enabled>true</enabled>
<watch-interval>20</watch-interval>
<watch-file>{context.root}/WEB-INF/flex/services-config.xml</watch-file>
<watch-file>{context.root}/WEB-INF/flex/messaging-config.xml</watch-file>
<touch-file>{context.root}/WEB-INF/web.xml</touch-file>
</redeploy>
</system>

</services-config>

Next, add the messaging-config.xml file.
<?xml version="1.0" encoding="UTF-8"?>
<service id="message-service" class="flex.messaging.services.MessageService">
<adapters>
<adapter-definition id="actionscript"
class="flex.messaging.services.messaging.adapters.ActionScriptAdapter"
default="true" />
<adapter-definition id="jms"
class="flex.messaging.services.messaging.adapters.JMSAdapter" />
</adapters>

<destination id="message-destination">
<properties>
<jms>
<destination-type>Topic</destination-type>
<message-type>javax.jms.ObjectMessage
</message-type>
<connection-factory>
java:comp/env/jms/flex/ActiveMqConnectionFactory
</connection-factory>
<destination-jndi-name>java:comp/env/jms/myTopic
</destination-jndi-name>
<delivery-mode>NON_PERSISTENT</delivery-mode>
<message-priority>DEFAULT_PRIORITY
</message-priority>
<acknowledge-mode>AUTO_ACKNOWLEDGE
</acknowledge-mode>
<initial-context-environment>
<property>
<name>Context.INITIAL_CONTEXT_FACTORY
</name>
<value>org.apache.activemq.jndi.ActiveMQInitialContextFactory
</value>
</property>
<property>
<name>Context.PROVIDER_URL</name>
<value>tcp://mycompanyurl:61616
</value>
</property>
</initial-context-environment>
</jms>
</properties>
<channels>
<channel ref="my-streaming-amf" />
</channels>
<adapter ref="jms" />
</destination>
</service>

Next up, we need to edit the myproject-config/pom.xml so that these resources get bundled up into a ZIP file (so we can more easily distribute them). Make a myproject-config/src/main/assembly directory then add a file called resources.xml and copy the following into it:
<assembly>
<id>resources</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/resources</directory>
<outputDirectory></outputDirectory>
</fileSet>
</fileSets>
</assembly>

And change the myproject-config/pom.xml so that those resources get built. Get rid of the dependencies element since we don't need it. Add a build execution as follows.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make shared resources</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptors>
<descriptor>src/main/assembly/resources.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>


Config module is done (use mvn install to test it, you should get a zip file containing the 2 resources) and now we need to get it into the webapp and Flex modules.
Edit the myproject-web/pom.xml file and add the dependency:
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>myproject-config</artifactId>
<version>1.0-SNAPSHOT</version>
<classifier>resources</classifier>
<type>zip</type>
<scope>provided</scope>
</dependency>

And the code to unpack the resource zip:
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-config</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-resources</phase>
<configuration>
<outputDirectory>${project.build.directory}/${project.build.finalName}/WEB-INF/flex</outputDirectory>
<includeArtifacIds>todolist-config</includeArtifacIds>
<includeGroupIds>${project.groupId}</includeGroupIds>
<includeClassifiers>resources</includeClassifiers>
<excludeTransitive>true</excludeTransitive>
<excludeTypes>jar,swf</excludeTypes>
</configuration>
</execution>
</executions>
</plugin>


Also into the plugins section you should put the flex-compiler-mojo:
<plugin>
<groupId>info.rvin.mojo</groupId>
<artifactId>flex-compiler-mojo</artifactId>
<extensions>true</extensions>
</plugin>


Since our Flex RIA is also using the config module, edit the myproject-ria/pom.xml and add the dependency just like above:
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>myproject-config</artifactId>
<version>1.0-SNAPSHOT</version>
<classifier>resources</classifier>
<type>zip</type>
<scope>provided</scope>
</dependency>

The plugin configuration is similar to the webapp one, but it has to change just slightly so that the config resources get extracted to a directory to get slurped up by the Flex mojo plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-config</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<phase>generate-resources</phase>
<configuration><outputDirectory>${project.build.directory}/generated-resources</outputDirectory>
<includeArtifacIds>myproject-config</includeArtifacIds>
<includeGroupIds>${project.groupId}</includeGroupIds>
<excludeTransitive>true</excludeTransitive>
</configuration>
</execution>
</executions>
</plugin>

Almost there, now we need to make sure the Flex swf file gets bundled into the web-app, so edit the myproject-web/pom.xml and add it as a dependency (watch out for the <type>swf</type>)
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>myproject-ria</artifactId>
<version>1.0-SNAPSHOT</version>
<type>swf</type>
</dependency>

And the code to unpack it (also in myproject-web/pom.xml):
<execution>
<id>copy-swf</id>
<phase>process-classes</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/${project.build.finalName}</outputDirectory>
<includeTypes>swf</includeTypes>
</configuration>
</execution>

Maven2 - BlazeDS, ActiveMQ and Flex project structure

(this is mostly the same as the great article from Sebastien Arbogast, I'm adapting it slightly to our needs and will focus more on the Eclipse/debug integration later on, but you should really read his post)

First up, getting the Maven project laid out!
mvn archetype:create -DgroupId=com.mycompany -DartifactId=myproject
I don't know of any way to generate the parent pom directly, so it's a couple quick steps to clean this up.
2) Go into the "myproject" directory and delete the src folder
3) Edit the pom.xml file and change the packaging element to "pom" instead of "jar" (while you're at it, go ahead and change the JUnit version to 4.5, might as well stay current)

Now that we've got the parent pom set up, let's start adding subprojects. We're going to need a Flex module, you have your choice of a couple projects, but the most promising one seems to be flex-mojos
First a quick stop back at our parent pom.xml file, add in the following before the dependencies element:
<repositories>
<repository>
<id>flex-mojos-repository</id>
<url>http://flex-mojos.googlecode.com/svn/trunk/repository/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>flex-mojos-repository</id>
<url>http://flex-mojos.googlecode.com/svn/trunk/repository/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
Then after the dependencies section add in:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2-beta-3</version>
</plugin>
</plugins>
</pluginManagement>
</build>


NOW create the Flex project... (this is all on ONE line)
mvn archetype:create -DarchetypeArtifactId=maven-archetype-flex -DarchetypeVersion=1.0 -DarchetypeGroupId=dk.jacobve.maven.archetypes -DgroupId=mycompany -DartifactId=myproject-ria -DpackageName=com.donbest
If you open up the pom.xml in the parent folder, we should see a new "modules" section
<modules>
<module>mycompany-ria</module>
</modules>

You should also now see another folder in this directory called "myproject-ria". We've got to make a couple quick changes here too... first remove the "properties" element since we're not going to use the "israfil" plugin. Next remove the plugin section itself and replace it with
<plugin>
<groupId>info.rvin.mojo</groupId>
<artifactId>flex-compiler-mojo</artifactId>
<version>1.0-beta7</version>
<extensions>true</extensions>
<configuration>
<locales>
<param>en_US</param>
</locales>
</configuration>
</plugin>

As the last step in the myproject-ria pom.xml, add the Flex dependencies...
<dependencies>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>playerglobal</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>swc</type>
<scope>external</scope>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>flex</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>swc</type>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>framework</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>swc</type>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>framework</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>resource-bundle</type>
<classifier>en_US</classifier>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>rpc</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>swc</type>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>rpc</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>resource-bundle</type>
<classifier>en_US</classifier>
</dependency>
<dependency>
<groupId>com.adobe.flex.sdk</groupId>
<artifactId>utilities</artifactId>
<version>3.0.0.3.0.0.477</version>
<type>swc</type>
</dependency>
</dependencies>

Whew, that was rough! But now you should be able to go to the parent folder and run
mvn install

Thursday, February 5, 2009

Jetty and double-slashes in the URL

Hit a really brief snag today with Jetty and Flex...

Basically, in order to make your Flex app portable for different environments, you want to use tokens in the server url
e.g. this snippet from a Flex services-config.xml

<channels>
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="flex.messaging.endpoints.AMFEndpoint">
</channel-definition>
...
</channels>

See the {context.root} part?
If you have this project generate foo.war and deploy it to a server, you would access it through a URL like http://myhost/foo/
So,
server.name=foo
server.port=80
context.root=foo

The Flex client will connect to: http://myhost/foo/messagebroker/amf

Now suppose you want to deploy as the root webapp instead... URL becomes http://myhost/
So,
server.name=foo
server.port=80
context.root=<empty>

The Flex client tries connecting to http://myhost//messagebroker/amf (note the double slash because the context root is actually blank).

This actually works fine in Tomcat and IIS, but Jetty takes a different interpretation of the double slash and throws up a 404.
HTTP ERROR: 404
NOT_FOUND
RequestURI=//messagebroker/amf
Powered by Jetty://

Luckily there's an easy fix if you're using Maven and the jetty-maven-plugin like I am... just edit the 'pom.xml' and add <compactPath>true</compactPath>

<plugins>
...
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>80</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
<webAppConfig>
<contextPath>/</contextPath>
<compactPath>true</compactPath>
</webAppConfig>
</configuration>
</plugin>
...
</plugins>

Sunday, February 1, 2009

long time no blog

Been a few weeks since last post, but I haven't been sitting idle. Got a very simple proof-of-concept going with real-time scores using JMS messaging to a BlazeDS server streaming to an Adobe Flex client (this is all overlaying a legacy system). Pretty exciting stuff and remarkably simple. It's all bleeding edge at this point - going to have to write a something like the guys over at Farata Systems did (NIO connector so we can support enough customers on BlazeDS).

Friday, January 9, 2009

Getting Started with Git on Ubuntu

I'm going to be taking a look at Git and how well/if it will fit in with an existing Subversion repository, so first thing to do is get it installed (I'm using Ubuntu 8.04 so your mileage may vary, but I followed the exact same steps on my laptop with 8.10)

First off - DON'T use the version in the Ubuntu repositories, it's out of date - but this is still super easy.

Grab the latest source tarball from the Git site. (was 1.6.1 at the time of writing this)

Open up a terminal.
$ sudo apt-get build-dep git-core git-doc
Go into the directory where you saved the git tarball.
$ tar -zxvf git-1.6.1.tar.gz
$ cd git-1.6.1/
$ sudo make prefix=/usr all doc

(Note: I'm installing it for all users, you could leave the 'prefix=/usr' part out to only install it for yourself. Also, this step will take a while...)
$ sudo make prefix=/usr install install-doc
$ git --version

Assuming all went well, you should see something like:
git version 1.6.1

Tuesday, January 6, 2009

Apple doesn't want iPhone developers?

Spent a little time over the holiday break thinking about potentially profitable applications for the iPhone... Finally came up with an idea I'm a little excited about. Ok, so it's probably not profitable, but at least it would be fun to write. Unfortunately, I hit a roadblock. I registered for the Apple Developer Connection to get the SDK to get started and couldn't find one for Linux.

A quick email to Apple returned:

"Thank you for contacting the Apple Developer Connection regarding the iPhone Developer Program.

The iPhone SDK requires an Intel processor-based Mac running Mac OS X Leopard. Please know that Apple does not offer a version of the iPhone SDK for Windows operating systems and we have not announced any plans to do so."

Have to admit I'm a little miffed about that - I'd love to have a nice Mac but if I don't have one by now, I'm sure not buying one just to develop an iPhone app. This seems a bit shortsighted on Apple's part. Oh well, Android seems pretty interesting too... hopefully there are some new Android-based phone offerings soon. In the meantime, here's a link to a recently published title from my favorite series:

Hello, Android

[edit: after thinking it over some more, it's pretty smart, albeit distasteful. The amount of sales that they're generating on the iPhone creates a large target for developers, who of course have no choice but to buy a Mac to develop an app for that market - a double win for Apple]