Category Archives: GWT

powermock – real power for testing untestable stuff

Last weekend was spent to make my Chrome extension base on GWT working with content scripts written using GWT. As one of main concept is to keep my trust for my code on appropriate level. To achieve this I’m practicing in TDD. For testing purposes I use JUnit 4.x and for mocking Mockito.

I’d like to spend some thime on MockitoMockito is really powerful mocking framework with sweat features, all ones you can find here http://code.google.com/p/mockito/wiki/FeaturesAndMotivations. As for me I really like nice and laconic syntax ans simplicity.

The information above is not a main reason of this post, the main one is to tell about my experience to test code using GWT classes e.g. finals and statics. After some time spent to google something will be helpful I’v found out PowerMock. This framework provides abilities to mock static classes and final methods and as you might know GWT uses a lot of such stuff.

For example I need to test method which handles browser page’ events from content script and pass related data to background page to process those data. The issue was to assign event for whole page, this is impossible as RootPanel.get() does not support event handlers. So I’ve found out another way – use Event.addNativePreviewHandler and NativePreviewHandler to hande events.

So I created implementation of NativePreviewHandler and started from test. Logic is quite simple but I was unable to use GWT code in my POJT (Plain Old Java Test), as NativeEvent returned by NativePreviewEvent as an argument for method NativePreviewHandler.onPreviewNativeEvent requires GWT.create method call, which in turn need a GWT context to be set up. But this is not what I want.  I’d like to have simple and fast tests without necessity to compile Java to JavaScript and only after to test.

In this situation I need mock GWT classes to be able to test my code, but Mockito does not support mocking for static classes and final methods. So PowerMock solved my trouble. Example of usage:

@RunWith(PowerMockRunner.class)
@PrepareOnlyThisForTest({ NativeEvent.class, SelectionProvider.class })
@SuppressStaticInitializationFor({ "com.google.gwt.dom.client.NativeEvent" })
public class ContentScriptEventHandlerTest {
 
        @Mock
	private ChromePort port;
 
        // Creates mock for class with final methods
	private NativeEvent event = PowerMockito.mock(NativeEvent.class);
 
	@Before
	public void setUp() throws Exception {
		MockitoAnnotations.initMocks(this);
                // Creates mock for static class
		PowerMockito.mockStatic(SelectionProvider.class);
                // assign return value for method from static class
		PowerMockito.when(SelectionProvider.getSelection()).thenReturn("word");
                // there was stackoverflow and we do not need to call for equals
		PowerMockito.suppress(event.getClass().getMethod("equals", Object.class));
                // assign return values for final methods
		PowerMockito.doReturn("mouseup").when(event).getType();
		PowerMockito.doReturn(true).when(event).getCtrlKey();
		PowerMockito.doReturn(true).when(event).getAltKey();
	}
 
	@Test
	public void shouldNotConsumeNotMouseupEvent() throws Exception {
		ContentScriptEventHandler eventHandler = new ContentScriptEventHandler(port);
		NativePreviewEvent previewEvent = mock(NativePreviewEvent.class);
		PowerMockito.doReturn("notamouseevent").when(event).getType();
		when(previewEvent.getNativeEvent()).thenReturn(event);
 
		eventHandler.onPreviewNativeEvent(previewEvent);
		verify(previewEvent, never()).consume();
	}
}

That’s it. Have enjoy testing…

Build extension for Google Chrome with GWT

From time-to-time I’m coming back to do some coding to provide Chrome Extension with helping to translate and remember words.

From this repository http://github.com/webdizz/clt now you can download the source code of the extension, however it is in the pre-pre-alpha state. But the main reason of this post is to tell about implemented abilities to write ContentScript using GWT.

Initial code of the GWT linker for Chrome Extension was pulled from SpeedTracer extension, after that I’ve provide some enhancements to support additional features.

  • GWT Linker for Chrome Extension was extracted into separate subproject;
  • ComponentGenerator was refactored and provided with initial pack of tests;
  • ExtensionScript artifact was added – which provides you abilities to define external JavaScript file to be injected into the BackgroundPage and loaded on extension initialization. This is useful for example when you are working with Google Language API or another required JavaScript library which you cannot include as a static content;
  • ContentScript now support all_frames attribute;
  • GwtContentScript artifact was introduced – which provides you abilities to implement ContentScript code with GWT and include it as a static ContentScript into your extension. Example of this you can find in my extension. But there is an issue – I’m not able to resolve path to extracted war to copy generated JavaScript file from GWTContentScript module into extension module, but there is workaround for this – you need to define own launcher (for Eclipse) to build extension. Example launcher can be downloaded here http://github.com/webdizz/clt/blob/master/clt-crx/Build-clt-extension.launch.

That’s it for now, have a nice week.

GWT based Chrome extension

Доброго времени суток, однако прошел уже месяц с хвостиком с момента моего последнего опуса, но дабы не допустить возникновения паутины в собственном блоге хочу поделиться повествованием об освоении написания расширения для браузера Chrome.

Как обычно знакомство с чем-то новым и неизведанным является очень интересным и занимательным занятием, помимо этого еще и с присутствием “challenges” . Ближе к делу…

Итак в этот раз я взялся за написание плагина для Chrome. Я не буду описывать архитектуру API для написания расширений, т.к. заинтересовавшийся разработчик сможе ознакомиться с ней тут. Вкратце – плагин представляет собой файл-декларация плагина – manifest.json, background-html страницу (содержит общую логику расширения) и подключаемые js-файлы. Написание плагина на чистом JavaScript не представляет собой огромного интереса, в отличии от использования для этого GWT.

Как оказалось официальной поддержки Chrome extensions в GWT нет, но погуглив я наткнулся на кое-какую информацию по эьлму вопросу. В релиз GWT 2.0 включается расширение для Chrome Speed Tracer, которое написано с использованием GWT. Заложенные возможности к гибкому расширению с помошью GWT linker позволяют применять этот замечательный фреймворк очень широко.  Сделав чекаут проекта Speed Tracer я начал вникать в способ написания расширения. Как оказалось линкер в Speed Tracer не поддерживает ряд появившихся в новом API Chrome методов и типов, так мною были дописаны возможность подключать скрипты в background page, пости-инициализация подключаемых скриптов и немного доработан линкер до нужд моего расширения.

Исходный код линкера  расширеним будет предоставлен немного позже, а пока перечислю некоторые проблемы, с которыми пришлось столкнуться мне.

  • коммуникация между страницами и скриптами раширения – временно решена использованием паттерна Singleton;
  • логика перехвата событий должна находиться в подключаемых скриптах, т.к. они исполняются в контексте загруженной страницы.

На этом пока все, продолжение следует…

4kGadget v1.1-beta

Доброго времени суток!

На этот раз могу и хочу поделиться с вами небольшой информацией об изменениях в гаджете для сервиса 4Конверта.  Преследуя выполнение желаний пользователей гаджета и собственно своих, в работе над новой версией гаджета были произведены следующие изменения:

  • появилась возможность вносить изменения в регулярные траты и цели. Это можно сделать нажав на дату, для которой есть регулярные траты либо зачисления на цель -> в появившемся окне появились кнопки дл редактирования , нажав на которую появится поле для ввода суммы зачисления. Сумма будет занесена на цель для счета по-умолчанию после потери фокуса с поля ввода;
  • улучшения отображения в Chrome;
  • перевод на использование API 1.1 сервиса 4конверта;
  • перевод на GWT 2.0, в частности использование UiBinder в некоторых виджетах;
  • оптимизация и улучшение кода.

На этом все, всем спасибо.

Эффективного бюджетного планирования.

P.S. С удовольствием принимаются комментарии и пожелания.

Реализация MVP паттерна для GWT – Mvp4g – дополнения

Последнее время мое внимание привлекала технология для разработки RIA – GWT. И как это обычно бывает в каждом подходе и технологии есть свои достоинства и недостатки, и GWT не исключение. Но описание достоинств и недостатков этой технологии гораздо шире и объемнее того, что я хотел бы написать в данном посте. Я хочу сосредоточиться на использовании и о небольших правках, сделанных мной для реализации паттерна MVP – опен-сорс проекта Mvp4g.

Все , что нужно для начало работы с этой реализацией вы можете получить на страницах проекта.

При использовании этого фреймворка у меня возникла необходимость в обеспечении модулярности и возможности указания альтернативного конфигурационного файла.

Модулярность – под модулярностью понимается возможность указания дополнительного конфигурационного файла, который так же будет содержать декларации views,  presenters, services и events,  относящихся только к этому модулю. Так же при использовании GWT 2.x и указании аттрибута async активация данного подуля будет обвернута в RunAsyncCallback вызов, что в свою очередь обеспечит разделение вашего приложения на несколько отдельных частей и загрузка каждой части будет происходить только в случае необходимости. В целом же конфигурационный файл приложения, поддерживающего модульность не отличается от стандартного, за исключением следующего момента:

<module source="modules/module.xml" event="activationEvent" async="true"/>

Где source – указывает на файл конфигурации модуля, event – говорит на какое событие модуль будет активирован и async – указывает будет ли активация модуля обворачиваться в RunAsyncCallback вызов.

Еще одной доработкой является возможность указания дополнительного конфигурационного файла. Данная функциональность может пригодиться в таких случаях как, например, у вас есть админка, которая тоже построена на GWT, но вы не хотите фунционал админки с фронтендом мешать в одну кучу. Что же нужно для этого? Для этого нужно наследовать интерфейс com.mvp4g.client.Mvp4gStarter и аннотровать свой интерфейс с помошью аннотации com.mvp4g.client.Configuration, указав в качестве значения путь к альтернативному конфигурационному файлу. Затем в entry point классе создать имплементацию интерфейса с помощью GWT.create().

Пример интерфейса:

@Configuration("mvp4g-alt-conf.xml")
public interface CustomMvp4Starter extends Mvp4gStarter {}

Пример кода в  entry point классе:

public class TestMvp4gEntryPoint implements EntryPoint {
	private Mvp4gStarter entry;
 
	public void onModuleLoad() {
		entry = GWT.create(CustomMvp4Starter.class);
		entry.start();
	}
}

На этом все, скачать правки можно тут.

GWT-2.x and maven

Хочу поделиться скриптом, который мне помогает сохранить время при работе над GWT-2.x проектом. В виду того, что эта ветка находиться в постоянной разработке, но использование стабильной ветки не подходит, то необходимо периодически обновляться до актуальной версии. Мы используем Maven для сборки проекта и локальный репозиторий (Artifactory) и после каждого обновления нужно деплоить в репозиторий новые джарники. Что бы избавиться от рутины предлагаю воспользоваться следующим скриптом. Ни чего замысловатого – просто вызов mvn deploy-file …. Ниже собственно содержимое скрипта:

@echo off
 
echo ====================================================================================
echo Start deploying GWT artifacts
echo ====================================================================================
 
SET GWT_SDK_VERSION=2.0.0
SET GWT_SDK_PATH=D:\src\lib\gwt2\trunk\build\staging\gwt-windows-2.0.0
SET REPO=http://artifactory:8081/artifactory/libs-releases-local
SET LREPO=file:///D:\src\repository
 
echo ====================================================================================
echo Deploy gwt-servlet.jar
echo ====================================================================================
call mvn deploy:deploy-file -DrepositoryId=artifactory -Durl=%REPO% -DgeneratePom=true -DgroupId=com.google.gwt -Dversion=%GWT_SDK_VERSION% -Dpackaging=jar -Dfile=%GWT_SDK_PATH%/gwt-servlet.jar -DartifactId=gwt-servlet 
call mvn deploy:deploy-file -DrepositoryId=artifactory -Durl=%LREPO% -DgeneratePom=true -DgroupId=com.google.gwt -Dversion=%GWT_SDK_VERSION% -Dpackaging=jar -Dfile=%GWT_SDK_PATH%/gwt-servlet.jar -DartifactId=gwt-servlet 
 
echo ====================================================================================
echo Deploy gwt-user.jar
echo ====================================================================================
call mvn deploy:deploy-file -DrepositoryId=artifactory -Durl=%REPO% -DgeneratePom=true -DgroupId=com.google.gwt -Dversion=%GWT_SDK_VERSION% -Dpackaging=jar -Dfile=%GWT_SDK_PATH%/gwt-user.jar -DartifactId=gwt-user 
call mvn deploy:deploy-file -DrepositoryId=artifactory -Durl=%LREPO% -DgeneratePom=true -DgroupId=com.google.gwt -Dversion=%GWT_SDK_VERSION% -Dpackaging=jar -Dfile=%GWT_SDK_PATH%/gwt-user.jar -DartifactId=gwt-user 
 
echo ====================================================================================
echo Deploy gwt-dev-oophm.jar
echo ====================================================================================
call mvn deploy:deploy-file -DrepositoryId=artifactory -Durl=%REPO% -DgeneratePom=true -DgroupId=com.google.gwt -Dversion=%GWT_SDK_VERSION% -Dpackaging=jar -Dfile=%GWT_SDK_PATH%/gwt-dev-oophm.jar -DartifactId=gwt-dev-oophm 
call mvn deploy:deploy-file -DrepositoryId=artifactory -Durl=%LREPO% -DgeneratePom=true -DgroupId=com.google.gwt -Dversion=%GWT_SDK_VERSION% -Dpackaging=jar -Dfile=%GWT_SDK_PATH%/gwt-dev-oophm.jar -DartifactId=gwt-dev-oophm 
 
echo ====================================================================================
echo Deploy gwt-dev-windows.jar
echo ====================================================================================
call mvn deploy:deploy-file -DrepositoryId=artifactory -Durl=%REPO% -DgeneratePom=true -DgroupId=com.google.gwt -Dversion=%GWT_SDK_VERSION% -Dpackaging=jar -Dfile=%GWT_SDK_PATH%/gwt-dev-windows.jar -DartifactId=gwt-dev -Dclassifier=windows
call mvn deploy:deploy-file -DrepositoryId=artifactory -Durl=%LREPO% -DgeneratePom=true -DgroupId=com.google.gwt -Dversion=%GWT_SDK_VERSION% -Dpackaging=jar -Dfile=%GWT_SDK_PATH%/gwt-dev-windows.jar -DartifactId=gwt-dev -Dclassifier=windows
 
echo ====================================================================================
echo Deploy gwt-dev-windows-libs.zip
echo ====================================================================================
call mvn deploy:deploy-file -DrepositoryId=artifactory -Durl=%REPO% -DgeneratePom=true -DgroupId=com.google.gwt -Dversion=%GWT_SDK_VERSION% -Dpackaging=zip -Dfile=%GWT_SDK_PATH%/gwt-dev-windows.jar -DartifactId=gwt-dev -Dclassifier=windows-libs
call mvn deploy:deploy-file -DrepositoryId=artifactory -Durl=%LREPO% -DgeneratePom=true -DgroupId=com.google.gwt -Dversion=%GWT_SDK_VERSION% -Dpackaging=zip -Dfile=%GWT_SDK_PATH%/gwt-dev-windows.jar -DartifactId=gwt-dev -Dclassifier=windows-libs

Google AppEngine first look

С недавних пор свое свободное время посвящаю реализации одной затеи в качестве платформы мы решили испоьзовать Google AppEngine для Java. И как оно обычно бывает с новыми и относительно неизвестными технологиями, с GAE это как оказалось – не исклбючение, появилось много проблем.

Проблемы в основном заключаются в сыроватости предоставляемых интерфейсов и органиченности их возможностей, думаю пока.

Начиналось все довольно спокойно – я установил SDK, поколдовал над рабочим окружением, что бы стало удобно работать и пустился в кодинг.

В моих планах было исследовать возможность работы Spring Framework 3.0.x, в частности меня интересует Spring MVC, а в качестве View – обычные старые добрые JSP. Со “спрингом” проблем особых не было, завелся, как говориться с пол пинка;).

Далее  – в качестве слоя для работы с базой взор был остановлен на JPA. Почему – потому что знакома и относительно привык работать с этой технологией. Но вот тут-то и начались “приколы”. Да на первый взгляд все заработало, но. Перечень “па” с бубном:

  • если вы используете Flex или GWT (как я) и хотите работать с объектами на стороне клиента, то одним из возможных подходов есть использование DTO объектов. В качестве конвертера объектов модели и объектов для клиентов я использую Dozer. И как оказалось в моем случае, из-за ограниченности в вариациях типов PrimaryKey, мне пришлось настраивать кастомные мапперы. В качестве PrimaryKey я использую предоставляемы Datastore тип Key, а клиент хранит его в виде строкового представления. До этого момента я получал сообщения об ошибках различного рода, связанные с ClassLoader-ом, с отсутствием поддержки типов ключей и т.д;
  • отсутствие поддержки связей. То есть если вам необходимо создать отношение One-To-Many, Many-To-Many или Many-To-One – у вас не будет возможности это сделать стандартным подходом. Это делается путем сохранения в ссылки на объект через его Key и последующей выборкой в случае необходимости. Не очень удобно, но работает. Надеюсь этого будет не всегда;
  • интересный нюанс с EntityManager.persist() или EntityManager.merge() – пока не вызовешь EntityManager.flush(), объект не будет сохранен. То есть если мне сразу же нужно вернуть сохраненный объект как результат выполнения метода, то я получу только то, что передал, хотя объект будет сохранен, но после.
  • еще вылезла проблема с использованием Spring Forms тегов. Немного погуглив, нашел решение проблемы – добавить в декларацию JSP аттрибут isELIgnored=”false” и переопределить PropertyEditors для каждого типа, используемого в бине, представляющем форму.

Так же еще одной из задач, которую нужно было решить – это подъем локального GAE сервера (в качестве сервлет контейнера используется Jetty) совместно с запуском GWT в дебаг-режиме. Тут конечно пришлось попотеть, т.к. стандартный подход Google к структуре проектов и отсутствие нативной поддержки с помощью Maven вынуждает к этому. Проблемы в основном были опять же с определением что откуда должно грузиться и совмещение это с тем, как мне нужно. После продолжительных не замысловатых действий вида “добавить ресурс, удалить ресурс” у меня все получилось. Теперь мой проект стартует из распакованного war-ника, поднимает Spring-контекст и подтягивает изменения в GWT на лету без перекомпиляции – то, что надо. Правда еще бы хотелось править JSP , без необходимости их последующего копирования в дерево проекта, но тогда придется жертвовать “чистотой” исходников проекта. Далее привожу свой конфиг для запуска GWT приложения в GAE контейнере.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
	<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
		<listEntry value="/webapp-ui" />
	</listAttribute>
	<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
		<listEntry value="4" />
	</listAttribute>
	<mapAttribute key="org.eclipse.debug.core.environmentVariables">
		<mapEntry key="JAVA_OPTS" value="-Xms128M -Xmx512M -XX:MaxPermSize=256M" />
		<mapEntry key="-Dappengine.sdk.root" value="D:\src\lib\gae-1.2.5" />
	</mapAttribute>
	<stringAttribute key="org.eclipse.debug.core.source_locator_id"
		value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector" />
	<stringAttribute key="org.eclipse.debug.core.source_locator_memento"
		value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;restio&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;lib-controller&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;lib-domain&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;lib-model&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;lib-service&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;lib-service-impl&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;mvp4g&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;javaProject name=&amp;quot;webapp-ui&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;" />
	<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;com.google.appengine.eclipse.core.GAE_CONTAINER&quot; path=&quot;3&quot; type=&quot;4&quot;/&gt;&#13;&#10;" />
		<!-- GWT 2.x feature - ability to start HostedMode within browser -->
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;D:/src/repository/com/google/gwt/gwt-dev-oophm/2.0.0/gwt-dev-oophm-2.0.0.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;D:/src/repository/commons-configuration/commons-configuration/1.6/commons-configuration-1.6.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;com.google.gwt.eclipse.core.GWT_CONTAINER&quot; path=&quot;3&quot; type=&quot;4&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;D:/src/repository/name/webdizz/rest/io/lib-service/0.0.1/lib-service-0.0.1.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;D:/src/repository/com/mvp4g/mvp4g/1.0-SNAPSHOT/mvp4g-1.0-SNAPSHOT.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-widget/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/lib-domain/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/lib-service/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/gwt-base/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/webapp-ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/webapp-ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/webapp-ui/src/main/webapp&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
	</listAttribute>
	<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER"
		value="org.maven.ide.eclipse.launchconfig.classpathProvider" />
	<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH"
		value="false" />
	<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER"
		value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/1.6" />
	<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE"
		value="com.google.gwt.dev.HostedMode" />
	<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS"
		value="-startupUrl /view/&#13;&#10;-war D:\tmp\restio\webapp-ui\webapp-ui&#13;&#10;-server com.google.appengine.tools.development.gwt.AppEngineLauncher&#13;&#10;name.webdizz.rest.io.ui.Engine" />
	<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR"
		value="webapp-ui" />
	<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER"
		value="org.maven.ide.eclipse.launchconfig.sourcepathProvider" />
	<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS"
		value="-Xmx512M" />
	<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY"
		value="D:\tmp\restio\webapp-ui\webapp-ui" />
</launchConfiguration>

Хотел было прокомментировать код, но начав, понял – это не очень уместно т.к. названия он интуитивно понятен.
На это все, продолжим исследования…

GWT 1.7 on SpringSource dm Server 2.0.0.M3 UPD

В продолжении исследований GWT в совокупности с dmServer я наткнулся на одну загадочную поначалу проблему – сериализация.

Что это такое сериализация. Я не буду рассматривать и рассказывать, что собой представляет сериализация и для чего она нужна – это вы можете легко узнать, благо в сети найдутся хорошие публикации на эту тему, в данном же контексту меня интересует сериализация, которую использует GWT-RPC. GWT-RPC – это подход, который используется в GWT для осуществления I/O общения между клиентской  и серверной частами приложения. Этот подход позволяет оперировать с Java-объектами на стороне клиента, но есть определенное количество нюансов, о которых можно узнать по приведенной выше ссылке.

В моей ситуации произошла следующая ситуация. Мое приложение имеет слой для работы с базой данных с помошью JPA и в качестве JPA Provider я использую Hibernate. Так же у меня имеется DTOs, с которыми оперируют интерфейсы приложения, об Entity объектах знают только имплементации сервисных интерфейсов. Сервисы менеджется и публикуются в OSGi Service Registry Spring-ом. Клиентское приложение получает ссылку на сервис с помошью специального сервлета, который немного расширяет функционал RemoteServiceServlet и предоставляет возможность по названияю сервиса получать ссылку на него из Spring WebApplication Context.
Continue reading GWT 1.7 on SpringSource dm Server 2.0.0.M3 UPD

Отладка GWT приложений на внешнем контейнере

Background

В предыдущем посте было упомянуто о необходимости запускать GWT приложение в Hosted Mode, используя при этом dmServer, тут же я бы хотел поделиться шагами, через которые мне пришлось пройти, чтобы реализовать эту возможность.

Итак как это было…

Прочитав FAQ тут, как говорится с “пол пинка” не завелось к сожалению( Но на этом мое намерение отлаживать мое приложение в dmServer не испарилось. Что я сделал – изменил настройки ланчера для запуска приложения в Hosted Mode: изменил параметр для открытия URL -startupUrl и добавил парметр, говорящий ланчеру не запускать встроенный сервлет контейнер (для GWT 1.7 – Jetty). Содержимое конфигурационного файла приведено ниже.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
	<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
		<listEntry value="/stock-watcher" />
	</listAttribute>
	<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
		<listEntry value="4" />
	</listAttribute>
	<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables"
		value="true" />
	<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;stock-watcher&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/stock-watcher/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;" />
		<listEntry
			value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;stock-watcher&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;" />
	</listAttribute>
	<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER"
		value="org.maven.ide.eclipse.launchconfig.classpathProvider" />
	<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH"
		value="false" />
	<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE"
		value="com.google.gwt.dev.HostedMode" />
	<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS"
		value="-startupUrl http://localhost:8080/stock-watcher/StockWatcher.html&#13;&#10;name.webdizz.gwt.stock.watcher.StockWatcher&#13;&#10;-war src/main/webapp&#13;&#10;-noserver" />
	<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR"
		value="stock-watcher" />
	<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER"
		value="org.maven.ide.eclipse.launchconfig.sourcepathProvider" />
	<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS"
		value="-Xmx256M" />
</launchConfiguration>

На этом мои действия, исходя из смысла приведенной выше статьи, должны были увенчаться успехом, но это было не совсем так. Дальнейшие “танцы с бубном” показали, что так как я использую Maven для сборки проекта и, в частности, gwt-maven-plugin для компиляции GWT специфичных артифактов, проблема скрывалась за несогласованными действиями Google Eclipse plugin и Maven. Этот момент был устранен настройкой maven-war-plugin в файле pom.xml проекта stock-watcher. Я исключил копирование содержимого директории src/main/webapp/stockwatcher из src/main/webapp/ (директива warSourceExcludes).

46
47
48
49
50
51
52
<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-war-plugin</artifactId>
	<configuration>
		<warSourceExcludes>**/stockwatcher/**</warSourceExcludes>
	</configuration>
</plugin>

`После этого собранный проект с помошью команды

mvn clean install

Результат сборки копируем в pickup директорию dmServer-а и запускаем ланчер в Eclipse. Ставим breakpoint и наслаждаемся…;) правда перед тем как окунуться в дебаг не знаю почему, но необходимо Hosted Browser рефрешить 5 раз и на 6-ой мы попадаем на точку останова)).

Debugging GWT application
Debugging GWT application

GWT 1.7 on SpringSource dm Server 2.0.0.M3 Part2

Previous background:

В этом посте я продолжу повествование о попытке запуска GWT приложения в виде OSGi бандла в dmServer-е. В предыдущем посте мы немного изменили структуру проекта, поправили сборку проекта с помошью Ant и запустили приложение в dmServer-е как обычный war-ник.

What next:

В данном посте мы попытаемся создать OSGi бандл из нашего war-ника и задеплоить его в dmServer, а также перевести сборку проекта с помошью Ant на Maven, что в будущем будет более эффективно и гибко. Let’s go)

Continue reading GWT 1.7 on SpringSource dm Server 2.0.0.M3 Part2