현재 웹 기술에는 증가된 요구가 주어진다. 이들은 사용자 계정, 업로드 내용 및 스트림 비디오를 관리할 수 있어야 한다. 이 요구로 인해 RIA 개발자는 일반적으로 인기있는 기능을 제공하는 동시에 개발 워크플로를 간소화하는 기술을 찾아야 한다. 개발자의 도전 과제는 이러한 서비스를 제공하기 위한 올바른 기술 세트를 선택하는 것이다.
Adobe Flex는 개발자에게 GUI를 작성하고, 그래픽을 그리고, 미디어를 재생하고 스트리밍하며, 웹 서비스에 연결하기 위한 리치 API 호출 세트를 제공하는 클라이언트측 기술이다. 서버측에서 Java 기술은 관계형 데이터베이스 관리 시스템(RDBMS)으로의 연결, 서비스 요청의 멀티스레드형 프로세싱 및 증가한 요구로 최적의 크기 조정 등과 같은 기능을 제공한다. 이러한 두 가지 기술을 함께 사용하면 RIA 애플리케이션의 요구를 충족하는 강력한 기술 스택이 제공된다.
이 기사에서는 간단하지만 강력한 RIA를 쓰는 방법을 시연하는데, 이는 클라이언트에 대해 Flex, 서버에 대해서는 Java 기술, 백엔드 데이터베이스에 대해서는 MySQL을 활용한다.
샘플 애플리케이션(아래 다운로드 섹션에서 사용 가능)은 연락처 정보 작성, 읽기, 업데이트 및 삭제(CRUD)를 지원하는 리치 UI를 제공하는데, 이는 Adobe Flash® (SWF) 애플리케이션을 사용한 것이다. 이 세 계층의 웹 아키텍처는 그림 1에 묘사되어 있다. 클라이언트가 웹 페이지에서 임베드된 SWF 파일로 표현되고, 서버 애플리케이션은 Java 서블릿 컨테이너(이 경우에는 Apache Tomcat)에서 실행되고, 데이터베이스는 MySQL이다. 결합된 이러한 세 계층은 동력 분배형 애플리케이션이다.
그림 1. 연락처 애플리케이션
Flash 애플리케이션과 Java 서블릿 컨테이너 사이의 통신을 위해 Adobe BlazeDS 프레임워크가 오브젝트 원격화를 제공한다. —Adobe ActionScript™ 오브젝트가 Java 오브젝트를 호출하도록 허용하는 경우 및 그 반대의 경우에 대한 RPC의 형태이다. Java 서버 애플리케이션과 관계형 데이터베이스 사이의 통신은 Hibernate ORM(Object Relational Mapping) 프레임워크로 처리된다. Hibernate를 통해 Java 오브젝트는 SQL 코드로 전환될 수 있으며, 그 반대의 경우도 될 수 있다.
첫 단계는 연락처 정보를 저장하기 위해 필요한 정보를 아우르는 Java 클래스를
작성하는 것이다. 샘플 애플리케이션은 기본 정보를 사용한 간단한 모델이 들어있다. Contact 오브젝트에 필요한 속성과 데이터 유형은 다음과 같다.
- String emailAddress
- String firstName
- long id
- String lastName
- String phoneNumber
- long serialVersionUID
+ Contact()
+ Contact(String first, String last, String email, String number)
+ String getEmailAddress()
+ String getFirstName()
+ long getId()
+ String getLastName()
+ String getPhoneNumber()
+ void setEmailAddress(String address)
+ void setFirstName(String first)
+ void setId(long newId)
+ void setLastName(String last)
+ void setPhoneNumber(String number)
+ StringtoString()
Java Contact 클래스는 비즈니스 오브젝트로 작동하는 POJO(Plain old Java object)로 간주되는데,
이는 비즈니스 도메인 특성과 작동을 표현한다는 의미이다. Contact
오브젝트 내의 데이터는 데이터베이스와 지속성을 갖추어야 한다. 그 해결책은 Hibernate와 같은 ORM 프레임워크를
사용하는 것이다. 이는 오브젝트를 데이터베이스 테이블 내에서 레코드를 향하다가 다시 돌아오도록 맵핑하는데 작업의 많은 부분을 수행한다. JPA(Java Persistence API) 어노테이션이
사용되면 ORM을 채우기 위해 필요한 코딩은 매우 적다.
Listing 1에서는 어노테이트된 Java 클래스
Contact를 시연한다.
Listing 1. Java Contact 클래스
package bcit.contacts;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
@Entity
@Table(name="contact")
@NamedQueries( {
@NamedQuery(name = "contact.findAll", query = "from Contact"),
@NamedQuery(name = "contact.getById", query =
"select c from Contact c where c.id = :id")
} )
public class Contact {
private static final long serialVersionUID = 123456789L;
public Contact() {
firstName = "N/A";
lastName = "N/A";
emailAddress = "N/A";
phoneNumber = "N/A";
}
public Contact(String first, String last, String email, String number) {
firstName = first;
lastName = last;
emailAddress = email;
phoneNumber = number;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", nullable = false, updatable=false)
private long id;
@Column(name = "lastName", nullable = false, unique = false)
private String lastName;
@Column(name = "firstName", nullable = false, unique = false)
private String firstName;
@Column(name = "emailAddress", nullable = false, unique = false)
private String emailAddress;
@Column(name = "phoneNumber", nullable = false, unique = false)
private String phoneNumber;
public void setPhoneNumber(String number) { phoneNumber = number; }
public String getPhoneNumber() { return phoneNumber; }
public String getEmailAddress() { return emailAddress; }
public void setEmailAddress(String address) { emailAddress = address; }
public String getFirstName() { return firstName; }
public void setFirstName(String first) { firstName = first; }
public String getLastName() { return lastName; }
public void setLastName(String last) { lastName = last; }
public long getId() { return id; }
public void setId(long newId) { id = newId; }
@Override
public String toString() {
return id + " " + firstName + " " + lastName + " " + emailAddress
+ " " + phoneNumber;
}
}
|
클래스는 단순하지만 어노테이션과 관련하여 많은 작업이 이루어지고 있다.
@Column: 열의 이름이 고유하든지, 열이 널 가능(nullable) 상태이든지 열의 이름을 포함한 선택을 사용하여 데이터베이스에서 열로 특성을 레이블한다.@Entity: 클래스를 엔티티 bean으로 선언하여, 이는 지속성을 위해 POJO 계획된 것임을 의미한다.@GeneratedValue: 기본 키를 생성하기 위한 전략을 지정한다. 선택 사항은AUTO,IDENTITY,SEQUENCE및TABLE이다.@Id: 특성이 각 Java 오브젝트에 대해 고유한 식별자(즉, 기본 키)임을 명시한다.@NamedQueries: 이름이 있는 쿼리의 그룹을 나열한다.@NamedQuery: 향후 실행에 참조될 수 있는 문자열 리터럴로 사전정의된 쿼리를 선언한다.@Table: Java 클래스를 데이터베이스 내에서 테이블로 지명한다.
내장 메모리 Java 오브젝트가 지속성을 위해 필요할 때마다 Hibernate는 Java 오브젝트의 상태 정보를 SQL 업데이트로 변환한다. 이와 마찬가지로 결과 세트가 있는 SQL 문은 Java 오브젝트를 입력하기 위해 사용된다. 그 결과, 모든 오브젝트가 데이터베이스 내에서 레코드로 저장될 수 있고 모든 레코드는 검색될 수 있으며 Java 오브젝트로 다시 변환될 수 있다.
어노테이션은 클래스 내에서 지속성이 고려되어야 하는 것을 알려준다. 하지만 이는 그림의 일부분에 불과하다.
서비스 클래스는 ORM을 실행하도록 Hibernate에 호출을 수행하기 위해 필요하다. Listing 2는 애플리케이션 서비스로 작동하는 ContactsService
클래스를 표시한다.
Listing 2. ContactsService 클래스
public class ContactsService {
private static Logger logger = Logger.getLogger(ContactsService.class);
private static final String PERSISTENCE_UNIT = "contacts";
private static EntityManagerFactory emf = null;
static {
logger.info("LOADING CONTACTSSERVICE CLASS.");
emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
}
public ContactsService() {
super();
}
public void addContact(Contact c) {
if(c == null) {
return;
}
EntityManager em = emf.createEntityManager();
logger.info("PERSISTENCE ENTITYMANAGER ACQUIRED.");
logger.info("ABOUT TO ADD CONTACT: fName: " + c.getFirstName()
+ ", lName: " + c.getLastName() + ", email:" + c.getEmailAddress()
+ ", phone: " + c.getPhoneNumber());
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.merge(c);
tx.commit();
} catch (Exception e) {
logger.error("CONTACT APP PERSISTING ERROR: " + e.getMessage());
tx.rollback();
} finally {
logger.info("CONTACT APP CLOSING ENTITY MANAGER.");
em.close();
}
}
public void editContact(Contact c) {
logger.info("CONTACT TO UPDATE: " + c);
addContact(c);
}
public void deleteContact(Long id) {
logger.info("ABOUT TO DELETE CONTACT");
EntityManager em = emf.createEntityManager();
logger.info("PERSISTENCE ENTITYMANAGER ACQUIRED.");
Query contactByIdQuery = em.createNamedQuery("contact.getById");
contactByIdQuery.setParameter("id", id);
Contact c = (Contact) contactByIdQuery.getSingleResult();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.remove(c);
tx.commit();
} catch (Exception e) {
logger.error("CONTACT APP PERSISTING ERROR: " + e.getMessage());
tx.rollback();
} finally {
logger.info("CONTACT APP CLOSING ENTITY MANAGER.");
em.close();
}
}
public List<Contact> getContacts() {
logger.info("ABOUT TO RETRIEVE CONTACTS");
EntityManager em = emf.createEntityManager();
logger.info("PERSISTENCE ENTITYMANAGER ACQUIRED.");
Query findAllContactsQuery =
em.createNamedQuery("contact.findAll");
List<Contact> contacts = findAllContactsQuery.getResultList();
if (contacts != null) {
logger.debug("CONTACT APP RETRIEVED: " + contacts.size()
+ " CONTACT(S)");
}
return contacts;
}
}
|
각 메소드는 내장 메모리 캐시를 표현하는 EntityManager로 참조를
얻는다. 캐싱은 효율성을 보장하는 강력한 기능이다. 왜냐하면 데이터베이스로부터
데이터를 전송하고 수신하는 것은 비용이 많이 드는 조작이 될 수 있기 때문이다. 작성된 모든 캐시가
데이터베이스로 커미트되고 원하지 않는 경우에 롤백되는지만 확인해야 한다.
JPA에서 캐시에 주어진 용어는 지속성 컨텍스트이며, 이는
EntityManager 클래스로 표현된다. 각 지속성 컨텍스트는
@Entity 어노테이션으로 어노테이트된 Java 오브젝트인 엔티티
세트를 관리한다. EntityManagerFactory 클래스는 지속성
단위를 표현하는데, 이는 데이터 저장소로의 연결을 구성하고(예: 관계형 데이터베이스),
엔티티 유형을 관리하며(다시 말해서, 데이터 저장소로 맵핑하는 데 필요한 주어진 컨텍스트 내의
모든 클래스), 마지막으로 지속성 컨텍스트의 인스턴스를 제공하는(즉, EntityManager) 책임이 있다.
비록 지속성 컨텍스트를 작성하는 프로세스가 저렴하고 시간 절약형이라고 하더라도,
지속성 단위를 작성하는 프로세스는 비용이 많이 든다. 데이터 저장소로 연결을 설정하고
엔티티로 어노테이트된 모든 클래스를 찾고 이러한 클래스를 데이터 저장소에서 엔티티로 바인딩하기 위한
지속성 로직을 구성하는 것은 빠른 조작이 아니다. 따라서 애플리케이션 시작 시에 EntityManagerFactory
인스턴스를 작성해야 한다. 지속성 컨텍스트를 위해 EntityManager를 또 작성하기 전에 파괴되었는지
확인하는 것에 주의하자. 또 다른 중요한 규칙은 entitymanager-per-request 패턴을 따르는 것이다. 이 패턴은 데이터베이스 호출을
그룹화(예를 들어, 요청과 업데이트)하기 때문에, 이 모두를 한 번에 전송할 수 있다. 이렇게 하면
JPA의 캐싱 메커니즘을 완전히 활용하는 것이 보장된다.
다음 요구사항은 클라이언트측이다.
Flex 프레임워크를 통해 사용자는 Adobe Flash Player에서 자유롭게 작업할 수 있는 애플리케이션을 작성할 수 있다. Flex는 다음으로 이루어진다.
- MXML로 알려진 선언적인 XML UI 언어
- ActionScript 프로그래밍 언어
- UI, 웹 연결 및 기타 많은 기능을 작성하기 위한 런타임 라이브러리
- 애플리케이션을 SWF 파일로 컴파일링하기 위한 개발자 도구
이 기사에서 참조된 클라이언트 애플리케이션은 Flex 버전 4를 사용한다. 클라이언트측 애플리케이션에 접근하기 전에 Flex 애플리케이션이 어떻게 작성되었고, 어떻게 Flash Player에서 실행 가능한 상태로 존재하는지 이해하는 것이 중요하다.
먼저 MXML 마크업과 ActionScript 코드의 조합을 사용하여 애플리케이션을 작성할 수 있다. 일반적인 워크플로는 MXML 형식을 사용하여 대부분의 GUI(프리젠테이션)를 작성한 다음에 이벤트 핸들링 및 비즈니스 로직을 위해 ActionScript 코드를 사용한다. MXML과 ActionScript가 둘 다 텍스트 기반이기 때문에 표준 텍스트 편집기와 Flex SDK가 Flash 애플리케이션을 작성하기 위해 필요한 전부이다.
두 번째, Flex 애플리케이션을 쓰면 MXML 컴파일러를 사용하여 코드를 컴파일한다. MXML 컴파일러는 웹 브라우저 내에서 실행될 수 있는 SWF 파일을 작성한다(Flash Player 브라우저 플러그인 사용).
마지막으로 Flash 애플리케이션은 타임라인 패러다임을 사용하는 AVM2(ActionScript Virtual Machine 2) 내에서 실행한다. 이 패러다임은 실행을 프레임으로 분해한다—이는 마치 영화와 같다. 컴파일 시간에 Flash 애플리케이션에서 초당 프레임 수를 지정한다. 추가적으로 Flash Player는 실행을 다음의 순서화된 태스크로 분해한다.
- 타이머와 마우스 이벤트와 같은 Flash Player 이벤트
- 사용자 코드
- Flash Player가 데이터 값 변경으로 인해 GUI를 업데이트해야 하는지 여부를 판단하기 위해 시도하는 사전 렌더링된 로직
- 데이터 값 변경으로 바인드된 사용자 코드
- Flash Player 렌더링
렌더링하는 초당 프레임의 수가 적으면 많은 수의 사용자 코드가 실행될 수 있다. 그러나 프레임 비율이 높으면(예: 초당 60프레임), 사용자 코드는 예상할 수 있는 것보다 더 많은 시간이 걸리기 때문에 Flash Player는 많은 수의 사용자 코드를 실행할 수 없을 것이다. Flash Player를 쓸 때에 이를 염두에 두는 것이 중요하다.
MXML은 다음과 같이 유용한 강력한 선언적인 XML 형식이다.
- XML 형식의 선언적인 성향으로 인해 GUI를 빌드하는 데 필요한 코드의 양이 최소화된다.
- 프리젠테이션 로직과 상호 작용 로직을 명백하게 분리할 수 있기 때문에 GUI 코드의 복잡도가 줄어든다.
- 소프트웨어 개발을 접근할 때에 설계 패턴의 사용을 촉진한다.
Listing 3에는 MXML Application
클래스가 표시된다.
Listing 3. ContactsApp 클래스
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:contact="bcit.contacts.*" creationComplete="initPage();"
layout="vertical" frameRate="30" pageTitle="Contacts Example"
horizontalAlign="center" verticalAlign="middle"
backgroundColor="#A9C0E7">
<mx:Style>
.mainBoxStyle {
borderStyle: solid;
paddingTop: 5px;
paddingBottom: 5px;
paddingLeft: 5px;
paddingRight: 5px;
}
.textMessages {
fontWeight: bold;
}
</mx:Style>
<mx:RemoteObject id="remotingService" showBusyCursor="false"
destination="contacts" fault="handleFault(event);"
result="handleResult(event);"/>
<mx:Script>
<![CDATA[
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.collections.ArrayCollection;
import bcit.contacts.dto.Contact;
[Bindable]
private var contacts:ArrayCollection = new ArrayCollection();
// For more on the Bindable metadata tag, see the devguide_flex3.pdf
// document, page 1249 (1257 in PDF page numbering)
[Bindable]
private var message:String = "Status: Ready";
private var contact:Contact;
public function setControlBarValid(valid:Boolean):void {
if(valid) {
// if the selected item is -1, then no item is selected but at
// the same time the fields are valid which means the user chose
// to add a contact, not update one
if(contactsDataGrid.selectedIndex == -1) {
createButton.enabled = valid;
} else {
editButton.enabled = valid;
}
} else {
// else nothing is valid
createButton.enabled = false;
editButton.enabled = false;
}
}
private function initPage():void {
editContactForm.setApp(this);
contact = new Contact();
getAllContacts();
resetPage();
}
private function createContact():void {
contact = editContactForm.getContact();
remotingService.addContact(contact);
message = "Status: Contact Added";
getAllContacts();
}
private function editContact():void {
var id:Number = contact.id;
contact = editContactForm.getContact();
contact.id = id;
remotingService.editContact(contact);
message = "Status: Contact Edited";
getAllContacts();
}
private function deleteContact():void {
if(contactsDataGrid.selectedItem != null) {
var c:Contact = contactsDataGrid.selectedItem as Contact;
// no sense in sending the whole contact - just send the id
// to cut down on bandwidth
remotingService.deleteContact(c.id);
message = "Status: Contact Deleted";
}
getAllContacts();
}
private function getAllContacts():void {
loadButton.enabled = false;
remotingService.getContacts();
loadButton.enabled = true;
resetPage();
}
private function populateFormWithContact():void {
contact = contactsDataGrid.selectedItem as Contact;
editContactForm.setContact(contact);
editButton.enabled = true;
deleteButton.enabled = true;
}
private function resetPage():void {
editContactForm.clearForm();
contact = new Contact();
createButton.enabled = false;
editButton.enabled = false;
deleteButton.enabled = false;
contactsDataGrid.selectedIndex = -1;
}
private function handleFault(e:FaultEvent):void {
message = "Status: Error"
+ "\nFault code: " + e.fault.faultCode
+ "\nFault detail: " + e.fault.faultDetail
+ "\nFault string: " + e.fault.faultString;
}
private function handleResult(e:ResultEvent):void {
// can get the results by accessing e.result property
//mx.controls.Alert.show(e.toString());
contacts = e.result as ArrayCollection;
var number:int = contacts.length;
//if(number == 1) {
// message = "Status: Retrieved 1 contact";
//} else {
// message = "Status: Retrieved " + contacts.length + " contacts";
//}
}
]]>
</mx:Script>
<mx:VBox styleName="mainBoxStyle">
<mx:Text id="titleText" text="Single click to select a contact"/>
<contact:ContactsDataGrid id="contactsDataGrid" dataProvider="{contacts}"
itemClick="populateFormWithContact();"
doubleClick="populateFormWithContact();"/>
<contact:EditContactForm id="editContactForm"/>
<mx:ControlBar horizontalAlign="center">
<mx:Button label="List" id="loadButton" click="getAllContacts()"
toolTip="Retrieve contacts from the server"/>
<mx:Button label="Add" id="createButton" click="createContact()"
toolTip="Create a new contact"/>
<mx:Button label="Update" id="editButton" click="editContact()"
toolTip="Edit a selected contact"/>
<mx:Button label="Delete" id="deleteButton" click="deleteContact()"
toolTip="Delete a selected contact"/>
<mx:Button label="Clear Form" id="clearButton" click="resetPage()"
toolTip="Clear the form"/>
</mx:ControlBar>
<mx:TextArea text="{message}" styleName="textMessages" wordWrap="true"
verticalScrollPolicy="auto" horizontalScrollPolicy="off" editable="false"
width="100%"/>
</mx:VBox>
</mx:Application>
|
여기에 Listing 3에 적용되는 몇 가지의 포인트가 더 있다.
- MXML 문서의 루트 요소는
Application클래스의 서브클래스이다. mx:Style요소는 CSS 특성이 UI 컴포넌트에 로컬 스타일링을 정의하도록 할 수 있다. 스타일링은 로컬 스타일 정의(Listing 3에 있는 대로), 외부 스타일 시트로의 참조, 컴포넌트 내에서의 인라이닝 스타일 및 ActionScript에서setStyle메소드를 사용하여 완성될 수 있다.RemoteObject클래스는 서버와의 원격 조작을 수행하는 HTTP 서비스 오브젝트를 표현한다.mx:Script요소는CDATA섹션에서 ActionScript 코드 블록을 포함한다.- 하나의 레이아웃이 있다(다시 말해,
VBox클래스). - UI 컴포넌트가 애플리케이션에서 선언될 때마다(예:
TextArea) 인스턴스 변수가 생성되는데, 이는 컴포넌트의id특성을 사용하여 애플리케이션 내에서 향후 참조될 수 있다. - 데이터 바인딩은 중괄호를 사용하여 수행된다(예를 들어,
TextArea요소의text속성은 ActionScriptmessage인스턴스 변수로 바인딩된다).
MXML이 GUI를 정의하는 동안, ActionScript는 이벤트 처리를 위한 작동, 데이터 바인딩
([Bindable] 메타데이터 태그 사용) 및 원격 서비스로 호출하는 기능을
제공한다. Listing 3에서 메소드 createContact, editContact,
deleteContact 및 getAllContacts는 모두
서버측에서 원격 메소드를 호출한다. 원격 메소드가 호출되면
ActionScript는 콜백 함수의 선언으로 인한 결함과 결과를 처리하기 위한 기회가 제공된다. Listing 3에서
handleResult 함수는 결과를
Object로 수신하고 이를 ArrayCollection로 캐스트한다.
BlazeDS는 List를 서버측에서
ArrayCollection으로 변환했다.
Listing 4에 Flash측에서 연락처 오브젝트를 표현하기 위해 작성한 ActionScript 클래스 Contact가
있다.
Listing 4. ActionScript Contact 클래스
package bcit.contacts.dto {
[RemoteClass(alias="bcit.contacts.Contact")]
public class Contact {
public function Contact() { id = -1; }
public var id:Number;
public var lastName:String;
public var firstName:String;
public var emailAddress:String;
public var phoneNumber:String;
public function toString():String {
return id + ", " + firstName + " " + lastName + " " + emailAddress
+ " " + phoneNumber;
}
}
}
|
이러한 ActionScript 오브젝트는 서버측으로 전송되는데, 이는 BlazeDS가
재주를 부려서 ActionScript 오브젝트를 Java 오브젝트로 변환하는 곳이다.
ActionScript Contact 클래스는 DTO(Data
Transfer Object)로 간주된다.
애플리케이션은 또한 서버의 특정 설정을 명시하는 구성 파일에 의존한다. 이 애플리케이션 내에서 두 개의 기본 구성 영역은 Hibernate와 BlazeDS이다.
표준 JPA 구성 파일인 persistence.xml을 사용하여 Hibernate를 구성할 수 있는데, 이는 Listing 5에 표시되어 있다.
Listing 5. persistence.xml 구성 파일의 서브세트
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="contacts" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.default_schema" value="contacts" />
<property name="hibernate.connection.driver_class"
value="com.mysql.jdbc.Driver" />
<property name="hibernate.connection.url"
value="jdbc:mysql://localhost:3306/contacts" />
<property name="hibernate.archive.autodetection" value="class, hbm"/>
<property name="hibernate.connection.username" value="root"/>
<property name="hibernate.connection.password" value="root"/>
</properties>
</persistence-unit>
</persistence>
|
persistence.xml 파일은 Hibernate가 읽을 수 있도록 웹 애플리케이션의 WEB-INF/classes/META-INF 폴더로 이동해야 한다. 제자리에 지정되면 Hibernate는 다음 정보가 필요하다.
- 데이터베이스 방언(다시 말해서, 많은 데이터베이스에 조금씩 다른 SQL 방언이 있기 때문에 말하는 데이터베이스가 어느 것인지 확인)
- 기본 스키마를 통한 테이블스페이스
- 데이터베이스로 연결하는 데 사용되는 데이터베이스 드라이버
- 데이터베이스 URL
- 자동 인식이 인식해야 하는 것(예를 들어, 어노테이트된 클래스, Hibernate 맵핑 XML 파일 등등)
- 사용자 이름 및 비밀번호
다른 정보는 Hibernate의 성능에 유용할 수 있지만 필수는 아니다.
BlazeDS는 네 가지의 구성 파일이 있다.
- messaging-config.xml: 게시-등록 메시징 정보를 정의한다.
- proxy-config.xml: HTTP와 웹 서비스에 대한 프록시 서비스 정보를 제공한다.
- remoting-config.xml: 기사 애플리케이션에 나온 것과 같은 원격 서비스에 대한 정보를 정의한다.
- services-config.xml: 다른 구성 파일을 참조하고 보안 제한조건, 채널 및 로깅도 제공하는 최상위 레벨 구성 파일
Listing 6에서는 services-config.xml 파일을 시연한다. 기사 애플리케이션에서는 애플리케이션이 BlazeDS 원격 서비스만 사용하고 있기 때문에 remoting-config.xml 파일만 관련되는 점에 유의하자.
Listing 6. services-config.xml 구성 파일의 서브세트
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service-include file-path="remoting-config.xml" />
<service-include file-path="messaging-config.xml" />
<service-include file-path="proxy-config.xml" />
<default-channels>
<channel ref="contacts-amf"/>
</default-channels>
</services>
<channels>
<channel-definition id="contacts-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://localhost:8080/contacts/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
<properties>
<polling-enabled>false</polling-enabled>
</properties>
</channel-definition>
</channels>
<logging>
<target class="flex.messaging.log.ConsoleTarget" level="Error">
<properties>
<prefix>[BlazeDS] </prefix>
<includeDate>false</includeDate>
<includeTime>false</includeTime>
<includeLevel>false</includeLevel>
<includeCategory>false</includeCategory>
</properties>
<filters>
<pattern>Endpoint.*</pattern>
<pattern>Service.*</pattern>
<pattern>Configuration</pattern>
</filters>
</target>
</logging>
</services-config>
|
services-config.xml 구성 파일은 다른 구성 파일을 참조하고(반드시 존재해야 함) BlazeDS 로깅을 구성하며 어느 채널이나 설정한다. 채널은 클라이언트가 서버와 통신하기 위해 사용되는 프로토콜을 위한 추상화이다. 기사 애플리케이션은 폴링 없이 표준 AMF 프로토콜을 사용한다. 폴링은 연결이 여전히 설정되도록 보장하기 위해 클라이언트가 서버와 지속적으로 통신하는 것을 의미한다.—이 애플리케이션에는 필요하지 않은 것이다.
채널 엔드포인트는 서버 URL을 지정한다. 이 엔드포인트는 프로젝트를 컴파일링하기 위해 필요하다. 클라이언트 Flash 애플리케이션은 하드 코드된 값으로 이를 사용하기 때문에 이는 서버가 연결할 대상을 인식한다. 그 대신에 실제로는 엔드포인트 URL을 MXML이나 ActionScript 코드에 바로 정의할 수 있다.
마지막으로 remoting-config.xml 구성 파일(Listing 7에 표시된 대로)은
원격 조작을 처리하기에 필요한 어댑터 클래스뿐만 아니라 원격 호출에 응답하는 실제 클래스도
지정한다. (이 경우에 bcit.contacts.ContactsService 클래스는 원격 요청에 대한 응답자로 주어졌다.)
Listing 7. remoting-config.xml 구성 파일의 서브세트
<?xml version="1.0" encoding="UTF-8"?>
<service id="remoting-service"
class="flex.messaging.services.RemotingService">
<adapters>
<adapter-definition id="java-object" default="true"
class="flex.messaging.services.remoting.adapters.JavaAdapter"/>
</adapters>
<default-channels>
<channel ref="contacts-amf"/>
</default-channels>
<destination id="contacts">
<properties>
<source>bcit.contacts.ContactsService</source>
<!--<scope>application</scope>-->
</properties>
</destination>
</service>
|
이 기사에서는 Tomcat 내에서 실행하며 연락처 정보에 대한 요청에 응답하는 Java 서버측 웹 애플리케이션을 쓰는 방법을 살펴보았다. 또한 클라이언트측 Flash 애플리케이션을 작성하기 위해 MXML과 ActionScript를 둘 다 사용하여 Flex 애플리케이션을 쓰는 방법에 대해서도 알아보았다. MySQL은 데이터 저장소로 작동하였고, Hibernate—ORM 프레임워크—는 쿼리하고 MySQL 데이터베이스를 업데이트할 수 있는 SQL 문으로 Java 오브젝트를 변환하는 데 사용되었다. 마지막으로 BlazeDS 프레임워크를 통해 Flash 애플리케이션은 원격 프로시저 호출과 Java 서버측 웹 애플리케이션에서 원격화를 수행할 수 있었다.
| 이름 | 크기 | 다운로드 방식 |
|---|---|---|
| JEE-BlazeDS-Flex-contacts.zip | 7MB | HTTP |
더 많은 다운로드
Notes
- 이 압축 파일은 이 프로젝트, WAR를 생성하기 위한 Ant 빌드 파일, 구성 파일 및 이 기사가 참조하고 사용하는 써드파티 라이브러리(JAR 파일의 양식)의 모든 소스 코드(Java, ActionScript 3, MXML)가 들어있다.
- 이 기사에서 예제 프로젝트로 사용하기 위해 필요한 오픈 소스 RDBMS
- 예제 프로젝트를 빌드하기 위한 Java 기반 빌드 도구
- 예제 프로젝트에서 Java 소스 코드를 컴파일하기 위해 필요한 Java SDK(JDK) 버전 6
- 예제 프로젝트에서 MXML 및 ActionScript 소스 코드를 컴파일링하기 위한 Flex 4 SDK
- 예제 프로젝트를 실행하기 위해 Java HTTP 웹 서버 환경을 제공하는 Apache Software Foundation 서블릿 컨테이너
- Java Platform인 Enterprise Edition(Java EE)으로 Flex 기술을 연결하기 위한 Adobe 프레임워크. 이 기사의 프로젝트 다운로드에 이미 포함된 대로 이 소프트웨어는 참조용에 불과하다.
- Java EE 컨테이너 미들웨어를 위한 Red Hat ORM 프레임워크. 이 기사의 프로젝트 다운로드에 이미 포함된 대로 이 소프트웨어는 참조용에 불과하다.
교육
-
JPA Concepts: Apache Software Foundation에 대해 자세히 알아보자.
-
Managing Entities:
엔티티로 작업하는 방법을 알아보자. 이를 Java EE 튜토리얼로 사용하자.
-
Hibernate
EntityManager User Guide: Hibernate
EntityManager로 작업하기 위해 필요한 정보를 찾아보자. -
Understanding
the Flex 3 Component and Framework Lifecycle(James Polanco and Aaron
Pedersen, DevelopmentArc): Flex 플랫폼에서 더 효율적으로 작업하는 방법을 알아보자.
-
Updated
"Elastic Racetrack" for Flash 9 and AVM2(Sean Christmann): Flash Player 내에서 이 프레임 실행 모델에 대해 자세히 알아보자.
-
Explicitly
mapping ActionScript and Java objects: Adobe LiveCycle®
데이터 서비스 개발자의 안내서를 자세히 읽어보자.
-
Creating a declarative
XML UI language(Arron Ferguson, developerWorks, 2009년 9월): 선언적인 XML UI에 대해 자세히 알아보자.
-
Flex 컴포넌트 작성하기(영문)(Sandeep Malik, developerWorks, 2009년 7월): 새로운 Flex 함수를 작성하는 방법에 대해 처음부터 알아보자.
-
SaaS(Software as a Service) 개발을 위한 Flex 4 기능(영문)(Dan Orlando, developerWorks, 2009년 7월):
RIA 사용자 경험을 향상시키기 위해 Flex 4를 사용하는 방법에 대해 알아보자.
-
developerWorks 웹 개발 영역: 웹 개발 영역에서는 Web 2.0 개발에 대한 도구와 정보를 볼 수 있다.
-
IBM
기술 행사 및 웹 캐스트: developerWorks 기술 행사 및 웹 캐스트를 통해 최신 정보를 얻을 수 있다.
- 무료 developerWorks Live!
briefing: IBM 제품 및 도구에 대한 정보뿐 아니라 IT 업계의 최신 경향까지도 빠르게 확인할 수 있다.
- developerWorks on-demand
demos: 입문자를 위한 제품 설치 및 설정부터 숙련된 개발자를 위한 고급 기능까지 다양한 데모를 제공한다.
제품 및 기술 얻기
-
자신에게 가장한 적합한 방법으로 IBM 제품을 평가해보자.
시험판 제품을 다운로드하거나, 온라인으로 제품을 사용해 보거나, 클라우드 환경에서 제품을 사용하거나, SOA Sandbox에서
SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다.
토론
- developerWorks Community:
개발자가 운영하고 있는 블로그, 포럼, 그룹 및 위키를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.

Arron Ferguson là một giảng viên đại học đã 12 năm, dạy về kỹ thuật phần mềm tại Viện Công nghệ British Columbia. Các lĩnh vực kinh nghiệm và quan tâm của ông là công nghệ Java, XML, các công nghệ Web, hoạt hình 2D và 3D, và viết cho các phương tiện truyền thông kỹ thuật số. Ông là soạn giả và nhà phê bình kỹ thuật tự do và có cuốn sách được xuất bản: Tạo Các Hệ thống Quản trị Nội dung trong Java (Creating Content Management Systems in Java) (Charles River Media, 2006). Arron cũng say mê Linux và công nghệ mã nguồn mở khác.