12/16/2009

Portable JNDI names in EJB 3.1

Java EE 6 made some efforts to standardize JNDI names at global,application, and module levels. Now EJB deployed to EJB-3.1-capable server will have various standard JNDI names registered. EJB 3.1 spec defines the following 3 levels of JNDI names for EJB:

Global JNDI name:

java:global[/<app-name>]/<module-name>/<bean-name>[!<fully-quali-fied-interface-name>]
Application-scope JNDI name:
java:app/<module-name>/<bean-name>[!<fully-qualified-interface-name>]
Module-scope JNDI name:
java:module/<bean-name>[!<fully-qualified-interface-name>]
The following exampels shows various EJB JNDI names, depending upon how the EJB is packaged, its business interface(s), and bean class:

Bean class: test.TestBean
business interface: none






















How the EJB is packaged

Global JNDI names


Application-scope JND names

Module-scope JNDI names

in testEJB.jar inside testApp.ear

java:global/testApp/testEJB/TestBean

java:global/testApp/testEJB/TestBean!test.TestBean


java:app/testEJB/TestBean

java:app/testEJB/TestBean!test.TestBean



java:module/TestBean


java:module/TestBean!test.TestBean


in testWeb.war



java:global/testWeb/TestBean

java:global/testWeb/TestBean!test.TestBean


java:app/testWeb/TestBean

java:app/testWeb/TestBean!test.TestBean


java:module/TestBean


java:module/TestBean!test.TestBean



in testEJB.jar



java:global/testEJB/TestBean

java:global/testEJB/TestBean!test.TestBean



java:app/testEJB/TestBean

java:app/testEJB/TestBean!test.TestBean



java:module/TestBean


java:module/TestBean!test.TestBean


12/10/2009

EJB Lite testing with JUnit and embeddable container


In EJB 3.1, one can unit-test EJB using standadard EJB API, JUnit, and outside any application server. This is how I created an Eclipse java project to unit-test a simple EJB with GlassFish v3.

1. inside Eclipse, create a new java project, named ejblite.


Build path: $GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar $GLASSFISH_HOME/modules/javax.ejb.jar

Output dir: testApp (it is used in global jndi name in the test)

2. create a new java class named TestBean:

package test;

import javax.ejb.Stateless;

@Stateless
public class TestBean {
public int add(int a, int b) {
return a + b;
}

public int multiply(int a, int b) {
return a * b;
}
}
3. select TestBean.java in the package panel, create a new JUnit Test Case (File -> New -> JUnit Test Case). Choose TestBean as the test target, and the test method for both business methods (add and multiply) will be generated, along with setUp and tearDown.

4. modify the test case:
package test;

import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;

import junit.framework.TestCase;

public class TestBeanTest extends TestCase {
private EJBContainer container;
private Context namingContext;
private TestBean testBean;

@Override
protected void setUp() throws Exception {
super.setUp();
container = EJBContainer.createEJBContainer();
namingContext = container.getContext();
testBean = (TestBean) namingContext.lookup("java:global/testApp/TestBean");
}

@Override
protected void tearDown() throws Exception {
super.tearDown();
namingContext.close();
container.close();
}

/**
* Test method for {@link test.TestBean#add(int, int)}.
*/
public final void testAdd() {
int a = 1, b = 2, expected = a + b;
assertEquals(expected, testBean.add(a, b));
}

/**
* Test method for {@link test.TestBean#multiply(int, int)}.
*/
public final void testMultiply() {
int a = 1, b = 2, expected = a * b;
assertEquals(expected, testBean.multiply(a, b));
}

}
5. select TestBeanTest class, and run it as JUnit test.

Our EJB classes all reside under testApp directory (the output dir of the current project), and testApp is also used as the module-name for the current EJB app. If you choose to use a different output dir value, you will also need to change the global jndi-name accordingly.

If you are interested in using DataSource and JPA in EJB embeddable, check out EJB lite, JPA, DataSource embedded in java application.

12/08/2009

A sample EJB 3.1 Lite client

EJB Lite is a lightweight subset of EJB 3.1, including the support for running EJB container embedded in the current java application. So one can run EJB without appserver. The following sample contains a no-interface stateless EJB, and a java application that looks up and invokes the EJB.

1. project structure:

mkdir -p ejblite/src/test
mkdir -p ejblite/testApp

2. go to ejblite/src/test, and edit 2 java files:
-------------
TestBean.java
-------------

package test;
import javax.ejb.Stateless;

@Stateless
public class TestBean {
public String hello(String name) {
return "Hello, " + name;
}
}

-----------
Client.java
-----------

package test;

import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import javax.naming.NamingException;

public class Client {
public static void main(String args[]) throws NamingException {
EJBContainer container = null;
try {
container = EJBContainer.createEJBContainer();
Context namingContext = container.getContext();
TestBean testBean = (TestBean) namingContext.lookup(
"java:global/testApp/TestBean");
String hi = testBean.hello("client");
System.out.println("testBean.hello method returned: " + hi);
} finally {
if(container != null) {
container.close();
}
}
}
}
3. compile and run from src/test directory, using GlassFish v3:
javac -d ../../testApp/ -cp $GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar:../../classes Client.java TestBean.java

java -cp $GLASSFISH_HOME/lib/embedded/glassfish-embedded-static-shell.jar:../../testApp test.Client
You will see the following output, when running with GlassFish v3:
Dec 8, 2009 3:47:45 PM com.sun.enterprise.v3.server.AppServerStartup run
INFO: GlassFish v3 (74.2) startup time : Embedded(1246ms) startup services(511ms) total(1757ms)
Dec 8, 2009 3:47:45 PM org.glassfish.admin.mbeanserver.JMXStartupService$JMXConnectorsStarterThread run
INFO: JMXStartupService: JMXConnector system is disabled, skipping.
Dec 8, 2009 3:47:46 PM com.sun.enterprise.transaction.JavaEETransactionManagerSimplified initDelegates
INFO: Using com.sun.enterprise.transaction.jts.JavaEETransactionManagerJTSDelegate as the delegate
Dec 8, 2009 3:47:46 PM AppServerStartup run
INFO: [Thread[GlassFish Kernel Main Thread,5,main]] started
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.SecurityLifecycle <init>
INFO: security.secmgroff
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.SecurityLifecycle onInitialization
INFO: Security startup service called
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.PolicyLoader loadPolicy
INFO: policy.loading
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.auth.realm.Realm doInstantiate
INFO: Realm admin-realm of classtype com.sun.enterprise.security.auth.realm.file.FileRealm successfully created.
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.auth.realm.Realm doInstantiate
INFO: Realm file of classtype com.sun.enterprise.security.auth.realm.file.FileRealm successfully created.
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.auth.realm.Realm doInstantiate
INFO: Realm certificate of classtype com.sun.enterprise.security.auth.realm.certificate.CertificateRealm successfully created.
Dec 8, 2009 3:47:47 PM com.sun.enterprise.security.SecurityLifecycle onInitialization
INFO: Security service(s) started successfully....
Dec 8, 2009 3:47:48 PM com.sun.ejb.containers.BaseContainer initializeHome
INFO: Portable JNDI names for EJB TestBean : [java:global/testApp/TestBean!test.TestBean, java:global/testApp/TestBean]
testBean.hello method returned: Hello, client
Dec 8, 2009 3:47:48 PM org.glassfish.admin.mbeanserver.JMXStartupService shutdown
INFO: JMXStartupService and JMXConnectors have been shut down.
Dec 8, 2009 3:47:48 PM com.sun.enterprise.v3.server.AppServerStartup stop
INFO: Shutdown procedure finished
Dec 8, 2009 3:47:48 PM AppServerStartup run
INFO: [Thread[GlassFish Kernel Main Thread,5,main]] exiting
It's a good practice to close the EJB container before program exists, to avoid any resource leaking. It's more easily handled in JUnit tearDown method. See my follow-up post EJB Lite testing with JUnit and embeddable container

How to get module name and app name

Java EE 6 brings an easy and portable way to get the module name and application name of the current app, using @Resource field injection or lookup:

    @Resource(lookup="java:module/ModuleName")
private String moduleName;

@Resource(lookup="java:app/AppName")
private String appName;
The above code can be in any Java EE component classes, such as servlet, servlet filter, web listener class, JSP tag handler class, JSF managed bean, EJB bean class, interceptor class, application client main class, and their super classes as well.

You can also look up the module name and app name by their reserved jndi names:
//look up inside a method
String mname = (String) initialContext.lookup("java:module/ModuleName");
String anmae = (String) initialContext.lookup("java:app/AppName");
If you only need moduleName or appName in a particular method, I feel looking them up on-demand is slightly better.

From Java EE 6 forward, the default module name is the base name of a WAR, ejb-jar, or application client jar, that is, the jar name without the .war and .jar extension. One can customize it in descriptors (web.xml, ejb-jar.xml, application-client.xml), though this is rarely needed.

The default app name is the basename of a EAR, i.e., the EAR name without the .ear extension. One can customize it in application.xml.

10/14/2009

A sample application-client.xml (Java EE 5)

Application client is another type of Java EE module, the least utilized one. It attempts to wrap up Java SE application, deploy it to application server, and make use of deployed EJB, platform services and resources.

The following application-client.xml declares an env-entry, ejb-ref and resource-ref.

<?xml version="1.0" encoding="UTF-8"?>
<application-client xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application-client_5.xsd">

<display-name>A sample application client</display-name>

<env-entry>
<description>admin email</description>
<env-entry-name>adminEmail</env-entry-name>
<env-entry-value>admin@example.x</env-entry-value>
</env-entry>

<ejb-ref>
<ejb-ref-name>ejb/testBean</ejb-ref-name>
<remote>test.TestRemoteInterface</remote>
<ejb-link>TestBean</ejb-link>
</ejb-ref>

<resource-ref>
<res-ref-name>HRDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<mapped-name>jdbc/__default</mapped-name>
</resource-ref>

</application-client>
The env-entry can be looked up with name "java:comp/env/adminEmail", or injected into the application client main class or its superclasses. For example:
@Resource(name="adminEmail")
//use static modifier in application client main class only
private static String adminEmail;
The ejb-ref and resource-ref can be looked up with names "java:comp/ejb/testBean" and "java:comp/env/HRDS", respectively.

The reason I include ejb-ref and resource-ref is to show how they fit in descriptors. In Java EE 5 and later, it is easier to use @EJB and @Resource to declare and inject them into component classes.

10/13/2009

A sample application.xml (Java EE 5 and Java EE 6)

The following is a sample application.xml that declares 3 modules (application client, ejb jar, and war), and a library directory. The library-directory element is redundant here since the default value is "lib".

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="5"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd">

<module>
<java>test-client.jar</java>
</module>

<module>
<ejb>test-ejb.jar</ejb>
</module>

<module>
<web>
<web-uri>test.war</web-uri>
<context-root>test</context-root>
</web>
</module>

<library-directory>lib</library-directory>
</application>

Java EE 6 application.xml:

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="6" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_6.xsd">

<application-name>test-app</application-name>
<initialize-in-order>true</initialize-in-order>
<module>
<web>
<web-uri>test-web.war</web-uri>
<context-root>test</context-root>
</web>
</module>

<module>
<ejb>test-ejb.jar</ejb>
</module>
<library-directory>lib</library-directory>
</application>

10/12/2009

A sample ejb-jar.xml (EJB 3.0)

ejb-jar.xml is optional starting from EJB 3.0. Annotations are extensively used to declare metadata in place of descriptor elements. In certain cases, ejb-jar.xml is still necessary, and 2 such cases I can think of are the declaration of env-entry and default interceptor.

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd">
<enterprise-beans>
<session>
<ejb-name>TestBean</ejb-name>
<ejb-ref>
<ejb-ref-name>ejb/fooremote</ejb-ref-name>
<ejb-ref-type>Session</ejb-ref-type>
<remote>test.FooRemoteIF</remote>
</ejb-ref>
<env-entry>
<description>admin email</description>
<env-entry-name>adminEmail</env-entry-name>
<env-entry-value>admin@example.x</env-entry-value>
</env-entry>
</session>
</enterprise-beans>

<interceptors>
<interceptor>
<interceptor-class>test.Interceptor1</interceptor-class>
</interceptor>
</interceptors>

<assembly-descriptor>
<interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>test.Interceptor1</interceptor-class>
</interceptor-binding>
</assembly-descriptor>
</ejb-jar>
This ejb-jar.xml assumes that TestBean is already annotated with either @Stateless or @Stateful. The descriptor adds a env-entry resource to this session bean's naming environment. This env-entry can be injected into TestBean class, or looked up in TestBean's naming environment.

The interceptor-binding element binds Interceptor1 to all EJBs packaged in the current ejb jar.

A minimal sample web.xml (servlet 2.5)

A very short web.xml (Servlet 2.5) that declares a servlet and its mapping. Some elements, like session-config and welcome-file-list have reasonable defaults provided by web containers.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<servlet>
<display-name>Servlet1</display-name>
<servlet-name>Servlet1</servlet-name>
<servlet-class>test.Servlet1</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/Servlet1</url-pattern>
</servlet-mapping>

</web-app>

A sample web.xml (servlet 2.5)

This is a sample web.xml based on Servlet 2.5 (part of Java EE 5) that declares common elements. All top-level elements are optional, and can be in any order.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<context-param>
<param-name>debug</param-name>
<param-value>false</param-value>
</context-param>

<session-config> <!-- 10 minutes -->
<session-timeout>10</session-timeout>
</session-config>

<servlet>
<display-name>Servlet1</display-name>
<servlet-name>Servlet1</servlet-name>
<servlet-class>test.Servlet1</servlet-class>
<init-param>
<param-name>sleep-time-in-seconds</param-name>
<param-value>10</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>Servlet1</servlet-name>
<url-pattern>/Servlet1</url-pattern>
</servlet-mapping>

<env-entry>
<description>admin email</description>
<env-entry-name>adminEmail</env-entry-name>
<env-entry-value>admin@example.x</env-entry-value>
</env-entry>

<resource-ref>
<res-ref-name>HRDS</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<mapped-name>jdbc/__default</mapped-name>
</resource-ref>

<filter>
<display-name>Filter1</display-name>
<filter-name>Filter1</filter-name>
<filter-class>Filter1</filter-class>
</filter>

<filter-mapping>
<filter-name>Filter1</filter-name>
<url-pattern>/Filter1</url-pattern>
</filter-mapping>

<filter-mapping>
<filter-name>Filter1</filter-name>
<servlet-name>Servlet1</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

<listener>
<listener-class>Listener1</listener-class>
</listener>

<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>

</web-app>

10/09/2009

Servlet without web.xml

One of the new features in Servlet 3.0 is the introduction of annotation to define a servlet, thus making web.xml optional. The following is a simple web app that only contains a servlet:

1. create project directory structure:

mkdir nowebxml
cd nowebxml/
mkdir -p src/test build/WEB-INF/classes
2. create TestServlet.java in src/test
cd src/test
-------------------

package test;

import java.io.IOException;
import java.io.PrintWriter;

import javax.ejb.EJB;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(urlPatterns = "/TestServlet", loadOnStartup = 1)
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}

protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
PrintWriter writer = response.getWriter();
writer.println("A servlet without web.xml: " + getServletName());
}
}
3. compile and package
javac -cp "$GLASSFISH_HOME/modules/*" -d ../../build/WEB-INF/classes/ TestServlet.java

cd ../../build/

jar cvf nowebxml.war WEB-INF/
4. deploy
cp nowebxml.war $GLASSFISH_HOME/domains/domain1/autodeploy/
5. run TestServlet
curl http://localhost:8080/nowebxml/TestServlet
A servlet without web.xml: test.TestServlet
GlassFish V3 is used for deploy and running the test web app, but this web app is portable and should work with any container that supports Servlet 3.0.

My javac command uses a wildcard in classpath values, which is only supported in Java SE 6 or later. Classpath wildcard is very convenient, especially in testing. In addition, Servlet 3.0 is part of Java EE 6, which itself requires Java SE 6.

Print array content with Arrays.toString or Arrays.asList

When directly printing an array, it gives its element type and hashcode, which is not the desired result in most cases. A better way is to use java.util.Arrays.toString(), or Arrays.asList() method to print the string representation of the array.

This test method tries 2 ways of printing the content of a string array:

public final void testPrintArray() {
   final String[] names = {"Linux", "Mac OS X", "Windows", "Solaris", null};
   System.out.println("print names array directly: " + names);
   System.out.println("print names with Arrays.toString(): " + Arrays.toString(names));
   System.out.println("print names with Arrays.asList(): " + Arrays.asList(names));
   //but String.valueOf() won't print array content
   System.out.printf("print names with String.valueOf(): %s%n", String.valueOf(names));
}
Test output:
print names array directly: [Ljava.lang.String;@5122cdb6
print names with Arrays.toString(): [Linux, Mac OS X, Windows, Solaris, null]
print names with Arrays.asList(): [Linux, Mac OS X, Windows, Solaris, null]
print names with String.valueOf(): [Ljava.lang.String;@293e86f
Between the 2 methods, I find toString() is more natural. Arrays.toString() was introduced in Java 5, somewhat later than Arrays.asList().

Also note that a List can take null as element value, and its string representation is "null".

4/28/2009

Use string constants in annotations

Generally speaking, string literals should be replaced with string constants in source code. This rule also holds when it comes to annotation-based resource injections.

For field or setter injections, any string constants can be declared in the enclosing class. The following example shows how to declare an env-entry in web.xml and inject it to a servlet class.

public static final String ADMIN_EMAIL = "adminEmail";

@Resource(name=ADMIN_EMAIL)
private String adminEmail;

<env-entry>
<description>admin email</description>
<env-entry-name>adminEmail</env-entry-name>
<env-entry-value>admin@example.x</env-entry-value>
</env-entry>
The name attribute is mandatory and used to match this injection to the appropriate env-entry in web.xml. This env-entry can be injected into several other classes, and therefore it is desirable to declare its name as a constant. This string constant may also reside in a separate class (e.g., a Constants class, utility class, or even some interface), depending on how it is used.

For type-level injections, these string constants must be declared in a separate class in order to be referenced in type-level annotations. This is probably because any field declarations have not be parsed when type-level annotations in the same class are processed. The follwoing example shows how to inject a stateful EJB reference at a servlet class level.
import static test.Constants.FOO_EJB_NAME;
import static test.Constants.FOO_EJB_REF_NAME;

@EJB(name=FOO_EJB_REF_NAME, beanName=FOO_EJB_NAME, beanInterface=FooBean.class)
public class Servlet2 extends HttpServlet {...}
In the above injection, beanName is optional only if there is only one EJB with business interface FooBean, and hence no ambiguity in mapping the EJB reference to target EJB. name and beanInterface attributes are always required when the @EJB injection is used at type level.

3/27/2009

Configure default target class name when copying class in Eclipse

I often copy a class and paste it into the same package, just as a template for creating another similar class. This is well supported in both NetBeans and Eclipse. The small difference between the 2 IDE is in the default name for the target class: NetBeans uses the format "Foo_1", while Eclipse uses "CopyOfFoo". Since I will never use a class like CopyOfFoo, I prefer the short form used in NetBeans.

So how to configure the default target class name in Eclipse? I searched in Eclipse preference to no avail. A grep through Eclipse installation directory reveals 2 jar files containing the string "CopyOf"

Binary file ./plugins/org.eclipse.jdt.ui_3.4.1.r341_v20080827-1100.jar matches
Binary file ./plugins/org.eclipse.wst.jsdt.ui_1.0.12.v200809172136.jar matches
The first one looks right. So I first quit Eclipse, back up plugins/org.eclipse.jdt.ui_3.4.1.r341_v20080827-1100.jar, expand it to /tmp/eclipse, and edit the file org/eclipse/jdt/internal/corext/refactoring/refactoring.properties.

Change the value of the following property from CopyOf{0} to the desired value (say X{0}):
- CopyRefactoring_cu_copyOf1=CopyOf{0}
+ CopyRefactoring_cu_copyOf1=X{0}
Update plugins/org.eclipse.jdt.ui_3.4.1.r341_v20080827-1100.jar with modified refactoring.properties:
cd /tmp/eclipse
jar uvf $HOME/eclipse/plugins/org.eclipse.jdt.ui_3.4.1.r341_v20080827-1100.jar org/eclipse/jdt/internal/corext/refactoring/refactoring.properties
Ideally, I wanted to append, rather than prepend, the extra 'X', but it turns out {0}X is resolved to a file name like Foo.javaX, not FooX.java. So Eclipse refactoring complains about it not being a valid compilation unit.

Restart Eclipse and see the new default name in action.  These steps have worked for me (my Eclipse is Version 3.4.1), but this should not be the preferred way to configure Eclipse.  I wish it can be configured in preferences.  Any changes to that plugin jar may be overwritten when it is updated from remote repository.

2/13/2009

Unable to copy and paste multiple files in Eclipse project explorer

One thing I often do inside a java IDE is to copy files from one package to another. The IDE (either Eclipse or NetBeans) does the refactoring automatically including renaming the package. I usually copy multiple files of mixed types (e.g., java source files, ant build.xml files, web.xml, etc) in one operation.

I recently found I can't do that in Eclipse project explorer (3.4.1 ganymede). I can only copy files of the same content type in one command. That is, I can copy-paste-refactor multiple java files, or multiple xml files from one package to another. But it would fail if I try to copy java source files AND xml files.

It seems the copying part failed. When I tried to paste files of mixed types, the files copied in the last command were pasted, not the one supposed to have been copied.

This has been working pretty well in NetBeans 6.1 and 6.5.

2/03/2009

Install subversion 1.5.5 on Ubuntu

The latest subversion is 1.5.5 (as of now 2/3/2009), but it's not available yet in Ubuntu repository or web site (svn 1.4.6). So I tried to install it manually. I only need the client, not the server, so I would skip those parts that are only for server. I went through the following steps and finally was able to install it. Take it at your own risk.

0. Switch (su) to root.

1. Go to http://subversion.tigris.org/getting.html, and click the link Source Releases Area. Download subversion 1.5.5 source archive and the dependency tarball to Desktop (note: 2 separate gz files: subversion-1.5.5.tar.gz & subversion-deps-1.5.5.tar.gz). This site also lists binary downloads for various platforms. I checked Ubuntu and other Linux platforms, but they don't have 1.5.5 yet.

2. Unpack subversion source and dependency tarballs (note that both must be unpacked inside the same directory)

cd $HOME/tmp
tar xzvf $HOME/Desktop/subversion-1.5.5.tar.gz
tar xzvf $HOME/Desktop/subversion-deps-1.5.5.tar.gz
3. Try to configure and install it
cd $HOME/tmp/subversion-1.5.5
./configure --without-berkeley-db --without-apache --without-neon --without-swig
make
make install
4. If it failed at step 3, most likely some dependencies are still missing. In my case, openssl is reported missing, though I'm sure openssl is already installed. Anyway, download and install openssl. It should be straight-forward to install openssl from source:

Go to openssl.org to download the latest stable version of openssl (currently openssl-0.9.8j). Note: OpenSSL 0.9.8 has a problem that can cause SSL negotiation failure (SSL negotiation failed: SSL error: decryption failed or bad record mac). For more details, see http://wiki.open.collab.net/wiki/Subversion_Client_FAQ
cd $HOME/tmp
tar xzvf $HOME/Desktop/openssl-0.9.8j.tar.gz
cd openssl-0.9.8j
./config
make
make install
5. Try installing subversion 1.5.5 again, with --with-openssl=/usr/local/ssl option, assuming openssl is installed to /usr/local/ssl.
cd $HOME/tmp/subversion-1.5.5
./configure --without-berkeley-db --without-apache --without-neon
--without-swig --with-openssl=/usr/local/ssl
6. If it fails due to zlib not present, try installing zlib1g-dev:
sudo apt-get install zlib1g-dev  (without sudo, if running as root)
7. Try installing subversion 1.5.5 again, with --with-zlib=... option:
./configure --without-berkeley-db --without-apache --without-neon
--without-swig --with-openssl=/usr/local/ssl --with-zlib=/usr/include
That should do it. Subversion 1.5.5 (at least the client) has been installed successfully!
/tmp > which svn
/usr/local/bin/svn

/tmp > svn --version
svn, version 1.5.5 (r34862)
compiled Feb 3 2009, 14:23:41

Copyright (C) 2000-2008 CollabNet.
Subversion is open source software, see http://subversion.tigris.org/
This product includes software developed by CollabNet (http://www.Collab.Net/).

The following repository access (RA) modules are available:

* ra_svn : Module for accessing a repository using the svn network protocol.
- handles 'svn' scheme
* ra_local : Module for accessing a repository on local disk.
- handles 'file' scheme
* ra_serf : Module for accessing a repository via WebDAV protocol using serf.
- handles 'http' scheme
- handles 'https' scheme

Update: After using this subversion client for about a month, I found a problem when committing changes to a repository with https. It only happens when running the commit sub-command using http protocol. It's probably related to the openssl issue mentioned above.

Change svn project to java project in Eclipse

I checked out a project from subversion repository using Eclipse (3.4.1 ganymede). I chose to check it out into a new Eclipse project, but it turned out to be a non-java, svn project was created. In the project properties, there is no associated builder. Its .project file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>project1</name>
<comment>project1</comment>
<projects>
</projects>
<buildSpec>
</buildSpec>
<natures>
</natures>
</projectDescription>
Now I want to turn it into a java project so it can be built with a java builder. I tried a few menus and other places inside Eclipse to no avail. Then I found I can just edit .project file, and that did the trick. Close Eclipse before doing these manual editing.
$HOME/workspace/project1/.project:
----------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>project1</name>
<comment>project1</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
I also added a .classpath file:
$HOME/workspace/project1/.classpath:
-----------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<!-- change the default bin directory to classes directory -->
<classpathentry kind="output" path="classes"/>
</classpath>

Finally, start Eclipse, open project1, and modify its source path, libraries, and other project properties.

1/01/2009

NetBeans 6.5 Quick Search Box, Slow Refactoring, and Transfering Repository Index

I recently upgraded to NetBeans 6.5 and have been pretty happy with it overall. These 3 new features you may have also noticed:

1. There is now a new Quick Search box at the upper right corner, with shortcut key Ctrl-I. The pull-down list shows it can search for Types, Actions, Options and help items. It seems interesting and I should use it more often.

I usually use Ctrl-O to jump to a class in my projects, which is very fast. Or use Alt-Shift-O to go to a file (much slower than Ctrl-O). In NetBeans 6.1, Ctrl-O (go to type) used to show all the useless javascript files except the java classes I was looking for. Now 6.5 seems to have fixed it.

2. Refactoring is extremely slow. Any refactoring, even renaming a local variable is slow. The blue progress bar stops at 100% for close to 1 minute in my computer. NetBeans seems to be searching across all projects and cache and gathering dependency.

3. Sometimes at startup, it shows a background task "Transfering Repository Index :Central Repository" (NetBeans typo: it should be Transferring). It's not blocking anything but does take quite long to complete.