Loom


Stripesにかなり似てるんですが、結構良い感じのActionベースのフレームワーク
AjaxianではアノテーションベースなWebフレームワークという紹介でしたがそうでもないかも。

レスポンスとしてどういうものを返すかをResolutionというクラスで表現しているのですが
これはかなりStripesに似てる。T2もその辺はStripesのシンプルっぽさを真似て
Navigationを作っているので似たような感じです。

サンプルのコードはこんな感じのようです。

public class MortgagesAction extends AbstractActionImpl {

	/** controller that handles the CRUD stuff */
	@Injected
	private BasicManager basicManager;
	
	@Injected
	private DemoManager demoController;
	
	/** the list in the main page */
	private PaginatedListImpl mortgages;
	
	/** the current editing instance */
	@NestedAnnotations(on="save")
	@RetrieveEntity(on={"save", "preEdit"})
	private Mortgage mortgage;
	
	/** the uploaded file, if any */
	@NumberValidation(propertyPath="fileSize", maxValue="102400", message="error.custom.imageFileSize", on="save")
	@StringValidation(propertyPath="filename", mask="(.*?)\\.(jpg|jpeg|png|gif)$", message="error.custom.imageFileName", maskCaseInsensitive=true, on="save")
	private PersistentFile uploadedFile; 
	
	/** list of selected rows */
	private Set<Integer> selectedRows;
	
	private static Log log = Log.getLog();
	
	@Event(defaultEvent=true)
	public Resolution listAll() {
		mortgages = new PaginatedListImpl(context.getRequest());
		basicManager.query(mortgages, "from Mortgage mortgage");
		return forward("listAll.jsp");
	}
	
	public Resolution preEdit() {
		return forward("editMortgage.jsp");
	}
	
	public Resolution save() {
		mortgage = demoController.merge(mortgage, uploadedFile);
		return redirect(MortgagesAction.class, "listAll");
	}
	
	/**
	 * Delete a selected list of Mortage instances
	 */
	public Resolution delete() {
		if (selectedRows != null) {
			for (Integer rowId : selectedRows) {
				try {
					demoController.delete(rowId);
				} catch (EntityNotFoundException e) {
					log.debug("Mortgage instance " + rowId + " could not be deleted because it was not found: " + e.getMessage());
				}
			}
		}
		return redirect(MortgagesAction.class, "listAll");
	}
	
	@SuppressWarnings("unchecked")
	public Set<Option> getCountries() {
		LabelComparator comparator = new LabelComparator();
		comparator.setRepository(getMessagesRepository());
		Set<Option> countries = new TreeSet<Option>(comparator);
		Locale currentLocale = getMessagesRepository().getLocale();
		Locale[] locales = Locale.getAvailableLocales();
		for (Locale locale : locales) { 
			countries.add(new Option(locale.getCountry(), locale.getDisplayCountry(currentLocale), false)); // translated by locale.getDisplayCountry()
		}
		return countries;
	}
	
	public Mortgage getMortgage() {
		return mortgage;
	}

	public PaginatedListImpl getMortgages() {
		return mortgages;
	}

	public void setBasicManager(BasicManager basicManager) {
		this.basicManager = basicManager;
	}

	public void setMortgage(Mortgage mortgage) {
		this.mortgage = mortgage;
	}

}

インジェクトする先の@Injectedでの明示的な指定とか、遷移先を
return redirect(MortgagesAction.class, "listAll");とかして上手く指定できるあたりは
中々よいんじゃないかと思います。ちょうどよい感じ。

ただし、AbstractActionImpl の継承はちょっといまいちかな。貴重なextendsはユーザさんのためにあけておいて
あげたいのが心情。あと、Validationがやや押し付けがましい感じがするかな。これは好みの問題ですが、
Validationもユーザさんが好きなものを選べばいいと思います。


全体としては好印象を受けました。SpringがDIコンテナのデフォルトですが、DependencyInjectionAdapterを実装して
好きに切り替えられるのでその辺の手軽さも好きです。