Articles Index
Enterprise JavaBeans (EJB) technology is a J2EE technology for developing business components in a component-based, enterprise Java application. Business components developed with EJB technology are often called Enterprise JavaBeans components or simply "enterprise beans." Enterprise beans typically provide the logic and represent the data needed to perform operations specific to a business area such as banking or retail. For example, an enterprise bean (perhaps together with other enterprise beans) might offer the data and logic needed to perform banking account operations, such as crediting and debiting an account. Other enterprise beans might offer the data and logic needed to perform "shopping cart" operations that allow customers to purchase goods online from a retail store.
EJB technology is generally viewed as powerful and sophisticated. The technology helps developers build business applications that meet the heavy-duty needs of an enterprise. In particular, applications built using EJB and other J2EE technologies are secure, scale to support very large numbers of simultaneous users, and are transaction-enabled so that data maintains its integrity even though it's processed concurrently by multiple users.
These needs are met through services that are provided by the EJB runtime environment -- the EJB container. What this means is that support for things like concurrent data access and security is automatically provided by the EJB container to all enterprise beans.
Despite its power and sophistication, some developers are hesitant to use EJB technology. The major obstacle for these developers is complexity. But there's good news in store. The next release of the Enterprise JavaBeans architecture, EJB 3.0, focuses primarily on making the technology easier for developers to use -- but not at the expense of the technology's power and sophistication. (In addition, the EJB 2.0 and 2.1 APIs are still available for use.) The idea is not only to make EJB technology easier to use for developers who already use it, but to attract a wider range of developers.
The next release of the Enterprise JavaBeans architecture, EJB 3.0, focuses primarily on making the technology easier for developers to use -- but not at the expense of the technology's power and sophistication.
|
This article covers some of the ease-of-development features that are being planned for EJB 3.0 and that are documented in the EJB 3.0 Specification (now available as an early draft). It's important to note that the EJB 3.0 Specification is a work in progress, and so might change over time.
But first, let's take a step back and look at some of the basics of EJB technology. Then let's look at how the technology works today. In other words, let's examine what a developer does to create and use enterprise beans with the current level of the technology, EJB 2.1. After that let's examine how EJB 3.0 simplifies things for developers.
Table of Contents
An Overview of Enterprise JavaBeans Technology
This section gives a brief overview of some of the key concepts related to EJB technology. A lot of the material in this section is extracted from the J2EE 1.4 Tutorial, which is an excellent resource for learning more about EJB terminology and concepts. See especially Chapter 23: Enterprise Beans for a more detailed introduction to Enterprise JavaBeans technology.
Enterprise beans are portable, reusable, and they're managed. The EJB container provides system-level services, so a developer is free to concentrate on developing the logic and data aspects of the application.
|
As mentioned earlier, enterprise beans provide the logic and represent the data for business-oriented operations. Enterprise beans are portable -- you can deploy them in any compliant J2EE application server. This gives developers a lot of flexibility in terms of distributing the components of an application. Enterprise beans are also reusable -- you can use them in multiple business applications. But perhaps what's most appealing about enterprise beans is that they're managed. The EJB container provides system-level services such as transaction management (to preserve the integrity of data in a multi-user environment) and security management (to protect against unauthorized access to resources). Because the EJB container provides these and other system-level services, a developer does not have to provide them in an application, and so is free to concentrate on developing the logic and data aspects of the application.
There are three types of enterprise beans: session beans, entity beans, and message-driven beans. Often a combination of these beans is used in an enterprise application.
Session Beans
A session bean represents a single unique session between a client and an instance of the bean. A session bean can't be shared. One instance of the bean is tied to a specific client in a specific session. The session bean exposes methods that a client can call to execute business tasks on the server. When the client's session ends, the session bean is no longer associated with that client.
There are two types of session beans: stateful and stateless. A stateful session bean maintains data about the unique client-bean session in its instance variables. The data represents the state (often called the "conversational state") of that specific session. The conversational state is maintained for the life of the client-bean association. Significantly, this means that the data is maintained across operations. If one of the bean's methods finishes executing and another is called during the session, the conversational state is maintained from one operation to the next. This makes stateful session beans useful for things like shopping cart and banking services that involve multiple, related operations. To maintain conversational state when space is constrained, the EJB container might write a stateful session bean to secondary storage and then later retrieve it.
By comparison, a stateless session bean does not maintain conversational state for its client. Because a stateless session bean cannot maintain conversational state across methods, it's typically used for one-step tasks, such as sending an email that confirms an online order.
An EJB container never writes a stateless session bean to secondary storage. One other distinguishing thing about a stateless session bean is that it can implement a web service.
Entity Beans
An entity bean represents data in a storage medium, such as a relational database. Each entity bean may correspond to a table in a relational database, and each instance of the bean corresponds to a row in that table. Entity beans are not limited to representing relational databases. They can represent data in other types of data stores. But the majority of enterprise applications that use EJB technology access data in relational databases, so this article focuses on that type of data store.
The emphasis here is on the word "represents." An entity bean in not part of a relational
database, but rather contains data that is loaded from the database at appropriate points in time.
The data is then written back to the database at other appropriate points in time.
An entity bean differs from a session bean in several ways. An entity bean can be shared (a session bean cannot), that is, multiple clients can use the same entity bean. The gives multiple clients the ability to access the same data that the bean represents. Of course, this is where the transaction management (and its data integrity control) provided by the EJB container is important. In addition, entity beans are persistent. This means that the entity bean's state exists even after the application that uses it ends. The bean is persistent because the database that underlies it is persistent. For that matter, the entity bean's state can be reconstructed even after a server crash (because the database can be reconstructed). Remember that the state of a stateful session bean exists only for the life of the client-bean session, and for a stateless session bean, only during the life of a method. Also, an entity bean can have relationships with other entity beans, much like a table in a relational database can be related to another table. For example, in a college enrollment application, an entity bean that represents data about students might be related to an entity bean that represents data about classes. By comparison, a session bean cannot be related to other session beans.
An entity bean can manage its own persistence (this is called bean-managed persistence) or let the EJB container manage it (container-managed persistence). With bean-managed persistence, the entity bean code includes SQL statements that access the database. With container-managed persistence, the EJB container automatically generates the necessary database access calls.
Message-Driven Beans
A message-driven bean processes asynchronous messages typically sent through the Java Message Service (JMS) API. Asynchronous messaging frees the message sender from waiting for a response from the message receiver.
A message-driven bean can process messages sent by any J2EE component (such as an application client, another enterprise bean, or a web component) or by a JMS application or system that does not use J2EE technology. Often message-driven beans are used to route messages. This makes them useful in many business-to-business communication scenarios.
Enterprise Bean Interfaces and Classes
Although it's easy to imagine a client directly calling a bean's methods, the reality is that it can't. Instead, for session or entity beans, a client calls methods defined in the bean's interfaces. These interfaces comprise the client's "view" of the bean. A session or entity bean must have two interfaces -- a component interface and a home interface -- as well as a bean class. Neither can a client access a message-driven bean directly -- it needs to send messages to the message endpoint serviced by the bean.
The component interface defines the business methods for the bean. For example, the component interface for a session bean that handles bank accounts might define a method that credits an account and one that debits an account. The home interface defines "life-cycle" methods for the bean. These are methods that do things like create an instance of a bean or remove it (or at least make it unavailable). For entity beans, the home interface can also define finder methods and home methods. Finder methods locate instances of a bean. Home methods are business methods that are invoked on all instances of an entity bean class.
The bean class implements the methods defined in the bean's interfaces.
Local and Remote Enterprise Beans
Clients can access beans locally, that is, where the client and bean are in the same Java virtual machine1, or remotely, where the client and bean can be in different Java virtual machines (or even different physical machines). A local or remote client can be a web component or another enterprise bean. A remote client can also be a web application. For local access, a session or entity bean must provide a component interface called a local interface, and a home interface called a local home interface. For remote access, a session or entity bean must provide a component interface called a remote interface, and a home interface.
The EJB Container and Persistence
Clearly, the EJB container plays an important role in Enterprise JavaBeans technology. It provides the runtime environment inside of an application server for enterprise beans. In doing that, it offers a wide variety of services to the beans, such as transaction management and security control. In addition, it manages persistence for entity beans with container-managed persistence, and relationships for entity beans with container-managed relationships. To manage persistence and relationships, the EJB container needs information beyond the information provided in the bean's interfaces and classes. This additional information is provided in an abstract schema that defines the bean's persistent fields (the fields that represent persistent data) and the bean's relationships. An abstract schema differs from the physical schema of the underlying database (which defines the table structures in the database and the table relationships). In EJB 2.0 and EJB 2.1 technology, you need to provide deployment descriptor information for each entity bean. The deployment descriptor specifies, among other things, the abstract schema for the bean (see Developing an Enterprise Bean with EJB 2.1 Technology, for an example).
EJB Query Language
Recall that an entity bean with container-managed persistence does not include SQL statements. So you might wonder, how does the EJB container know what persistent data to access and what to return? The answer is through finder methods and Enterprise JavaBeans Query Language (EJB QL) queries. Finder methods find instances of an entity bean or collections of entity beans. EJB QL queries query the abstract schema. You specify finder methods in the home interface of the bean, and associate them with queries written in EJB QL. An EJB QL query looks like an SQL query -- it contains a SELECT clause, a FROM clause, and a WHERE clause. However, unlike an SQL-based query, an EJB QL query queries the abstract schema, not the database. When a finder method is invoked, the EJB container executes the EJB QL query associated with that method and returns references to one or more entity beans that represent data satisfying the query criteria. An example of a finder method might be one that identifies bank accounts that have an account balance that is less than a certain threshold. The EJB QL query for that finder method queries the abstract schema and returns references to one or more entity beans that represent accounts meeting the query criteria. In addition to the abstract schema it defines, the deployment descriptor for an entity bean in EJB 2.1 technology defines the EJB QL queries for the bean and associates the queries with finder methods.
Posted by 아름프로
잘알고 있다고 생각들 하는 JDBC ...
JDBC 전문가가 이야기하는거 한번 들어보세요.. ^^
Posted by 아름프로
Roadmap to
Agile methods and tools
What Is Agile Software Development?
In the late 1990's several methodologies began to get increasing
public attention. Each had a different combination of old ideas, new
ideas, and transmuted old ideas.
But they all emphasized close collaboration
between the programmer team and business experts; face-to-face
communication (as more efficient than written documentation);
frequent delivery of new deployable business value; tight,
self-organizing teams; and ways to craft the code and the
team such that the inevitable requirements churn was not a crisis.
Early 2001 saw a workshop in Snowbird, Utah, USA, where various
originators and practitioners of these methodologies met to figure
out just what it was they had in common. They picked the word "agile"
for an umbrella term and crafted the
Manifesto for Agile Software
Development, whose most important part was a statement of
shared development values:
We are uncovering better ways of developing
software by doing it and helping others do it.
Through this work we have come to value:
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration over contract negotiation
Responding to change over following a plan
That is, while there is value in the items on
the right, we value the items on the left more.
(See also the attached principles.)
The Manifesto struck a chord, and it led to many new Agile projects
being started. As with any human endeavor, some succeeded and
some failed. But what was striking about the successes was how much
both the business people and the technical people loved their
project. This was the way they wanted software development
done. Successful projects spawned enthusiasts.
The Agile Alliance exists to help more Agile projects succeed and to
help the enthusiasts start more Agile projects. This particular page
is to help people learn more about Agile software development. In
keeping with the Agile emphasis on face-to-face communication, we urge
you to visit a
users group and talk to your peers about their
experience. But we also provide this Roadmap, which intends to give you a quick
introduction to various agile methods and tools. Click on the links
below to get more info on a certain method or tool. On the following
pages you will find links to pages that contain the specific
information you need.
If you're looking for more information regarding the history of the Agile
Alliance then look at
the articles section.
Each page contains links to books published on the method. Please use
the supplied link if you intend to order any books. By doing so you
sponsor the Agile Alliance. No hassle for you but a great help for us:
href="http://www.amazon.com/exec/obidos/external-search?tag=agilealliance-20&keyword=agile%20methods&mode=books">
General Books on Agile Methods.
Version 2.3 revised on December 16th, 2004
Posted by 아름프로
Eclipse Rich Client Platform
While the Eclipse platform is designed to serve as an open tools platform, it is architected so that its components could be used to build just about any client application. The minimal set of plug-ins needed to build a rich client application is collectively known as the Rich Client Platform.
Applications that don't require a common resource model can be built using a subset of the platform. These rich applications are still based on a dynamic plug-in model, and the UI is built using the same toolkits and extension points. The layout and function of the workbench is under fine-grained control of the plug-in developer in this case.
When we say that the Rich Client Platform is the minimal set of plug-ins needed to build a platform application with a UI, we mean that your application need only require two plug-ins, org.eclipse.ui and org.eclipse.core.runtime, and their prerequisites. However, rich client applications are free to use any API deemed necessary for their feature set, and can require any plug-ins above the bare minimum. Examples include the Help UI, and the Update Manager.
Posted by 아름프로
Web Services Metadata(WSM)에 대한 BEA 자료
Posted by 아름프로
Securing XML is an essential element in keeping Web services secure. Created in partnership with our sister site, SearchSecurity.com, this SearchWebServices.com Learning Guide is a compilation of resources that review different types of XML security standards and approaches for keeping your XML Web services secure. Send us an e-mail to let us know what other learning guides you'd like to see on SearchWebServices.com.
Posted by 아름프로
BPEL4WS – Business Process Execution Language for Web Services
Java developers need to publish synchronous and asynchronous Web services and compose them into reliable and transactional business flows. Web service orchestration standards (SOAP Conversation, BPEL4WS and WS-Transaction) are emerging and need to be packaged into a reliable and easy-to-manage software solution. So we've gathered a wealth of information to get you up-to-speed quickly.
Posted by 아름프로
PMD for Eclipse install instructions
Start Eclipse.
Start the installation procedure : select the Help>Software Updates>Find and
Install... menu item.
Select "Search for new features to install" option and click Next.
Click New Remote Site...
Give a name (ie PMD Eclipse Site), enter the URL http://pmd.sourceforge.net/eclipse
Select this new site in the Sites to include in search list and click Next.
Select PMD for Eclipse 3 and Apache Xerces in the "Select the features to install" list and click Next.
Accept the terms of the license agreements and click Next.
Verify that the install location is your Eclipse installation directory, otherwise
select the correct one, click Finish.
A warning appear telling the feature is not signed. Ignore and click Install to
continue.
Accept to restart the workbench to load PMD into the workbench.
Eclipse is restarted and a PMD welcome page is displayed : the plugin is correctly
installed.
Posted by 아름프로
|
SOA 서비스의 정의, 특성화, 구현 방법
Average rating:
(29 ratings) |
Ali Arsanjani, 박사
아키텍트, SOA and Web services Center of Excellence, IBM
2004년 11월 9일
서비스 지향 모델링과 아키텍쳐의 핵심을 논의한다. 서비스
지향 아키텍쳐(SOA)를 구현하는데 필요한 분석과 디자인을 위한 핵심 액티비티를 논의한다. 필자는 서비스의
정의, 특성화, 실현에 필요한 기술을 언급하는 것의 중요성을 강조하고 있다. 또한 서비스의 흐름과 구성,
SOA에 요구되는 서비스의 품질을 실현 및 보증하기 위해 필요한 엔터프라이즈급 컴포넌트의 중요성에 대해서도
이야기 하고 있다.
Introduction
그동안 서비스 지향 아키텍쳐(SOA)와 웹 서비스로서 이를 구현하는 것을 둘러싸고 많은 소문과 가설들 -
이중 몇몇은 사실이고 어떤 것은 전혀 근거 없는 것이었다 - 이 무성했다. 분석가들은 예언했고, 학자들은
가설을 내놓았으며, 교수들은 강의했고, SOA는 제품이 아니라는 중요한 사실을 망각한 기업들은 그들이 가진
것을 SOA로 포장해서 팔기에 급급했다. 디자인 원리, 패턴, 기술을 사용하는 비지니스와 제휴된 IT 서비스를
통해 비지니스와 IT 사이의 차이를 이으려고 한다.
ZDNet은
최근 "Gartner는 2008년 까지, 60 퍼센트 이상의 기업들이 중요한 미션을 수행하는 애플리케이션과
프로세스를 만들 때 "가이드 원리" 로서 SOA를 사용할 것"이라고 발표했다."
SOA의 개발과 구현에 대한 수요는 대단하다. 만일 SOA가 이들을 구현하는 것을 돕는 단순한 제품과
표준에 관한 것이 아니라면, 예를 들어, 웹 서비스 같이, 어떤 추가적인 요소들이 SOA를 구현하는데 필요한가?
서비스 지향 모델링은 전통적인 객체 지향의 분석과 디자인(OOAD)에는 없는 추가적인 액티비티와 가공물들이
필요하다. “Elements
of Service-oriented Analysis and Design" 에서는 OOAD 그 이상의
것이 필요한 이유를 설명하고 있다. 또한 이 글에서는, 비지니스 프로세스 관리 또는 엔터프라이즈 아키텍쳐(EA)와
OOAD가 분석과 디자인을 수행하기에 부적절한지도 설명한다. IBM Redbook, “Patterns:
Service-Oriented Architecture and Web Services"에서는 서비스
지향 모델링 접근방식의 주요 액티비티를 설명하고 있다.
하지만 몇 가지 더 고려해야 할 중요한 사항들이 있다.우선, 현재 OOAD 방식은 SOA의 세 가지 핵심
요소들인 서비스, 플로우, 서비스를 구현하는 컴포넌트를 다루지 않는다. 또한, 서비스의 구분, 특성화,
구현에 필요한 기술과 프로세스, 흐름과 구성, 엔터프라이즈 급의 컴포넌트를 분명히 다룰 필요도 있다.
두 번째, SOA에서 두 가지 핵심 역할들인 서비스 공급자와 서비스 소비자에 대한 분명한 요구사항을 파악하기
위해 패러다임 변화가 필요하다. 셋째로, 하나의 엔터프라이즈 또는 비지니스 라인을 위해 구현될 애플리케이션들은
공급 체인 안에서 사용되어야 하고, 이 애플리케이션들을 새로운 애플리케이션으로 구성, 결합 및 캡슐화 할
비지니스 파트너들에게 노출되어야 한다. 이러한 개념을 서비스 생태계 또는 서비스 밸류 네트(value-net)
라고 한다.
이는 "분산 객체" 보다 약간 올라선 개념이다. 네트워크 효과를 통해 만들어진 가치에
대한 것이다; 예를 들어 Google의 검색 서비스와 Amazon.com을 결합하고, 이들을 eBay 서비스와
결합하여 혼합 솔루션을 구현했다고 해보자. 이런 예도 생각할 수 있다. 여행사가 항공 예약 시스템과 연계하고,
렌트카 에이전시와 호텔 체인과 협업할 때, 기록들이 업데이트 되고 여행 일정이 전자 계획표상에 전송된다.
애플리케이션이 무엇이든 간에, SOA를 성공적으로 구현하기 위해서는 좋은 툴과 표준 그 이상이 필요하다.
SOA의 수명 주기를 지원할 통시적 시각이 필요하다; 분석, 디자인, 서비스 구현, 플로우, 컴포넌트 등에
대한 기술이 필요하다. 따라서 엔터프라이즈 애플리케이션 개발에 관심이 있는 사람이라면 서비스 지향 모델링과
아키텍쳐에 개입된 상세한 단계들을 이해해야 한다.
이들 과정을 자세히 설명하기 전에 여러분이 만들어내고자 하는 것을 이해하도록 하자; SOA가 무엇이고
어떤 모습인가? SOA의 개념을 정의한 후에 SOA의 레이어를 설명하고, 프로젝트, 비지니스 성격, 엔터프라이즈
목적, 밸류 체인에 합당한 SOA의 청사진을 구현할 수 있도록 그러한 레이어들 마다 핵심적인 아키텍쳐 결정을
수립하는 방법을 설명하겠다.
서비스 지향 아키텍쳐:
개념 모델
이 개념은 세 가지 주요 참여자들인 서비스 공급자(서비스 디스크립션의 퍼블리시 및 서비스 구현 공급),
서비스 사용자(서비스 디스크립션의 URI를 직접 사용하거나 서비스 레지스트리에서 서비스 디스크립션을 찾아
호출), 서비스 브로커(서비스 레지스트리의 공급 및 관리) 사이의 인터랙션을 정의하는 아키텍쳐 스타일에
기반하고 있다.
아래 그림 1은 이러한 관계를 보여주는 메타-모델이다.
그림 1: SOA 아키텍쳐 스타일의 개념 모델
아키텍쳐 스타일과 원리
SOA를 정의하는 아키텍쳐 스타일은 약결합된, 비지니스와 연계된 서비스를 만드는 일련의 패턴과 가이드라인을
설명한다. 디스크립션, 구현, 분리의 문제를 해결하지는 못한다. 새로운 비지니스 위협과 기회에 대한 응답에
있어 전례가 없는 유연성을 제공한다.
SOA는 리소스를 "온 디맨드" 방식으로 연결하기 위한 엔터프라이즈급의 IT 아키텍쳐이다.
SOA에서 리소스들은 밸류 네트, 엔터프라이즈, 비지니스 라인에 참여할 수 있다. 한 조직의 비지니스 프로세스
및 목표를 집합적으로 수행하는 비지니스와 제휴된 IT 서비스들로 구성되어 있다. 이러한 서비스들을 합성
애플리케이션으로 구성할 수 있고 표준 프로토콜을 통해 호출할 수 있다.(그림
2)
서비스는 외부화된 서비스 디스크립션을 갖춘 소프트웨어 리소스이다. 서비스 디스크립션은 서비스 소비자들에
의해 검색, 바인딩, 호출될 수 있다. 서비스 공급자는 서비스 디스크립션 구현을 실현하고 서비스 소비자들에게
양질의 서비스 요구 사항들을 제공한다. 서비스들은 선언적 정책에 의해 관리되어 동적으로 재
설정 가능한 아키텍쳐 스타일을 지원하는 것이 이상적이다.
그림 2: SOA의 애트리뷰트
비지니스민첩성은 SOA에 의해 제공되는 인터페이스, 구현, 바인딩(프로토콜)의 분리로 인해 유연해진
IT 시스템을 통해 이룩될 수 있다. 어떤 서비스 공급자가 새로운 비지니스 요구 사항들에 기반하여 주어진
지점과 시간에 적절한지를 선택할 여유도 주어진다. (함수적 요구사항과 비 함수적 요구사항(예를 들어, 퍼포먼스,
보안, 확장성 등)).
fractal realization pattern으로 내부 비지니스단위 또는 비지니스파트너들 사이의
밸류 체인을 통해 서비스들을 재사용할 수 있다. fractal realization은 인터랙션 모델에 참여한
참여자들의 패턴과 역할을 적용하기 위해 아키텍쳐 스타일의 기능을 참조한다. 아키텍쳐 내의 한 티어에 이를
적용할 수 있고 엔터프라이즈 아키텍쳐를 통해 멀티 티어에도 적용할 수 있다. 프로젝트 사이에서는 하나의
밸류 체인 안에서 일관성 및 확장 가능한 방식으로 비지니스 단위들과 비지니스 파트너들 간 존재할 수 있다.
콘텍스트
이 섹션에서는 서비스 지향 모델링의 구분, 특성화, 구현, 가공물의 고급 작동에 대해 소개하겠다. 서비스
지향 모델링은 비지니스분석, 프로세스, 목표와 연계된 SOA의 모델링, 분석, 디자인, 제작을 위한 서비스
지향 분석과 디자인(SOAD) 프로세스 이다.
우선, 구현하려고 하는 것, 주로 SOA와 이것의 레이어를 살펴보자. 그런 다음, SOA를 구현할 때
필요한 주요 액티비티와 기술을 설명하면서 SOA 구현 방법을 설명하겠다.
예를 들어, 여러분이 프로젝트를 진행 중이고 목표는 셀프 서비스 어카운팅 시스템을 갖고 있는 비지니스의
뱅킹 라인의 한 부분을 SOA로 마이그레이션 한다고 생각해보자.
SOA로 마이그레이션 하기 위해서는 서비스 모델링 외에도 몇 가지 추가 요소들이 필요하다:
- 채택과 성숙 모델. SOA와 웹 서비스의 채택에 있어 엔터프라이즈의 상대적 성숙도는 어느 정도인가?
다양한 채택 레벨들은 그 레벨 마다 고유한 필요가 있다.
- 평가. 파일럿을 갖고 있는가? 웹 서비스에 관여해 본 적이 있는가? 결과 아키텍쳐의 상태는 어떠한가?
같은 방향으로 지속적으로 가야하는가? 이것이 엔터프라이즈 SOA로 이끌 것인가? 고려해야 할 사항들을
생각해 보았는가?
- 전략과 플래닝 액티비티. SOA로 마이그레이션하기 위해 어떤 계획을 세웠는가? 절차, 툴, 메소드,
기술, 표준, 교육 등 고려해야 할 사항들은 무엇인가? 로드맵과 전망은 무엇이며, 어떻게 도달 할 것인가?
계획은 무엇인가?
- 관리. 기존 API 또는 기능들이 서비스가 되어야 하는가? 그렇지 않다면 어떤 것이 가능한가?
모든 서비스들은 어떤 방식으로든 비지니스에 가치를 창출하도록 구현되어야 한다. 여기에 개입하지 않고
프로세스를 어떻게 관리하는가?
- 최상의 사용법 구현. 보안을 구현하고, 퍼포먼스를 보장하고, 상호운용성을 위한 표준에 순응하고 변화를
계획하기 위해 시도된 방식은 무엇인가?
이 글에서 설명한 구분, 특성화, 구현 외에도 서비스 지향 모델링 방식에는 SOA의 전체 수명 주기를 지원하는데
필요한 전개, 모니터링, 관리 기술들이 포함된다.
SOA로의 마이그레이션과 구현 후 추가 액티비티에 대한 논의는 다음 글에서 서서히 다루도록 하겠다. 지금은
기존 시스템 또는 서비스를 새로운 시스템과 서비스로 변형하는 것에 초점을 맞추겠다. 서비스 지향 아키텍쳐를
구현하기 위해 서비스 지향 모델링을 시작할 수 있다.
SOA를 위한 아키텍쳐
템플릿
SOA의 추상적인 모습은 비지니스프로세스와 제휴된 합성 서비스들이 부분적으로 레이어 된 아키텍쳐이다.
그림 3은 이러한 유형의 아키텍쳐를 묘사하고 있다.
서비스와 컴포넌트 사이의 관계는 엔터프라이즈급 컴포넌트들(크게 구분된 엔터프라이즈 또는 비지니스 라인
컴포넌트)이 서비스를 실현하고, 기능을 제공하고 서비스의 품질을 관리하는 것이다. 비지니스프로세스 플로우는
이렇게 노출된 서비스들을 합성 애플리케이션에 구성함으로서 지원될 수 있다. 통합 아키텍쳐는 Enterprise
Service Bus (ESB)를 사용하여 서비스, 컴포넌트, 플로우의 라우팅, 중개, 트랜슬레이션을 지원한다.
그림 3: SOA의 레이어
이들 각 레이어의 경우 디자인과 아키텍쳐 결정을 내려야 한다. 따라서 SOA를 문서화하려면 각 레이어에
상응하는 섹션들로 구성된 문서를 만들어야 한다.
다음은 SOA 아키텍쳐 문서를 위한 템플릿이다:
- 범위 <이 아키텍쳐가 필요한 엔터프라이즈 영역은?>
- 연산 시스템 레이어
- 패키지 애플리케이션
- 커스텀 애플리케이션
- 아키텍쳐 결정
- 엔터프라이즈 컴포넌트 레이어
- 엔터프라이즈 컴포넌트로 지원되는 기능 영역
- <이 엔터프라이즈 컴포넌트로 지원되는 비지니스도메인, 목표 프로세스는 무엇인가?>
- 관리와 관련된 결정
- <클라이언트 조직 내에서 엔터프라이즈 컴포넌트로서 선택된 것들의 기준은 무엇인가?>
- 아키텍쳐 결정
- 서비스 레이어
- 서비스 포트폴리오 범주
- 아키텍쳐 결정
- 비지니스프로세스 및 합성 레이어
- 구성법으로 표현되는 비지니스프로세스
- 아키텍쳐 결정
- <어떤 프로세스가 구성법으로 연결되어야 하고 어떤 것이 애플리케이션 안으로 구현되어야
하는가?>
- 액세스 또는 표현 레이어
- <이 레이어 상의 웹 서비스와 SOA의 문서 포함; 예를 들어 사용자 인터페이스 레벨에서
웹 서비스를 호출하는 포틀릿의 사용과 그러한 레이어의 기능에 대한 내용>
- 통합 레이어
- <ESB 고려사항 포함>
- <서비스의 클라이언트가 필요로 하는 서비스 레벨 계약(SLA)와 서비스 품질(QoS)을
어떻게 보장하는가?>
- 보안 문제 및 결정
- 퍼포먼스 문제 및 결정
- 기술과 표준 한계 및 결정
- 서비스의 모니터링과 관리
- 디스크립션과 결정
이제 각 레이어를 상세히 살펴보겠다.
Layer 1: 연산 시스템 레이어. 기존의 커스텀 구현 애플리케이션, 다시 말해서 레거시
시스템들(기존 CRM, ERP 패키지 애플리케이션, 오래된 객체 지향 시스템 구현, 비지니스정보 애플리케이션)로
구성된다. 이 레이어에서는 기존 시스템을 사용하고 이들을 서비스 지향 통합 기술을 사용하여 통합할 수 있다.
Layer 2: 엔터프라이즈 컴포넌트 레이어. 노출된 서비스의 기능을 구현하고 QoS를
관리하는 책임을 맡고 있다. 이 특별한 컴포넌트는 엔터프라이즈 또는 비지니스단위 레벨의 자금 지원을 받는
엔터프라이즈 자산으로 관리된다. 엔터프라이즈급의 자산으로서 애플리케이션을 사용할 때 SLA 순응해야 한다.
이 레이어에서는 애플리케이션 서버 같은 컨테이너 기반의 기술을 사용하여 컴포넌트, 워크로드 관리, 고 가용성,
로드 밸런싱을 구현한다.
Layer 3: 서비스 레이어. 비지니스가 자금을 지원하고 노출하기로 선택한 서비스들이
이 레이어에 있다. 이 서비스들은 발견될 수도 있고 또는 정적으로 바인딩 되어 호출될 수도 있고 또는 합성
서비스로 구성될 수도 있다. 이 서비스 노출 레이어는 엔터프라이즈급 컴포넌트, 비지니스단위 스팩의 컴포넌트,
프로젝트 스팩의 컴포넌트를 가져다가 서비스 디스크립션의 형태로 그들의 인터페이스들의 하위 세트를 외부화하는
메커니즘을 제공한다. 따라서, 엔터프라이즈 컴포넌트는 인터페이스가 제공하는 기능을 사용하여 런타임 시 서비스
구현을 제공한다. 이 레이어에서 인터페이스는 서비스 디스크립션으로서 반출된다. 이곳이 노출되어 사용되는
곳이다. 고립되어 존재할 수도 있고 합성 서비스로서 존재할 수 있다.
Level 4: 비지니스프로세스 합성 및 구성 레이어. Layer 3에서 노출된 서비스들의
합성과 구성이 이 레이어에서 정의된다. 서비스들은 오케스트레이션 또는 구성법을 통해 플로우로 번들되고 따라서
하나의 애플리케이션으로서 함께 작동한다. 이러한 애플리케이션들은 특정 사용 케이스와 비지니스프로세스를
지원한다. IBM® WebSphere® Business Integration Modeler 또는 Websphere
Application Developer Integration Edition 같은 시각적인 플로우 합성 툴은
애플리케이션 플로우의 디자인에 사용될 수 있다.
Layer 5: 액세스 또는 표현 레이어. 이 레이어는 이 글의 범위에서 벗어나지만 점점
관련성이 커지고 있다. Web Services for Remote Portlets Version 2.0
및 기타 기술 같이 애플리케이션 인터페이스 또는 표현 레벨에 웹 서비스를 활용하는 표준으로 집중되기 때문에
다루기로 결정했다. 미래의 솔루션에 고려해야 할 미래형 레이어로 생각해도 좋다. SOA는 사용자 인터페이스와
컴포넌트의 결합을 해제하고 액세스 채널에서 서비스 또는 서비스 합성 까지 엔드투엔드 솔루션을 제공한다.
Level 6: 통합 (ESB). 이 레이어에서는 지능형 라우팅, 프로토콜 중개, 기타
변형 메커니즘 (주로 ESB- 참고자료) 같은 신뢰성 있는
기능들을 도입하여 서비스 통합을 실현한다. Web Services Description Language
(WSDL)는 바인딩을 지정하는데 이는 서비스가 제공되는 위치를 포함하고 있다. 반면 ESB는 위치와 독립적인
통합 메커니즘을 제공한다.
Level 7: QoS. 이 레이어는 보안, 퍼포먼스, 가용성 같은 QoS를 모니터, 관리,
유지에 필요한 기능을 제공한다. 이것은 SOA 애플리케이션의 상태를 감시하는 감지와 반응 메커니즘과 툴을
통한 백그라운드 프로세스이다. SOA의 QoS를 구현하는 WS-Management, 기타 관련 프로토콜,
표준의 구현들이 포함되어 있다.
서비스 지향 모델링과
아키텍쳐에 대한 접근 방법
이 섹션에서는 탑-다운 방식의 비지니스주도형 방식과 기존 투자를 활용하는 바톰-업 방식을 결합하는 방법을
설명하겠다.
서비스 지향 모델링 방식은 SOA의 근본을 정의하는 모델링, 분석, 디자인 기술, 액티비티들을 제공한다.
각 SOA 레이어의 요소들을 정의하고 각 레벨 마다 중요한 아키텍쳐 결정을 내리는 데 도움이 된다. 기존
자산과 시스템을 활용하여 서비스 구분을 수행하는 작업 스트림과 결합된 탑-다운 방식의 비지니스주도적인
방식의 서비스를 사용한다.
이러한 방식으로, 고급 비지니스 프로세스 기능은 서비스를 위해 외부화된다. 고급 서비스를 구현하는 것을
돕는 세분화된 서비스는 기존 레거시 기능을 검토하고 어댑터와 래퍼를 만드는 방법을 결정하거나 시스템 내에서
잠겨진 기능을 외부화하기 위해 레거시를 컴포넌트화하여 구분된다.
마지막으로, goal-service
modeling을 사용하여 크로스 섹션 접근 방식을 통해 이미 확인된 후보 서비스들을 잘라 낼 수
있다. 보다 중립적인 방법은 우선 탑-다운 방식을 수행하는 것이고 그런 다음 goal-service modeling
이고 마지막은 기존 자산의 바톰-업 레거시 분석이다. 프로젝트를 관리 가능하고 현실적인 범주로 빠르게 잡아갈수록
핵심 서비스에 집중된 가치를 더 빨리 실현할 수 있다.
기능적 비지니스목표와 레거시 시스템에 대한 기존 투자의 활용을 결합하면 현대적인 SOA로 엔터프라이즈를
마이그레이션하려는 조직에 놀라운 솔루션을 제공할 수 있다. 서비스 지향 통합을 통한 소프트웨어 애플리케이션의
결합은 가능하다.
서비스 지향 통합은 Enterprise Application Integration (EAI)의 진화이다.
소유권이 있는 커넥션이 ESB 개념을 통해 표준 기반의 연결로 대체된다. ESB는 위치가 투명하고 라우팅,
중개, 변형 기능이 유연하다.
서비스 지향 모델링:
서비스의 분석과 디자인
지금까지 SOA를 설명했다. SOA를 구현하기 위해서는 SOA의 각 레이어에 대한 핵심 아키텍쳐 결정이
이뤄져야 하고, 디자인은 비지니스와 제휴된 서비스들과 구성법을 반영해야 한다는 것을 설명했다.
객체(objects) 라는 편안한 세계와는 달리, SOA의 경우 두 가지 관점을 고려해야 한다; 서비스
소비자와 서비스 공급자에 대한 전망. 서비스 브로커는 현재 주류에 속하지 않아 나중에 다루겠다.
SOA를 위한 디자인 전략은 웹 서비스 기반 방법에서 쓰이는 "바톰-업"으로 시작하지
않는다. SOA는 보다 전략적이고 비지니스와 제휴되어 있다는 것을 기억해야 한다. 웹 서비스는 SOA의
임시적인 구현이다. 중요한 많은 액티비티와 결정들이 통합 아키텍쳐 뿐만 아니라 엔터프라이즈와 애플리케이션
아키텍쳐에 영향을 주고 있다. 여기에는 소비자와 공급자 라는 두 가지 핵심의 액티비티들이 포함된다.(그림
4)
그림 4: 서비스 지향 모델링의 액티비티
그림 4는 공급자와 소비자의 각 역할에 의해 전형적으로 수행되는
액티비티들이다. 공급자의 액티비티는 소비자의 액티비티의 상위세트라는 것에 주목하라. (예를 들어, 공급자는
서비스 구분, 범주 등과 관련되어 있다). 많은 경우 역할의 차이는 소비자가 원하는 서비스를 지정한다는
사실에서 기인한다. 그리고 일단 찾고있는 서비스 스팩과 맞으면 필요한 서비스를 바인드하여 호출한다. 공급자는
지원하고자 하는 서비스를 퍼블리시 해야 한다. 소비자가 요구하는 QoS의 관점에서 가장 중요한 것과 기능을
고려해야 한다. 여기에는 소비자와 공급자가 SLA로 계약을 맺고 있어야 함을 함축하고 있다.
위에 설명된 액티비티는 서비스 지향 모델링과 아키텍쳐 메소드 내에서의 흐름을 설명하고 있다.(그림
5)
그림 5: 서비스 지향 모델링과 아키텍쳐 메소드
서비스 지향 모델링과 아키텍쳐의 프로세스는 세 가지 일반적인 단계로 구성된다: 구분, 서비스의 특성화와
구현, 컴포넌트와 플로우(서비스 구성법).
서비스 구분
이 프로세스는 탑-다운, 바톰-업, 미들-아웃(middle-out) 기술로 도메인 디컴포지션, 기존 자산
분석, goal-service modeling의 결합으로 구성되어 있다. 탑-다운 관점에서, 비지니스사용
케이스의 청사진은 비지니스서비스를 위한 스팩을 제공한다. 이 탑-다운 프로세스는 도메인 디컴포지션(decomposition)이라고
하는데, 이는 비지니스 도메인을 기능 영역과 하위 시스템으로 나눈다. 플로우 또는 프로세스 디컴포지션은
프로세스, 하위 프로세스, 고급 비지니스 사용 케이스로 나누는 것이다. 이 사용 케이스들은 엔터프라이즈의
끝에 노출된 비지니스 서비스에 적합하다. 비지니스 라인을 통해 엔터프라이즈 영역 안에 사용되어도 좋다.
바톰-업 부분의 프로세스 또는 기존 시스템 분석에서, 기존 시스템들은 비지니스프로세스를
지원하는 기저의 서비스 기능의 구현에 저렴한 비용의 솔루션을 제공할 수 있는 후보로서 선택된다. 이 프로세스에서
레거시 애플리케이션과 패키지 애플리케이션에서 API, 트랜잭션, 모듈을 분석 및 활용할 수 있다. 어떤
경우 레거시 시스템의 컴포넌트화는 서비스 기능을 지원하기위해 기존 자산들의 재모듈화에 필요하다.
미들-아웃(middle-out) 뷰는 탑-다운 또는 바톰-업 서비스 구분 방식으로 잡히지
않은 다른 서비스들을 확인하기 위한 goal-service modeling으로 구성된다. 서비스를 목표(goal)와
하위 목표, 핵심 퍼포먼스 인디케이터, 메트릭스로 묶는다.
서비스 구분 또는 범주화
서비스가 구분될 때 이 액티비티가 시작된다. 서비스 구분을 서비스 계층으로 시작하는 것이 중요하다. 서비스의
합성 또는 서비스의 프랙탈 속성을 반영해야 한다: 서비스는 세세하게 가려진 컴포넌트와 서비스들로 구성되어야
한다. 구분은 합성과 레이어링을 결정할 때 도움이 될 뿐만 아니라 계층에 근거한 상호 의존적인 서비스들의
구현을 조정한다. 또한 서비스의 '증식 증후군'을 막는데 도움이 된다. 세분화된 서비스들은 관리를 많이
하지 않고도 정의, 디자인 전개될 수 있어 주요 퍼포먼스, 확장성, 관리가 수월하다. 무엇보다도 중요한
것은 서비스 증식은 비지니스에 유용한 서비스를 제공하지 못한다.
하위시스템 분석
이 액티비티는 도메인 디컴포지션 동안 찾아낸 하위 시스템들을, 하위 시스템들 간 상호 의존성과 플로우를
지정한다. 하위 시스템 인터페이스에 노출된 서비스로서 도메인 디컴포지션 동안 사용 케이스를 구분한다. 하위
시스템 분석은 내부 작동을 나타내는 객체 모델 만들기와 서비스를 노출하고 이를 구현할 하위 시스템을 포함하는
디자인으로 구성된다. "하위시스템"의 디자인 구조는 다음 액티비티에서 서비스를 구현하는
크게 구분된 컴포넌트의 구현 구조로서 실현된다.
컴포넌트 특성화
서비스를 구현하는 상세한 컴포넌트들이 지정된다:
- 데이터
- 규칙
- 서비스
- 설정 가능한 프로파일
- 변수
메시징 및 이벤트 스팩과 관리 정의는 이 단계에서 발생한다.
서비스 할당
서비스 할당은 서비스들을 하위 시스템들로 할당하는 것으로 구성된다. 이 하위 시스템들은 퍼블리시된 기능을
구현하는 엔터프라이즈 컴포넌트를 갖고 있다. 하위 시스템들은 엔터프라이즈 컴포넌트와 일대일 상관관계가 있다고
보면 된다. 컴포넌트를 구현은 패턴을 사용하여 엔터프라이즈
컴포넌트를 다음과 같은 요소들로 만들 때 발생한다:
- 중개
- Facade
- 규칙 객체
- 설정 가능한 프로파일
- 팩토리
서비스 할당은 SOA의 레이어로 실현하는 서비스와 컴포넌트를 할당하는 것으로 구성된다. SOA의 레이어로 컴포넌트와
서비스를 할당하는 것은 핵심 아키텍쳐 디자인의 문서화와 결정을 필요로 하는 핵심 태스크이다. 애플리케이션 아키텍쳐
뿐만 아니라 런타임 시 SOA 실현을 지원하는데 사용되는 작동 아키텍쳐와도 관련되어 있다.
서비스 실현
이 단계는 주어진 서비스를 구현하는 소프트웨어가 선택 또는 커스텀 구현된다는 것을 인식한다. 사용할 수
있는 다른 옵션들에는 통합, 변형, 등록, 아웃소싱 등이 있다. 이 단계에서 주어진 서비스를 실현하는데
어떤 레거시 시스템 모듈이 사용되는 지와 처음부터 구현되어야 할 서비스가 무엇인지 결정해야 한다. 기타
구현 결정 사항으로는 보안, 관리, 서비스 모니터링 등이 있다.
실제로는 모든 것을 부합하기 위해 노력이 들기 대문에 세 가지 흐름을 병렬로 수행하는 것을 권한다.
탑-다운 도메인 디컴포지션 (프로세스 모델링과 디컴포지션, 변수 지향 분석, 정책 및 비지니스규칙 분석,
도메인 스팩의 작동 모델링(문법과 다이어그램 사용))은 기존 자산의 바톰-업 분석과 병행하여 수행된다.
프로젝트 뒤의 비지니스의도를 파악하고 비지니스의도 대로 서비스를 제휴하기 위해 goal-service
modeling이 수행된다.
결론
이 글에서 서비스 지향 아키텍쳐의 기초, 레이어, 관련 유형의 아키텍쳐 결정을 설명했다. 서비스 지향 모델링의
액티비티를 설명하고 서비스 소비자와 공급자 관점에서의 중요한 액티비티를 설명했다. 이 방식은 서비스 지향
아키텍쳐의 세 가지 근본적인 측면(서비스, 플로우, 서비스를 구현하는 컴포넌트)을 결정하기 위한 분석 및
디자인 액티비티에 대한 특정 가이드라인이다. 아키텍쳐 결정에 사용할 수 있는 템플릿도 설명했다.
서비스 구분과 관련해서 탑-다운, 바톰-업, 크로스-섹션, goal-model analysis을 결합하는
것의 중요성을 강조했다. 서비스 스팩과 실현의 주요 활동도 살펴보았다.
다음 칼럼에서는 이 방식을 은행의 어카운트 관리에 적용하여 각 단계를 예시를 들어 설명할 것이다. 구분,
특성화, 구현 외에도 서비스 지향 모델링 방식의 나머지 액티비티를 설명할 것이다. 전개, 모니터링, 관리
등 전체 SOA의 수명 주기를 지원하는데 필요한 것들이다.
감사의 말
조언과 피드백을 아끼지 않은 많은 분들께 감사드립니다. (순서 없음): Luba Cherbakov, Kerrie
Holley, George Galambos, Sugandh Mehta, David Janson, Shankar
Kalyana, Ed Calunzinski, Abdul Allam, Peter Holm, Krishnan
Ramachandran, Jenny Ang, Jonathan Adams, Sunil Dube, Ralph
Wiest, Olaf Zimmerman, Emily Plachy, Kathy Yglesias-Reece,
David Mott.
참고자료
|
|
필자소개
Ali Arsanjani 박사는 IBM Global Services의 SOA and Web Services
Center of Excellence의 아키텍트이다. |
|
|
Posted by 아름프로
October 2004
Enterprise JavaBeans(EJB)는 분산 컴포넌트를 작성하기 위해 소개되었다. 등장 시점에는 모든 이슈들과 CORBA의 복잡성을 해결하겠다는 의지를 가지고 있었다. EJB는 몇몇 메이저 버전업을 통하면서 J2EE의 핵심이 되었고, 많은 특징들을 갖춰 나갔다. 초창기부터 많은 개발자들은 EJB에 대한 호감을 가졌고, 큰 고민없이 EJB를 적용한 어플케이션을 개발해 나갔다. 결국 많은 개발자들은 EJB를 사용한 프로젝트가 원만하지 않을 경우, EJB에 책임을 돌리게 되었다.
EJB 개발은 결코 쉽지 않으며, 릴리즈 될 때 마다 그 복잡함을 더해 갔다. EJB는 그 복잡성과 무거운 속성 때문에 코끼리에 비유되고 있다. 많은 개발자들은 EJB를 도넛에 입혀진 두터운 설탕 시럽처럼 느끼고 있다. 저탄수화물과 Atkins 다이어트에 열광하고 있는 시대에, EJB expert committee 또한 EJB 개발을 쉽고 간편하게 만들어야 함은 당연하다. EJB 3.0 expert committee는 JavaOne 2004에서 EJB 3.0 Specification의 첫번째 public draft를 발표했을 때 샘플 이미지를 배포 하였다.
언뜻 보기에 EJB의 새로운 모델은 꽤 괜찮아 보였따. 이 글에서는 개발자들에게 어필하기 위해 EJB 3.0이 작은 사이즈의 멋진 외양으로 변신하려는 지 알아보련다. 다음 글에서는 EJB 3.0이 퍼시스턴스 모델을 어떻게 간소화 하려는 지 알아 보겠다.
Cleaning up the Dirty Laundry
EJB 3.0이 새로이 내놓을 것들을 다루기 전에, 현재의 EJB 모델에서 볼 수 있는 복잡성을 먼저 보도록 하자.
현재 사용되는 EJB 모델은 컴포넌트 인터페이스와 불필요한 몇몇 callback 메소드 구현이 필요하다.
컴포넌트 인터페이스는 EJBObject나 EJBLocalObject를 구현해야 하며, 적잖은 수의 불필요한 Exception 핸들링을 필요로 한다.
EJB 모델에 기반한 CMP는 개발과 관리에 더더욱 복잡하다. CMP는 몇가지 기본 원칙들을 준수하지 못하고 있다. 예를 들면 데이터베이스 시퀀스를 사용하는 프라이머리키를 정의하는 것에서 그렇다. 그리고 EJBQL은 아주 제한적이다.
EJB 컴포넌트는 그 자체가 OO 처럼 보이지 않는다. 상속이나 다형성을 사용하는데 있어 제약이 있기 때문이다.
EJB의 가장 큰 단점은 EJB 컨테이너 밖에서는 EJB 모듈을 테스트 할 수 없다는 점이다. 컨테이너에서의 디버깅 또한 개발자들에겐 공포이다.
EJB를 사용한다면 EJB를 룩업하고, 호출해야 하는 복잡한 구조를 알아야 한다. 즉 어플리케이션에서 EJB를 사용만 하기 위해서 최소한 JNDI 정도는 알아야 한다.
Simplifying Developers view
최신 명세로 EJB를 개발한다면 HelloWorldEJB와 같은 간단한 EJB를 개발하는 것이 얼마나 어려운 것인가를 깨닫게 될 것이다. 최소한 두개의 인터페이스와 빈 클래스, 그리고 DD가 필요하다. 대부분의 개발자들은 자신이 왜 이 모든 것을 해야 하는 지 의아해 한다. Oracle JDeveloper, Eclipse 같은 IDE 들이나, Xdoclet은 개발자들을 좀 더 편하게 만들어준다. 그렇더라도 개발자들은 컨테이너에 EJB가 디플로이 될 수 있도록 클래스들을 컴파일 하고, DD를 패키징 하여야 한다.
EJB 3.0은 복잡성에 대응하기 위해 다음과 같이 하고 있다.
인터페이스와 DD의 필요성을 제거하고, metadata annotation을 사용하여 컨테이너에 의해 생성될 수 있도록 한다.
EJB로 보통의 자바 클래스를 사용하고, EJB를 위한 보통의 비즈니스 인터페이스를 사용한다.
Metadata Annotations
EJB 3.0은 metadata annotaion에 큰 의존을 하고 있다. metadata annotaion은 JSR 175에서 표준화되었고, J2SE 5.0에서 채텍될 것이다. Annotaion은 XDoclet과 유사한 attribute 기반 프로그래밍의 한 종류이다. 사전 컴파일이 필요한 XDoclet과 달리, annotaion은 클래스 안에서 컴파일 시점에 자바 컴파일러에 의해 컴파일 된다. (@Retention이 설정되었을 때이다.) 개발자 관점에서 보자면, annotation은 public과 같은 제한자이며, 클래스, 필드, 메소드, 변수, 로컬변수, 생성자, enumeration, 패키지에서 사용될 수 있다. Annotation은 attribute를 자바 소스에 기술해서 사용한다. Attribute는 코드 생성, 문서화, 비즈니스 레베 보안 향상과 같은 특정 서비스나 런타임 시의 특정 비즈니스 로직 등이 있다. J2EE 1.5 (5.0)의 목표는 annotaion을 사용하여 개발 과정을 보다 간소화 하는 것이고, 결국에는 annotation이 필수 요소가 되도록 하는 것이다. Annotation은 다음처럼 @ 로 표시되어 진다. @Author("Debu Panda") @Bean public class MySessionBean
EJB 3.0의 목표는 개발 과정을 간소화 하는 것이다. 이를 위해서 인터페이스와 같은 몇몇 산출물을 생성하기 위해 metadata annotation을 사용하고, DD 대신에 annotaion을 사용한다.
Using POJOs and POJIs
전통적으로, 자바빈즈와 인터페이스는 종종 POJO와 POJI로 간주된다. EJB 클래스는 이제 POJO와 POJI와 유사해 질 것이다. 홈 인터페이스와 같은 불필요한 객체는 곧 제거될 것이다.
개발자는 javax.ejb 패키지에 있는 EJB 인터페이스(SessionBean, EntityBean, MessageDrivenBean)를 구현하거나, 빈 구현 클래스에 있는 annotaion을 사용해야 한다. 빈 클래스에 annotate를 달기 위해 Stateless, Stateful, MessageDriven, Entity 들 중 하나를 사용할 수 있다. 예를 들면 HelloWorld란 stateless EJB를 만든다면, 다음과 같이 EJB를 정의한다. @Remote @Stateless public class HelloWorldBean { public String sayHello(String s) { System.out.println("Hello: "+s; } }
리모트, 또는 로컬과 같은 EJB를 위한 인터페이스는 EJBOBject, 또는 EJBLOcalObject를 구현할 필요가 없다. EJB를 위한 비즈니스 인터페이스를 제공하거나, 빈 클래스에서 인터페이스를 구현하거나, 디플로이 하는 동안 생성되면 된다. 인터페이스는 Entity Bean에서는 옵션이지만, SessionBean과 MessageDrivenBean에서는 반드시 필요하다. 세션 빈에서 인터페이스를 구현하지 않는다면, 빈 인터페이스는 자동으로 생성할 것이다. 생성된 인터페이스는 빈 클래스에 사용된 annotation에 따라 로컬이거나, 리모트가 된다. 위의 코드를 보면, HelloWorld 빈을 위해 리모트 인터페이스를 생성하도록 @Remote 가 사용되었음을 알 수 있다. 필요할 경우, 리모트와 로컬 인터페이스를 모두 생성할 수 있다.
위의 예제를 보면, 인터페이스 정의와 콜백 메소드를 구현 등의 EJB 컴포넌트를 만들기 위한 일련의 작업들이 더 이상 개발자의 몫이 아님을 알 수 있다.
생성된 인터페이스의 이름은 빈 구현 클래스의 이름으로과 연관되어 만들어진다. 생성된 인터페이스들은 개발자들에겐 더할 나위없이 적합한 것들이다. 하지만 Oracle JDeveloper와 같은 IDE에서 이러한 것들을 생성해 주는 것만큼의 효용을 볼 수는 없었다.
이번 Draft에선 클라이언트에서 EJB를 룩업하기 위해 필요한 것이 무엇인지가 명확하지 않다. 그리고 EJB를 호출하기 위해 필요한 인터페이스들을 어떻게 해야 할 지에 관해서도 막연하기만 하다. 아래 열거한 이유들 때문에 난 생성된 인터페이스를 사용하는 것을 지지하지 않는다.
생성된 인터페이스의 이름은 빈 이름으로부터 생성된다.
EJB에서 인터페이스로 내보내고 싶지 않은 어떤 메소드가 있을 수 있지만, 기본적으로 모든 메소드들이 생성된 인터페이스에 내보내 질 것이다.
클라이언트 측에서는 EJB를 호출하기 위한 EJB가 필요하다.
Removing need for Callback methods
EJB 2.1과 그 이전 릴리스에서는 몇몇 라이프 사이클과 관련한 메소드의 구현이 반드시 필요했다. 라이프 사이클 메소드로는 ejbPassivate, ejbActivate, ejbLoad, ejbStore 등이 있다. 이런 것들이 필요없는 EJB들도 모두 구현해야 했다. 예를 들면 무상태 세션 빈에서 ejbPassivate는 필요가 없다. 빈 클래스에서 이 메소드를 구현하기 전까지는 전혀 사용되지 않는 것이다. 그래서 EJB 3.0에서 이러한 라이프 사이클 메소드들은 옵션으로 되어 있다. EJB에서 어떤 콜백 메소드를 구현했을 때에만 컨테이너가 그 메소드를 호출할 것이다.
단 하나의 예외는 Stateful 세션 빈의 ejbRemove 메소드이다. Stateful 세션빈의 비즈니스 메소드에서 Remove annotation을 사용하여 표시한다. Remove annotation은 표시된 메소드를 정상, 또는 비정상 종료한 후에 Stateful 세션빈이 제거될 수 있도록 컨테이너에게 알려주는 역할을 한다. 예를 들면 Stateful 세션빈 인스턴스의 checkOut 메소드가 실행된 후 제거될 수 있도록 다음과 같이 기술하면 된다. @Stateful public class Cart { ... ... @Remove public void checkOut() { ... } }
Annotations vs. deployment descriptors
상기한 바와 같이, EJB에서 DD 대신에 annotation이 쓰이게 될 것이다. DD에서 각 attribute의 기본 값은 설정되어 있으며, 개발자들은 기본 값 이외의 다른 값을 필요로 하지 않는 한 특별히 기술하지 않았다. 이것들은 빈 클래스 자체에서 annotation을 사용하여 기술될 것이다. EJB 3.0 스펙에는 개발자가 사용할 수 있는 metadata annotaion set을 정의되어 있다. metadata annotation에는 bean type, type of interfaces, resource references, transaction attributes, security 등이 있다. 간단한 예로 특정 EJB를 위한 resource reference를 쓰고 싶다면, 다음과 같이 한다.
@Resource(name="jdbc/OracleDS", resourceType="javax.sql.DataSource")
Oracle, BEA, IBM 과 같은 J2EE 벤더들은 벤터 자체의 DD에서 쓰이는 attribute를 위한 annotaion 들을 추가할 것이다. 그리고 개발자들은 DD를 사용하지 않기 위해서 추가된 annotation들을 사용할 것이다. 개발자들에겐 아주 흥미있는 소식이 될 것이다. 왜냐하면 그들이 꽤나 귀찮아 하던 xml 디스크립터들이 사라지기 때문이다. 하지만 몇가지 문제점은 있다. Annotation을 받아 들이기 전에 좀 더 주의할 필요가 있다.
아무 곳에서나 구동할 수 있는 장점이 퇴색할 수 있다. EJB가 벤더에 종속된 DD를 사용한다면, 다른 벤더에 적용하기 위해 EJB를 컴파일하고 패키징하는 작업을 새로 해주어야만 한다.
DD는 EJB 모듈에 대한 정보를 제공한다. 그래서 EJB 내부를 들여다 보지 않아도 assembler와 deployer에게 EJB 모듈에 관한 정보를 알 수 있도록 해준다. DD는 각 디플로이먼트 단위로 필요하다. 만약 descriptor가 사용될 수 없거나, 디플로이먼트가 끝날 때까지 생성되지 않는다면 개발자들에겐 또 하나의 난관이 될 것이다.
DD는 EJB 모듈에서 툴에 의해 EJB를 정의하도록 사용되어져 왔다. 그래서 DD는 다른 컨테이너로의 전환에 아주 유용하게 사용되어져 왔다. EJB 3.0도 DD에서 annotation을 오버라이드 할 수 있는 방법을 제공한다. 하지만 annotation을 오버라이딩하는 부분은 Draft에서 빠져있는 상태이다.
DD를 제거하게 된다면 새로운 개발자들에겐 보다 쉽게 느껴질 것이다. 그러나 주의하지 않는다면 관리적인 측면에서 새로운 암초로 등장할 수도 있을 것이다.
Simplifying Container Managed Persistence
CMP 엔티티 빈은 EJB 3.0에서 개발자들을 감탄시킬 수 있을 만큼의 대대적인 정비가 이루어 지고 있다. 복잡하고 무거운 엔티티 빈을 싫어하는 개발자들은 TopLink나 Hibernate 같은 Persistence 프레임웍은 J2EE 어플리케이션을 위한 퍼시스턴스 프레임웍을 애용하고 있다. EJB 3.0은 CMP를 간편하게 하기 위해 TopLink나 Hibernate 같은 경량 퍼시스턴스 모델을 적용하고 있어 개발자들을 기대하게 만들고 있다. 엔티티 빈을 위한 플랜을 훑어 보자. 그리고 다른 아티클에서 퍼시스턴스 향상에 관한 세부적인 내용을 다시 다루겠다.
엔티티 빈은 POJO로 다시 태어났다. 그리고 엔티티 빈에 필수였던 컴포넌트 인터페이스는 필요없다. 이제 엔티티 빈은 상속과 다형성을 지원하는 순수한 오브젝트의 모습이 될 것이다.
Following is the source code for an entity bean: @Entity public class Employee { private Long empNo; private String empName; private Address address; private Hashmap projects = new Hashmap(); private Double salary; @Id(generate=SEQUENCE) public Long getEmpNo() { return empNo; } protected void setEmpNo(Long empNo) { this.empNo = empNo; }
public String getEmpName() { return EmpName; } public void setEmpName(String EmpName) { this.EmpName = EmpName; } @Dependent public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public Set getProjects() { return projects; } public void setProjects(Set projects) { this.projects = projects; } public Double getSalary() { return salary; } public void setSalary(Double salary) { this.salary = salary; } .... }
코드를 보면, 빈 클래스가 보통의 엔티티 빈과 같은 추상 클래스가 아닌 보통 클래스란 것을 알 수 있을 것이다.
EJB QL에서 쿼리 능력 향상, 엔티티 빈에서의 SQL 질의 지원 등 몇가지 향상된 기능이 추가 되었다. 새로운 EntityManager API(Hibernate, TopLink의 Session API의 단순화된 버전과 유사)는 엔티티 빈을 조작하기 위해 제안되었다. (예: creattion, removal, finding of entity beans)
다음에 나올 아티클에서 제안된 CMP 엔티티 빈에 관해 더 상세히 보겠다.
Simplifying Client View for EJBs
EJB를 사용하는 건 EJB가 어플리케이션에 들어 있다 하더라도 매우 복잡하다. J2EE 1.4와 EJB 3.0 스펙은 EJB를 바라보는 클라이언트 측을 보다 더 단순화시켜 놓았다.
지금은 EJB를 사용하고 싶을 경우 DD에서 ejb-ref나 ejb-local-ref를 정의해 놓아야 한다. 예를 들어 HelloWorld EJB를 호출하고 싶다면, 보통 다음과 같은 방법으로 EJB를 호출하게 된다.
먼저 EJB 레퍼런스를 DD에 등록한다. HelloWorldEJB Session hello.HelloWorldHome hello.HelloWorld
다음 처럼 EJB를 lookup 한다. 빈 인스턴스의 EJB 룩업과 생성을 위한 예외를 명시적으로 처리해 주어야 한다. try { Context context = new InitialContext(); HelloWorldHome helloHome = (HelloWorld)PortableRemoteObject.narrow( context.lookup("java:comp/env/ejb/HelloWorldEJB"), HelloWorldHome.class); HelloWorld hello = helloHome.create(); .... } catch(RemoteException e) { System.err.println("System/communication error: " + e.getMessage()); } catch(NamingException e) { System.err.println("Communication error: " + e.getMessage()); } catch(CreateException e) { System.err.println("Error creating EJB instance: " + e.getMessage()); }
다른 방법으로 EJB 3.0은 EJB를 룩업하고 호출할 수 있는 setter injection을 쓰는 방안을 제공한다.
다음은 다른 EJB에서 setter injection을 사용하여 HelloWorldEJB를 호출하는 방법이다. @Inject private void setSessionContext(SessionContext ctx) { this.ctx = ctx } ... myHello = (HelloWorld)ctx.lookup("java:comp/env/ejb/HelloWorldEJB");
코드를 보면 setSessionContext 메소드에 @Inject가 붙어 있다. @Inject는 이 메소드가 dependency injection에 사용됨을 나타내는 것이다. @Inject 처리된 메소드는 EJB의 있는 메소드가 호출되기 전 EJBContext를 세팅할 때 호출된다.
HelloWorld 세션빈을 inject 하기 위해서 @EJB public HelloWorld myHello 처럼 해도 된다. 이렇게 하면 HelloWorld 빈 인스턴스가 myHello에 inject 된다.
dependency injection은 DataSource, JMS, Mail, Web Service 같은 타입의 environment나 resource reference 를 룩업하기 위해 사용할 수도 있다.
Testability Usability Outside Container
EJB 개발자들의 가장 주요한 관심사는 EJB 개발이 복잡하다는 것 뿐만 아니라, 테스트가 쉽지 않다는 점에도 있다. EJB 컨테이너는 EJB를 개발하고 테스트 하는데 반드시 필요하다. 그래서 개발자는 테스트를 수행할 배포 플랫폼에 익숙해야만 한다. 이 부분은 많은 엔터프라이즈 개발자들에게 주요한 이슈가 아닐 수도 있다. 그렇지만 다양한 벤더를 지원해야 하는 ISV들에는 이슈가 된다. 그들은 다양한 환경에서의 동작을 보증하기 위해 그들의 EJB 테스팅을 수행해야 하기 때문이다. EJB 3.0 스펙은 컨테이너 외부에서의 테스팅 기능 제공을 약속하고 있다. 그러나 아직 Draft 스펙에는 누락되어 있다.
Conclusion
packing, assembly, API 등 많은 정보가 누락되긴 했지만, EJB 3.0에 담긴 제안들은 엔터프라이즈 자바 개발자들에겐 매우 환영할 만한 것들이다. 제안들로 인해 개발자들에게 부여되었던 복잡성을 컨테이너 벤더들에게 넘겨버리게 되므로 확실히 단순화 될 것이다. 컨테이너 벤더들이 이것들을 어떻게 구현할지, 엔터프라이즈 어플리케이션을 개발할 때 EJB 3.0을 보다 수월하게 할 수 있는 방법들을 볼 수 있게 될 것이다.
References and further reading
Author Bio
Debu Panda is a principal product manager of Oracle Application Server development team, where he focuses his efforts on the EJB container and Transaction Manager. He has more than 13 years of experience in the IT industry and has published articles in several magazines and has presented at many technology conferences. His J2EE-focused weblog can be found at http://radio.weblogs.com/0135826/.
PRINTER FRIENDLY VERSION
Posted by 아름프로
Article
NetBeans IDE 4.1
|
An Integrated Development Environment (IDE) is a necessity for enterprise developers who want to have all their productivity tools under one umbrella. An IDE enables the developer to move from one phase of application development to the next without having to worry about manual management of source code or tool interfaces. With so many IDEs available on the market today, it has become a time-consuming task to decide on and choose one. Ideally, you should be looking for an IDE that maximizes your productivity: easy to use, fun to work with, enables you to get the work done, and has out-of-the-box support for open standards and technologies.
The NetBeans IDE 4.1 Early Access 2 (EA2), which was released in January 2005, includes Java 2 Platform, Enterprise Edition (J2EE) and web services development capabilities. This new release allows developers to not only develop applications in the web tier but also includes Enterprise JavaBeans (EJBs) and web service development capabilities. NetBeans IDE 4.1 is a single platform with out-of-the-box development capabilities and support for enterprise (J2EE 1.4) applications and web services, mobile/wireless Java 2 Platform, Micro Edition (J2ME) applications and services and desktop Java 2 Platform, Standard Edition (J2SE) applications.
NetBeans IDE
The NetBeans IDE, which is sponsored by Sun Microsystems, is a free and open source IDE that enables you to develop J2EE 1.4 applications and web services, mobile/wireless applications and services, and J2SE desktop applications. Using the NetBeans IDE, developers get their work done in a fun development environment that enables them to concentrate on the business logic of their applications. It provides productivity tools, and supports refactoring and integrated debugging, that simplify the work of Java technology developers.
NetBeans IDE 4.1 (EA2) supports the development of J2EE 1.4 applications and web services, and their deployment on the freely available Sun Java System Application Server 8.1 Platform Edition (Application Server PE 8) and Tomcat. Application Server PE 8 is the first compatible "production grade" application server implementation of the J2EE 1.4 specification, and it is free for development and deployment. This release of NetBeans IDE 4.1 with support for J2EE 1.4 will make a big difference for enterprise applications developers as it provides them with out-of-the-box support for J2EE 1.4, which helps them increase their productivity.
In this release (4.1), J2EE EJBs and web services are first class citizens. The IDE features ease of use, and the overall design of the IDE keeps the tools you need the most immediately at your fingertips. Developers get everything they need to develop J2EE 1.4 enterprise applications and web services with a single download.
The NetBeans IDE 4.1 (EA2) allows developers to:
- Create J2EE applications, automatically add EJB modules and web modules, and deploy the applications
- Create EJBs from existing source, from scratch, or from an existing database
- Automatically generate EJB business methods, database calls, and calls to EJBs
- Create a web module for deployment to the freely available Application Server PE 8 or Tomcat
- Add multiple source folders to an EJB module or web module, and create unit tests as part of the project
- Edit deployment descriptors in a graphical editor that is automatically synchronized with the underlying XML
- Create, register, and test web services
- Import your existing J2EE projects
- Validate your applications with the J2EE Verifier
- Visually configure your EJBs, web Services and web components
Note that the NetBeans IDE is not only meant to be used by advanced developers, but also by beginners who wish to learn about J2EE and web services technologies. The NetBeans IDE 4.1 allows you to try out the sample applications to learn and understand the J2EE technology and web services.
The IDE increases the productivity of developers by guiding them through the development process, and automatically building the underlying J2EE infrastructure. The distribution provides several great quick starts and tutorials that enable beginners to get up to speed quickly. In addition, it comes with numerous samples of J2EE applications that can be easily accessed from within the IDE, as well as contributions from the J2EE Java BluePrints catalog.
NetBeans IDE 4.1 (EA2) Key Features
The NetBeans IDE 4.1 (EA2) features significant new development capabilities for J2EE 1.4, including EJB components and web services. It has over 15 new modules for developing J2EE 1.4 applications and services. The three key features are:
Creating EJB Modules, Session Beans, Entity Beans, and Message Driven Beans
- The NetBeans IDE 4.1 guides the user through the process to easily learn how to write an EJB as well as deploy, package, and test applications.
- A GUI is available to select an EJB and perform tasks such as adding business methods and editing deployment descriptors.
- All EJB infrastructure methods are generated automatically and are hidden in a power code fold.
- The resulting EJB module can be easily added to a J2EE application.
- Entity beans can be created using an existing database schema.
- The NetBeans project structure matches J2EE Java BluePrints standards and relies on the Ant open standard for the build system.
Calling EJBs
- Using the "Call EJB" feature from within a Servlet, JSP, or web service saves the user the headache of writing the JNDI lookup code and required exception handling. It will even work with your existing ServiceLocator class.
Develop and Test Web Services
- Developers can create, modify, package, deploy, and test web services from the IDE.
- Web services can be created from scratch, existing code, and/or WSDL.
- Once created, the web service can be registered with the IDE, which creates an internal client for testing the web services.
- NetBeans also supports creating web service client code from existing WSDL.
Getting Started
The NetBeans IDE 4.1 is available for the Solaris Operating System, Microsoft Windows, and Linux. It runs on J2SE SDK 1.4.2 as well as J2SE SDK 5.0. I recommend that you download the NetBeans IDE 4.1 EA2 + AS 8.1 Bundle Installer.
Sample Projects
If you wish to experiment with some J2EE projects, chose File -> New Project then expand the Samples folder. In addition, the IDE includes samples from the Java BluePrints Solution Catalog as shown in Figure 2. You can select one of these samples and install it.
|
Figure 2: Java BluePrints Solution Catalog
|
Once you select a sample project, press F6 to run the project. Figure 3 shows a sample run.
|
Figure 3: Sample Run
|
You can easily develop new J2EE applications and web services. To learn how to create J2EE applications, EJB modules, web modules, and to call, deploy, and test applications, please see the Quick Start Guide for J2EE Applications, and Exposing and Consuming Web Services, which provide you with a step-by-step tutorial on how to get started with J2EE and web services development.
NetBeans.org Needs You
The NetBeans IDE 4.1 (EA2) currently supports deployment on the Application Server PE 8 and Tomcat, but netbeans.org needs your support in order to ensure that the majority of J2EE 1.4 compliant application servers can be used as both development and deployment targets from the NetBeans IDE.
If you are interested in supporting this effort, you can download the source code that implements the support for J2EE 1.4 in the NetBeans IDE 1.4 (EA2) from the NetBeans.org J2EE page.
You can subscribe to a mailing list by sending a blank email to nbj2ee-subscribe@netbeans.org, and you can send questions and feedback to nbusers@netbeans.org.
If you are a serious Java developer looking for an IDE with out-of-the-box support for J2EE and web services, that is easy to install, simple to learn and use, fun to work with, and enables you to get the job done, then try the NetBeans IDE 4.1 early access. Download it today and see for yourself. Learning about J2EE and building JEE applications and web services has never been easier.
For More Information
Posted by 아름프로
Getting around JSF: The role of JSP
Learn how to use JavaServer Pages with JavaServer Faces
Summary
In this excerpt from JavaServer Faces in Action (Manning, November 2004), author Kito Mann explains how JavaServer Pages fits with JavaServer Faces. (1,800 words; December 13, 2004)
By Kito D. Mann
avaServer Faces (JSF) applications require some sort of display technology, such as JavaServer Pages. One of the cool things about JSP is the ability to extend it with custom tags. A custom tag is a special XML element backed by Java code, that can be used in addition to standard JSP elements or HTML elements. A custom tag can do almost anything: display the value of variables, parse XML, conditionally display parts of a page, access a database, and so on (whether or not anyone should be doing all of these things with JSP tags is a question for another day...). Their main purpose is to keep Java code out of the pages and allow frontend developers to use simple, familiar tags instead. A group of related custom tags forms a tag library.
JSF is integrated with JSP using custom tags. All of the JSF tags we've shown so far in this book —<h:inputText> , <h:outputText> , <h:form> , <f:view> , and so on—are custom tags. JSF implementations must support JSP with custom tag libraries that provide access to all of the standard components, renderers, validators, and converters. These libraries (included in the JSF JARs) are listed in the table below.
JSF custom tag libraries
URI | Name | Common prefix | Description | http://java.sun.com/jsf/core | Core | f | Contains tags that are independent of a particular render kit (like <f:view> , <validator> , and so on) | http://java.sun.com/jsf/html | HTML | h | Contains tags for all of the standard components and the HTML render kit |
|
All of the tags in these libraries must be named and implemented in a specific manner. This way, your JSP-based applications are guaranteed to be portable across different JSF implementations. Most IDEs can be used with JSP.
For the most part, using JSF with JSP is just a matter of using the JSF custom tag libraries. There are, however, some nuances you should be aware of, like using JSP includes.
Using JSP includes
One of JSP's key features is the ability to integrate content from multiple JSPs into a single page. This is often used for fun tasks like including a header or a footer. JSP supports two types of includes: dynamic and static. Dynamic includes (performed with the <jsp:include> tag or the JSTL <c:import> tag) access a resource at runtime. In this case, control is forwarded to the included JSP. The response from the included JSP is merged with the response sent back from the calling page. When changes are made to a dynamically included page, they automatically show up in all calling pages.
Static includes integrate the resource at translation time—when the page is morphed into Java code and compiled. The contents of the source page are essentially copied into the calling page. Changes made to the included content generally aren't automatically noticed by calling pages because they already have their own copy of the content. They have to be "touched" so that they can be recompiled with the new content. (JSP 2.0's implicit includes, which can be configured in web.xml , are processed like static includes.)
JSF works with both types of JSP includes. For dynamic includes, there are two requirements:
- Included pages must be enclosed in a JSF
<f:subview> core tag. This tag can either be in the included page or around the include statement.
- All template text and non-JSF tags inside included pages should be enclosed with the JSF
<f:verbatimgt; core tag.
So, let's say we had the following snippet in a JSP page:
<f:view>
...
<jsp:include page="foo.jsp"/>
...
</f:view>
Foo.jsp might look like this:
<f:subview>
<h:outputText value="heyah!"/>
...
<f:verbatim>
<b>Template text.</b>
<customtag:dothis/>
</f:verbatim>
</f:subview>
As you can see, the entire included page is enclosed in an <f:subview> tag, and all non-JSF tags and template text are enclosed in an <f:verbatim> tag. Alternatively, we could move the <f:subview> tag into the first page, around the <jsp:include> tag.
Using a static include is much simpler. There are no restrictions—you don't even have to use the <f:subview> tag.
In the last example, we showed a fictitious custom tag, <customtag:dothis> , that performs some random task. This underscores an important point: you can use JSF with other JSP custom tags.
Using JSF with JSTL and other JSP custom tags
All of this talk about JSF's custom tag libraries is nice, but what if you have your own custom tags, or third-party ones? Or what if you're using the JSP Standard Tag Library (JSTL), which is a set of standard tags that do all of those neat things we just mentioned? For the most part, you can mix and match them with JSF tags. Faces tags can be nested within other tags and vice versa. Some products, like IBM's WebSphere Application Developer, encourage this approach, while others, like Sun's Java Creator Studio, opt for a pure JSF approach. Oracle's JDeveloper on the other hand, lets you mix and match, but also encourages the pure JSF approach.
Note: Whenever you nest a JSF tag inside a non-JSF custom tag, you must assign the JSF tag a component identifier.
Because JSTL is standard and familiar to many, we'll use it to demonstrate the use of JSF with custom tags. (If you're thirsty for general information on JSTL, check out Shawn Bayern's excellent book, JSTL in Action.) Let's start with the simple example (shown in Listing 1) that mixes and matches some JSTL tags with JSF tags. This code imports both JSF tag libraries and the core JSTL tag libraries.
Listing 1. Mixing JSTL tags with JSF tags
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<html>
<head>
<title>JSF in Action: JSTL Example 1 - Mixing JSF with other custom tags
</title>
</head>
<body bgcolor="#FFFFFF">
<f:view>
<h1>
<h:outputText value="Example of using JSF tags with other custom tags"/>
</h1>
<p>
<b>
<c:out value="Here's the value of your web.xml (don't do this at home):"/>
</b>
<blockquote>
<f:verbatim>
<c:import url="WEB-INF/web.xml"/>
</f:verbatim>
</blockquote>
</p>
</f:view>
</body>
</html></code>
In this example, both JSTL and JSF tags are nested within the JSF <f:view> tag, which defines the start of the JSF component tree. The example uses the JSF HtmlOutputText component (<h:outputText> ) and the JSTL <c:out> tag to display text. A JSTL <c:import> tag includes the system's web.xml file in the page (this isn't exactly something you want to share with others, so don't do this on a real server). Because web.xml is an XML file, the <c:import> tag is nested in an <f:verbatim> tag, which is a JSF UIOutput component whose renderer escapes the XML so it can be displayed normally in an HTML page. This example doesn't do much, but it does demonstrate the ability to use different tags on the same page together.
Note that we nested a JSTL tag inside the JSF <f:verbatim> tag. In general, it's easier to nest JSF tags inside other tags than vice versa. As a matter of fact, any component that displays its own children, like HtmlDataTable and HtmlPanelGrid , requires that any template text or nested tags be within an <f:verbatim> tag.
What's great about using JSTL tags with JSF tags is that they both use similar expression languages to reference objects (this is true for JSP 2.0's expression language as well). This allows you to easily share data between JSTL and JSF tags in an intuitive manner. To illustrate this point, let's look at another example that allows the user to input a number into an HtmlInputText control and then uses that value to display a string repeatedly with a JSTL <c:forEach> tag. This code is shown in Listing 2.
Listing 2. Using JSF and JSTL tags together with the same backing bean
...
<f:view>
<jsp:useBean class="org.jia.examples.TestForm" id="exampleBean" scope="session"/>
<h1>
<h:outputText value="Example of using JSF and JSTL expression languages"/>
</h1>
<h:form>
<h:outputLabel for="inputInt">
<h:outputText value="How many times do you want to repeat the Oracle's prophecy?"/>
</h:outputLabel>
<h:inputText id="inputInt" value="#{sessionScope.exampleBean.number}"/>
<h:commandButton value="Go!"/>
<p>
<c:if test="${sessionScope.exampleBean.number > 0}">
<c:forEach begin="0" end="${sessionScope.exampleBean.number - 1}" var="count">
Queen Tracey will achieve world domination.<br>
</c:forEach>
</c:if>
</p>
</h:form>
...
</f:view>
...
Warning: If you're using JSP or JSTL expressions with managed beans, you need to ensure that the beans have been created first, either by a JSF expression, Java code, or your own custom tag. This is because these older expression languages don't know about JSF's Managed Bean Creation facility.
This listing references a JavaBean, called exampleBean that has a number property of type int . An HtmlInputText component is used to update the value of the bean's property based on user input. When the user clicks the Go! button (an HtmlCommandButton component), the number property is updated and the page is redisplayed. When this happens, the JSTL <c:forEach> tag repeats the text displayed by a JSTL <c:out> tag exampleBean.number times. The <c:forEach> tag only executes if exampleBean.number is greater than 0; this is controlled by a JSTL <c:if> tag.
You cannot use JSF component tags inside tags that iterate over their body, like the JSTL <c:forEach> tag. The recommended workaround is to use the HtmlDataTable component or another component iterates over a dataset or collection.
In this example, there are no JSF components nested inside the JSTL <c:if> tag. But what happens if a component is displayed once and then hidden by a conditional tag like <c:if> when the page is redisplayed? The first time the component is displayed, it will be added to the view. The second time, if the <c:if> tag doesn't display the component, JSF will delete it from the view. This means that any input controls will lose their local values, and that you won't be able to reference these components (via client identifiers or in code). As an example, take a look at Listing 3, which is from the same page as Listing 2.
Listing 3. Conditionally displaying JSF components with JSTL tags
...
<h:form>
<h:outputText value="If you entered a number greater than 10,
two input controls will display below."/>
<p>
<c:if test="${sessionScope.exampleBean.number > 10}">
<h:outputLabel id="inputStringLabel"for="inputString">
<h:outputText id="outputStringLabel" value="Enter in your string.
JSF will remember the value unless this control is hidden."/>
</h:outputLabel>
<h:inputText id="inputString"/>
<h:commandButton value="Go!"/>
</c:if>
</p>
</h:form>
...
The JSTL <c:if> tag will execute its body if the value of exampleBean.number is greater than 10. If the body is executed, then all of the nested components will be added to the view and displayed. If not, the components will be removed (if they have been added previously). This is shown graphically in Figure 1. If you control visibility of components with JSTL conditional tags (or other custom tags), the components will be removed from the view if they're not displayed. This means that the components will forget their local values as well.
Figure 1. The JSTL <c:if> tag will execute its body if the value of exampleBean.number is greater than 10. Click on thumbnail to view full-sized image.
|
Figure 2 shows the output of the JSP page used for Listings 2 and 3. The value of the input field at the top (an HtmlInputText component) is wired to the exampleBean.number backing bean property, which is used by the JSTL <c:forEach> tag to display a string exampleBean.number times. In the bottom portion of the page, a JSTL <c:if> tag shows a form with JSF components if exampleBean.number is greater than 10. Otherwise, the components will not be displayed, and they are removed from the view (and the input control will lose its value).
Figure 2. The output of the JSP page shown in Listings 2 and 3. Click on thumbnail to view full-sized image.
|
You can achieve the same effect as the code in Listing 3 by placing these components in an HtmlPanelGroup and setting its rendered property to equal the same expression. An HtmlPanelGroup is used as a container for multiple components. Here's an example:
<h:panelGroup rendered="#{sessionScope.exampleBean.number > 10}">
<h:outputLabel id="inputStringLabel2"for="inputString">
<h:outputText id="outputStringLabel2" value="Enter in your string. JSF
will remember the value."/>
</h:outputLabel>
<h:inputText id="inputString2"/>
<h:commandButton value="Go!"/>
</h:panelGroup>
If exampleBean.number is greater than 10, this panel becomes visible. In this case, the components won't be deleted if they're not displayed. This is a good example of the types of things you can do with pure JSF tags without JSTL.
Tip: Even though custom tags like the ones provided by the JSTL provide a lot of functionality, if you're developing from scratch (or refactoring), you should first look to see if you can implement the desired behavior with standard JSF components. Using good components and well-designed backing beans, you can usually avoid the need for many JSTL tags in your pages. You can hide or display entire panels and do all sorts of powerful things with standard JSF.
Here are a few other interoperability constraints for using JSF tags with JSTL internationalization and formatting tags:
- Use of
<fmt:parseDate> and <fmt:parseNumber> is not recommended. You should use the HtmlInputText component with a DateTime or Number converter.
- The
<fmt:requestEncoding> tag, which is used to determine or specify the character encoding for the page, should not be used. Usually, JSF handles this automatically, and if you need to force a particular encoding, you should use the JSP page directive: <%page contentType="[contenttype];[charset]"%> .
- The
<fmt:setLocale> tag shouldn't be used either. Because it doesn't know about JSF, it may cause your JSTL tags to use one locale and your JSF components may use another, which is a recipe for disaster. Instead, you should use JSF's internationalization features. To control the locale for a particular page, use the locale property of the UIViewRoot component. JSF's internationalization features work for both JSF and JSTL.
Combining JSF with the JSTL can be quite powerful. Custom tags that you have developed or obtained from third parties should work with JSF as well as the JSTL tags we've shown here. In general, though, you should stick with JSF tags when possible.
About the author
Kito D. Mann is a consultant specializing in enterprise architecture, mentoring, and development. A programmer since the tender age of 12, he has written several articles on Java-related technologies, and also speaks at user groups and conferences. He has consulted with several Fortune 500 companies and has been the chief architect of an educational application service provider. Mann is also the founder of the JSF Central community Website, and a member of the JSF 1.2 and JSP 2.1 expert groups. He holds a B.A. in computer science from Johns Hopkins University and lives in Stamford, Connecticut, with his wife, four cats, and two parrots. In his spare time, he enjoys making incomplete compositions with electronic music equipment.
Posted by 아름프로
Service-context propagation over RMI
A lightweight design approach for supporting transparent service-context propagation over RMI
Summary
CORBA supports the passing of service-context information implicitly with requests and replies over remote object interface invocation. Without instrumenting the underlying protocol, Java RMI (Remote Method Invocation) can't easily support transparent service-context propagation. This article describes a simple and efficient design approach for supporting such capability over RMI. In building RMI-based distributed applications, such an approach can serve as a basic building block for implementing infrastructure-level functions, such as transaction, security, and replication. (3,000 words; January 17, 2005)
By Wenbo Zhu
ORBA's service context provides an efficient and elegant design and implementation approach for building distributed systems. Java RMI (Remote Method Invocation) can't easily support transparent service-context propagation without instrumenting the underlying protocol. This article describes a generic lightweight solution for supporting transparent and protocol-independent service-context propagation over RMI. Reflection-based techniques are used to emulate what's normally seen in protocol-specific service-context implementations.
This article introduces you to a real-world solution and the related distributed-computing design concept, as well as Java reflection techniques. We start with an overview of the CORBA object request broker (ORB) interceptor and the service-context design architecture. Then a concrete implementation example describes the actual solution and demonstrates how RMI invocation is actually massaged to propagate service-context data, such as transaction context, which is usually offered through the IIOP (Internet Inter-ORB Protocol) layer. Lastly, performance considerations are discussed.
Interceptor and service context in CORBA
In the CORBA architecture, the invocation interceptor plays an important role in the function provided by the ORB runtime. Generally speaking, four interception points are available through the ORB runtime. As shown in Figure 1, these interception points are for:
- Out-bound request messages from the client process
- In-bound request messages to the server process
- Out-bound response messages from the server process
- In-bound response messages to the client process
The so-called portable interceptor can support both a request-level interceptor (pre-marshaling) and a message-level interceptor (post-marshaling). More specific details can be found in the CORBA specification documents.
Figure 1. ORB invocation interceptors.
|
Interceptors provide a powerful and flexible design support to both ORB vendors and application builders for constructing highly distributed applications. Value-adding functions can be transparently added and enabled at the protocol, ORB, or application layers without complicating standardized application-level APIs. Examples include invocation monitoring, logging, and message routing. In some sense, we are looking for a kind of RMI-level AOP (aspect-oriented programming) support.
Among the many uses of interceptors, propagating service-context data is one of the most important. Effectively, service-context propagation provides a way to extend the ORB runtime and IIOP protocol without affecting applications built on top of the ORB, such as IDL (interface definition language) definitions. CORBA services, such as transaction and security services, standardize and publish the structure of their specific service-context data as IDL data types, together with the unique context identifiers (context_id ).
Simply put, service-context data is information that the ORB runtime (RMI runtime, for this article's purposes) manages to support infrastructure-level services that the runtime provides to hosted applications. The information usually must be piggybacked on each invocation message between the client process and the server process. ORB runtime and related infrastructure-level services are responsible for sending, retrieving, interpreting, and processing such context data and delivering it to the application layer whenever necessary. Service-context data is passed with each request and reply message with no application interface-level exposure, such as at the IDL layer.
Nevertheless, it is not fair to ask RMI to directly support such capabilities as it is only a basic remote method invocation primitive, while CORBA ORB is at a layer close to what the J2EE EJB (Enterprise JavaBean) container offers. In the CORBA specification, service context is directly supported at the IIOP level (GIOP, or General Inter-Orb Protocol) and integrated with the ORB runtime. However, for RMI/IIOP, it is not easy for applications to utilize the underlying IIOP service-context support, even when the protocol layer does have such support. At the same time, such support is not available when RMI/JRMP (Java Remote Method Protocol) is used. As a result, for RMI-based distributed applications that do not use, or do not have to use, an ORB or EJB container environment, the lack of such capabilities limits the available design choices, especially when existing applications must be extended to support new infrastructure-level functions. Modifying existing RMI interfaces often proves undesirable due to the dependencies between components and the huge impact to client-side applications. The observation of this RMI limitation leads to the generic solution that I describe in this article.
The high-level picture
The solution is based on Java reflection techniques and some common methods for implementing interceptors. More importantly, it defines an architecture that can be easily integrated into any RMI-based distributed application design. I demonstrate the solution through an example implementation that supports the transparent passing of transaction-context data, such as a transaction ID (xid ), with RMI. The example's source code is available for download from Resources. The solution contains the following three components:
- RMI remote interface naming-function encapsulation and interceptor plug-in (
rmicontex.interceptor.* )
- Service-context propagation mechanism and server-side interface support (
rmicontex.service.* )
- Service-context data structure and transaction-context propagation support (
rmicontex.* )
The components' corresponding Java class packages are shown in Figure 2.
Figure 2. The component view of packages
|
The example is not meant to be used as a whole package solution; rather, the implementation demonstrates the underlying design approach. The implementation assumes that RMI/IIOP is used. However, it by no means implies that this solution is only for RMI/IIOP. In fact, either RMI/JRMP or RMI/IIOP can be used as the underlying RMI environments, or even a hybrid of the two environments if the naming service supports both.
Naming-function encapsulation
To implement our solution, first we encapsulate the naming function that provides the RMI remote interface lookup, allowing interceptors to be transparently plugged in. Such an encapsulation is always desirable and can always be found in most RMI-based applications. The underlying naming resolution mechanism is not a concern here; it can be anything that supports JNDI (Java Naming and Directory Interface). In this example, to make the code more illustrative, we assume all server-side remote RMI interfaces inherit from a mark remote interface ServiceInterface , which itself inherits from the Java RMI Remote interface. Figure 3 shows the class diagram, which is followed by code snippets that I will describe further:
Figure 3. Class diagram of ServiceInterface and ServiceManager
|
package rmicontext.service;
public interface ServiceInterface extends Remote {
}
package rmicontext.service;
public class Service
extends PortableRemoteObject
implements ServiceInterface,
ServiceInterceptorRemoteInterface {
....
}
package rmicontext.service;
public interface ServiceManagerInterface {
public ServiceInterface getServiceInterface(String serviceInterfaceClassName);
}
package rmicontext.service;
public class ServiceManager
implements ServiceManagerInterface {
/**
* Gets a reference to a service interface.
*
* @param serviceInterfaceClassName The full class name of the requested interface
* @return selected service interface
*/
public ServiceInterface getServiceInterface(String serviceInterfaceClassName) {
// The actual naming lookup is skipped here ...
}
}
The Service serves as the base class for any server-side RMI remote interface implementation. No real code is needed at the moment. For simplicity, we just use the RMI remote interface Class name as the key for the interface naming lookup. The naming lookup is encapsulated through the class ServiceManager , which implements the interface ServiceManagerInterface as the new encapsulated naming API.
In the next section, you find out how the interceptor is plugged in. A simple interface-caching implementation is also included to complete the class ServiceManager .
RMI invocation interceptor
To enable the invocation interceptor, the original RMI stub reference acquired from the RMI naming service must be wrapped by a local proxy. To provide a generic implementation, such a proxy is realized using a Java dynamic proxy API. In the runtime, a proxy instance is created; it implements the same ServiceInterface RMI interface as the wrapped stub reference. Any invocation will be delegated to the stub eventually after first being processed by the interceptor. A simple implementation of an RMI interceptor factory follows the class diagram shown in Figure 4.
Figure 4. RMI interceptor factory
|
package rmicontext.interceptor;
public interface ServiceInterfaceInterceptorFactoryInterface {
ServiceInterface newInterceptor(ServiceInterface serviceStub, Class serviceInterfaceClass) throws Exception;
}
package rmicontext.interceptor;
public class ServiceInterfaceInterceptorFactory
implements ServiceInterfaceInterceptorFactoryInterface {
public ServiceInterface newInterceptor(ServiceInterface serviceStub, Class serviceInterfaceClass)
throws Exception {
ServiceInterface interceptor = (ServiceInterface)
Proxy.newProxyInstance(serviceInterfaceClass.getClassLoader(),
new Class[]{serviceInterfaceClass},
new ServiceContextPropagationInterceptor(serviceStub)); // ClassCastException
return interceptor;
}
}
package rmicontext.interceptor;
public class ServiceContextPropagationInterceptor
implements InvocationHandler {
/**
* The delegation stub reference of the original service interface.
*/
private ServiceInterface serviceStub;
/**
* The delegation stub reference of the service interceptor remote interface.
*/
private ServiceInterceptorRemoteInterface interceptorRemote;
/**
* Constructor.
*
* @param serviceStub The delegation target RMI reference
* @throws ClassCastException as a specified uncaught exception
*/
public ServiceContextPropagationInterceptor(ServiceInterface serviceStub)
throws ClassCastException {
this.serviceStub = serviceStub;
interceptorRemote = (ServiceInterceptorRemoteInterface)
PortableRemoteObject.narrow(serviceStub, ServiceInterceptorRemoteInterface.class);
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
// Skip it for now ...
}
}
I have simplified the above code to focus more on the underlying design. Here, only one type of interceptor is created, and it is implemented as the ServiceContextPropagationInterceptor class. This interceptor is responsible for retrieving and passing all the service-context data available under the current invocation scope. More detail will be covered later. The interceptor factory is used by the naming-function encapsulation described in the previous section.
To complete the ServiceManager class, a simple interface proxy cache is implemented:
package rmicontext.service;
public class ServiceManager
implements ServiceManagerInterface {
/**
* The interceptor stub reference cache.
* <br><br>
* The key is the specific serviceInterface sub-class and the value is the interceptor stub reference.
*/
private transient HashMap serviceInterfaceInterceptorMap = new HashMap();
/**
* Gets a reference to a service interface.
*
* @param serviceInterfaceClassName The full class name of the requested interface
* @return selected service interface
*/
public ServiceInterface getServiceInterface(String serviceInterfaceClassName) {
// The actual naming lookup is skipped here.
ServiceInterface serviceInterface = ...;
synchronized (serviceInterfaceInterceptorMap) {
if (serviceInterfaceInterceptorMap.containsKey(serviceInterfaceClassName)) {
WeakReference ref = (WeakReference) serviceInterfaceInterceptorMap.get(serviceInterfaceClassName);
if (ref.get() != null) {
return (ServiceInterface) ref.get();
}
}
try {
Class serviceInterfaceClass = Class.forName(serviceInterfaceClassName);
ServiceInterface serviceStub =
(ServiceInterface) PortableRemoteObject.narrow(serviceInterface, serviceInterfaceClass);
ServiceInterfaceInterceptorFactoryInterface factory = ServiceInterfaceInterceptorFactory.getInstance();
ServiceInterface serviceInterceptor =
factory.newInterceptor(serviceStub, serviceInterfaceClass);
WeakReference ref = new WeakReference(serviceInterceptor);
serviceInterfaceInterceptorMap.put(serviceInterfaceClassName, ref);
return serviceInterceptor;
} catch (Exception ex) {
return serviceInterface; // no interceptor
}
}
}
}
Optionally, the ability to distinguish between an interceptor-enabled service interface and a non-interceptor-enabled service interface can be added. For a non-interceptor-enabled service interface, the raw RMI stub reference will return. Further, a registration mechanism can be used when multiple interceptors need to be invoked according to some predefined invocation order for each different ServiceInterface type. To make the local proxy more robust, we also need to detect stale remote references in each interceptor. However, to keep the example more concise, such error handlings are not included for the above implementation.
In the next section, I describe the actual context data we'd like to use as well as the related interceptor proxy implementation—the invoke() method from the java.lang.reflect.InvocationHandler interface.
Transaction context
Transaction context is the most commonly used service-context data. As described in the Java Transaction Service specification, transaction context, such as transaction ID (xid ), must be associated with threads currently involved in a transaction. Thus, the transaction-context data must be propagated from the client JVM to the target server JVM with each RMI invocation.
On the server-side, an RMI thread is assigned to service the invocation call and hence the enclosing transaction. Obviously, it is impossible to require each RMI ServiceInterface to include an additional argument for each of its operations to pass such context data. Even if we choose to do so, the client code is not supposed to be aware of such invocation semantics. Therefore, for each RMI invocation in the client code, the context fetching and propagating should occur in a way that is totally transparent to client code.
According to the API convention described in the CORBA Transaction Service Specification, the following classes are defined to serve as the target service-context data structure and provide the required runtime support:
package rmicontext;
public class ServiceContext implements Serializable {
public static final int TRANSACTION_CONTEXT_ID = 2;
public int contextId = 0; // Unknown
public Object contextData = null;
public ServiceContext() {
}
public ServiceContext(int contextId) {
this.contextId = contextId;
}
public boolean isContextId(int id) {
if (contextId == id) {
return true;
} else {
return false;
}
}
public int getContextId() {
return contextId;
}
public Object getContextData() {
return contextData;
}
public void setContextData(Object data) {
contextData = data;
}
}
package rmicontext;
public class TransactionContextData implements Serializable {
public static final int UNASSIGNED_XID = 0;
private int xid = UNASSIGNED_XID; // Not assigned
public TransactionContextData() {}
public TransactionContextData(int xid) {
this.xid = xid;
}
public int xid() {
return xid;
}
}
package rmicontext;
public class Current {
private static ServiceContextList contextList = new ServiceContextList();
public static void setServiceContextList(ServiceContext[] contexts) {
contextList.set(contexts);
}
public static void clearServiceContextList() {
contextList.set(null);
}
public static ServiceContext[] getServiceContextList() {
return (ServiceContext[]) contextList.get();
}
/**
* To set the transaction ID to the associated context data.
*/
public static void setXid(int xid) {
// ...
}
/**
* To fetch the transaction id from the associated context data.
*/
public static int getXid() {
// ...
}
}
/**
* The list of service contexts associated with the current thread. Package access only.
*/
class ServiceContextList extends InheritableThreadLocal {
}
Class ServiceContext contains a context ID and context data. The context ID is predefined and known to both the client and server code. Context data does not require type-safety and is only opaque data as far as the service-context propagation protocol is concerned. In this example, context data for the transaction service context contains only an xid as defined in the class TransactionContextData . For the current thread, all service contexts, defined as ServiceContext[] , are maintained as thread local data through the Current class. For convenience, this class also provides direct API support for fetching and setting xid , which represents the transaction context in this example.
Until now, I haven't revealed the real solution for the RMI service-context propagation. The next section describes what's required on the client and server sides to make that happen.
The realization of service-context propagation
So far, I have described the infrastructure support for enabling the RMI interceptor and service context. To realize the implicit service-context propagation over RMI, the ultimate approach is still to add an additional argument for each RMI invocation. However, such an argument is only passed behind the scenes, and the client code still invokes the original RMI service interface method. I begin to reveal the real mechanism by first going through the following server-side code:
package rmicontext.interceptor;
/**
* This interface will be implemented by each Service class.
*/
public interface ServiceInterceptorInterface {
/**
* The interceptor method that decodes the incoming request message on the Service side.
*
* @param methodName The method name
* @param arguments The arguments
* @param argumentTypes The argument class names to be used to identify an implementation Method
* @param contextList The ContextList to be set to Current
* @return The return value of the method invocation
* @throws RemoteException if any RMI error
* @throws InvocationTargetException that wrapps the cause exception of the invocation
*/
Object exec(String methodName, Object[] arguments, String[] argumentTypes, ServiceContext[] contextList)
throws RemoteException, InvocationTargetException;
}
package rmicontext.interceptor;
/**
* The remote version of ServiceInterceptorInterface.
*/
public interface ServiceInterceptorRemoteInterface extends ServiceInterceptorInterface, Remote {
}
Instead of having a server-side skeleton interceptor, above I have defined the ServiceInterceptorInterface and ServiceInterceptorRemoteInterface , two interfaces that the Service base class must implement. The reason for two interfaces is to decouple the remoteness from the functional interface definition. (By doing so, we can support even local method propagation.) Now it is time to complete the Service class's implementation:
package rmicontext.service;
public class Service
extends PortableRemoteObject
implements ServiceInterface,
ServiceInterceptorRemoteInterface {
public Service() throws RemoteException {
super();
}
// ==== Service Interceptor Server-side Implementation ====
public Object exec(String methodName, Object[] arguments, String[] argumentTypes, ServiceContext[] contextList)
throws RemoteException, InvocationTargetException {
Class serviceClass = getClass();
try {
Class[] argTypes = ClassUtil.forNames(argumentTypes);
Method serviceMethod = serviceClass.getMethod(methodName, argTypes);
Current.setServiceContextList(contextList);
return serviceMethod.invoke(this, arguments);
} catch (ClassNotFoundException ex) {
processExecReflectionException(ex);
} catch (NoSuchMethodException ex) {
processExecReflectionException(ex);
} catch (IllegalAccessException ex) {
processExecReflectionException(ex);
}
return null; // javac
}
/**
* Process a reflection exception.
*
* @throws InvocationTargetException a wrapped exception
*/
private void processExecReflectionException(Exception ex) throws InvocationTargetException {
// The cause exception has to be a runtime exception.
throw new InvocationTargetException(new IllegalArgumentException("Interceptor Service.exec() failed: " + ex));
}
}
As a base class for each server-side ServiceInterface implementation, the Service class provides a generic way for accepting service-context data as an implicit argument via a generic exec() method, which is available to every client-side proxy stub. The magic also lies in the logics of finding the target method that the actual RMI invocation is to be delegated to. Because methods can be overloaded in every class, an exact argument type-matching is needed. That explains why the exec() method must pass the class names of all the argument types. Regarding this point, you may have noticed the use of the ClassUtil class. This class enhances the java.lang.Class class by defining a more convenient forName() method that covers primitive types too. ClassUtil 's contents follow:
package rmicontext;
public final class ClassUtil {
/**
* Get the class names than can be used in remote reflection invocation.
* @param argTypes The method argument classes
* @return class names
*/
public static String[] getNames(Class[] argTypes) {
String[] result = new String[argTypes.length];
for (int i = 0; i < argTypes.length; i++) {
result[i] = argTypes[i].getName();
}
return result;
}
/**
* Get the classes from names.
*
* @param argTypes The method argument classes' names
* @return ClassNotFoundException if any class can not be located
*/
public static Class[] forNames(String[] argTypes) throws ClassNotFoundException {
Class[] result = new Class[argTypes.length];
for (int i = 0; i > argTypes.length; i++) {
result[i] = forName(argTypes[i]);
}
return result;
}
/**
* Enhanced java.lang.Class.forName().
*
* @param name The class name or a primitive type name
* @return ClassNotFoundException if no class can be located
*/
public static Class forName(String name) throws ClassNotFoundException {
if (name.equals("int")) {
return int.class;
} else if (name.equals("boolean")) {
return boolean.class;
} else if (name.equals("char")) {
return char.class;
} else if (name.equals("byte")) {
return byte.class;
} else if (name.equals("short")) {
return short.class;
} else if (name.equals("long")) {
return long.class;
} else if (name.equals("float")) {
return float.class;
} else if (name.equals("double")) {
return double.class;
} else {
return Class.forName(name);
}
}
}
On the client side, we now complete the only interceptor we are supporting here, particularly, the invoke() method from the java.lang.reflect.InvocationHandler interface. To support the service-context propagation, this is the only change required on the client side. Because the interceptor is deployed transparently on the client side, client code will never be aware of any underlying service-context propagation. The related implementation looks like:
package rmicontext.interceptor;
/**
* This is the invocation handler class of the service context propagation
* interceptor, which itself is a dynamic proxy.
*/
public class ServiceContextPropagationInterceptor
implements InvocationHandler {
/**
* The delegation stub reference of the original service interface.
*/
private ServiceInterface serviceStub;
/**
* The delegation stub reference of the service interceptor remote interface.
*/
private ServiceInterceptorRemoteInterface interceptorRemote;
/**
* Constructor.
*
* @param serviceStub The delegation target RMI reference
* @throws ClassCastException as a specified uncaught exception
*/
public ServiceContextPropagationInterceptor(ServiceInterface serviceStub)
throws ClassCastException {
this.serviceStub = serviceStub;
interceptorRemote = (ServiceInterceptorRemoteInterface)
PortableRemoteObject.narrow(serviceStub, ServiceInterceptorRemoteInterface.class);
}
/**
* The invocation callback. It will call the service interceptor remote interface upon each invocation.
*
* @param proxy The proxy instance
* @param m The method
* @param args The passed-in args
* @return Object The return value. If void, then return null
* @throws Throwable Any invocation exceptions.
*/
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
Object result;
// In case the context is explicitly specified in the method signature as the last argument.
if (args != null && args.length > 0) {
Class[] argTypes = m.getParameterTypes();
Class argType = argTypes[argTypes.length - 1]; // Last argument
if (argType == ServiceContext[].class) {
try {
return m.invoke(serviceStub, args);
} catch (InvocationTargetException ex) { // including RemoteException
throw ex.getCause();
}
// Ignore the IllegalAccessException
}
}
try {
if (args == null || args.length == 0) {
result =
interceptorRemote.exec(m.getName(), args, new String[]{}, Current.getServiceContextList());
} else {
String[] argTypes = ClassUtil.getNames(m.getParameterTypes());
result =
interceptorRemote.exec(m.getName(), args, argTypes, Current.getServiceContextList());
}
return result; // Null if void
} catch (RemoteException ex) {
throw ex;
} catch (InvocationTargetException ex) {
throw ex.getCause();
}
}
}
Based on the groundwork we already established in the earlier steps, the above implementation is quite straightforward. One thing you must note is the exception-processing logic. Considering transaction-context propagation is mainly one-way and request context is more important, for simplicity, we don't piggyback the client-side service context in the response message, which is the return value of the invoke() method. However, adding the response service-context support does not require much work, so I leave this task to you.
The final design is shown in Figure 5.
Figure 5. ServiceInterceptorInterface class diagram
|
I'd like to return to the ServiceInterceptorRemoteInterface and ServiceInterceptorInterface interfaces as an approach for eliminating the coupling with the server-side ServiceInterface implementations. A dynamic server-side skeleton interceptor could also be used for the same purpose. However, I consider server-side transparency to be less significant than on the client-side, and to increase runtime efficiency and reduce deployment overhead, I chose this simpler approach.
However, my decoupling approach does incur some coding cost, mainly due to the JDK 1.4 rmic bug (bug-id: 5002152, reported by the author). When RMI/IIOP (rmic -iiop ) is used, the ServiceInterceptorRemoteInterface interface must be redeclared for each subclass of the Service class. In some cases, this may cause problems, for instance, when the source code of the specialized Service implementation is not available. This bug does not apply to RMI/JRMP.
Performance consideration
The performance costs mainly come from reflection, both on the client and server sides:
- Dynamic proxy creation for the interceptor on the client side—this is a one-time cost
- Cost associated with dynamic proxy invocation handler implementation on the client side
- Reflection cost for identifying the target method on the server side
- Marshaling cost for passing argument type names
Costs related to the service-context propagation function itself are not included. From the above analysis, we can see that the costs are mostly decided by the class signature, the number of overloaded methods, the target method signature, and the number of arguments. In other words, the total cost is static and does not depend on the size of instance data passed as arguments in the runtime, as opposed to RMI marshaling costs on the IIOP or JRMP layers. In most cases, the performance overhead is negligible, especially considering the overhead of RMI marshaling (let alone the IIOP marshaling).
Some simple measurements show that on a standard PC environment, with JDK 1.4, the RMI invocation overhead will be less than 5 ms for methods that have at least two arguments and two overloaded variants. In reality, the average overhead could be lower. I don't have any performance numbers for the equivalent cost associated with IIOP service-context propagation. Regardless, it will be much smaller than the cost of argument data marshaling.
Conclusion
You've been presented with some ways that RMI can be extended to meet the challenging design requirements we face in building today's distributed applications. The common concepts of service context and interceptor were illustrated to establish the high-level application context.
Many other items remain to be explored, such as a local method-invocation interceptor at the component level, a deployment strategy for registering, loading, configuring, and controlling service-specific interceptors, as well as further API-level abstraction. With all these further developments, the solution presented in this article can be easily made into a ready-to-use framework component.
About the author
Wenbo Zhu joined Sun Microsystems in 1997 as part of its Java development promotion force. For the past three years, he has been developing a carrier-grade network management application platform for Nortel as a senior Java designer. He's also studying as a part-time PhD student at Carleton University (Ottawa, Canada) in the real-time and distributed systems lab, focusing on software performance engineering and modeling for high-reliability distributed systems.
Posted by 아름프로
달력
« 2025/02 »
일 |
월 |
화 |
수 |
목 |
금 |
토 |
|
|
|
|
|
|
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 |
|
Total :
Today :
Yesterday :
|
|