Статьи

Создание GWT-приложений корпоративного класса для Spring

MyEclipse для весны 8,6

Genuitec и Skyway Software недавно анонсировали производственную версию MyEclipse для Spring 8.6 . Последний выпуск расширяет набор расширенных ускорителей для разработки Spring, предоставляя новые возможности для быстрой генерации приложений Adobe Flex, Spring MVC, Google Web Toolkit (GWT), Spring Web Flow и iPhone. В этом выпуске также представлен новый набор редакторов разработки Spring, Code Assistants, которые облегчают разработку на основе аннотаций Spring и JAX-WS.

MyEclipse for Spring (ME4S) 8.6 теперь поддерживает создание программных компонентов многократного использования на основе вашей модели данных. Если вы начинаете с таблиц RDBMS, POJO, объектов JPA или WSDL, ME4S может дать вам много отличных технологий для использования в вашем проекте. Вы можете увидеть, как эти компоненты можно использовать в базовом, работающем приложении на основе CRUD сразу после того, как вы завершите работу над простым мастером, но большая ценность действительно заключается в возможности использовать все эти технологии при создании других частей. вашего проекта. Если у вас есть существующий проект и вы хотите выйти из бизнеса по поддержанию уровня JPA / DAO / Service вручную, ME4S также может взять на себя эту ответственность за вас.

С выпуском 8.6 мы добавили множество новых вариантов генерации пользовательского интерфейса, потому что мы постоянно слышим от разработчиков Java, что при изучении использования таких технологий, как Flex и Google Web Toolkit (GWT), иногда трудно найти отличные примеры. Разработчики сказали нам, что, когда они начинают процесс оценки этих технологий, было бы неплохо продемонстрировать, что они работают контекстуально с собственными данными для своих внутренних и внешних партнеров. ME4S позволяет вам брать существующую модель данных из приложения, которое необходимо модернизировать, и очень быстро генерировать базовые сервисы, которые вам нужны на бэкэнде, а также создавать приятные чистые компоненты пользовательского интерфейса, адаптированные для вашей модели данных.

Если вы создали программное обеспечение, используя практически все, что заканчивается в WT, вы будете знать, что много времени уходит на интеграцию компонентов пользовательского интерфейса Windowing Toolkit в модель данных и настройку приложения для использования передовых методов для этой технологии пользовательского интерфейса. ME4S делает многое для вас и, как всегда, гарантирует, что у вас есть работающий пример на каждом этапе процесса.

GWT

В этой статье я расскажу о том, что генерируется из возможностей генерации GWT, более подробно. Я рассмотрю параметры генерации внутреннего интерфейса Spring на высоком уровне, поскольку этот стек используется всеми нашими сгенерированными пользовательскими интерфейсами, а затем углублюсь в код Java, который генерируется для внешнего интерфейса. Важно отметить, однако, что внешний интерфейс GWT также может быть сгенерирован так, чтобы быть независимым от персистентного уровня, так что вы можете связать свой внешний интерфейс GWT с любой внутренней реализацией, которую вы пожелаете.

Предпосылки

Если вы еще не прочитали учебник ME4S по GWT Scaffolding, вам следует прочитать его здесь . Этот учебник шаг за шагом проведет вас через процесс использования программного обеспечения для создания вашего приложения GWT. Как только вы освоитесь с этой информацией, мы можем немного углубиться в код, который был фактически сгенерирован.

Философия дизайна

Интеграция GWT в MyEclipse для Spring предназначена специально для GA-версии GWT и наиболее распространенных версий Spring (как 2.5, так и 3.0). Мы хотели убедиться, что разработчики имели инструменты, поддерживающие технологии, доступные им прямо сейчас для использования в производственных приложениях. ME4S в настоящее время поддерживает GWT 2.0.4, который обычно доступен во время выпуска 8.6. Мы намерены поддерживать GWT 2.1 как можно быстрее, поскольку он приближается к общедоступности. Код , который генерируется из ME4S предназначен следовать рекомендациям по GWT 2.0 , как прописанные Ray Ryan «s презентации Google I / O .

Мы также думали , что это было важно , чтобы включать в себя , как несколько 3 — й партии проектов GWT , как это возможно в реализации по умолчанию. Есть несколько отличных проектов и несколько отличных коммерческих поставщиков компонентов GWT, но мы хотели быть уверены, что в этом случае мы не приняли слишком много решений для вас, и что то, что мы создали для GWT, было основано на том, что вы может получить прямо из Google.

Краткое изложение целей

Цель Описание
Использовать существующие технологические активы Java Beans, RDBMS, JPA, WSDL
Архитектура MVP Следуйте подходу Presenter Model View
UiBinder Определите пользовательский интерфейс, используя UWinder GWT
Шаблон команды Инкапсуляция с помощью команд , как описано в Ray Ryan «s презентации Google I / O
DTO Используйте объекты Data Transfer для инкапсуляции данных, которые должны быть отправлены клиенту, и для управления отношениями
Свободные и ленивые отношения Продвигать шаблон проектирования, который откладывает загрузку связанных данных до тех пор, пока он не понадобится, благодаря свободному управлению взаимоотношениями на основе сервера.
Back End Agnostic Свяжите интерфейс GWT с автоматически сгенерированным уровнем персистентности Spring / JPA или просто создайте заглушку Service, а затем вручную закодируйте персистентность для интеграции с любым бэкэндом.
Поддержка I18N Создание пользовательских интерфейсов, которые уже настроены для локализации
Пакеты клиентов Используйте клиентские пакеты для оптимизации

Приложение

В руководстве GWT мы использовали базу данных Derby для песочницы, которая поставляется вместе с ME4S, для создания приложения GWT, которое управляет клиентами и платежами, используя таблицы Customer и Payment в качестве входных данных для процесса. Полученное приложение выглядит примерно так:

Это приложение предоставляет вам базовые возможности CRUD для управления вашими клиентами и платежами и имеет несколько хороших встроенных возможностей, включая сортируемые списки, разбивку на страницы, «средства выбора» для добавления и удаления элементов из отношений, редактирования с несколькими вкладками и т. Д. Этот пользовательский интерфейс это действительно набор из нескольких приятных, многократно используемых компонентов, которые могут быть переопределены в другие дизайны. Поскольку мы используем шаблон команд, архитектуру Event Bus и архитектуру на основе MVP, эти компоненты легко соединить в разные шаблоны приложений.

Понимание архитектуры

Посмотрев презентацию Google I / O по архитектуре GWT-приложений, вы найдете следующую иллюстрацию полезной для построения ментального представления об используемой архитектуре. Ключевыми архитектурными преимуществами являются четкое разделение между глупой моделью и представлением, которое также глупо, и Presenter, который изолирует логический (и тестируемый) код в вашем приложении. Действия и события, используемые на EventBus, позволяют чрезвычайно слабо связывать код в вашем приложении. Сервисный уровень RPC и удаленная служба GWT на стороне сервера действуют как асинхронная информационная служба. В этом случае служба GWT поддерживается созданным бэкэндом Spring / JPA.

Многоразовые программные компоненты

Следующая диаграмма иллюстрирует повторно используемые программные компоненты, которые были созданы в вашем проекте в результате выполнения строительных лесов GWT. Мастер скаффолдинга позволяет разработчику выбирать слои, которые он хочет сгенерировать, и те, которые он хочет исключить. В этом случае мы создали стандартную многоуровневую серверную архитектуру на стороне Spring с уровнем Service, DAO и Domain, а также с веб-уровнем, основанным на GWT. Сгенерированные компоненты GWT предлагают дополнительную информацию о коде, который используется для реализации архитектуры, как это было показано выше.

План проекта

Мастер скаффолдинга ME4S дает вам варианты того, куда вы хотите поместить свой код, как он упакован и т. Д. Давайте рассмотрим макет проекта по умолчанию для приложения, описанного выше.

Если мы примем значения по умолчанию, весь сгенерированный код и конфигурация будут помещены в папку с именем «сгенерировано». Вы все еще можете собрать весь свой собственный код в существующей папке src и использовать код в сгенерированной папке src. Сгенерированный код не имеет никакого отношения к инструментам или технологии генерации. Вы можете взять этот код и делать то, что вам нравится с ним на данный момент. Если вы хотите иметь возможность многократного запуска скаффолдинга, вам следует избегать внесения изменений в сгенерированные файлы .java, хотя есть варианты, связанные с повторной генерацией, которые мы подробно рассмотрим в отдельном блоге.

Компоненты «на стороне сервера» помещены в com.show. — (дао, домен и сервис) пакеты. Пакеты, начинающиеся с gwt.-, являются программными компонентами gwt. ME4S генерирует отдельный модуль GWT для каждого объекта Domain, чтобы вы могли смешивать и сопоставлять их позже, а также общий модуль с служебными классами и модуль точки входа, который объединяет их в пример приложения GWT.

Изучение модели GWT заказчика

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

Лучшие пакеты модулей GWT

Первые два пакета в модуле Customer — это пакеты gwt.customer и gwt.customer.client.

Пакет gwt.customer просто содержит файл определения GWT Module.xml для модуля Customer.

Модуль gwt.customer.client содержит представление объекта DTO для Customer, класс ID для уникальной идентификации Customer, интерфейсы Service и Async Service, а также сообщения CSS и I18N, используемые пользовательским интерфейсом Customer.

Пакет действий

Пакет действий содержит действия или команды, которые можно передавать на сервер и обратно для выполнения конкретных задач. Существуют действия для загрузки, хранения и удаления клиентов, а также Действия для управления отношениями с клиентом. Это важный момент, поскольку действия по управлению отношениями являются общими, так что вы можете использовать одно и то же действие для управления отношениями между SalesRep и Customer, а также Product и Customer. Этот подход также позволяет нам сделать экраны для управления отношениями с Заказчиком более привлекательными. Вы также заметите, что для каждого действия есть классы Action, объект ответа и действие «ed». Этот подход помогает инкапсулировать код для передачи этих действий и получения их асинхронных обратных вызовов. Далее мы рассмотрим конкретный пример.

Пакет мероприятий

Пакет событий объявляет набор событий, которые можно прослушивать на EventBus. Сгенерированные компоненты пользовательского интерфейса автоматически регистрируются для прослушивания на шине событий определенных событий, чтобы иметь дело с изменениями данных. Если вы хотите повесить какое-то новое поведение на стороне удаляемого клиента, например, все, что вам нужно сделать, это добавить обработчик CustomerDeleted в шину событий, и ваш код будет уведомлен.

Серверный пакет

Пакет Server содержит код, который выполняется на стороне сервера приложения. Этот код может содержать ссылки на классы, которые не могут быть скомпилированы GWT, и позволяет нам интегрироваться с уровнями Service, DAO и Domain. Если вы решите не создавать служебные слои и уровни DAO в мастере, реализация службы будет сгенерирована с использованием заглушенных методов, которые впоследствии можно будет реализовать для вызова собственной реализации внутреннего интерфейса. Класс GWTCustomerUtil — это служебный класс, который может выполнять преобразование ваших объектов DTO, поступающих от клиента GWT, обратно в постоянную версию этих объектов на сервере и обратно. Реализация по умолчанию заботится о слиянии данных и использовании уровней DAO и Service для обработки постоянства. Этот подход не требует 3- го например, сторонние проекты, которые пытаются сопоставить JPA с GWT и оставляют разработчика открытым для реализации других подходов к постоянству.

Пакет компонентов

Пакет компонентов содержит компоненты пользовательского интерфейса GWT, предназначенные для предоставления разработчику подробных java-объектов курса, которые можно использовать для создания пользовательских интерфейсов, которые в этом случае работают с клиентами. Эти компоненты обычно используют UIBinder, предварительно настроены для работы с EventBus, Actions и Events, сгенерированными в других пакетах, и они позволяют реализации Actions быть зависимостью, внедренной в Presenter, чтобы позволить им оставаться свободно связанными и тестируемыми. Существуют разбитые на страницы таблицы как для «перечисления» объектов в таблице, так и для «выбора» объектов с несколькими столбцами, как вы можете видеть в «Средстве выбора». Существуют общие виджеты «Правка», которые обеспечивают ввод на основе формы и отображение полей и свойств объектов. Есть виджеты Отношения для управления отношениями с клиентом,как одиночные, так и (односторонние) отношения, или (многие) односторонние отношения. Для вашего удобства есть также диалоги для редактирования или выбора клиентов. Все таблицы являются конкретными реализациямиPagingScrollTable из проекта инкубатора GWT .

Код

Лучший способ почувствовать код — это загрузить MyEclipse для Spring и просмотреть учебное пособие по GWT. Давайте кратко рассмотрим сгенерированный код, чтобы почувствовать его. Мы искренне приветствуем отзывы сообщества о том, как мы можем продолжать улучшать продукт, и поэтому, если у вас есть мысли, пожалуйста, разместите их на наших форумах .

Классы на стороне сервера

Клиент

Класс Customer — это объект JPA, который был сгенерирован в этом случае из нашего определения таблицы RDBMS. Вы видите, что мы автоматически генерируем аннотации @NamedQuery для общих именованных запросов, которые могут понадобиться вам для поиска клиентов. Каждое поле было аннотировано на основе информации, полученной из базы данных, обычно довольно стандартного материала.

package com.showme.domain;

import java.io.Serializable;

import java.lang.StringBuilder;

import java.math.BigDecimal;

import java.util.LinkedHashSet;
import java.util.Set;

import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;

import javax.xml.bind.annotation.*;

import javax.persistence.*;

/**
 */

@Entity
@NamedQueries( {
		@NamedQuery(name = "findAllCustomers", query = "select myCustomer from Customer myCustomer"),
		@NamedQuery(name = "findCustomerByAddressline1", query = "select myCustomer from Customer myCustomer where myCustomer.addressline1 = ?1"),
		@NamedQuery(name = "findCustomerByAddressline1Containing", query = "select myCustomer from Customer myCustomer where myCustomer.addressline1 like ?1"),
		@NamedQuery(name = "findCustomerByAddressline2", query = "select myCustomer from Customer myCustomer where myCustomer.addressline2 = ?1"),
		@NamedQuery(name = "findCustomerByAddressline2Containing", query = "select myCustomer from Customer myCustomer where myCustomer.addressline2 like ?1"),
		@NamedQuery(name = "findCustomerByCity", query = "select myCustomer from Customer myCustomer where myCustomer.city = ?1"),
		@NamedQuery(name = "findCustomerByCityContaining", query = "select myCustomer from Customer myCustomer where myCustomer.city like ?1"),
		@NamedQuery(name = "findCustomerByContactfirstname", query = "select myCustomer from Customer myCustomer where myCustomer.contactfirstname = ?1"),
		@NamedQuery(name = "findCustomerByContactfirstnameContaining", query = "select myCustomer from Customer myCustomer where myCustomer.contactfirstname like ?1"),
		@NamedQuery(name = "findCustomerByContactlastname", query = "select myCustomer from Customer myCustomer where myCustomer.contactlastname = ?1"),
		@NamedQuery(name = "findCustomerByContactlastnameContaining", query = "select myCustomer from Customer myCustomer where myCustomer.contactlastname like ?1"),
		@NamedQuery(name = "findCustomerByCountry", query = "select myCustomer from Customer myCustomer where myCustomer.country = ?1"),
		@NamedQuery(name = "findCustomerByCountryContaining", query = "select myCustomer from Customer myCustomer where myCustomer.country like ?1"),
		@NamedQuery(name = "findCustomerByCreditlimit", query = "select myCustomer from Customer myCustomer where myCustomer.creditlimit = ?1"),
		@NamedQuery(name = "findCustomerByCustomername", query = "select myCustomer from Customer myCustomer where myCustomer.customername = ?1"),
		@NamedQuery(name = "findCustomerByCustomernameContaining", query = "select myCustomer from Customer myCustomer where myCustomer.customername like ?1"),
		@NamedQuery(name = "findCustomerByCustomernumber", query = "select myCustomer from Customer myCustomer where myCustomer.customernumber = ?1"),
		@NamedQuery(name = "findCustomerByPhone", query = "select myCustomer from Customer myCustomer where myCustomer.phone = ?1"),
		@NamedQuery(name = "findCustomerByPhoneContaining", query = "select myCustomer from Customer myCustomer where myCustomer.phone like ?1"),
		@NamedQuery(name = "findCustomerByPostalcode", query = "select myCustomer from Customer myCustomer where myCustomer.postalcode = ?1"),
		@NamedQuery(name = "findCustomerByPostalcodeContaining", query = "select myCustomer from Customer myCustomer where myCustomer.postalcode like ?1"),
		@NamedQuery(name = "findCustomerByPrimaryKey", query = "select myCustomer from Customer myCustomer where myCustomer.customernumber = ?1"),
		@NamedQuery(name = "findCustomerBySalesrepemployeenumber", query = "select myCustomer from Customer myCustomer where myCustomer.salesrepemployeenumber = ?1"),
		@NamedQuery(name = "findCustomerByState", query = "select myCustomer from Customer myCustomer where myCustomer.state = ?1"),
		@NamedQuery(name = "findCustomerByStateContaining", query = "select myCustomer from Customer myCustomer where myCustomer.state like ?1") })
@Table(schema = "CLASSICCARS", name = "CUSTOMER")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(namespace = "ShowMeTheMoney/com/showme/domain", name = "Customer")
@XmlRootElement(namespace = "ShowMeTheMoney/com/showme/domain")
public class Customer implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 */

	@Column(name = "CUSTOMERNUMBER", nullable = false)
	@Basic(fetch = FetchType.EAGER)
	@Id
	@XmlElement
	Integer customernumber;
	/**
	 */

	@Column(name = "CUSTOMERNAME", length = 50)
	@Basic(fetch = FetchType.EAGER)
	@XmlElement
	String customername;
//** This is only a partial listing of the generated file **//

Полный список Customer.java

CustomerDAOImpl

Класс CustomerDAOImpl является реализацией интерфейса CustomerDAO, который определяет поведение, необходимое для сохранения клиентов. Этот класс является аннотированным Spring @Repository и имеет аннотации @Transactional для управления транзакциями. EntityManager внедряется Spring. Существуют методы для каждого из NamedQueries из объекта JPA клиента. Этот класс используется сервисным уровнем для управления постоянством клиентов.

package com.showme.dao;

import com.showme.domain.Customer;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.skyway.spring.util.dao.AbstractJpaDao;

import org.springframework.dao.DataAccessException;

import org.springframework.stereotype.Repository;

import org.springframework.transaction.annotation.Transactional;

/**
 * DAO to manage Customer entities.
 * 
 */
@Repository("CustomerDAO")
@Transactional
public class CustomerDAOImpl extends AbstractJpaDao implements CustomerDAO {

	/**
	 * Set of entity classes managed by this DAO.  Typically a DAO manages a single entity.
	 *
	 */
	private final static Set
 
  > dataTypes = new HashSet
  
   >(Arrays.asList(new Class
   [] { Customer.class }));

	/**
	 * EntityManager injected by Spring for persistence unit MyEclipse_Derby
	 *
	 */
	@PersistenceContext(unitName = "MyEclipse_Derby")
	private EntityManager entityManager;

	/**
	 * Instantiates a new CustomerDAOImpl
	 *
	 */
	public CustomerDAOImpl() {
		super();
	}

	/**
	 * Get the entity manager that manages persistence unit MyEclipse_Derby
	 *
	 */
	public EntityManager getEntityManager() {
		return entityManager;
	}

	/**
	 * Returns the set of entity classes managed by this DAO.
	 *
	 */
	public Set
   
    > getTypes() {
		return dataTypes;
	}

	/**
	 * JPQL Query - findCustomerByCreditlimit
	 *
	 */
	@Transactional
	public Set
    
      findCustomerByCreditlimit(java.math.BigDecimal creditlimit) throws DataAccessException {

		return findCustomerByCreditlimit(creditlimit, -1, -1);
	}

	/**
	 * JPQL Query - findCustomerByCreditlimit
	 *
	 */

	@SuppressWarnings("unchecked")
	@Transactional
	public Set
     
       findCustomerByCreditlimit(java.math.BigDecimal creditlimit, int startResult, int maxRows) throws DataAccessException {
		Query query = createNamedQuery("findCustomerByCreditlimit", startResult, maxRows, creditlimit);
		return new LinkedHashSet
      
       (query.getResultList());
	}


      
     
    
   
  
 

Полный список CustomerDAOImpl.java

CustomerServiceImpl

Класс CustomerServiceImpl является реализацией интерфейса CustomerService, который определяет поведение службы CRUD для Customer. Этот класс является компонентом Session scoped и аннотируется как Spring @Service. Этот класс имеет DAO, которые он должен внедрить в Spring, и имеет методы для управления основными операциями CRUD, а также для управления зависимостями для клиента.

package com.showme.service;

import com.showme.dao.CustomerDAO;
import com.showme.dao.PaymentDAO;

import com.showme.domain.Customer;
import com.showme.domain.Payment;

import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Scope;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

/**
 * Spring service that handles CRUD requests for Customer entities
 * 
 */
@Scope("session")
@Service("CustomerService")
@Transactional
public class CustomerServiceImpl implements CustomerService {

	/**
	 * DAO injected by Spring that manages Customer entities
	 * 
	 */
	@Autowired
	private CustomerDAO customerDAO;

	/**
	 * DAO injected by Spring that manages Payment entities
	 * 
	 */
	@Autowired
	private PaymentDAO paymentDAO;

	/**
	 * Instantiates a new CustomerServiceImpl.
	 *
	 */
	public CustomerServiceImpl() {
	}

	/**
	 * Load an existing Customer entity
	 * 
	 */
	@Transactional
	public Set
 
   loadCustomers() {
		return customerDAO.findAllCustomers();
	}

	/**
	 * Save an existing Customer entity
	 * 
	 */
	@Transactional
	public void saveCustomer(Customer customer) {
		customer = customerDAO.store(customer);
		customerDAO.flush();
	}
//** This is only a partial listing of the generated file **//

 

Полный список CustomerServiceImpl.java

Классы на стороне клиента

GWTCustomer

GWTCustomerClass — это автоматически сгенерированный объект DTO, который используется для передачи информации о клиенте от клиента на сервер, а также объект DTO, связанный со всеми компонентами пользовательского интерфейса, созданными для поддержки клиента. Объект DTO НЕ содержит никаких отношений. Вместо этого GWTService for Customer содержит методы для ленивой загрузки отношений, а также есть Действия, События и Компоненты пользовательского интерфейса, которые поддерживают управление отношениями для каждого Типа данных в модели Домена.

package gwt.customer.client;


import java.io.Serializable;

import java.lang.StringBuilder;


/**
 * A lightweight POJO without relationships that can be used for RPC style remote calls with GWT.
 * The GWT Services have methods for adding, removing and loading Objects and for managing relationships on Objects.
 * This approach encourages loose coupling between objects and asynchronous loading of related data
 * 
 * @see  Customer 
 */
public class GWTCustomer implements Serializable{
	private static final long serialVersionUID = 1L;

		/**
		* Declare customernumber
		*/
		 Integer customernumber;
		/**
		* Declare customername
		*/
		 String customername;
		/**
		* Declare contactlastname
		*/
		 String contactlastname;
		/**
		* Declare contactfirstname
		*/
		 String contactfirstname;
		/**
		* Declare phone
		*/
		 String phone;
		/**
		* Declare addressline1
		*/
		 String addressline1;
		/**
		* Declare addressline2
		*/
		 String addressline2;
		/**
		* Declare city
		*/
		 String city;
		/**
		* Declare state
		*/
		 String state;
		/**
		* Declare postalcode
		*/
		 String postalcode;
		/**
		* Declare country
		*/
		 String country;
		/**
		* Declare salesrepemployeenumber
		*/
		 Integer salesrepemployeenumber;
		/**
		* Declare creditlimit
		*/
		 Double creditlimit;
	

		/**
		* Setter for customernumber
		 */
		public void setCustomernumber(Integer customernumber) {
			this.customernumber = customernumber;
		}

//** This is only a partial listing of the generated file **//

Полный список GWTCustomer.java

GWTCustomerServiceImpl

Класс GWTCustomerServiceImpl является реализацией класса GWT RemoteServiceServlet, который включает уровень GWT RPC. Целью этого класса является обработка запросов от клиента GWT, которые поступают на сервер в форме действий (или команд). Этот класс реализует обеспечивает реализацию метода execute (Action <T> action), а затем делегирует эти действия соответствующим обработчикам, которые используют класс CustomerUtil для преобразования туда и обратно из объекта DTO в объект JPA и выполнения основных операций управления CRUD и Relationship. ,

package gwt.customer.server;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import com.showme.dao.CustomerDAO;
import com.showme.dao.PaymentDAO;

import com.showme.service.CustomerService;

import gwt.common.client.Action;
import gwt.common.client.CrudException;
import gwt.common.client.Response;

import gwt.customer.client.GWTCustomer;
import gwt.customer.client.GWTCustomerID;
import gwt.customer.client.GWTCustomerService;

import gwt.customer.client.actions.DeleteCustomer;
import gwt.customer.client.actions.DeleteCustomerResponse;
import gwt.customer.client.actions.LoadCustomer;
import gwt.customer.client.actions.LoadCustomerResponse;
import gwt.customer.client.actions.StoreCustomer;
import gwt.customer.client.actions.StoreCustomerResponse;

import gwt.payment.client.GWTPayment;

import gwt.payment.client.actions.AddPaymentToRelated;
import gwt.payment.client.actions.AddPaymentToRelatedResponse;
import gwt.payment.client.actions.LoadRelatedPayment;
import gwt.payment.client.actions.LoadRelatedPaymentResponse;
import gwt.payment.client.actions.RemovePaymentFromRelated;
import gwt.payment.client.actions.RemovePaymentFromRelatedResponse;

import gwt.payment.server.GWTPaymentUtil;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.beanutils.ConvertUtils;

import org.apache.commons.beanutils.converters.BigDecimalConverter;
import org.apache.commons.beanutils.converters.CalendarConverter;
import org.apache.commons.beanutils.converters.DateConverter;

import org.springframework.web.context.WebApplicationContext;

import org.springframework.web.servlet.support.RequestContextUtils;




/**
 * Remote Service implementation for CRUD based operations for Customer
 * @see com.showme.domain.Customer
 */
public class GWTCustomerServiceImpl extends RemoteServiceServlet implements GWTCustomerService {
		
	/**
	 * Default constructor.
 	 */
	public GWTCustomerServiceImpl() {
		java.util.Date defaultValue = null;
		java.util.Calendar defaultCalendarValue = null;
		java.math.BigDecimal defaultBigDecimalValue = null;
		
		DateConverter dateConverter = new DateConverter(defaultValue);
		CalendarConverter calendarConverter = new CalendarConverter (defaultCalendarValue);
		BigDecimalConverter bigDecimalConverter = new BigDecimalConverter (defaultBigDecimalValue);
			
		ConvertUtils.register(dateConverter, java.util.Date.class);
		ConvertUtils.register(calendarConverter, java.util.Calendar.class);
		ConvertUtils.register(bigDecimalConverter, java.math.BigDecimal.class);
	}

	/**
	 * Default execute method for Actions passed to this Service
	 * See the MVP approach and the use of the Command Pattern GWT Best Practices
	 *
	 */
	 @SuppressWarnings("unchecked")
	public 
 
   T execute(Action
  
    action) throws CrudException{
		if (action instanceof DeleteCustomer)
			return (T)execute ((DeleteCustomer)action);
		else if (action instanceof LoadCustomer)
			return (T)execute ((LoadCustomer)action);
		else if (action instanceof StoreCustomer)
			return (T)execute ((StoreCustomer)action);
		else if (action instanceof AddPaymentToRelated)
			return (T)execute ((AddPaymentToRelated
   
    )action);
		else if (action instanceof LoadRelatedPayment)
			return (T)execute ((LoadRelatedPayment
    
     )action);
		else if (action instanceof RemovePaymentFromRelated)
			return (T)execute ((RemovePaymentFromRelated
     
      )action);
	
	
		throw new CrudException ("Invalid Action, no handler specified:" + action); 
	}
	
	/**
	 * Get the DataUtil handler to convert Objects from GWT DTOs to their persisted version, and back
	*/
	private GWTCustomerUtil dataUtils() {
		return new GWTCustomerUtil(getDAO());
	}
	
	/**
	 * Default implementation loads all 
	 */
	private LoadCustomerResponse execute (gwt.customer.client.actions.LoadCustomer action) throws CrudException{
		return new LoadCustomerResponse (dataUtils().toGWT(getService().loadCustomers()));		}
	
	/**
	*/
	private DeleteCustomerResponse execute (gwt.customer.client.actions.DeleteCustomer action) throws CrudException {
		try {
			for (GWTCustomer customer: action.getCustomers()) {
				getService().deleteCustomer(dataUtils().toPersisted(customer));
			}
		}
		catch (javax.persistence.EntityExistsException ex) {
			throw new CrudException ("Unable to delete this Customer, it may be associated with a another record", ex);
		}
		catch (Exception e) {
			throw new CrudException ("Unable to delete this Customer", e);
		}
		return new DeleteCustomerResponse(action.getCustomers());
	}
	

//** This is only a partial listing of the generated file **//

     
    
   
  
 

Полный список GWTCustomerServiceImpl.java

CustomerEditor

CustomerEditor — это ведущий в шаблоне MVP для объектов Customer. Вверху вы заметите, что докладчик определяет интерфейс с именем Display, который определяет API, который должен быть соблюден, чтобы быть «представлением» для этого докладчика. Также обратите внимание на использование HasValue <x> вместо определенных компонентов пользовательского интерфейса. Такой подход позволяет легко передавать имитирующие дисплеи и обеспечивает большую гибкость реализации пользовательского интерфейса при реализации договора с докладчиком. Метод bindDisplay заботится о получении элементов из Display и добавлении обработчиков событий и т. Д., Где это необходимо. Методы редактирования и сохранения делают именно то, что вы, вероятно, ожидаете, они привязывают данные в пользовательском интерфейсе и из него к модели и заботятся о вызове метода execute на сервере, передавая действие StoreCustomer.

package gwt.customer.client.components;

import com.google.gwt.core.client.GWT;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;

import com.google.gwt.user.client.Window;

import com.google.gwt.user.client.ui.HasValue;

import gwt.common.client.EventBus;

import gwt.customer.client.CustomerMessages;
import gwt.customer.client.GWTCustomer;
import gwt.customer.client.GWTCustomerID;
import gwt.customer.client.GWTCustomerService;
import gwt.customer.client.GWTCustomerServiceAsync;

import gwt.customer.client.actions.StoreCustomer;
import gwt.customer.client.actions.StoredCustomer;

import gwt.customer.client.events.CustomerStoredEvent;
import gwt.customer.client.events.CustomerStoredHandler;

/**
 * Presenter for GWTCustomer editing
 */
public class CustomerEditor {
	// Display Interface that must be implemented to bind a Display to this
	// editor
	interface Display {
		public HasValue
 
   getCustomernumberBox();

		public HasValue
  
    getCustomernameBox();

		public HasValue
   
     getContactlastnameBox();

		public HasValue
    
      getContactfirstnameBox();

		public HasValue
     
       getPhoneBox();

		public HasValue
      
        getAddressline1Box();

		public HasValue
       
         getAddressline2Box();

		public HasValue
        
          getCityBox();

		public HasValue
         
           getStateBox();

		public HasValue
          
            getPostalcodeBox();

		public HasValue
           
             getCountryBox();

		public HasValue
            
              getSalesrepemployeenumberBox();

		public HasValue
             
               getCreditlimitBox();

		public HasClickHandlers getCancelButton();

		public HasClickHandlers getSaveButton();

		public void setIsNew(boolean isNew);

		public boolean isValid();
	}

	/**
	 * Reference to the Display that is bound to this presenter
	 */
	private Display display;

	/**
	 * The GWTCustomer being edited
	 */
	private GWTCustomer customer;

	/**
	 * The Crud Service used to handle saves from this Editor
	 */
	private GWTCustomerServiceAsync service;

	/**
	 * The I18N Messages
	 */
	private final static CustomerMessages messages = (CustomerMessages) GWT
			.create(CustomerMessages.class);

	/**
	 * Constructor that takes an instance of the Display to bind to this
	 * presenter
	 */
	public CustomerEditor(Display display) {
		this(display, GWTCustomerService.Util.getInstance());
	}

	/**
	 * Constructor that takes an instance of the Display to bind to this
	 * presenter, and an override to disable calls through to the server This
	 * constructor just avoids creating an instance of the Default crud Service.
	 * The Editor will only perform saves if the Async Crud Service is present
	 * 
	 */
	public CustomerEditor(Display display, boolean performSave) {
		bindDisplay(display);
		if (performSave)
			this.service = GWTCustomerService.Util.getInstance();
	}

	/**
	 * Constructor that takes the Display to bind to this presenter, and the
	 * Async Service to use for saving
	 */
	public CustomerEditor(Display display, GWTCustomerServiceAsync service) {
		bindDisplay(display);
		this.service = service;
	}

	/**
	 * Binds the view to the presenter
	 */
	void bindDisplay(final Display display) {
		this.display = display;

		if (display.getSaveButton() != null) {
			display.getSaveButton().addClickHandler(new ClickHandler() {
				public void onClick(ClickEvent event) {
					if (display.isValid())
						doSave();
					else
						doError();
				}
			});
		}

		if (display.getCancelButton() != null) {
			display.getCancelButton().addClickHandler(new ClickHandler() {
				public void onClick(ClickEvent event) {
					doCancel();
				}
			});
		}

		EventBus.get().addHandler(CustomerStoredEvent.TYPE,
				new CustomerStoredHandler() {
					public void onCustomerStored(CustomerStoredEvent event) {
						if (new GWTCustomerID(event.getCustomer())
								.equals(new GWTCustomerID(customer)))
							edit(event.getCustomer());
					}
				});
	}

//** This is only a partial listing of the generated file **//

             
            
           
          
         
        
       
      
     
    
   
  
 

Полный список CustomerEditor.java

CustomerEditWidget

CustomerEditWidget — это класс, отвечающий за загрузку пользовательского интерфейса с использованием UIBinder. Он реализует интерфейс Display из CustomerEditor. Вы можете увидеть код в верхней части класса, который определяет XML-файл определения UIBinder и загружает его через GWT.create (). Аннотации @UIField позволяют привязывать UIElements, определенные в XML-файле, к Java-коду в CustomerEditWidget. Сгенерированные виджеты редактирования предлагают «тупые» возможности пользовательского интерфейса, такие как включение и отключение, а также базовую проверку того, что поля формы достаточно действительны для привязки к модели данных.

package gwt.customer.client.components;

import com.google.gwt.core.client.GWT;

import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiTemplate;

import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;

import gwt.common.client.DecimalBox;
import gwt.common.client.IntegerBox;


/**
 * The view for editing GWTCustomer
 * This view is constructed using UIBinder
 */
public class CustomerEditWidget extends Composite implements  CustomerEditor.Display {
	
	/**
	* Interface for UIBinder extension
	*/ 
	@UiTemplate("CustomerEditWidget.ui.xml")
	interface EditBinder extends UiBinder
 
  {}
	
	/**
	* Instance of EditBinder interface loaded through GWT.create
	*/ 
	private static EditBinder binder = GWT.create(EditBinder.class);
	
		/**
		* UI Input for customernumber
		*/ 
		@UiField
		IntegerBox customernumberBox;
		/**
		* UI Input for customername
		*/ 
		@UiField
		TextBox customernameBox;
		/**
		* UI Input for contactlastname
		*/ 
		@UiField
		TextBox contactlastnameBox;
		/**
		* UI Input for contactfirstname
		*/ 
		@UiField
		TextBox contactfirstnameBox;
		/**
		* UI Input for phone
		*/ 
		@UiField
		TextBox phoneBox;
		/**
		* UI Input for addressline1
		*/ 
		@UiField
		TextBox addressline1Box;
		/**
		* UI Input for addressline2
		*/ 
		@UiField
		TextBox addressline2Box;
		/**
		* UI Input for city
		*/ 
		@UiField
		TextBox cityBox;
		/**
		* UI Input for state
		*/ 
		@UiField
		TextBox stateBox;
		/**
		* UI Input for postalcode
		*/ 
		@UiField
		TextBox postalcodeBox;
		/**
		* UI Input for country
		*/ 
		@UiField
		TextBox countryBox;
		/**
		* UI Input for salesrepemployeenumber
		*/ 
		@UiField
		IntegerBox salesrepemployeenumberBox;
		/**
		* UI Input for creditlimit
		*/ 
		@UiField
		DecimalBox creditlimitBox;
	
	/**
	* Cancel button
	*/ 
	@UiField
	Button cancelButton;
	
	/**
	* Save button
	*/ 
	@UiField
	Button saveButton;

//** This is only a partial listing of the generated file **//

 

Полный список CustomerEditWidget.java

CustomerEditWidget.ui.xml

Этот файл определяет пользовательский интерфейс для EditWidget, используя смесь тегов HTML и GWT. Этот подход обеспечивает очень четкое разделение между определением пользовательского интерфейса и кодом Java. Реализация по умолчанию заботится о добавлении тегов <ui: msg>, которые могут использоваться компилятором GWT для генерации файлов I18N для локализации.

<ui:UiBinder
  ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat'
  ui:generateKeys="com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator"
  ui:generateLocales="default"
  xmlns:ui='urn:ui:com.google.gwt.uibinder'
  xmlns:dp='urn:import:com.google.gwt.user.datepicker.client'
  xmlns:gc='urn:import:gwt.common.client'
	xmlns:payments='urn:import:gwt.payment.client.components'
  xmlns:g='urn:import:com.google.gwt.user.client.ui'>
  <ui:style src='../Customer.css'/>

  <g:HTMLPanel addStyleNames="{style.gwt-CrudEdit}">
  <table cellpadding="0" cellspacing="0" id="viewTable">
	<tr><td class="label" valign="top"><ui:msg description="customernumberLabel">Customernumber:</ui:msg></td><td><gc:IntegerBox ui:field="customernumberBox"></gc:IntegerBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="customernameLabel">Customername:</ui:msg></td><td><g:TextBox ui:field="customernameBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="contactlastnameLabel">Contactlastname:</ui:msg></td><td><g:TextBox ui:field="contactlastnameBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="contactfirstnameLabel">Contactfirstname:</ui:msg></td><td><g:TextBox ui:field="contactfirstnameBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="phoneLabel">Phone:</ui:msg></td><td><g:TextBox ui:field="phoneBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="addressline1Label">Addressline1:</ui:msg></td><td><g:TextBox ui:field="addressline1Box"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="addressline2Label">Addressline2:</ui:msg></td><td><g:TextBox ui:field="addressline2Box"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="cityLabel">City:</ui:msg></td><td><g:TextBox ui:field="cityBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="stateLabel">State:</ui:msg></td><td><g:TextBox ui:field="stateBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="postalcodeLabel">Postalcode:</ui:msg></td><td><g:TextBox ui:field="postalcodeBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="countryLabel">Country:</ui:msg></td><td><g:TextBox ui:field="countryBox"></g:TextBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="salesrepemployeenumberLabel">Salesrepemployeenumber:</ui:msg></td><td><gc:IntegerBox ui:field="salesrepemployeenumberBox"></gc:IntegerBox></td></tr>
	<tr><td class="label" valign="top"><ui:msg description="creditlimitLabel">Creditlimit:</ui:msg></td><td><gc:DecimalBox ui:field="creditlimitBox"></gc:DecimalBox></td></tr>
  </table>
  <g:HorizontalPanel ui:field="buttonPanel">
  <g:Button addStyleNames="{style.gwt-CrudEditSaveButton}" ui:field="saveButton"><ui:msg description="SaveButton">Save</ui:msg></g:Button>
  <g:Button addStyleNames="{style.gwt-CrudEditCancelButton}" ui:field="cancelButton"><ui:msg description="CancelButton">Cancel</ui:msg></g:Button>
  </g:HorizontalPanel>
  </g:HTMLPanel>
</ui:UiBinder>

Full listing of CustomerEditWidget.ui.xml

Let us know what you think

We hope that you found this article informative and that you are able to get a good sense for the code that is being generated using MyEclipse For Spring, specifically as it relates to our GWT integration. As excited as we are about this new feature, we are eager to hear what the developer community thinks about our GWT scaffolding capability. You can download MyEclipse for Spring 8.6 here. Please post all feedback, questions and issues to the MyEclipse for Spring section of the MyEclipse forums.