Where he blogs about his eclipse musings
experiences
Run JRuby From Within A Jar And Package Your Own Gems Along
Apr 10th
Jruby-in a jar already bundles rspec and rake, so the goal was to find out where it gets packaged.
Download the jruby source zip, extract it and open the build.xml file, search for “rspec” (there’s two occurences) and you’ll find that it’s passed in as an argument to the gem installer, add in another line with “cucumber”:
<target name="install-gems">
<property name="jruby.home" value="${basedir}"/>
<java classname="org.jruby.Main" fork="true" maxmemory="${jruby.launch.memory}" failonerror="true">
<classpath refid="build.classpath"/>
<classpath path="${jruby.classes.dir}"/>
<sysproperty key="jruby.home" value="${jruby.home}"/>
<arg value="--command"/>
<arg value="maybe_install_gems"/>
<arg value="rspec"/>
<arg value="rake"/>
<arg value="cucumber"/> <!-- add cucumber -->
<arg value="--env-shebang"/>
</java>
</target>
Then run ant:
$ ant jar-complete
To verify that everything is fine:
$ java -jar lib/jruby-complete.jar -S gem list *** LOCAL GEMS *** builder (2.1.2) cucumber (0.2.3) diff-lcs (1.1.2) polyglot (0.2.5) rake (0.8.4) rspec (1.2.2) sources (0.0.1) term-ansicolor (1.0.3) treetop (1.2.5)
Great we’ve now managed to package jruby-in-a-jar with some additional gems. Now to run cucumber on jruby in eclipse.
Eclipse and TDD
Oct 13th
It’s been after quite a while that I’m back to doing eclipse plugins. In this span of time I learnt some good techniques — TDD. It’s a really good safety check to know that your code is in a working state.
Test-first-development or TDD as it is called is where you generally write tests first and then write code that makes these tests pass. This in some ways ensures that you write clean APIs that are simple to test, use and understand. The APIs don’t really do more that what is needed for the job. So far so good.
The fun began when I moved back to doing eclipse. I now realize that it is difficult to do TDD when I’m writing code that implements (or extends) some third party interfaces. These implementations return some more interfaces, which only compounds to the problem of test-first-development.
The way I write eclipse plugins is create a null implementation that does nothing at all. It merely prints method names of the methods being called on to the console. I then start off eclipse (in debug mode), understand the sequence in which the interface is invoked, and what eclipse does with the return values (that’s walking though a lot of code, BTW), I also sometimes need to look at similar implementation to understand what they do. I then gradually implement those methods in a way that what eclipse expects. This gives another meaning to BDD — breakpoint driven development.
This process is what most developers would probably do when using a framework for the first time. Most frameworks (unlike the eclipse frameworks) are much simpler than eclipse is. There are some known interfaces that are exposed. Since there are very few interfaces to implement, developers gradually get to know them very well. As the expectation out of these interfaces is known well, gradual use of these interfaces, makes it easy to write tests for the interface before implementing the interface itself, which means that developers can move from BDD to TDD.
This process is a quick (and IMO, dirty) way of writing code. The initial code is not written with testability in mind, writing unit tests at the end is quite difficult, and testability can be achieved after quite some bit of refactoring to allow make the code testable.
An example of this is a typical java webapp that consists of some servlets, and jsps containing tag-libs etc. There are very few known interfaces that the clients implement (the HttpServlet, Filter, FilterChain etc.) Since the webapp framework itself is very compact, a framework to unit test the implementations of interfaces exposed by the servlet spec has evolved over a period of time. Cactus is one such example:
Cactus is a simple test framework for unit testing server-side java code (Servlets, EJBs, Tag Libs, Filters, …) [...] It uses JUnit and extends it.
To test a servlet one would write a test as:
public class TestSampleServlet extends ServletTestCase {
public void testThatServletPrintsHelloWorld(){
Servlet servlet = new HelloWorldServlet();
...
session.setAttribute("name", "bob");
...
assertTrue("hello, bob", servlet.printHelloWorld());
}
}
It is mind boggling to imagine a framework similar to cactus to test the thousands of interfaces exposed by a framework like eclipse, and we are still not talking about the other, “smaller” projects under the eclipse umbrella.
Given this scenario, how do you do test-first-development ?
Source-opening of open-source ?
Sep 5th
zx recently pointed out to this interesting, source-opening of a very popular eclipse based web development tools, Aptana.
Could this be a case of an over zealous new management person who’s at worst ignored his/her devs’ inputs.
So what does this mean to me as an eclipse developer ?
This change in the licence terms mean that in case I build plugins on top of Aptana, then I need to ask for a licence from Aptana because “The new license just gives us[Aptana] more visibility into who is redistributing the IDE. We[Aptana] want to keep a very high level of quality, therefore we[Aptana] want to be aware of any redistribution of the IDE, and we[Aptana] ask that if you are going to redistribute, you contact us for a license (free or otherwise).”
The license faq makes for an interesting read.
So what are the implications of this going forward?
It may mean that the next time I contribute something back to any open source project, I may need to look up as to what may become of my contribution. I contributed my work under EPL, but since the copyright belongs to the company maintaining the project, the company may just decide to change the license terms altogether.
I may also want to investigate who makes such decisions in the company. Is it an aliance of contributors from various companies (like OSGi); is it a foundation like Apache, or Eclipse, or Gentoo that has representatives from a lot of other companies which may put their foot down on such issues ?
Flash Player on 64-bit FireFox on Linux
Aug 1st
Here’s how to get your firefox running on a 64 bit Linux machine to work with 32 bit plugins. I’ve tried this with gentoo, it should work with ubuntu or kubuntu or any other distro of your choice.
Download and install nspluginwrapper from http://gwenole.beauchesne.info/projects/nspluginwrapper/.
For ubuntu, there are some instructions on how to do this here (http://ubuntuforums.org/showthread.php?t=341727). On gentoo I had to
#emerge nspluginwrapper
Download the flash installer from the website here: http://fpdownload.macromedia.com/get/flashplayer/current/install_flash_player_9_linux.tar.gz. Untar the installer:
$ tar -zxvf install_flash_player_9_linux.tar.gz
Step into the installer directory:
cd install_flash_player_9_linux
Edit the installer file:
vi flashplayer-installer #use nano or gedit if you like
Navigate to line 47 (this inside the function exit_cpu) and comment out the line that says exit 1.
# the architecture is not supported
exit_cpu () {
echo ""
echo "ERROR: Your architecture, \'$1\', is not supported by the"
echo " $PRODUCT installer."
echo ""
#exit 1 # this was line 47
}
Install the flash plugin, I used the default paths, you can choose the paths according to what you have on your computer (I used /home/ketan/.mozilla)
./flashplayer-installer
Now verify that the plugin actually got installed in the directory that you specified
$ ls /home/ketan/.mozilla/plugins
Use nspluginwrapper to create a 64-bit wrapper around this 32 bit plugin. (thanks to Lance for a typo update)
$ nspluginwrapper -i /home/ketan/.mozilla/plugins/libflashplayer.so
Now restart firefox, that should get you to run firefox with flash player.
Composite Logger for Ant
Apr 4th
When doing long builds in CruiseControl, ant build logs are generally logged using the XmlLogger.
In certain cases, say for example when the build is taking longer than usual, and needs to be killed/stopped, the XmlLogger does not flush contents to disk.
It would be nice if there is some sort of a composite logger than can chain any logger along with the XmlLogger.
I just happened to write one sometime today. This is the source code for a CompositeLogger that logs to a default logger, and the XmlLogger. This needs the environment variable ANT_LOG_PREFIX to be set (there’s no better way I can think of)
package com.thoughtworks.ant.logger;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildLogger;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.XmlLogger;
/**
* @author Ketan Padegaonkar
*/
public class CompositeAntLogger implements BuildLogger {
private ArrayList loggers;
private String logPrefix;
private PrintStream out;
public CompositeAntLogger() {
checkLogPrefix();
createLoggers();
}
public void setEmacsMode(boolean emacsMode) {
for (Iterator iterator = loggers.iterator(); iterator.hasNext();) {
BuildLogger logger = (BuildLogger) iterator.next();
logger.setEmacsMode(emacsMode);
}
}
public void buildFinished(BuildEvent event) {
for (Iterator iterator = loggers.iterator(); iterator.hasNext();) {
BuildLogger logger = (BuildLogger) iterator.next();
logger.buildFinished(event);
}
}
public void buildStarted(BuildEvent event) {
for (Iterator iterator = loggers.iterator(); iterator.hasNext();) {
BuildLogger logger = (BuildLogger) iterator.next();
logger.buildStarted(event);
}
}
public void messageLogged(BuildEvent event) {
for (Iterator iterator = loggers.iterator(); iterator.hasNext();) {
BuildLogger logger = (BuildLogger) iterator.next();
logger.messageLogged(event);
}
}
public void targetFinished(BuildEvent event) {
for (Iterator iterator = loggers.iterator(); iterator.hasNext();) {
BuildLogger logger = (BuildLogger) iterator.next();
logger.targetFinished(event);
}
}
public void targetStarted(BuildEvent event) {
for (Iterator iterator = loggers.iterator(); iterator.hasNext();) {
BuildLogger logger = (BuildLogger) iterator.next();
logger.targetStarted(event);
}
}
public void taskFinished(BuildEvent event) {
for (Iterator iterator = loggers.iterator(); iterator.hasNext();) {
BuildLogger logger = (BuildLogger) iterator.next();
logger.taskFinished(event);
}
}
public void taskStarted(BuildEvent event) {
for (Iterator iterator = loggers.iterator(); iterator.hasNext();) {
BuildLogger logger = (BuildLogger) iterator.next();
logger.taskStarted(event);
}
}
public void setMessageOutputLevel(int level) {
for (Iterator iterator = loggers.iterator(); iterator.hasNext();) {
BuildLogger logger = (BuildLogger) iterator.next();
logger.setMessageOutputLevel(level);
}
}
public void setOutputPrintStream(PrintStream output) {
// do nothing
}
public void setErrorPrintStream(PrintStream err) {
// do nothing
}
private void createLoggers() {
loggers = new ArrayList();
try {
loggers.add(createDefaultLogger());
loggers.add(createXmlLogger());
} catch (FileNotFoundException e) {
throw new BuildException("The loggers could not open the file", e);
}
}
private void checkLogPrefix() {
logPrefix = System.getenv("ANT_LOG_PREFIX");
if (logPrefix == null || logPrefix.trim().length() == 0)
throw new BuildException("You need to set the environment variable ANT_LOG_PREFIX.");
}
private DefaultLogger createDefaultLogger() throws FileNotFoundException {
DefaultLogger logger = new DefaultLogger();
out = new PrintStream(new FileOutputStream(logPrefix + ".txt"));
logger.setOutputPrintStream(out);
logger.setErrorPrintStream(out);
return logger;
}
private XmlLogger createXmlLogger() throws FileNotFoundException {
XmlLogger logger = new XmlLogger();
out = new PrintStream(new FileOutputStream(logPrefix + ".xml"));
logger.setOutputPrintStream(out);
logger.setErrorPrintStream(out);
return logger;
}
}
Lotus Notes beta 8, and Eclipse
Mar 23rd
I saw this post by ZX on Lotus Notes 8 based on the Eclipse platform.
ZX says:
…
In the end, this is great news for Eclipse which now gets to interact with a whole new set of users and developers.Eclipse (OSGi) on clients… devices… servers… , what’s next?
…
OSGi is great on my desktop, laptop, servers, even mobile phones. But lotus notes based on OSGi ? I’m a die hard Eclipse fan and developer. But when it comes to usability of the software that I have to use everyday I’m a tough guy to convince.
For another potshot at this installer (for now) it is about 673 MB, is packaged as a .tar (Anyone at IBM lotus notes packaging team heard about something called as gzip/bzip2?) The documentation (which is about a total of 3MB) comes as a separate download. While it is a good idea to package the documentation separately I wonder if the installer comes with some documentation.
I just hope that I do not have to go through all these pains and musings and some more pains to install Lotus Notes 8. Especially after downloading something that is 673 MB.
SVNAnt: Using ant with SVN
Mar 13th
While working on some ant build scripts today. A lot of custom batch files that were being exec’ed from within ant builds; batch files that would perform svn updates, reverts and commits, among other svn actions.
A good alternative would be to use SVNAnt (some snippets follow):
<svn>
<delete>
<fileset dir="workingcopy/deleteTest">
<include name="**/*.del"/>
</fileset>
</delete>
<commit message="commit deleted files"
dir="workingcopy/deleteTest"/>
</svn>
<svn> <update dir="dir/to/update/" recurse="true" /> </svn>
Overriding equals and hashCode in Eclipse
Mar 1st
Most colleagues use IntelliJ Idea as their favorite Java IDE. I’m an Eclipse plugin developer, and Eclipse happens to be my favorite Development platform of choice.
A lot more that just once, I’ve seen peers curse Eclipse for the lack of keyboard support, lack of refactoring, lack of functionality, lack of blah, blah, blah…
Let me address some of these issues:
Read the rest of this entry »
How would you clean this up ?
Feb 14th
This was an interesting piece of code from a long time mentor and friend. He’s just started to learn Java some time ago, and has written this program to demonstrate how to draw an Ellipse.
He’s about 60 years of age. Has been a long time Turbo C-2.0 programmer. Mostly he develops small graphics programs to demonstrate some engineering drawing and mechanics concepts.
import java.awt.*; // Requisite files od JAVA
import java.awt.event.*;
//import java.awt.geom.*;
import javax.swing.*;
//import java.math.*;
//import java.util.*;
//Projest name and the following class name should be same
public class leenaTrial1 extends JFrame {
JFrame frame = new JFrame("Applet");
JPanel drawingArea = new JPanel(); // Drawing Area is named
JButton b1; // Defines action Button
int mouse_i = 1; // Counter for action
public void run() {
frame.setSize(1000, 500); // Frame size is defined
final Container content = getContentPane();
content.setBackground(Color.lightGray);
// color of frame or container is defined
content.setLayout(new FlowLayout());
drawingArea.setPreferredSize(new Dimension(750, 700));
// Size of draw area is defined & color in the next line
drawingArea.setBackground(Color.black);
content.add(drawingArea);
b1 = new JButton("Next Step"); // b1 is button for the next step
content.add(b1);
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
Graphics g = drawingArea.getGraphics();
Font font20 =new Font("TimesRoman",Font.PLAIN,20);
Font f0nt14=new Font("TimesRoman",Font.PLAIN,14);
g.setFont(font20);
int r1 = 250, r2 = 150, x0 = 375, y0 = 415;
// r1, r2 semi-major & semi-minor axex, x0,y0 center of circles
double inc = 0.005; // increment
int x1, y1, x2, y2; // Two ends of a line
// inner circle in
if(mouse_i == 1) {
g.setColor(Color.white);
//g.drawOval(w, w, x, y); // top, left, width, height
g.drawString("Step 1: Draw inner circle with the radius equal to semi-minor axis of the ellipse.", 20, 20);
ovalByPixels(x0,y0,r2,r2,1,Color.blue);
}
// outer circle
if(mouse_i == 2) {
g.setColor(Color.white);
g.drawString("Step 2: Draw outer circle with the radius equal to semi-major axis of the ellipse.", 20, 35);
ovalByPixels(x0,y0,r1,r1,1,Color.red); // Color spesified by name
}
// twelve lines
if(mouse_i == 3) {
g.setColor(Color.white);
g.drawString("Step 3: Divide the circles in twelve parts.", 20, 50);
for(int k = 0; k < 6; k++) {
double cos = Math.cos(k*2*Math.PI/12);
double sin = Math.sin(k*2*Math.PI/12);
double f1 = 1.06 ; // numbers are printed out side of larger circle
double f2 = 0.9 ; // numbers are printed in side of smaller circle
x1 = x0 + (int)(r1*cos); // x1,y1 are on outer circle on one end of diameter
y1 = y0 - (int)(r1*sin);
x2 = x0 - (int)(r1*cos); // x2,y2 are on outer circle at other end of diameter
y2 = y0 + (int)(r1*sin);
lineByPixels(x1, y1, x2, y2,5,new Color(255,100,100)); // 5 is delay value
// Color spesified by values of three colors
g.drawString("a" + Integer.toString(k+1), x0-10+ (int)(f1*r1*cos), y0+10 - (int)(f1*r1*sin));
g.drawString("a" + Integer.toString(k+7), x0-10 - (int)(f1*r1*cos), y0+10 + (int)(f1*r1*sin));
g.drawString("b" + Integer.toString(k+1), x0 -10+ (int)(f2*r2*cos), y0+10 - (int)(f2*r2*sin));
g.drawString("b" + Integer.toString(k+7), x0 -10- (int)(f2*r2*cos), y0+10 + (int)(f2*r2*sin));
delay(500); // call delay function
}
}
if(mouse_i == 4) { // Step 4 * * * * * * * * * * *
g.setColor(Color.white);
g.drawString("Step 4: From inner circle points, draw horizontal lines and from corresponding points ", 20, 70);
g.drawString(" on the outer circle, draw vertical lines. Cutting points are points on Ellipse.", 20, 85);
Color cr = new Color(255,200,100);
for(int k = 0; k <= 11; k++) {
//int x1,x2,y1,y2;
x1 = x0 + (int)(r1*Math.cos((2*Math.PI/12)*k)); // point on outer circle
y1 = y0 - (int)(r1*Math.sin((2*Math.PI/12)*k));
x2 = x0 + (int)(r2*Math.cos((2*Math.PI/12)*k)); //points on inner circle
y2 = y0 - (int)(r2*Math.sin((2*Math.PI/12)*k));
int x1L = x0 + (int)(1.2*r1*Math.cos((2*Math.PI/12)*k)); // point on outer circle
int y2L = y0 - (int)(0.9*r2*Math.sin((2*Math.PI/12)*k));
g.setColor(Color.green);
if(k==3 || k==9){
lineByPixels(x0-20, y2, x0+20, y2,5,cr );}// 5 is delay value
//g.drawLine( x0-20, y2, x0+20, y2);}
else{
lineByPixels(x2, y2, x1L, y2,5,new Color(255,100,200));}
//g.drawLine(x2, y2, x1L, y2);} // horizontal line
g.setColor(Color.yellow);
if(k==0 || k==6){
lineByPixels(x1, y0-20, x1, y0+20,5,new Color(100,100,200));}
//g.drawLine(x1, y0-20, x1, y0+20);}// Vertical line
else {
lineByPixels(x1, y1, x1, y2L,5,new Color(100,255,100));}
//g.drawLine(x1, y1, x1, y2L);}// vertical line
delay(500); // call delay function
g.setColor(Color.white);
g.drawOval(x1-3, y2-3, 6, 6); // Box (top, left, width, height
delay(500);
}
}
if(mouse_i == 5) {
g.setColor(Color.white);
g.drawString("Step 5: Draw a smooth curve connecting the twelve cutting points.", 20, 100);
g.setColor(Color.RED);
for(double i = 0; i < 2*(3.1415926); i=i+inc) {
g.drawLine((int)(x0+r1*Math.cos(i)), (int)(y0-r2*Math.sin(i)), (int)(x0+(r1+1)*Math.cos(i)), (int)(y0-(r2+1)*Math.sin(i)));
delay(1); // calls delay function
} // Ellipse drawing is complete
g.drawString("Ellipse by Concentric Circles, by Dr. Vasant D. Barve", 20, 680);
g.drawString("DONE!", 670, 350);
}
mouse_i++;
}
});
pack();
setVisible(true);
}
// This is delay procedure
public void delay(int dt1)
{
try {
Thread.sleep(dt1);
}
catch (InterruptedException e) {
}
} // Delay procedure ends
// This is Oval (circle or ellipse) by pixel procedure * * * * * * * * * * * * * * *
public void ovalByPixels(float x0, float y0, float r1, float r2, int dt, Color objColor)
{ // Center of circle at x0,y0 Radius r dt delay after each pixel counterclckwise
Graphics g = drawingArea.getGraphics();
double rm = Math.max(r1, r2); // Greater of the two picked up
for(double i = 0; i < 2*(3.1415926); i=i+1/rm) {
double x1 = x0+ r1*Math.cos(i);
double y1 = y0+r2*Math.sin(i);
double x2 = x0+(r1+1)*Math.cos(i);
double y2 = y0+(r2+1)*Math.sin(i);
g.setColor(objColor);
g.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
delay(dt); // call delay function
}
} // OvalByPixels procedure Ends
// lineByPixels Procedure starts * * * * * * * * * * *
public void lineByPixels(int x1, int y1, int x2, int y2, int dt,Color objCol ) {
// Line starts at (x1,y1) & ends at (x2,y2) each point is drawn as short line of one pixel length
Graphics g = drawingArea.getGraphics();
//g.setColor(Color.white);
g.setColor(objCol);
int L= (int) Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
// length of line is found
double th=Math.atan2(y2-y1,x2-x1); // inclination of line with x axis
for(int i=0; i<=L-1; i=i+1)
{ g.drawLine((int) (x1+i*Math.cos(th)), (int) (y1+i*Math.sin(th)),(int) (x1+(i+1)*Math.cos(th)), (int) (y1+(i+1)*Math.sin(th)));
delay(dt); //delay introduced to see line being drawn
}
} // lineByPixels Procedure Ends
// Paint graphics starts * * * * * * * * *
public void paint(Graphics g) {
super.paint(g);
} // Paint graphics Ends
public static void main(String[] args) {
leenaTrial1 ecc = new leenaTrial1(); // here the function is called
ecc.run();
}
}
Fighting Spam
Feb 19th
Posted by Ketan in experiences
1 comment
I recently enabled comments on my blog. Ever since this has happened, there’s been a whole lot of spam comments that I’ve had to delete.
Unfortunately my web host does not allow external connections, so using a service like askimet seems to be out of question.
So how do I stop spam comments from being entered in my site ?
Captcha? Naah… It’s way too unreadable for most users.
Simple math? Yeah… It restricts comment spam by throwing the commenter a simple math question.