TurboManage

David Chandler's Journal of Java Web and Mobile Development

  • David M. Chandler


    Web app developer since 1994 and Google Cloud Platform Instructor now residing in Colorado. Besides tech, I enjoy landscape photography and share my work at ColoradoPhoto.gallery.

  • Subscribe

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 224 other subscribers
  • Sleepless Nights…

    March 2023
    S M T W T F S
     1234
    567891011
    12131415161718
    19202122232425
    262728293031  
  • Blog Stats

    • 1,039,383 hits

Archive for the ‘Eclipse’ Category

GWT, App Engine, maven, and… IntelliJ!

Posted by David Chandler on June 5, 2014

I’ve been putting off migrating to Android Studio for a while now because I’m frankly loathe to learn a new IDE. I’ve used Eclipse for a decade and grew to become very productive in it. But a completely different team at Google may have just well forced me into it.

When I joined the GWT Developer Relations team in 2010, I worked closely with the Google Plugin for Eclipse team to get maven support into GPE. With their excellent work, we eventually achieved the holy grail: you could import a POM containing maven-gae-plugin and gwt-maven-plugin into Eclipse and all the GWT + GAE stuff from GPE like launching dev mode would just work (well, if you had the right supporting plugins like m2e-wtp). So the other day, I picked up a POM that worked in those days and tried it out on Kepler + GPE + m2e-wtp. Amazingly enough, the GWT stuff still works. Google now supports its own appengine-maven-plugin, so I swapped out the old GAE plugin for the new. It’s supposed to work with GPE and WTP, but so far no dice. It will be great when they get the kinks worked out. Funny thing is, I remember seeing exactly the same problem with GAE-maven integration way back in my GWT days. Only then, I could just walk over to Rajeev’s desk and he would fix it 🙂

In the mean time, imagine my surprise to discover that I could just import the POM into IntelliJ (full edition) and everything works. The maven project imported, I can launch GWT dev mode, set breakpoints and debug, etc. It’s funny to me that JetBrains can keep up to date with Google App Engine better than the GPE team, but that’s how big companies move sometimes….

So I’m off to learning new keyboard shortcuts (I could use the Eclipse keymap, but I had customized Eclipse, too. It’s easier than I thought to learn new tricks). And I have to say, the performance of IntelliJ is impressive. A couple of my teammates way back at Intuit will no doubt be glad to hear that I finally came around to maven + IntelliJ, not to mention some of my Android DevRel mates. We’ll see how this goes, but so far, I’m impressed.

 

Advertisement

Posted in AppEngine, Eclipse, Google Web Toolkit | 1 Comment »

Android demo tips: behind the scenes at Google I/O

Posted by David Chandler on May 20, 2013

Last week at Google I/O, Brad Abrams and I decided to go for the gold and give a talk on building a cloud backend for your Android app, complete with live coding on stage. For a more interesting example, we chose to build a sample app which relies on location services and Google Cloud Messaging. It all seemed like a good idea at the time. For the benefit of other Android presenters, I wanted to give a behind-the-scenes view of how we overcame some significant challenges to pull off the demos. Also, I will explain the anomalies we saw.

Device display. The easiest way to show an app is using an emulator, but Maps V2 API is part of Google Play Services, which doesn’t currently run on the Android emulator, so we needed physical devices. Droid@Screen works, but it updates slowly and requires the USB cable, which we needed for hardwired Ethernet. Some devices support HDMI out (Galaxy Nexus, N10) which can be used with a BlackMagic or similar HDMI capture box to show the device display on your Mac screen. Note that this requires a Mac with two Thunderbolt ports (hello Retina! BlackMagic in, Mac display adapter out). We could not use this because the phone’s HDMI connector is shared with the USB connector, which we needed for hardwired Ethernet. Fortunately, we had a Wolfvision projector camera available and a 4-port video switcher to show our Macs or the camera. For best results with a projector, set the phone display brightness to max.

The network. Gather 5000+ geeks in a massive concrete structure, give them all a new wifi device, and then try to get a working Internet connection… Despite Google’s stellar efforts, getting wireless Internet at Moscone Center remains very challenging. 3G / 4G / LTE is inhibited by the concrete and steel, and even 5GHz wifi suffered from RF interference or sheer demand. Thus, we knew in advance that we would need hardwired Internet. This was no problem for our laptops, but we also needed 2 phones for the demos to show continuous queries in action. Fortunately, Galaxy Nexus phones support wired connections using a USB OTG adapter paired with a USB to Ethernet adapter, and it just worked. So far so good.

Deferred launch. Because we were coding live in Eclipse, we deployed the code to the phones in real time via the USB cable. When you run an Android app in Eclipse, the default action is to install and run the app. But this  created a problem for the simple version of our demo app, which needed an Internet connection on launch to send a location update immediately. We had to disconnect the USB cable, walk the phone over to the camera, plug in the hardwired Ethernet, and only then launch the app via the home screen. Fortunately, Eclipse has a way to support this. In the project’s run configuration on the Android tab, simply select “Do nothing” for the launch action. This installs the app, but doesn’t launch it.

Live coding. Speaking of live coding, we have all seen those demos with lots of tedious cut and paste operations from a text file to the IDE. As a presenter, I want something visible in the IDE to prompt me for the fully manually steps as well as an easy way to paste in blocks of code. Eclipse snippets are just the thing. You can show them off to the side of your main editor window, then double-click on a snippet to paste it in. I used snippet titles without code to prompt me for the manual steps. Another challenge of live coding is that you might make a mistake at an early stage of the demo which ruins a later stage. In order to facilitate recovery, we created snapshots of working demo code at six different stages during our presentation. It would have been nice to preinstall these app versions on the devices; however, they all use the same package name and the Android installer doesn’t allow more than one at the same time. Hence the need for adb install and the deferred launch technique. Also we had to uninstall the prior version of the app on the device before each coding stage. Furthermore, Eclipse doesn’t let you create multiple projects with the same name. There is a workaround, but it kind of spoils the magic of our talk and I’ve already spilled enough secrets… let’s just say Eclipse workspaces and “Copy project” are a beautiful thing.

Location services. This is the one that snagged us in the first demo. It turns out that location services are not in fact magic, but require GPS (in Moscone?!) or wifi, which we turned off just before speaking to eliminate the possibility of conflict with the hardwired Ethernet. Fortunately, we were able to get a good enough wifi signal in a later demo to show location. On desktop devices, geolocation works with a hard-wired connection, so I’m not yet sure whether this is a bug or a feature on mobile… it is admittedly an uncommon use case.

The unanticipated. After recovering from the first demo glitch by turning on wifi, I demonstrated that location services worked, but it never sent the location to the server. It turns out the code was working as intended–I had already enabled authentication for my next segment, but the app from the previous segment which I revisited did not have auth turned on. Finally, the last demo showed two markers when it should have showed only one. The second marker turned out be a fellow Googler in the audience who had helped dogfood an early version of the demo app. So once again, the code worked as intended, just not as I intended at the moment. But… this is the stuff live demos are made of. I still prefer live demos with a few glitches to an hour full of slides 🙂

So now you know what Brad and I were busily doing while the other was talking and why talks of this complexity are pretty much impossible without two people and a lot of presentation gear. It was a teeny bit intimidating knowing that our session was part of the Google I/O live stream with thousands of viewers, but in the end, it came off (almost) without glitches and our gracious audience was wowed by the Mobile Backend Starter project. Mission accomplished!

See my next post for a complete list of resources on Mobile Backend Starter.

Posted in Android, Eclipse, Headsmack | 10 Comments »

Getting Started with Java APT for Android in Eclipse

Posted by David Chandler on September 28, 2012

Fellow Googler Ian Ni-Lewis and I have recently been working on annotation-driven code generation tools for Android and I wanted to pass along a few tech notes on getting it all up and running. The tool I’m working on is a lightweight ORM for the SQLite database in the Android OS. In a nutshell, you add the @Entity to any POJO that you want to make persistable, and the annotation processor generates a corresponding DAO class and the necessary SQLiteOpenHelper.

In this post, I won’t focus on the ORM tool as it’s not quite ready, but rather on how to work with Eclipse to build an annotation processor of your own (or contribute to the ORM project once I make it available). Despite the annoyances of the Mirror API, I found it reasonably straightforward and rewarding to write my own annotation processor.

What can APT do?

Java 5 introduced an Annotation Processing Tool (apt) which can be run standalone. Eclipse 3.3 added support for annotation processing as part of the Java build process. In a nutshell, an annotation processor can find annotations in your source code, inspect the code using something like reflection, and generate new source code as a result. In addition, an annotation processor can throw errors which will show up as red squigglies in Eclipse in the original annotated source code. This is extremely powerful as it allows your annotation processor to alert the developer regarding the correct use of the annotation. For example, when you tag a POJO with @Entity, my forthcoming ORM tool shows you immediately if any field types in the POJO are unsupported.

What’s in an annotation processor?

  1. The definition of your annotations using @interface.
  2. A class that extends AbstractProcessor, part of the Java 6 annotation processing APIs. One processor class can process multiple annotations.
  3. Code that inspects the annotated source code using the Mirror API.
  4. META-INF/services/javax.annotation.processing.Processor, which contains merely the fully-qualified name of your AbstractProcessor class, like “com.example.storm.MainProcessor”.

How to run your annotation processor

To use your annotation processor, package it in a jar. There are several ways to run it:

  1. On the command line, run Java apt. In Java 6 and later, javac will automatically search the classpath for annotation processors. You can bypass the default discovery mechanism using the -processor and -processorpath compiler options. You can also include your own AnnotationProcessorFactory to replace the default factory.
  2. In ant, add the -processorPath and -processor options or use the ant apt task (pre-Java 6).
  3. In Eclipse, add your annotation processor jar to the annotation factory classpath as described in the Eclipse guide to APT .

While the Eclipse JDT-APT integration is cool as it enables editor integration (hello, red squigglies), there is one limitation that makes it a pain to work on the annotation processor code itself. Whether by design or bug, when you make changes to your annotation processor, you must repackage as a jar and manually remove / add it to the annotation factory classpath of any projects using it. The packaging can be easily automated as I will show, but I haven’t yet found a way to get Eclipse to pick up changes to apt jars, even when they’re referenced as jars in an Eclipse project (vs. external jars).

Getting Started

During development, I found it useful to organize the annotation processor code into 3 separate projects: API, impl, and test.

The API project

The API project contains the @interface definitions for your annotations. Since this code will need to be on the classpath of any project that uses your annotations, it’s reasonable to also include other non-generated code that will be used at runtime. For example, my API project includes a DAO base class and other runtime code as well as the annotations. Since some of the runtime code has Android dependencies, I made this an Android library project by checking the Library box when running the New Android Application Project wizard. The project has no Activities, and the AndroidManifest.xml contains only the <uses-sdk> element.

As we noted earlier, the only way to run an annotation processor in Eclipse is from a jar or plugin. Since the API is required by the annotation processor, it must exist as a jar on the annotation factory classpath of any client projects such as the test project below. Therefore, it’s helpful to build the jar every time you make a change to the project. Eclipse does not do this for you automatically. However, you can set it up using ant as follows:

Once you’ve created the API project and configured the build path as required, export an ant build file using File | Export | Ant Buildfiles.

Edit the build.xml and add this inside the build-project target:

<jar destfile="your-api.jar" basedir="bin/classes"></jar>

Go to project properties | Builders and add a New Ant Builder. In the Main tab, click Browse Workspace… to choose the build.xml file, then finish. The default targets are OK. Now every time you save a change to the API, you’ll see it build your-api.jar in the project.

The APT implementation project

The impl project includes one or more AbstractProcessors, supporting model classes, and the META-INF file that specifies the name of your processor class. The APT impl project has no Android dependencies because the Android references exist only in the Freemarker templates. This project has on its build path the API project. You could depend on the API jar since it’s getting updated by your ant task, but depending on the project will facilitate refactoring. As for the API project, you’ll want to use an Ant Builder to update the impl jar every time the project is built.

My annotation processor uses the Freemarker template language to generate source code. The basic approach is to inspect the annotated code, populate a model class (like EntityModel) which simply has getters and setters for the fields that will be needed by the template, then invoke Freemarker, passing it the model and the template. This is my annotation processor:

package com.example.storm;

import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.JavaFileObject;

import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

@SupportedAnnotationTypes({ "com.example.storm.Entity","com.example.storm.Database" })
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class MainProcessor extends AbstractProcessor {
	private ProcessorLogger logger;
	private Configuration cfg = new Configuration();
	private List entities = new ArrayList();

	@Override
	public boolean process(Set<? extends TypeElement> annotations,
			RoundEnvironment roundEnv) {
		this.logger = new ProcessorLogger(processingEnv.getMessager());
		logger.info("Running MainProcessor...");

		cfg.setTemplateLoader(new ClassTemplateLoader(this.getClass(), "/res"));

//		for (TypeElement annotationType : annotations) {}

		for (Element element : roundEnv.getElementsAnnotatedWith(Entity.class)) {
			Entity annotation = element.getAnnotation(Entity.class);
			EntityModel em = new EntityModel(element);
			processTemplateForModel(em);
			// retain for DatabaseHelper class
			entities.add(em);
		}

		for (Element element : roundEnv.getElementsAnnotatedWith(Database.class)) {
			Database dbAnno = element.getAnnotation(Database.class);
			DatabaseModel dm = new DatabaseModel(element, dbAnno.name(), dbAnno.version());
			// Add entity DAOs
			for (EntityModel em : entities) {
				dm.addDaoClass(em.getQualifiedClassName());
			}
			processTemplateForModel(dm);
		}

		return true;
	}

	private void processTemplateForModel(ClassModel model) {
		JavaFileObject file;
		try {
			file = processingEnv.getFiler().createSourceFile(model.getQualifiedClassName());
			logger.info("Creating file  " + file.getName());
			Writer out = file.openWriter();
			Template t = cfg.getTemplate(model.getTemplatePath());
			logger.info("Processing template " + t.getName());
			t.process(model, out);
			out.flush();
			out.close();
		} catch (IOException e) {
			logger.error("EntityProcessor error", e, model.getElement());
		} catch (TemplateException e) {
			logger.error("EntityProcessor error", e, model.getElement());
		}
	}

}

The hardest part about using Freemarker was locating the templates on the classpath. I found it is easiest to use Freemarker’s ClassTemplateLoader and put my .ftl templates in src/res. The name happens to coincide with the standard Android resources folder; however, this is not required as the impl project is not an Android project. Furthermore, my res folder is under src, not a sibling directory as the standard Android folder. This is the easiest way for ClassTemplateLoader to find it.

Note the catch clauses in processTemplateForModel() that invoke logger.error(). Messages written to the ProcessorLogger show up in Eclipse’s Error Log view. In addition, by passing the Element which triggered the error, Eclipse can generate red squigglies in source code using the annotation.

In my current design, EntityModel and DatabaseModel correspond to the @Entity and @Database annotations and extend ClassModel, which does the heavy lifting using the Mirror API. Here are the key methods from ClassModel which show how to introspect on an annotated class and its fields using the Mirror API:

	public ClassModel(Element element) {
		TypeElement typeElement = (TypeElement) element;
		this.typeElement = typeElement;
		readFields(typeElement);
	}

	protected void readFields(TypeElement type) {
		// Read fields from superclass if any
		TypeMirror superClass = type.getSuperclass();
		if (TypeKind.DECLARED.equals(superClass.getKind())) {
			DeclaredType superType = (DeclaredType) superClass;
			readFields((TypeElement) superType.asElement());
		}
		for (Element child : type.getEnclosedElements()) {
				if (child.getKind() == ElementKind.FIELD) {
					VariableElement field = (VariableElement) child;
					Set modifiers = field.getModifiers();
					if (!modifiers.contains(Modifier.TRANSIENT) && !modifiers.contains(Modifier.PRIVATE)) {
						String javaType = getFieldType(field);
						addField(field.getSimpleName().toString(), javaType);
					}
				}
		}
	}

	protected String getFieldType(VariableElement field) {
		TypeMirror fieldType = field.asType();
		return fieldType.toString();
	}

I’m not completely happy with this design and plan to move all the mirror API code into processor classes, leaving the model classes pure. This should enable even more fine-grained error reporting. Here’s a bit of my Freemarker template src/res/EntityDao.ftl. All variable names inside ${} invoke the corresponding getters in the model class passed to the process() method above.

public class ${className} extends ${baseDaoClass}<${entityName}>{

	public static void onCreate(SQLiteDatabase db) {
		String sqlStmt =
			"CREATE TABLE ${tableName}(" +
				<#list fields as field>
				"${field.colName} ${field.sqlType}<#if field.primitive> NOT NULL<#if field_has_next>," +
				</#list>
			")";
		db.execSQL(sqlStmt);
	}
	...
}

The test project

The test project is an Android Application Project which has on its build path the API project. However, because the API project is an Android Library project, you must add it as a referenced library under project properties | Android, not the usual Java Build Path. In addition, you must add 3 jars to the annotation factory classpath in Eclipse: the Freemarker jar, the API jar, and the impl jar.

There are several ways to invoke Android unit tests. I found it easiest to include test instrumentation directly in an Android Application Project created using the Application Project wizard (not the Android Test Project wizard, as that requires yet another project under test). It has the default MainActivity, and you can simply add the test instrumentation to AndroidManifest.xml as follows:

    <instrumentation
        android:name="android.test.InstrumentationTestRunner"
        android:targetPackage="com.example.storm.test" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/title_activity_main" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <uses-library android:name="android.test.runner" />
    </application>

The activity should already be there; you just need to add the uses-library tag and the instrumentation tag pointing to the package which contains your Junit 3 tests (note: Android doesn’t use Junit 4). To create tests, simply run the New Junit Test wizard. To run them, right-click on the project and select Run As | Android Junit Test. This will run the test on an emulator or device and show results in the standard JUnit console.

Wrapping up

Creating an annotation processor has been a pretty fun learning project, and I trust the final product will be useful to a lot of Android developers. The most painful part is having to manually remove and re-add the jars from the test project’s annotation factory classpath every time I make a change to one of the jars. I have to go to project properties | Java Compiler > Annotation Processing > Factory Path, uncheck the boxes next to the api and impl jars, click OK and rebuild, then go back in to recheck the boxes and rebuild. Doesn’t take long with keyboard shortcuts, but still… I’ll be glad when then the ORM jars are stable, and (I hope), so will you.

Posted in Android, Eclipse | 8 Comments »

Google Plugin for Eclipse now open source

Posted by David Chandler on November 16, 2011

This is pretty big news for the GWT community and should make it easier for GWT frameworks to build tooling for Eclipse. Witness JBoss, who was working on GWT integration even before the announcement. Congrats to the GPE team!

Posted in Eclipse, Google Web Toolkit | Leave a Comment »

Configuring Eclipse on a Mac

Posted by David Chandler on July 8, 2010

I’m attempting to make the switch from Windows to Mac courtesy of my new employer (I’m pleased to be working with the GWT team at Google). While I very much like my shiny new MacBook Pro, I simply don’t get all the Mac hype. The biggest problem has been that I’m a keyboard person, and Macs are for mouse people. Most Windows keyboard shortcuts have Mac equivalents, but they’re just different enough to make for a bit of a learning curve, like Ctrl+arrows instead of Home and End. Thankfully, many keys can be remapped via System Preferences, but I haven’t found a way to map the editing keys yet (anyone?). My biggest annoyance is that holding down the Alt key doesn’t bring up menu shortcuts as on Windows. That makes going to Run | External Tools a real pain in Eclipse. Alt+R, E becomes Ctrl+F2,R,R,down arrow,E. Runner up annoyance is that there’s no way to use the keyboard to click “No” in a confirmation dialog.

At any rate, here are a few notes for other Eclipse users making the switch from Windows to Mac.

The first thing is to make the fonts readable. This is not necessarily a Mac thing, but the system font does seem especially small. In Eclipse preferences (Command+comma in most apps on the Mac–nice), I bumped up the font size (General | Appearance | Colors and Fonts | Basic | Text Font). This increases it for the Console window, but some plug-ins seem to use the system font instead, which leaves these “over the hill” eyes squinting at logs. Which, in turn, brings us to an almost-unbelievable discovery: you can’t change the system font on a latest, greatest Mac (OS X 10.6.3)! Problem still unsolved.

Now, time to move over my Eclipse external tools configurations from Windows. The external tools config to launch Finder in the selected project directory is very cool, but far from obvious. In the Run | External Tools | Configuration… dialog, enter the following information:

Location: /usr/bin/open
Working Directory: ${project_loc}
Arguments: .

This corresponds to typing “open .” in a Terminal window, which launches Finder in the current directory. Now if I could just configure a shortcut key to launch Terminal so I don’t have to click the icon in the dock every time… See Finder toolbar button to open Terminal to go the other way.

You can also create an external tool to launch Terminal in the selected project directory. Well, almost. To launch Terminal, you have to key into the Location field the full path /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal; however, it always opens in your home directory, even if you set Working Directory to the ${project_loc} variable. That defeats the purpose of this particular tool launcher. Fortunately, xterm is a little smarter, so the following settings work:

Location: /usr/X11/bin/xterm
Working Directory: ${project_loc}
Arguments: (none)

Unfortunately, the default xterm font is miniscule.

Fortunately, you can put the following lines in ~/.Xdefaults to increase the default font size:

XTerm*font:     *-fixed-*-*-*-20-*
XTerm*boldFont: *-fixed-*-*-*-20-*

Now we’ve replicated the command prompt external tool from Windows Eclipse, but unfortunately, it’s a pain to get to the Run | External Tools menu via the keyboard. So we might as well punt and make it even easier with the mouse. thanks to this nifty open source plug-in for Eclipse. Once you’ve installed the plug-in, simply right-click on a project and select “Open in Terminal” to open a new xterm. Someone should contribute “Open in Finder” to that project, too.

Most keyboard shortcuts on Windows map directly to the map in the standard way; that is, you press the Command (Apple) key on the Mac instead of Ctrl on the PC. One notable exception is Ctrl+Space, which is identical on the Mac. Command+Space would be more consistent; however, this brings up the Spotlight search tool on the Mac. Since I’d rather have Ctrl+Space work in Eclipse where my muscle memory is expecting it, I swapped these keystrokes in Eclipse Preferences and Mac System Preferences. Another alternative is to disable Spotlight in favor of Google Quick Search Box.

Eclipse on Mac tricks welcome in the comments. No flames, please.

Posted in Eclipse | 7 Comments »

Coding getLogger() quickly with an Eclipse template

Posted by David Chandler on March 12, 2010

The AppEngine admin console log viewer is so powerful that it makes me want to log anything and everything. One obstacle to this (however minor) is that it’s a pain to write this for every class in which I want to log:

private static final Logger LOG = Logger.getLogger(MyLongClassName.class.getName());

Eclipse code templates to the rescue! Open Window | Preferences | Java | Editor | Templates and create a new template. Name it “log”, set the context to “Java type members”, and enter this pattern:

private static final Logger LOG = Logger.getLogger(${enclosing_type}.class.getName());
${imp:import(java.util.logging.Logger)}${cursor}

Click OK to save it. Now open any class in the editor, and type “log” followed by Ctrl-Space. Press Enter. Presto!

I feel a template for AsyncCallbacks coming on…

Posted in Eclipse | 3 Comments »

Tips on organizing GWT modules

Posted by David Chandler on November 19, 2009

My current project is at the point where I’m starting to work on an administrative UI. I wanted to package it as a separate GWT module in order to not clutter up the main application module. There is some code such as the domain model that is shared between the main app and the admin code, so the question is how to organize the modules in order to share code between them.

My first attempt was to have the admin module simply inherit the main app module named ROA in admin.gwt.xml, like this:

	<inherits name="com.roa.app.ROA" />

This had two undesired results:

  • Both modules declare an entry-point, so GWT tries to load both, beginning with the inherited module.
  • At GWT compile time, all the code from the inherited module was duplicated under the admin module in the war structure.

My next attempt was to have the admin and app Java packages each point to a third sister package called “common” containing the domain model and other common code. Java is happy with this, but in order for GWT to compile the common code into JavaScript, you must make it a module. So now there are three packages, each with its own module:

admin.gwt.xml
app.gwt.xml
common.gwt.xml

Admin and app each have entry points and inherit the common module, which does not define an entry point. This works fine.

In the common module, I use the source tag to include multiple directories instead of just the default client directory. Note that if you specify one or more source directories, GWT no longer compiles the client directory by default, so you have to explicitly include it, as well:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.7.1//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.7.1/distro-source/core/src/gwt-module.dtd">
<module>
	<inherits name="com.google.gwt.user.User" />

	<source path="client" />
	<source path="domain" />
	<source path="presenter" />
</module>

Also, I moved the “public” directory containing CSS and images from the admin and app packages into the common module, and discovered that GWT does not create a “common” directory in the war, but rather copies the contents of the public folder from the inherited common module into both the admin and app module directories. That’s OK, though, as my CSS and images now exist in only one place in source control.

A final note: thank goodness for Eclipse refactoring tools. The easiest way to move a package (say, com.roa.app.domain to com.roa.common.domain) is to select it in the Project Explorer, then right-click, Refactor, and Rename… It’s a bit non-intuitive to use Rename rather than the Move command, but the latter appears to work only for moving packages to another project or source folder, whereas rename allows you to “move” it within the current source folder. Eclipse automatically fixes all the package and import declarations in affected code (whew!).

Posted in Eclipse, Google Web Toolkit, Headsmack | 14 Comments »

Eclipse keyboard shortcut for working with interfaces

Posted by David Chandler on November 3, 2009

Maybe everyone knows this already, but in case not…

Put your cursor in the name of an interface (say, a Display interface nested in a GWT presenter) and press Ctrl-T. Eclipse shows you all the classes that implement the interface and you can then navigate directly to an implementing class. I frequently use this in combination with Ctrl+Alt+H to find out how a method gets called.

You can find all of my favorite keyboard shortcuts in my Eclipse Google notebook listed in the left sidebar.

Posted in Eclipse | 1 Comment »

More Benefits of gwt-dispatch

Posted by David Chandler on September 25, 2009

When you use gwt-dispatch, a single dispatch servlet handles all service requests, so you no longer need to configure individual servlets in web.xml. Furthermore, action handlers are configured in Java code, like this:

		bindHandler(FindPrayerListsAction.class, FindListsHandler.class);
		bindHandler(AddPrayerListAction.class, AddPrayerListHandler.class);
		bindHandler(DeletePrayerListsAction.class, DeletePrayerListsHandler.class);

Because all configuration is done in Java, you can rename any handler, Action, Result, etc., and the Eclipse refactoring tools automatically rename it everywhere.

And speaking of Eclipse, did you know you can type only the capital letters of a class name and hit Ctrl+Space to let Eclipse automatically expand it? For example, to type “FindPrayerListsAction” above, I could simply type “FPLA” and hit Ctrl+Space. This also works in the Open Type (Ctrl+Shift+T) and Open Resource (Ctrl+Shift+R) dialogs.

Posted in Eclipse, Google Web Toolkit, Model-View-Presenter | Leave a Comment »

Eclipse Keyboard Shortcut of the Week

Posted by David Chandler on August 14, 2006

Just a quickie time-saver here. I hate having to use the mouse to navigate through code (yes, I can still use vi) as it slow and bothered my right shoulder enough over time to force me to mouse with my left hand. If you’re like me, you’ll want to know about:

Ctrl+Shift+T (Open Type) Just type the first few letters of the Java class you’re looking for, and voila, you can use the arrow keys to find exactly the right one. No more clicking on folders in Package Explorer.

Ctrl+Shift+R (Open Resource) Same drill, but works for any resource in the Package Explorer.

You can find a bunch more of my favorite keyboard shortcuts in my Eclipse Google Notebook (linked on left).

/dmc

Posted in Eclipse, Ergonomics | Comments Off on Eclipse Keyboard Shortcut of the Week

 
%d bloggers like this: