Teach Yourself J2EE in 21 Days (Sams).

Teach Yourself J2EE in 21 Days (Sams).

00 0672323842 FM 3/20/02 9:31 AM Page i

Martin Bond

Dan Haywood

Debbie Law

Andy Longshaw

Peter Roxburgh

Teach Yourself

J2EE in

21

Days

201 West 103rd St., Indianapolis, Indiana, 46290 USA

00 0672323842 FM 3/20/02 9:31 AM Page ii

Sams Teach Yourself J2EE in 21 Days

Copyright

2002 by Sams Publishing

All rights reserved. No part of this book shall be reproduced, stored in a retrieval system, or transmitted by any means, electronic, mechanical, photocopying, recording, or otherwise, without written permission from the publisher. No patent liability is assumed with respect to the use of the information contained herein. Although every precaution has been taken in the preparation of this book, the publisher and author assume no responsibility for errors or omissions. Nor is any liability assumed for damages resulting from the use of the information contained herein.

International Standard Book Number: 0-672-32384-2

Library of Congress Catalog Card Number: 2001098579

Printed in the United States of America

First Printing: April, 2002

03 02 01 00 4 3 2 1

Trademarks

All terms mentioned in this book that are known to be trademarks or service marks have been appropriately capitalized. Sams Publishing cannot attest to the accuracy of this information. Use of a term in this book should not be regarded as affecting the validity of any trademark or service mark.

Warning and Disclaimer

Every effort has been made to make this book as complete and as accurate as possible, but no warranty or fitness is implied. The information provided is on an “as is” basis. The authors and the publisher shall have neither liability nor responsibility to any person or entity with respect to any loss or damages arising from the information contained in this book.

E

XECUTIVE

E

DITOR

Michael Stephens

M

ANAGING

E

DITOR

Matt Purcell

A

CQUISITIONS

E

DITOR

Todd Green

D

EVELOPMENT

E

DITOR

Michael Watson

P

ROJECT

E

DITOR

Christina Smith

C

OPY

E

DITOR

Pat Kinyon

I

NDEXERS

Tom Dinse

Erika Millen

P

ROOFREADER

Melissa Lynch

T

ECHNICAL

E

DITOR

Harold Finz, Steve Heckler,

Farooq Karim, and Ari

Krupnikov

T

EAM

C

OORDINATOR

Pamalee Nelson

I

NTERIOR

D

ESIGNER

Gary Adair

C

OVER

D

ESIGNER

Aren Howell

P

RODUCTION

Cheryl Lynch

Michelle Mitchell

00 0672323842 FM 3/20/02 9:31 AM Page iii

Contents at a Glance

Introduction

W

EEK

1 Introducing J2EE and EJBs

Day 1 The Challenge of N-Tier Development

2 The J2EE Platform and Roles

3 Naming and Directory Services

4 Introduction to EJBs

5 Session EJBs

6 Entity EJBs

7 CMP and EJB QL

W

EEK

2 Developing J2EE Applications

Day 8 Transactions and Persistence

9 Java Message Service

10 Message-Driven Beans

11 JavaMail

12 Servlets

13 JavaServer Pages

14 JSP Tag Libraries

W

EEK

3 Integrating J2EE into the Enterprise

Day 15 Security

16 Integrating XML with J2EE

17 Transforming XML Documents

18 Patterns

19 Integrating with External Resources

20 Using RPC-Style Web Services with J2EE

21 Web Service Registries and Message-Style Web Services

Appendixes

Appendix A An Introduction to UML

B SQL Reference

C An Overview of XML

D The Java Community Process

Glossary

Index

1

333

335

395

429

461

501

555

603

27

81

125

165

7

9

211

271

651

653

701

741

787

827

869

923

965

977

987

999

1003

1025

00 0672323842 FM 3/20/02 9:31 AM Page iv

Contents

Introduction 1

W

EEK

1 Introducing J2EE and EJBs 7

D

AY

1 The Challenge of N-Tier Development 9

Monolithic Development ......................................................................................10

Consequences of Monolithic Applications ......................................................10

The Move into the Second Tier ............................................................................11

Consequences of the 2-Tier Design ................................................................12

Complexity Simplified by Modularity ................................................................14

Component Technology ..................................................................................15

Benefits of Modularity ....................................................................................16

Benefits of the 3-Tier Scenario ............................................................................16

A Model for Enterprise Computing ....................................................................17

Lifecycle ..........................................................................................................18

Persistence ......................................................................................................18

Naming ............................................................................................................18

Transaction ......................................................................................................19

Java 2 Enterprise Edition (J2EE) ..........................................................................20

Components and Containers ............................................................................20

J2EE Standard Services ..................................................................................21

J2EE Blueprints ..............................................................................................23

J2EE Compatibility Test Suite ........................................................................24

The Future of J2EE ..............................................................................................25

Summary ..............................................................................................................25

Q&A ......................................................................................................................25

Exercises ..............................................................................................................26

D

AY

2 The J2EE Platform and Roles 27

Revisiting the J2EE Platform ..............................................................................28

Using Sun Microsystems’ J2EE SDK ..................................................................28

Installing J2EE SDK 1.3 ................................................................................29

Starting the J2EE Reference Implementation (RI) ........................................32

Troubleshooting J2EE and Cloudscape ..........................................................34

Closing Down J2EE RI and Cloudscape ........................................................37

Optional Software Used in this Book ............................................................37

Understanding Tiers and Components ..................................................................38

The Business Tier ..........................................................................................39

The Presentation Tier ......................................................................................44

00 0672323842 FM 3/20/02 9:31 AM Page v

Components: Web-Centric ..............................................................................45

The Client Tier ................................................................................................49

Standalone Client ............................................................................................52

Understanding Containers ....................................................................................55

Understanding the Services Containers Supply to Components ..........................56

Hypertext Transfer Protocol (HTTP) ..............................................................57

HTTP over Secure Sockets Layer (HTTPS) ..................................................57

Java Database Connectivity (JDBC) ..............................................................57

Java Transaction API (JTA) ............................................................................58

Java Authentication and Authorization Service (JAAS) ................................58

Java API for XML Parsing (JAXP) ................................................................58

Java Naming and Directory Interface (JNDI) ................................................59

JavaBeans Activation Framework (JAF) ........................................................59

JavaMail ..........................................................................................................60

Java Message Service (JMS) ..........................................................................60

Java Interface Definition Language (Java IDL) ..............................................60

Remote Method Invocation over Internet Inter-Orb Protocol (RMI-IIOP) ....61

Connector Architecture ....................................................................................62

Introducing Platform Roles ..................................................................................62

J2EE Product Provider ....................................................................................63

Application Component Provider ....................................................................63

Application Assembler ....................................................................................63

Application Deployer ......................................................................................64

Systems Administrator ....................................................................................64

Tool Provider ..................................................................................................65

Future of J2EE tools ........................................................................................65

Packaging and Deploying J2EE Applications ......................................................66

J2EE applications ..........................................................................................67

Breaking Modules down into Components ....................................................68

Summary ..............................................................................................................70

Q&A ......................................................................................................................70

Exercises—Case Study ........................................................................................71

The Job Agency ..............................................................................................72

Using the Agency Case Study ........................................................................73

Practice Makes Perfect ....................................................................................75

The Case Study Directory on the CD-ROM ..................................................76

Installing the Case Study Database ................................................................76

D

AY

3 Naming and Directory Services 81

Naming and Directory Services ............................................................................82

Why Use a Naming Service? ................................................................................82

What is JNDI? ......................................................................................................83

00 0672323842 FM 3/20/02 9:31 AM Page vi vi Sams Teach Yourself J2EE in 21 Days

Common Naming Services ..................................................................................83

Naming Conventions ............................................................................................84

Using JNDI ..........................................................................................................85

Using Sun Microsystems’ J2EE Reference Implementation ..........................85

Obtaining an Initial Context ................................................................................86

Initial Context Naming Exceptions ......................................................................86

Defining the JNDI Service ..............................................................................87

JNDI Properties Files ......................................................................................88

Application Properties ....................................................................................89

Applet Parameters ..........................................................................................90

Hard-Coded Properties ....................................................................................90

Binding JNDI Objects ..........................................................................................90

Binding Objects ..............................................................................................91

Binding Problems ............................................................................................91

Name Persistence ............................................................................................92

Rebinding Objects ..........................................................................................92

Unbinding Objects ..........................................................................................92

Renaming Objects ..........................................................................................93

JNDI Name Lookup ..............................................................................................93

Changing Contexts ..........................................................................................94

Narrowing RMI-IIOP Objects ........................................................................95

Contexts ................................................................................................................96

Listing Contexts ..............................................................................................96

Creating and Destroying Contexts ..................................................................98

More on JNDI Names ........................................................................................100

Special Characters ........................................................................................100

Composite and Compound Names ................................................................100

URLs ..............................................................................................................101

Attributes ............................................................................................................102

Overview of LDAP X.500 Names ................................................................102

Obtaining an LDAP Server ..........................................................................103

Using OpenLDAP ........................................................................................104

Configuring JNDI to use LDAP ....................................................................106

Testing the LDAP Server ..............................................................................107

Obtaining a Directory Context ......................................................................108

Reading Attributes ........................................................................................108

Searching for Objects ....................................................................................109

Manipulating Attributes ................................................................................112

More on Objects ................................................................................................114

Loading Classes from a Code Base ..............................................................114

Defining a Code Base ....................................................................................114

References ....................................................................................................117

00 0672323842 FM 3/20/02 9:31 AM Page vii

Contents

What Else Can JNDI Do? ..................................................................................120

JNDI Events ..................................................................................................120

Security ..........................................................................................................121

Summary ............................................................................................................122

Q&A ....................................................................................................................123

Exercise ..............................................................................................................124

D

AY

4 Introduction to EJBs 125

What Is an EJB? ................................................................................................126

Beans, Clients, Containers, and Servers ........................................................126

The EJB Landscape ......................................................................................127

Discovering EJBs ..........................................................................................127

Types of EJB ................................................................................................128

Common Uses of EJBs ..................................................................................128

Why Use EJBs? ..................................................................................................129

Hiding Complexity ........................................................................................130

Separation of Business Logic from UI and Data Access ..............................130

Container Services ........................................................................................131

What’s in an EJB? ..............................................................................................132

The Business Interface ..................................................................................132

The Business Logic ......................................................................................134

Factory Information ......................................................................................140

Bean Metadata ..............................................................................................141

How Do I Create an EJB? ..................................................................................142

The Creation Mechanism ..............................................................................142

Caveats on Code Creation ............................................................................143

Create the Deployable Component ..............................................................143

How Do I Deploy an EJB? ................................................................................147

Plugging into the Container ..........................................................................147

Performing the Deployment ..........................................................................148

How Do I Use an EJB? ......................................................................................148

Discovery ......................................................................................................148

Retrieval and Use ..........................................................................................149

Disposing of the EJB ....................................................................................150

Running the Client ........................................................................................150

Deploying and Using an EJB in the J2EE Reference Implementation ..............151

Opening the Case Study EAR File ................................................................152

Examining the Case Study Application ........................................................154

Deploying the Case Study Application ........................................................156

Testing the Case Study Application ..............................................................158

Troubleshooting the Case Study Application ................................................160

vii

00 0672323842 FM 3/20/02 9:31 AM Page viii viii Sams Teach Yourself J2EE in 21 Days

Summary ............................................................................................................161

Q&A ....................................................................................................................161

Exercises ............................................................................................................162

D

AY

5 Session EJBs 165

Overview ............................................................................................................165

The javax.ejb

Package for Session Beans ........................................................167

Stateless Session Bean Lifecycle ........................................................................168

Specifying a Stateless Session Bean ..................................................................172

Implementing a Stateless Session Bean ............................................................175

Implementing javax.ejb.SessionBean

........................................................175

Implementing the Home Interface Methods ................................................175

Implementing the Remote Interface Methods ..............................................177

Exceptions ....................................................................................................179

Configuring and Deploying a Stateless Session Bean ......................................180

Using deploytool

..........................................................................................181

Structural Elements ......................................................................................182

Presentational Elements ................................................................................183

Session Element ............................................................................................184

Deploying the Enterprise Application ..........................................................193

Stateful Session Bean Lifecycle ........................................................................193

Specifying a Stateful Session Bean ....................................................................196

Implementing a Stateful Session Bean ..............................................................198

Passivation ....................................................................................................198

Timeouts ........................................................................................................199

Chaining State ..............................................................................................200

Configuring and Deploying a Stateful Session Bean ........................................200

Client’s View ......................................................................................................201

Patterns and Idioms ............................................................................................202

Business Interface ..........................................................................................203

Adapter ..........................................................................................................204

Coarse-Grained ..............................................................................................205

Gotchas ..............................................................................................................205

Summary ............................................................................................................206

Q&A ....................................................................................................................207

Exercises ............................................................................................................207

D

AY

6 Entity EJBs 211

Overview ............................................................................................................211

The N-tier Architecture Revisited ................................................................212

Comparison with RDBMS Technology ........................................................213

Identifying Entities ........................................................................................214

00 0672323842 FM 3/20/02 9:31 AM Page ix

Contents

The javax.ejb

Package for Entity Beans ..........................................................216

Entity Bean Types ..............................................................................................217

Remote Versus Local Interfaces ........................................................................217

BMP Entity Bean Lifecycle ................................................................................219

Specifying a BMP Entity Bean ..........................................................................225

Local-Home Interface ....................................................................................225

Local Interface ..............................................................................................230

Implementing a BMP Entity Bean ....................................................................231

Implementing the Local-Home Interface Methods ......................................235

Implementing the Local Interface Methods ..................................................241

Generating IDs ..............................................................................................243

Granularity Revisited ....................................................................................245

Beware Those Finder Methods! ....................................................................245

EJB Container Performance Tuning ..............................................................247

Configuring and Deploying a BMP Entity Bean ..............................................248

Entity Element ..............................................................................................249

Client’s View ......................................................................................................252

Session Beans Revisited ....................................................................................254

Patterns and Idioms ............................................................................................258

Interfaces, Façades, and State ......................................................................258

Use Local Interfaces for Entity Beans ..........................................................258

Dependent Value Classes ..............................................................................259

Self-Encapsulate Fields ................................................................................261

Don’t Use

Enumeration for Finders ..............................................................262

Acquire Late, Release Early ..........................................................................262

Business Interface Revisited ........................................................................264

Gotchas ..............................................................................................................264

Summary ............................................................................................................265

Q&A ....................................................................................................................266

Exercises ............................................................................................................266

D

AY

7 CMP and EJB QL 271

Overview of Container-Managed Persistence ....................................................271

N-tier Architecture (Revisited Again) and CMP Fields ................................273

A Quick Word about the Case Study Database ............................................276

CMP Entity Bean Lifecycle ................................................................................277

Container-Managed Relationships ......................................................................279

Relationship Types ........................................................................................280

Navigability ..................................................................................................282

cmr-fields

....................................................................................................282

Manipulating Relationships ..........................................................................286

ix

00 0672323842 FM 3/20/02 9:31 AM Page x x Sams Teach Yourself J2EE in 21 Days

EJB QL ..............................................................................................................291

Select Methods ..............................................................................................291

Syntax and Examples ....................................................................................293

Further Notes ................................................................................................300

Specifying a CMP Entity Bean ..........................................................................301

The Local-Home Interface ............................................................................301

The Local Interface ......................................................................................301

Implementing a CMP Entity Bean ....................................................................302

Implementing javax.ejb.EntityBean

..........................................................302

Implementing the Local-Home Interface Methods ......................................305

Finder Methods ..............................................................................................308

Implementing the Local Interface Methods ..................................................312

Configuring a CMP Entity Bean ........................................................................313

The entity

Element ......................................................................................313

The relationships

Element ........................................................................317

Deploying a CMP Entity Bean ..........................................................................322

Patterns and Idioms ............................................................................................323

Normalize/Denormalize Data in ejbLoad()/ejbStore()

............................323

Don’t Expose cmp-fields

............................................................................324

Don’t Expose cmr-fields

............................................................................325

Enforce Referential Integrity Through the Bean’s Interface ........................326

Use Select Methods to Implement Home Methods ......................................327

Gotchas ..............................................................................................................328

Summary ............................................................................................................329

Q&A ....................................................................................................................329

Exercises ............................................................................................................330

W

EEK

2 Developing J2EE Applications 333

D

AY

8 Transactions and Persistence 335

Overview of Transactions ..................................................................................336

Container-Managed Transaction Demarcation ..................................................338

Bean Managed Transaction Demarcation ..........................................................345

Motivation and Restrictions ..........................................................................345

Using the Java Transaction API ....................................................................345

Deploying a BMTD Bean ............................................................................349

Client-Demarcated Transactions ..................................................................350

Exceptions Revisited ....................................................................................350

Extended Stateful Session Bean Lifecycle ........................................................352

Transactions: Behind the Scenes ........................................................................354

Transaction Managers, Resource Managers, and 2PC ..................................354

The JTA API ..................................................................................................356

00 0672323842 FM 3/20/02 9:31 AM Page xi

Contents

What If It Goes Wrong? ................................................................................359

JTA Versus JTS ..............................................................................................361

Overview of Persistence Technologies ..............................................................363

JDBC ..................................................................................................................365

SQLj ....................................................................................................................367

SQLj Part 0 ....................................................................................................368

SQLj Part 1 ....................................................................................................373

SQLj Part 2 ....................................................................................................378

JDO ....................................................................................................................383

JDO Concepts ................................................................................................384

javax.jdo

Classes and Interfaces ................................................................387

Queries ..........................................................................................................389

Other Features ..............................................................................................391

Gotchas ..............................................................................................................392

Summary ............................................................................................................393

Q&A ....................................................................................................................393

Exercises ............................................................................................................394

D

AY

9 Java Message Service 395

Messaging ..........................................................................................................395

Message Passing ..........................................................................................396

Java Message Service API ..................................................................................397

JMS and J2EE ..............................................................................................398

JMS API Architecture ........................................................................................399

Message Domains ..........................................................................................400

Developing JMS Applications Using JBoss1 ....................................................402

JMS Implementation in JBoss ......................................................................402

Programming a JMS Application Using J2EE RI ..............................................404

J2EE RI Connection Factories ......................................................................404

Adding Destinations in J2EE RI ..................................................................404

Creating a Queue in J2EE RI ........................................................................404

Point-to-Point Messaging Example ....................................................................406

JMS Messages ..............................................................................................407

Creating a Message ......................................................................................409

Sending a Message ........................................................................................409

Closing the Connection ................................................................................410

Send JMS Text Message Example ................................................................410

Consuming Messages ....................................................................................411

Simple Synchronous Receiver Example ............................................................412

Receive JMS Text Message Example ..........................................................413

Asynchronous Messaging ............................................................................414

The Publish/Subscribe Message Domain ..........................................................415

xi

00 0672323842 FM 3/20/02 9:31 AM Page xii xii Sams Teach Yourself J2EE in 21 Days

Point-to-Point Messaging Example ....................................................................416

Bulletin Board Publisher ..............................................................................417

Bulletin Board Subscriber ............................................................................418

Creating Durable Subscriptions ....................................................................420

Additional JMS Features ..............................................................................422

Introduction to XML ..........................................................................................425

What Is XML and Why Would You Use It? ..............................................425

Summary ............................................................................................................426

Q&A ....................................................................................................................426

Exercise ..............................................................................................................427

D

AY

10 Message-Driven Beans 429

What Are Message-Driven Beans? ....................................................................430

The Message Producer’s View ......................................................................430

Similarities and Differences with Other EJBs ..............................................431

Programming Interfaces in a Message-Driven Bean ..........................................431

Life Cycle of a Message-Driven Bean ..............................................................432

The Message-Driven Bean Context ..............................................................433

Creating a Message-Driven Bean ......................................................................434

Method-Ready Pool ......................................................................................434

The Demise of the Bean ................................................................................435

Consuming Messages ....................................................................................435

Handling Exceptions ....................................................................................436

Container- and Bean-Managed Transactions ..............................................436

Message Acknowledgment ............................................................................437

JMS Message Selectors ................................................................................438

Writing a Simple Message-Driven Bean ............................................................439

Implementing the Interfaces ........................................................................439

Running the Example ........................................................................................440

Creating the Queue ........................................................................................441

Deploying the Message-Driven Bean ............................................................442

Create a Sender Client to Create a Message ................................................445

Developing the Agency Case Study Example ..................................................447

Step 1—Sender Helper Class ......................................................................447

Step 2—Agency and Register Session Bean ................................................449

Step 3—The Message-Driven Bean ............................................................451

Step 4—Create the JMS Queue ....................................................................456

Step 5—Deploy the EJBS ............................................................................456

Step 6—Testing the ApplicantMatch Bean ..................................................457

Using Other Architectures ..................................................................................457

Summary ............................................................................................................458

Q&A ....................................................................................................................458

Exercise ..............................................................................................................458

00 0672323842 FM 3/20/02 9:31 AM Page xiii

Contents

D

AY

11 JavaMail 461

Understanding E-Mail ........................................................................................462

SMTP ............................................................................................................463

Post Office Protocol 3 (POP3) ......................................................................463

Internet Message Access Protocol (IMAP) ..................................................464

Other Protocols ..............................................................................................464

Multipurpose Internet Mail Extensions (MIME) ..........................................464

Introducing the JavaMail API ............................................................................465

Setting up Your Development Environment ......................................................465

Sending a First E-mail ........................................................................................466

Creating a First E-mail ..................................................................................466

Creating Multi-Media E-mails ............................................................................472

Creating the Message: Approach #1 ............................................................472

Creating the Message: Approach #2 ............................................................476

Sending E-mails with Attachments ....................................................................482

Exploring the JavaMail API ................................................................................485

Retrieving Messages ......................................................................................485

Deleting Messages ........................................................................................489

Getting Attachments ......................................................................................490

Authenticating Users and Security ................................................................494

Summary ............................................................................................................497

Q&A ....................................................................................................................497

Exercises ............................................................................................................499

D

AY

12 Servlets 501

The Purpose and Use of Servlets ......................................................................502

Tailored for Web Applications ....................................................................502

Server and Platform Independence ..............................................................503

Efficient and Scalable ....................................................................................503

Servlets Integration with the Server ..............................................................503

Introduction to HTTP ........................................................................................504

HTTP Structure ............................................................................................504

Other HTTP Methods ....................................................................................507

Server Responses ..........................................................................................507

Introduction to HTML ........................................................................................509

The Servlet Environment ....................................................................................513

Servlet Containers ........................................................................................513

The Servlet Class Hierarchy ..............................................................................513

Simple Servlet Example ....................................................................................514

Passing Parameter Data to a Servlet ..................................................................519

How to Access Parameters ............................................................................519

Servlet Example with Parameters ................................................................520

xiii

00 0672323842 FM 3/20/02 9:31 AM Page xiv xiv Sams Teach Yourself J2EE in 21 Days

Using a POST Request ..................................................................................522

The Servlet Lifecycle ....................................................................................522

The Servlet Context ......................................................................................524

Web Applications ................................................................................................525

Web Application Files and Directory Structure ..........................................525

The Web Application Deployment Descriptor ..............................................526

Handling Errors ..................................................................................................528

HTTP Errors ..................................................................................................528

Servlet Exception Handling ..........................................................................529

Retaining Client and State Information ..............................................................530

Using Session Objects ..................................................................................530

Hidden Form Fields ......................................................................................532

Cookies ..........................................................................................................532

Creating a Cookie ........................................................................................533

URL Rewriting ..............................................................................................535

Servlet Filtering ..................................................................................................535

Programming Filters ......................................................................................535

Example Auditing Filter ................................................................................537

Deploying Filters ..........................................................................................538

Event Listening ..................................................................................................541

Deploying the Listener ..................................................................................543

Servlet Threads ..................................................................................................545

Security and the Servlet Sandbox ......................................................................546

Agency Case Study ............................................................................................546

AgencyTable

Servlet Code ............................................................................546

Deploying the

AgencyTable

Servlet ..............................................................548

Summary ............................................................................................................552

Q&A ....................................................................................................................553

Exercises ............................................................................................................553

D

AY

13 JavaServer Pages 555

What is a JSP? ....................................................................................................556

Separating Roles ............................................................................................557

Translation and Execution ............................................................................557

JSP Syntax and Structure ....................................................................................557

JSP Elements ................................................................................................558

First JSP example ..............................................................................................560

JSP Problems ................................................................................................563

JSP Lifecycle ......................................................................................................563

Detecting and Correcting JSP Errors ............................................................565

JSP Lifecycle Methods ..................................................................................569

00 0672323842 FM 3/20/02 9:31 AM Page xv

Contents

JSP Directives ....................................................................................................570

The include

Directive ..................................................................................570

The page

Directive ........................................................................................571

Accessing HTTP Servlet Variables ....................................................................575

Using HTTP Request Parameters ......................................................................576

Simplifying JSP pages with JavaBeans ..............................................................577

What Is a JavaBean? ......................................................................................578

Defining a JavaBean ......................................................................................579

Getting Bean Properties ................................................................................579

Setting Bean Properties ................................................................................580

Initializing Beans ..........................................................................................581

Using a Bean with the Agency Case Study ..................................................581

Adding a Web Interface to the Agency Case Study ..........................................585

Structure and Navigation ..............................................................................585

Look and Feel ................................................................................................588

Error Page Definition ....................................................................................595

Deploying the Case Study JSPs ....................................................................597

Comparing JSP with Servlets ............................................................................600

Summary ............................................................................................................601

Q&A ....................................................................................................................601

Exercise ..............................................................................................................602

D

AY

14 JSP Tag Libraries 603

The Role of Tag Libraries ..................................................................................604

Developing a Simple Custom Tag ......................................................................605

Using a Simple Tag ......................................................................................605

The Tag Library Descriptor (TLD) ..............................................................606

Custom Java Tags ..........................................................................................608

The doStartTag()

Method ............................................................................610

The

“Hello World”

Custom Tag ..................................................................611

Deploying a Tag Library Web Application ..................................................612

Defining the TLD Location ..........................................................................614

Using Simple Tags ........................................................................................614

Tags with Attributes ............................................................................................615

Tags that Define Script Variables ........................................................................618

Iterative Tags ......................................................................................................622

Co-operating Tags ..............................................................................................626

Using Shared Scripting Variables ..................................................................626

Hierarchical Tag Structures ..........................................................................627

Defining Tag Extra Info Objects ........................................................................634

Validating Attributes ......................................................................................635

Defining Scripting Variables ........................................................................637

xv

00 0672323842 FM 3/20/02 9:31 AM Page xvi xvi Sams Teach Yourself J2EE in 21 Days

Processing Tag Bodies ........................................................................................637

JavaServer Pages Standard Tag Library (JSPTL) ..............................................640

Using the JSPTL with the J2EE RI ..............................................................641

Using the JSPTL forEach

Tag ....................................................................643

Other JSPTL Tags ........................................................................................645

JSPTL Scripting Language ............................................................................645

Other Jakarta Tag Libraries ..........................................................................646

Summary ............................................................................................................647

Q&A ....................................................................................................................647

Exercise ..............................................................................................................648

W

EEK

3 Integrating J2EE into the Enterprise 651

D

AY

15 Security 653

Security Overview ..............................................................................................654

Security Terminology ....................................................................................654

Common Security Technology ..........................................................................656

Symmetric Encryption ..................................................................................656

Asymmetric Encryption ................................................................................658

SSL and HTTPS ............................................................................................659

Checksums and Digests ................................................................................660

Digital Certificates ........................................................................................660

Security in J2EE ................................................................................................661

J2EE Security Terminology ..........................................................................661

Working with J2EE RI Security ....................................................................663

Security and EJBs ..............................................................................................666

Defining EJB Security ..................................................................................666

Defining Roles ..............................................................................................666

Defining the Security Identity ......................................................................668

Defining Method Permissions ......................................................................670

Mapping Principals to Roles ........................................................................674

Using Roles as the Security Identity ............................................................676

Security in Web Applications and Components ................................................682

Web Authentication ......................................................................................683

Configuring J2EE RI Basic Authentication ..................................................684

Declarative Web Authorization ....................................................................685

Programmatic Web Authorization ................................................................691

Adding Programmatic Web Security to the Case Study ..............................692

Using Secure Web Authentication Schemes ................................................694

Security and JNDI ............................................................................................695

Simple LDAP Authentication ........................................................................696

SASL Authentication ....................................................................................696

00 0672323842 FM 3/20/02 9:31 AM Page xvii

Contents

Summary ............................................................................................................698

Q&A ....................................................................................................................699

Exercises ............................................................................................................699

D

AY

16 Integrating XML with J2EE 701

The Drive to Platform-Independent Data Exchange ..........................................702

Benefits and Characteristics of XML ............................................................703

Origins of XML ..................................................................................................703

Structure and Syntax of XML ............................................................................704

HTML and XML ..........................................................................................705

Structure of an XML Document ..................................................................705

Declarations ..................................................................................................706

Elements ........................................................................................................706

Well-Formed XML Documents ....................................................................708

Attributes ......................................................................................................708

Comments ......................................................................................................709

Creating Valid XML ..........................................................................................710

Document Type Definitions ..........................................................................710

Namespaces ..................................................................................................714

Enforcing Document Structure with an XML Schema ................................715

How XML Is Used in J2EE ..........................................................................718

Parsing XML ......................................................................................................718

The JAXP Packages ..........................................................................................720

Parsing XML using SAX ....................................................................................720

Document Object Model (DOM) Parser ..........................................................725

Modifying a DOM Tree ......................................................................................731

Java Architecture for XML Binding ..................................................................732

Differences Between JAXP and JAXB ........................................................733

Extending the Agency Case Study ....................................................................734

Step 1—Change Session Beans ....................................................................735

Step 2—Amend the

MessageSender

Helper Class ........................................736

Step 3—Amend the

ApplicantMatch

Message-Driven Bean ......................737

Summary ............................................................................................................739

Q&A ....................................................................................................................739

Exercises ............................................................................................................740

D

AY

17 Transforming XML Documents 741

Presenting XML to Clients ................................................................................742

Presenting XML to Browsers ........................................................................743

Extensible Stylesheet Language (XSL) ........................................................744

XSL-FO XSL Formatting Objects ................................................................744

xvii

00 0672323842 FM 3/20/02 9:31 AM Page xviii xviii Sams Teach Yourself J2EE in 21 Days

Extensible Stylesheet Transformations (XSLT) ................................................745

Applying Stylesheets ....................................................................................746

Storing Transformed Documents on the Server ............................................746

Presenting XML Documents and Stylesheets to the Client ..........................747

Transforming the XML Document on the Server ........................................747

Using XALAN with J2EE ..................................................................................748

Transforming XML Documents with XALAN ............................................749

Using XALAN from the Command Line ....................................................750

Using XSLT in Java Applications ....................................................................751

XSLT Stylesheets ................................................................................................755

Template Rules ..............................................................................................756

Text Representation of XML Elements ......................................................761

Using XPath with XSLT ................................................................................762

Default Stylesheet Rules ..............................................................................764

Processing Attributes ....................................................................................765

Using Stylesheet Elements ................................................................................767

Processing Whitespace and Text ..................................................................767

Adding Comments ........................................................................................769

Attribute Values ............................................................................................770

Creating and Copying Elements ....................................................................771

Attributes and Attribute Sets ........................................................................774

Additional XSL Elements ............................................................................777

XSLT Compilers ................................................................................................780

Summary ............................................................................................................781

Q&A ....................................................................................................................782

Exercises ............................................................................................................782

D

AY

18 Patterns 787

J2EE Patterns ......................................................................................................788

What Are Patterns? ........................................................................................788

Why Use Patterns? ........................................................................................790

Types of Patterns ..........................................................................................790

J2EE Patterns ................................................................................................791

Pattern Catalogs ............................................................................................792

Applying J2EE-Specific Patterns ......................................................................792

Applying Patterns in a Context ....................................................................793

Generic Patterns ............................................................................................794

J2EE Presentation-Tier Patterns ....................................................................795

J2EE Business-Tier Patterns ..........................................................................795

J2EE Integration-Tier Patterns ......................................................................796

Patterns Within J2EE ....................................................................................797

00 0672323842 FM 3/20/02 9:31 AM Page xix

Contents

Patterns in Context ..............................................................................................797

Analysing the Case Study ............................................................................797

Session Facades and Entity EJBs ..................................................................798

Data Exchange and Value Objects ................................................................800

Data Access Without Entity EJBs ................................................................804

Messages and Asynchronous Activation ......................................................811

Composing an Entity ....................................................................................812

Composing a JSP ..........................................................................................813

JSPs and Separation of Concerns ..................................................................817

Client-Side Proxies and Delegates ................................................................820

Locating Services ..........................................................................................821

Any Other Business ......................................................................................822

Refactoring the Case Study ..........................................................................822

Directions for J2EE Patterns ..............................................................................823

Summary ............................................................................................................824

Q & A ..................................................................................................................824

Exercises ............................................................................................................825

D

AY

19 Integrating with External Resources 827

Reviewing External Resources and Legacy Systems ........................................828

Introducing Connector Architecture ..................................................................829

Overview of the Architecture ........................................................................829

Roles and Responsibilities ............................................................................830

Using the Common Client Interface ..................................................................834

Interacting with an EIS ..................................................................................834

Installing a Resource Adapter ......................................................................835

Creating a First CCI Application ..................................................................836

Managing Transactions and Exploring Records ..........................................843

Introducing Other Connectivity Technologies ....................................................848

Introducing CORBA ..........................................................................................849

Introducing Java IDL ..........................................................................................851

Using RMI over IIOP ........................................................................................851

RMI over JRMP Example ............................................................................852

RMI over IIOP Example ..............................................................................857

Introducing JNI ..................................................................................................860

Evaluation of Integration Technologies ..............................................................865

Summary ............................................................................................................865

Q&A ....................................................................................................................866

Exercises ............................................................................................................867

D

AY

20 Using RPC-Style Web Services with J2EE 869

Web Service Overview ......................................................................................870

What Is a Web Service? ................................................................................870

Why Use Web Services? ..............................................................................872

xix

00 0672323842 FM 3/20/02 9:31 AM Page xx xx Sams Teach Yourself J2EE in 21 Days

Web Service Technologies and Protocols ....................................................873

Web Service Architecture ..............................................................................873

Web Services for J2EE ......................................................................................875

J2EE Web Service Architecture ....................................................................875

Tools and Technologies ................................................................................876

Integrating Web Services with Existing J2EE Components ........................878

Using an RPC-style SOAP-Based Web Service ................................................879

RPC-Oriented Web Services ........................................................................880

Setting up Axis under Tomcat 4.0 ................................................................881

Service Description Information ..................................................................883

Anatomy of a WSDL Document ..................................................................883

Creating a Java Proxy from WSDL ..............................................................885

Calling the Web Service Through SOAP ......................................................889

A Half-Way House ........................................................................................891

Debugging a SOAP Interaction ....................................................................892

Implementing an RPC-Style SOAP-Based Web Service ..................................894

Wrapping up a Java class as a Web Service ..................................................894

A Client for Your Web Service ......................................................................898

Starting from WSDL ....................................................................................900

Using Axis JWS files ....................................................................................903

Session Context and Web Services ..............................................................905

Wrapping Existing J2EE Functionality as Web Services ............................909

Parameter Types and Type Mapping ..................................................................911

Mapping Between Java and SOAP/WSDL Types ........................................911

Mapping Complex Types with Serializers ....................................................912

Going Further with Complex Type Mapping ................................................919

Summary ............................................................................................................919

Q&A ....................................................................................................................920

Exercises ............................................................................................................921

D

AY

21 Web Service Registries and Message-Style Web Services 923

Registries for Web Services ................................................................................924

What is a Web Service Registry? ..................................................................924

Why Do I Need One? ....................................................................................924

How Do They Work? ....................................................................................925

Types of Registry ..........................................................................................925

ebXML Registry and Repository ..................................................................926

UDDI Overview ............................................................................................928

Accessing Information in a UDDI Registry ......................................................929

Manipulating Service Information using UDDI4J ........................................929

Manipulating Service Information Using the IBM WSTK Client API ........932

Retrieving and Using Service Information ..................................................933

00 0672323842 FM 3/20/02 9:31 AM Page xxi

Contents

Using JAXR for Registry Access ......................................................................934

A Generic Approach ......................................................................................934

Using JAXR to Store and Retrieve Service Information ..............................936

Using a Message-Based SOAP Interface ..........................................................937

Message-Style Versus RPC-style ..................................................................937

Creating a Client ............................................................................................938

Creating a Service ........................................................................................939

Sending and Receiving SOAP Messages with JAXM ......................................939

JAXM and J2EE ............................................................................................941

Configuring JAXM ........................................................................................941

Sending Basic SOAP Messages ....................................................................942

Running the Simple Client ............................................................................947

Populating the Message ................................................................................947

Headers and Attachments ..............................................................................951

Receiving a SOAP Message Using JAXM ..................................................952

Using a JAXM Profile ..................................................................................955

Sending a Message Using a JAXM Profile ..................................................957

Receiving a Message Using a JAXM Profile ................................................959

Summary ............................................................................................................962

Q&A ....................................................................................................................962

Exercises ............................................................................................................963

Appendix A An Introduction to UML 965

Introducing the UML ..........................................................................................965

Use Case Diagrams ............................................................................................967

Class Diagrams ..................................................................................................969

Associations ..................................................................................................969

Attributes ......................................................................................................970

Operations ......................................................................................................971

Generalization ................................................................................................972

Constraints ....................................................................................................973

Sequence Diagrams ............................................................................................973

Appendix B SQL Reference 977

Commonly Used SQL Statements (SQL99) ......................................................978

ALTER TABLE

..................................................................................................978

CREATE TABLE

..................................................................................................979

CREATE VIEW

..................................................................................................979

DELETE

............................................................................................................980

DROP TABLE

....................................................................................................980

DROP VIEW

......................................................................................................980

INSERT

............................................................................................................980

xxi

00 0672323842 FM 3/20/02 9:31 AM Page xxii

SELECT

............................................................................................................981

UPDATE

............................................................................................................983

Commonly Used SQL Clauses ..........................................................................983

FROM

................................................................................................................983

WHERE

..............................................................................................................983

GROUP BY

........................................................................................................984

HAVING

............................................................................................................984

ORDER BY

........................................................................................................984

Appendix C An Overview of XML 987

What Is XML? ....................................................................................................988

Elements ........................................................................................................988

Declarations ..................................................................................................989

Comments ......................................................................................................990

Special Characters ........................................................................................990

Namespaces ..................................................................................................991

Enforcing XML Document Structure ................................................................991

Document Type Definition (DTD) ................................................................992

XML Schema ................................................................................................995

Where to Find More Information ......................................................................997

Appendix D The Java Community Process 999

Introducing the JCP ............................................................................................999

Getting Involved ..............................................................................................1000

JCP Members ..............................................................................................1000

Expert Groups ..............................................................................................1000

The Public ....................................................................................................1000

Process Management Office (PMO) ..........................................................1001

Executive Committees ................................................................................1001

Understanding the JSR Process ........................................................................1001

Taking the Next Step ........................................................................................1002

Glossary 1003

Index 1025

00 0672323842 FM 3/20/02 9:31 AM Page xxiii

About the Authors

The authors of this book work for Content Master Ltd., a technical authoring company in the United Kingdom specializing in the production of training and educational materials.

For more information on Content Master, please see its Web site at www.contentmaster.com

.

Martin Bond, B.Sc. M.Sc. C.Eng, M.B.C.S., was born near Manchester England in

1958. Martin left a budding academic career to develop parallel processing compilers for

Inmos. Martin has designed and developed systems using C++, Java, and JavaScript and has developed training courses on Unix programming, Solaris security, Java programming, and XML. Martin has an honors degree and a masters degree in computer science from Aberystwyth, Wales, and is a European chartered engineer. Martin currently works as an IT trainer and consultant based in Cornwall, England.

Dan Haywood has been working on large and small software development projects for more than 12 years. These days, he fills his days with consulting, training and technical writing, specializing in OO design, Java and J2EE, Sybase technical consulting, and data modeling. Previously, Dan worked at Sybase Professional Services, performing a variety of roles, mostly in the financial industry, including architect, performance specialist, and project manager. Dan started his IT career at (what was then) Andersen Consulting, working as a developer on large-scale projects in government and in utilities. Dan is married and has a baby daughter.

Debbie Law B.Sc., was born in Romsey, England in 1959. Debbie started on compiler development for parallel processing systems, later working on the design and development of client server applications. As a technical manager for Siemens, she was one of a small group of select staff on an intensive learning program studying worldwide business practices, including several weeks at MIT and Harvard. Debbie has an honors degree in computer science from Southampton, England and currently works as an IT consultant based in Cornwall, England.

Andy Longshaw is a consultant, writer, and educator specializing in J2EE, XML, and

Web-based technologies and components, particularly the design and architecture decisions required to use these technologies successfully. Andy has been explaining technology for most of the last decade as a trainer and in conference sessions. A wild rumor suggests that some people have managed to stay awake in these sessions. Despite being well educated and otherwise fairly normal, Andy still subjects himself and his family to “trial by unpredictability” by watching Manchester City FC far more often than is healthy.

00 0672323842 FM 3/20/02 9:31 AM Page xxiv

Peter Roxburgh graduated with a first class degree with honors in business, and has since followed a diverse career path. From his home in the medieval walled town of

Conwy, North Wales, he authors a wide-variety of training courses, and books including

Building .NET Applications for Mobile Devices (Microsoft Press, 2002). He has also written and contributed to a number of journals and Web sites on cutting-edge technologies.

Peter spends his spare time playing guitar and bouldering on nearby sea cliffs and mountain crags. When he is not strumming or risking life and limb, he enjoys spending relaxing and quality time with his daughter, Chloe.

00 0672323842 FM 3/20/02 9:31 AM Page xxv

Dedication

To Sarah, for encouragement, advice, and regular supplies of flapjacks; and to Adam and Josh, for providing me with a life that doesn’t revolve around computers. —AL

To Sue: Thank you for all these happy years. —Love, Dan.

Acknowledgments

The authors would like to thank the various project managers and editors involved in this book, without whom it would never have seen the light of day. Special thanks go to

Suzanne Carlino at Content Master and Todd Green, Michael Watson, Christy Franklin, and the editing team at SAMS. We would also like to acknowledge the work of Alex

Ferris and John Sharp in the initial phases of this project.

00 0672323842 FM 3/20/02 9:31 AM Page xxvi

Tell Us What You Think!

As the reader of this book, you are our most important critic and commentator. We value your opinion and want to know what we’re doing right, what we could do better, what areas you’d like to see us publish in, and any other words of wisdom you’re willing to pass our way.

As an executive editor for Sams Publishing, I welcome your comments. You can fax, e-mail, or write me directly to let me know what you did or didn’t like about this book— as well as what we can do to make our books stronger.

Please note that I cannot help you with technical problems related to the topic of this book, and that due to the high volume of mail I receive, I might not be able to reply to every message.

When you write, please be sure to include this book’s title and author as well as your name and phone or fax number. I will carefully review your comments and share them with the author and editors who worked on the book.

Fax: 317-581-4770

E-mail: [email protected]

Mail: Michael Stephens

Executive Editor

Sams Publishing

201 West 103rd Street

Indianapolis, IN 46290 USA

01 0672323842 Intro 3/20/02 9:23 AM Page 1

Introduction

The world has come a long way since Duke first started tumbling in early versions of

Netscape Navigator. Java has outgrown its humble origins as a cool way of providing interactivity on Web pages and has found a new role as a major, server-side development platform. The actual Java language has changed little in the intervening years, but an enterprise-quality infrastructure has risen up around it. This infrastructure, Java 2

Enterprise Edition or J2EE for short, allows Java developers to create sophisticated and powerful enterprise applications that provide mission-critical functionality for many thousands of users.

Unlike competing platforms, such as Microsoft .NET, J2EE is a specification rather than a product. The capabilities and functionality of each release of J2EE is agreed on through the Java Community Process (JCP). The platform is then implemented by application server vendors and producers, such as BEA, IBM, iPlanet, ATG, SilverStream, and

JBOSS. This means that J2EE developers have a choice of product vendors from whom to select, based on quality, support, or ease of use. The ability to submit technologies through the JCP, and the two-way flow that exists between the main Java vendors and the open-source community, ensures that a constant stream of new ideas helps to move J2EE forward.

This book intends to take you on a journey through the J2EE landscape, from the simplest components through design considerations and on to the latest Web Services. There is a lot to learn in three weeks—but this should provide the essential grounding you need to use the J2EE platform effectively. If you need to create robust enterprise applications and Java is your tool of choice, read on.

How This Book is Organized

Sams Teach Yourself J2EE in 21 Days covers version 1.3 of the J2EE platform. It is organized as three separate weeks that guide you through the different functionality provided by J2EE.

The first week gives you a broad grounding in J2EE before moving on to investigate

Enterprise JavaBeans (EJBs) in detail:

• Day 1, “The Challenge of N-Tier Development,” defines the landscape in which

J2EE applications operate and provides the architectural concepts with which you need to become familiar to create J2EE applications.

01 0672323842 Intro 3/20/02 9:23 AM Page 2

2 Sams Teach Yourself J2EE in 21 Days

• Day 2, “The J2EE Platform and Roles,” takes you on a whistle-stop tour of the

J2EE platform, the major technologies, the types of component from which J2EE applications are assembled, and the container with which they interact. You also install the J2EE platform and start to look at the case study used throughout the book.

• On Day 3, “Naming and Directory Services,” you start using your first J2EE API, the Java Naming and Directory Interface (JNDI), to store, retrieve, and manipulate information that can be accessed by all J2EE components.

• Day 4, “Introduction to EJBs,” introduces Enterprise JavaBeans (EJB)—the core technology of the J2EE platform. You will examine the role of EJBs and how they work. You will then deploy an example EJB and create a simple client application for it.

• On Day 5, “Session EJBs,” you will explore Session EJBs in more depth. This includes the creation of both stateful and stateless Session EJBs.

• Day 6, “Entity EJBs,” moves on to Entity EJBs and examines their role and lifecycle. Particular attention is paid to how state is stored and retrieved using Bean-

Managed Persistence (BMP).

• On Day 7, “CMP and EJB QL,” the discussion of Entity EJBs expands to cover entities that use Container-Managed Persistence (CMP) to store and retrieve their state. This includes an exploration of the EJB Query Language and Container-

Managed Relationships that were introduced in J2EE 1.3.

The second week moves beyond EJBs to look at asynchronous interaction and the development of Web-based components:

• On Day 8, “Transactions and Persistence,” you will delve deeper into the use of transactions in the J2EE platform—what they can achieve and how your components can take advantage of them. Some alternative persistence mechanisms are also explored.

• Day 9, “Java Message Service,” looks at asynchronous messaging with the Java

Message Service (JMS) using message queues and topics. You will apply JMS to implement a producer and consumer of asynchronous messages.

• Day 10, “Message-Driven Beans,” builds on the coverage of JMS to associate message queues with Message-driven EJBs. You will create an EJB whose functionality is triggered on receipt of an asynchronous message.

• On Day 11, “JavaMail,” another asynchronous communication mechanism is examined—namely e-mail. You will learn how to send and retrieve e-mail under

J2EE and how this can be applied to transport data in a J2EE application.

01 0672323842 Intro 3/20/02 9:23 AM Page 3

Introduction

• Day 12, “Servlets,” is the first of three Web-oriented days that explore the creation of Web-oriented J2EE applications. This starts by creating servlets to take advantage of the EJB-based services you built earlier. You will look at the servlet lifecycle and central issues, such as session tracking and state management.

• Day 13, “JavaServer Pages,” looks at how JavaServer Pages (JSP) can help to integrate Java and J2EE functionality with HTML content. It examines the role of JSPs and how JavaBeans can be used to encapsulate Java functionality in JSPs.

• On Day 14, “JSP Tag Libraries,” you will use custom JSP tag libraries to encapsulate Java functionality to improve the maintainability of the JSP pages.

The third week explores essential aspects of enterprise applications, such as security and integration, before moving on to application design and ending with a look at the Web

Service functionality that will form the future of J2EE:

• Day 15, “Security,” begins week 3 by applying security to your J2EE application.

You will weigh up the benefits of declarative and programmatic security and how they can be applied within your application.

• On Day 16, “Integrating XML with J2EE,” you will examine the role of XML in

J2EE applications. You will create J2EE components that produce and consume

XML documents and process data using the Java APIs for XML Processing

(JAXP).

• Day 17, “Transforming XML Documents,” focuses on the transformation of XML documents into other formats, including other dialects of XML, primarily using the

XSLT transformation language. Again, JAXP allows you to do this programmatically from within J2EE components.

• On Day 18, “Patterns,” you will take some time to consider the bigger picture and examine design issues for J2EE applications. The specific focus will be on common patterns that have been found as people have applied J2EE technologies in live applications. You will use this knowledge to improve parts of the case study design.

• Day 19, “Integrating with External Resources,” explores the various technologies that can be used to integrate J2EE applications with non-J2EE components and services. These mechanisms include the Java Connector Architecture, CORBA, RMI-

IIOP, and the Java Native Interface.

• Day 20, “Using RPC-Style Web Services with J2EE,” looks ahead to the use of

J2EE components as Web Services. You will use common Web Service technologies (such as SOAP and WSDL) to expose Java functionality as Web Services, and look at how the Java API for XML-Based RPC (JAX-RPC) addresses this.

3

01 0672323842 Intro 3/20/02 9:23 AM Page 4

4 Sams Teach Yourself J2EE in 21 Days

• Day 21, “Web Service Registries and Message-Style Web Services,” concludes the examination of J2EE-based Web Services by examining the role of XML-based registries and how the Java API for XML Registries (JAXR) enables access to this information. You will also create a message-oriented producer and consumer of

Web Services using the Java API for XML Messaging (JAXM).

About This Book

This book is a practical, down-to-earth guide for intermediate Java developers. It is not intended to be a reference book, with lists of API calls or extensive discussion of the inner workings of the technologies. Rather, it provides you with a grounding in applying the essential J2EE technologies and leads you through the essential steps required to get a program or component written, packaged, and deployed on the J2EE platform. By the time you finish Sams Teach Yourself J2EE in 21 Days, you should have the confidence to create or maintain code that uses any of the major J2EE APIs.

Who Should Read This Book?

This book in intended for experienced Java developers who have been involved with Java development for at least 3–6 months. You should be confident writing Java code and familiar with the commonly used Java 2 Standard Edition APIs, such as string handling,

JDBC, collections, iterators, and so on.

In addition to a firm grasp of Java, the following knowledge will speed your progress through the book:

• An understanding of how the Web operates, such as the use of a Web browser to retrieve pages of HTML from Web Servers.

• Familiarity with XML syntax to the level of reading small extracts of XML containing elements, attributes, and namespaces.

• An understanding of relational databases and how data is structured in tables. A familiarity with basic SQL to the level of understanding simple queries, inserts, updates, and joins.

• Familiarity with distributed systems, such as n-tier development, client-server programming, and remote procedure calls.

If you are not familiar with one or more of these topics, don’t panic! There are appendixes on the CD-ROM that provide introductory material on XML and SQL. The essential concepts of distributed systems and Web-based development are covered in the main body of the book as required.

01 0672323842 Intro 3/20/02 9:24 AM Page 5

Introduction

How This Book is Structured

This book is intended to be read and absorbed over the course of three weeks. During each week, you read seven chapters that present concepts related to J2EE and the creation of enterprise applications in Java. Care has been taken to try to ensure that concepts and technologies are introduced in an appropriate order, so it is best to read the chapters sequentially if possible.

At the end of each lesson are a set of questions asked about the subject covered that day.

Answers to these questions are provided by the authors. There are also exercises for you to test your newly found skills by creating some related application or service.

The exercises in the book are largely based around a case study that is described in detail at the end of Day 2. The files for the case study and worked solutions to the exercises can be found on the CD-ROM that accompanies this book. The idea of the case study is that it will help you apply J2EE technologies and techniques in a consistent context and as part of a working application. This should provide you with a deeper understanding of the technology involved and how to apply it than is possible working with standalone examples.

Typographic Conventions

Note

A Note presents interesting, sometimes technical, pieces of information related to the surrounding discussion.

5

Tip

A Tip offers advice or suggests an easier way of doing something.

Caution

A Caution advises you of potential problems and helps you avoid causing serious damage.

Text that you type, text that should appear on your screen, and the names of Java classes or methods are presented in monospace type.

01 0672323842 Intro 3/20/02 9:24 AM Page 6

02 0672323842 Week1 3/20/02 9:35 AM Page 7

W

EEK

1

Introducing J2EE and EJBs

1 The Challenge of N-Tier

Development

2 J2EE Platform and Roles

3 Naming and Directory Services

4 Introduction to EJBs

5 Session EJBs

6 Entity EJBs

7 CMP and EJB QL

5

6

7

3

4

1

2

02 0672323842 Week1 3/20/02 9:35 AM Page 8

03 0672323842 CH01 3/20/02 9:26 AM Page 9

W

EEK

1

D

AY

1

The Challenge of N-Tier

Development

The current trend in enterprise program development is to provide n-tier frameworks aimed at delivering applications that are secure, scalable, and available.

To this end, Sun Microsystems introduced Java 2 Enterprise Edition (J2EE), and Microsoft Corporation ventured the .NET framework to help developers build applications that are Web-friendly and frequently used to deliver e-commerce solutions. There are a myriad of application servers available to house enterprise applications, and many service providers are writing modular tools to plug in and extend the rich functionality. The clients that are taking advantage of this distributed architecture can be as simple as a Web browser (a so-called thin client).

This is the overarching vision and the state of the art. But, how did we get here?

To understand this landscape, this chapter investigates the principles of multiple tiers, component environments, and standards that underlie the frameworks.

One of the objectives will be to give you a clear understanding of concepts and

03 0672323842 CH01 3/20/02 9:26 AM Page 10

10 Day 1 terminology used when discussing such frameworks. Such terminology can frequently be confusing and inconsistently used. As a start along this road, please note that for the purposes of the following discussions, a tier refers to a physical separation (a different machine), and a layer refers to a logical layer in software terms, such that multiple layers can be on the same machine.

Monolithic Development

In the days of the mainframe or the standalone personal computer, when an application was housed on a single machine, it was common to find monolithic applications containing all the functionality of the application in one large, frequently unmaintainable piece of software (sometimes referred to as spaghetti code). All user input, verification, business logic, and data access could be found together. This suited the world of the mainframe and corporate data center because everything was controlled and the systems themselves tended to evolve slowly. However, as the world has speeded up over the last two decades, the high levels of maintenance required to keep up with changing business needs using such an application would mean that recompilation would be almost a daily event.

Note Even today, if you need a very simple application where, for example, the client application accesses and updates information on a database, locally you need only one tier. However, as you will see, you will still probably want to use components and or layers to control its complexity.

Figure 1.1 shows how this application may look running on a single machine.

F

IGURE

1.1

Monolithic code scenario.

Monolithic Code

Presentation

Logic

Business

Logic

Data

Access Logic

Database

Consequences of Monolithic Applications

If you are writing a simple utility that does not use network connectivity, the previous scenario might suffice. However, any changes required to any part of the functionality may

03 0672323842 CH01 3/20/02 9:26 AM Page 11

The Challenge of N-Tier Development 11 potentially affect other parts. Because the Presentation, Business, and Data Access logic are located within the same piece of application code, recompilation of many parts of the code may be necessary, increasing the overhead of adding or changing functionality.

Worse still, changes in part of the code may introduce unintentional bugs in other, seemingly unrelated, parts.

Of course, updating the application involves only one machine, but the rollout of new versions of the software gets more complicated as more users install and use the application.

The Move into the Second Tier

The move towards 2-tier systems was born from the desire to share data between multiple applications installed on different machines. To do this, a separate database server machine was required. Figure 1.2 shows how this is achieved. The application now consists of presentation and business logic. Data is accessed by connecting to a database on another machine. Any changes to the Data Access logic should not affect the

Presentation or Business logic in the application.

As indicated by Figure 1.2, splitting out Data Access Logic into a second tier keeps the data access independent and can deliver a certain amount of scalability and flexibility within the system.

F

IGURE

1.2

2-tier scenario.

2 Tier

Presentation

Logic

Business

Logic

Data

Access Logic

1

Database

The advantage of having the Data Access Logic split into a separate physical environment means that not only can data be shared, but any changes to the data access logic are localized in that second tier. In fact, the whole of the second tier could be replaced with a different database and different code as long as the interface between the two tiers remained the same.

This provides an alternative way of looking at the program logic. Each part of the logic from the monolithic system could be regarded as a separate layer.

03 0672323842 CH01 3/20/02 9:26 AM Page 12

12 Day 1

The logical division into layers of functionality can be based on the different responsibilities of parts of the code, namely,

Presentation Logic—This dictates how the user interacts with the application and how information is presented.

Business Logic—This houses the core of the application, namely the rules governing the business process (or any other functionality) embedded in the application.

Data Access Logic—This governs the connection to any datasources used by the application (typically databases) and the provision of data from those datasources to the business logic.

So, we have two tiers in Figure 1.2 with two logical layers. The Presentation and

Business Logic layers are still lumped together as one piece of potentially monolithic code.

Consequences of the 2-Tier Design

One of the central problems faced by application developers using the type of architecture shown in Figure 1.2 was that the client is still full of business code and it still needs to know details about the location of its data sources. Because there is such a concentration of functionality on the client, this type of client is generally termed a thick client.

Thick clients usually need to be updated whenever the application changes.

Because the users of a thick client application have much of the application code installed on their local systems, there is a need to install fresh copies of the updated application when changes are made. This presents a serious manageability issue in terms of roll out and version control. Also, it is not always practical to use a thick client, because the application user may not want to install code on his or her machine to use a particular application. Similarly, the application provider may not want to provide code containing its business logic to relatively unknown third parties, even if it is precompiled.

Another issue with the use of thick clients relates to data access. The need to provide access to the back-end data for all clients of the application severely limits the reach and scalability of the application.

In addition to these inherent problems, many applications written with tools aimed at the two-tier environment still had all of their code in a single executable module. This increased maintenance headaches because there was a need to update the program design and implementation if any changes are required to any part of the system. With the advent of the Internet, there was a movement to separate Business logic from the user interface. Internet users, or more precisely Web users, need to access applications

03 0672323842 CH01 3/20/02 9:26 AM Page 13

The Challenge of N-Tier Development 13 without installing new code on their machines. In fact, they want to be able to use the same client application—a Web browser—to access all of the different applications they encounter on the Web. Because the application logic associated with a thick client is no longer resident on the user’s machine, this type of client is known as a thin client. The implication is that all of the “bulk” of the application has been moved into another tier.

When a Web browser is used as a thin client, the application code will be run on the Web servers with which the browser communicates (or on other machines with which the Web servers communicate). The presentation tier logic for such an application must generate

Hypertext Markup Language (HTML) rather than manipulate graphical elements on a

GUI screen.

All of this has a serious implication for 2-tier systems. If a 2-tier system is to be adapted for use on the Internet, the thick client part that contains the business logic and the presentation logic must be re-written to run on a Web server. This will then mean that there are two copies of the business logic—one housed in the original thick client and the other housed in the Web-based version of the application. This is a nightmare in maintenance terms because any changes or updates must be made in both places. More decoupling is required to improve the manageability and maintainability of the application.

The decoupling of application logic by introducing additional tiers, as started with the two-tier system shown in Figure 1.2, can be continued with the separation of the

Business and Presentation Logic. By housing the separated Business Logic in another tier, the thick client suddenly becomes thinner, as Figure 1.3 shows.

F

IGURE

1.3

3-tier scenario.

3 Tier

Presentation

Logic

Business

Logic

Data

Access Logic

1

Database

The Presentation Logic is now separated into its own layer in its own tier. This means that different types of Presentation Logic, such as HTML-based and GUI-based user interface code, can all access the same Business Logic on the middle tier.

This 3-tier model has become the de-facto architecture for Web-based business systems.

The separation into layers makes systems more flexible so that parts can be changed independently. An example of this would be creating a presentation layer specifically

03 0672323842 CH01 3/20/02 9:26 AM Page 14

14 Day 1 targeted at mobile devices. Given the separation of business and presentation functionality, this should not require any changes to the business of Data Access logic. The separation into separate physical tiers provides the opportunity to inject enhanced scalability and availability by replicating machines and software at the different tiers.

With the logic now separated into layers, it is far easier to write code that is tailored to its particular task. For example, because the Presentation logic is now housed in its own physical and logical layer, such code can be written by a developer who is skilled in this particular area. Developers who are skilled in the use of Java Web components, such as servlets (see Day 12, “Servlets”) and Java Server Pages (JSPs) (see Day 13, “JavaServer

Pages”) can write the code for this layer. These developers do not need to know about the technologies used in the business or data access code.

Complexity Simplified by Modularity

When designing a system, certain concepts will naturally sit together. By placing these into shared modules, a certain amount of separation can be achieved that is independent of the layering discussed so far. Functionality can be split into classes, and these classes can be grouped in packages or components. By reducing the dependencies between the classes and packages, this functionality can be used by different parts of the application.

By defining and maintaining interfaces between classes and packages, the actual implementation of a class can be replaced without requiring a change to other classes that depend on it. The Unified Modeling Language (UML) diagram in Figure 1.4 shows this type of decomposition.

F

IGURE

1.4

Modularity.

Component

Package

Class

State

Behaviour

Class

State

Behaviour

Class

State

Behaviour

Package

Class

State

Behaviour

Class

State

Behaviour

03 0672323842 CH01 3/20/02 9:26 AM Page 15

The Challenge of N-Tier Development 15

Object-oriented (OO) modeling promotes modularity to a large extent. Objects encapsulate their data or state and offer functionality through their interfaces. If designed correctly, the dependencies between different objects can be minimized. This reduction in dependency means that the objects are loosely coupled. Loosely coupled systems tend to be easier to maintain and evolve.

Object-oriented programming tried to improve maintainability with encapsulation and to aid system design with a definition of specific classes for specific roles, providing coherent groups of functionality. This significantly improved the previously poorly designed monolithic code and made things more maintainable and flexible. However, it was language-specific (Java, C++, and Smalltalk) and so did not make deployment or integration easier.

Although it is not the whole solution, you have some useful tools for modularizing your applications in Java:

• A Java class is a way of adding modularity by housing all state and behavior belonging to an entity into one part of the design.

• A Java package is another way of using modularity to house all classes and interfaces that belong together to perform a specific set of functions.

What you then need is a way of going beyond simple objects to provide more coarsegrained packages of functionality that can be glued together to create custom applications. To be correctly glued, these packages must conform to certain rules that are defined by a framework. This leads us to components.

Component Technology

A component is a unit of functionality that can be used within a particular framework.

Component frameworks have evolved to provide support for simplified application development. When using a component framework, a container provides the components with certain standard services, such as communication and persistence. Because standard mechanisms are used for component definition and inter-component communication, it becomes possible to write tools that examine components and display their information to an application writer. A developer can then use the tool to drag and drop these components into his or her application. This can be seen in the typical GUI interface builder environments, such as Visual Basic, or the Java equivalents, such as Borland’s JBuilder and IBM’s Visual Age for Java.

The component principle applies to non-visual components also. Whole distributed applications can be created from components. One of the benefits of distributed component frameworks is that they can provide language independence. Using CORBA, for example, components written in C can communicate with those written in OO languages such as Java and Smalltalk.

1

03 0672323842 CH01 3/20/02 9:26 AM Page 16

16 Day 1

In Java, there are several component frameworks from which to choose. The J2EE platform uses components extensively to provide modularity within the layers of an application. As such

• A Java component is yet another way of using modularity to house all packages required to perform a specific task. In the 3-tier environment, for example, the functionality of the Data Access Logic layer would be split into multiple components.

• A component will publish its interface defining the functionality it offers. This functionality can then be used by the application itself or by other components.

Benefits of Modularity

If separate parts of the design can be identified, the most appropriate developers can be tasked with the implementation simultaneously. Some components can also be purchased from third parties and integrated quite easily because all components will conform to the framework. This brings down the time to market and is, therefore, a significant cost benefit.

The system is more maintainable if identifiable parts are capable of being upgraded and re-implemented without hindering the existing running of the system. With modularity comes the possibility of loose coupling, which means the system itself is extendable without introducing dependencies. If a module has loose coupling, its maintenance is simpler.

Using components within layers allows you to further modularize the functionality in those layers.

Benefits of the 3-Tier Scenario

A modern n-tier application architecture, such as that provided by J2EE, involves the separation of functionality both by using layers and tiers and also the use of components within those layers (and objects within those components).

Now, presentational developers need not know anything of the business rules in the system, and any changes to any of the layers should not impact the effectiveness of any of the others. This aids in maintenance of the system and promotes scalability and extensibility. The separation into components helps with the division of tasks even further.

With the advent of the Internet, enabling more businesses to deliver goods and services online, it is easier to deliver functionality to customers and business users. There may be issues with particular versions of browsers, but compared to the situation where thick

03 0672323842 CH01 3/20/02 9:26 AM Page 17

The Challenge of N-Tier Development 17 client applications would need to be distributed and installed on each client machine, the relative merit of distributing functionality using the Internet remains overwhelming considering the potential user base companies are striving to reach.

It is this emphasis on the Internet that many enterprise service vendors are seeking to exploit. Most organizations have some form of Web presence and many are trying to use this to offer services to their customers. Since such services interact directly with the customer, their levels of reliability and usability must be high. Such Web-based applications are now common currency, and the world is evolving further. Web services are being discussed as the next generation of n-tier development, allowing applications to be created from components distributed across the Internet. As this model evolves, the distributed Internet becomes the computer.

Enterprise applications can be Web centric, but need not be. To cover Web-centric programming, this book shows how to integrate Servlets (see Day 12, “Servlets”) and Java

Server Pages (JSPs) (see Day 13, “JavaServer Pages”) into Enterprise applications.

Within an organization, or even when creating business-to-business (B2B) links, enterprise applications need not use Servlets or JSPs. In this case, clients may connect directly to business components, in the shape of Enterprise JavaBeans (see Day 4, “Introduction to EJBs”), over RMI or CORBA.

As the provision of functionality over the Internet gains importance, most companies will expose the functionality of their internal applications as part of a Web-based application.

As functionality is exposed, it becomes important to maintain the integrity of the data in the corporate systems. Transactions provide a common mechanism for doing this.

Transactions are covered in detail in Day 8, “Transactions and Persistence.”

A Model for Enterprise Computing

So, an n-tier, component-based, Web-friendly environment is needed, but what about the detail—what specific functionality is needed to support such applications? When considering what is needed for a distributed environment, we can turn to an organization that has been involved in this area for a long time. Since its inauguration in 1989, the OMG has been working with key industry players (such as 3Com Corporation, American

Airlines, Canon, Inc., Data General, Hewlett-Packard, Philips Telecommunications N.V.,

Sun Microsystems, and Unisys Corporation) to produce a component-based software market by hastening the introduction of standardized object software. Many of the

OMG’s specifications have become a standard part of the Distributed Object Computing landscape, such as the Common Object Request Broker Architecture (CORBA), the

Internet Inter-Orb Protocol (IIOP), and the Unified Modeling Language (UML).

1

03 0672323842 CH01 3/20/02 9:26 AM Page 18

18 Day 1

By examining some of the key requirements outlined in the OMG’s Enterprise

Computing Model, it is possible to explore what is required from a modern distributed computing environment.

Lifecycle

There must be a safe mechanism for creating, copying, moving, and deleting distributed objects. A distributed component environment must provide containers to manage the lifetime of components and assist in their deployment. There are also other lifecycle issues that must be addressed in a distributed environment. For example, distributed garbage collection (getting rid of unused objects) can be handled in different ways according to the operating environment. With Java’s Remote Method Invocation (RMI), a distributed leasing mechanism is used. With CORBA, there are lifecycle management services. Microsoft’s Distributed COM (DCOM), on the other hand, relies on objects controlling their own lifetimes.

Persistence

In an enterprise application, you need to be able to store data permanently for later retrieval. Object Database Management Systems (ODBMS) and Relational Database

Management Systems (RDBMS) commonly support this requirement. A distributed application environment must provide a way of accessing and updating persistent data in a simple yet flexible way. It is also important to support different types of data persistence (different databases, legacy systems, and so on) and different ways of accessing this data (locally or across a network). Any help that can be given to the developer for data persistence is generally very welcome.

Naming

Distributed applications will be formed from components that reside on different machines. The parts of the application that use components on other machines must be able to locate and invoke such components. What is needed is a directory service in which components or services can register themselves. Any part of the application that wants to use such a service can look up the location of the service and retrieve information about how to contact it.

Common directory services and protocols include the following:

CORBA Common Object Services (COS) Naming Service—This allows you to store object references in a namespace. The COS naming service is widely used in

Java-based distributed environments as a way of storing information about the location of remote objects. Further information on COS Naming can be found online at http://www.omg.org

.

03 0672323842 CH01 3/20/02 9:26 AM Page 19

The Challenge of N-Tier Development 19

X.500—This defines an information model for storing hierarchical information and an access protocol called the Directory Access Protocol (DAP). Further information about X.500 can be found online at http://java.sun.com/products/ jndi/tutorial/ldap/models/x500.html

.

Lightweight Directory Access Protocol (LDAP)—This is a lightweight version of the X.500 protocol that runs over TCP/IP. Further information on LDAP can be found online at http://www.openldap.org

.

Domain Name System—This is an Internet protocol that allows translation between host names and Internet addresses. DNS is used by all Internet clients, such as Web browsers. More information on DNS can be found at http://www.dns.net/dnsrd/rfc/

.

Microsoft Active Directory—The Active Directory service allows organizations to store central information on data and services within an enterprise. Further information on Active Directory can be found online at http://www.microsoft.com/ windows2000/technologies/directory/default.asp

.

Transaction

In a distributed enterprise application, certain business processes will involve multiple steps. For example, a typical exchange of goods or services for payment will need to take payment details, verify those payment details, allocate the goods to be shipped, arrange the shipping, and take the payment. At any stage, the customer might be interrupted or the server could crash, not completing the entire transaction. If that happens, the enterprise application must be able to retrieve the previous state to continue with the transaction at a later time or to roll back the transaction so that the system is restored to its original state.

Transaction services provide a way of grouping updates to data so that either all of the updates are performed or none of them are performed. A transaction coordinator will be responsible for ensuring this. Transaction information is persisted so that the state of a transaction can survive a system crash. Transactions can be propagated across distributed method calls and even across message-based systems.

Security

A secure enterprise application environment will provide the following:

Authentication—Are you who you say you are?

Authorization—Are you permitted to do things you are requesting to do?

In addition to this, many enterprise application environments will support both programmatic and declarative security. Programmatic security is enforced within the enterprise application itself, while declarative security is enforced by the enterprise application

1

03 0672323842 CH01 3/20/02 9:26 AM Page 20

20 Day 1 environment within which the application runs. The enterprise application environment will consult configuration information to decide which security restrictions to enforce for a particular application. Changes to this information would not necessitate recompilation of the application itself.

Java 2 Enterprise Edition (J2EE)

J2EE is an on-going standard for producing secure, scalable, and highly-available enterprise applications. The standard defines which services should be provided by servers that support J2EE. These servers will provide J2EE containers in which J2EE components will run. The containers will provide a defined set of services to the components.

The J2EE specification provides a definition from which enterprise vendors can produce

J2EE application servers on which J2EE-compliant applications can be deployed. An impressive list of expert group members produced the latest version of the associated

Java Specification Request (JSR 58 which contains the standard definition for J2EE version 1.3), which can be found online at http://java.sun.com/j2ee/sdk 1.3/ index.html

.

Although the J2EE specification defines a set of services and component types, it does not contain information on how to arrange the logical architecture into physical machines, environments, or address spaces.

The J2EE platform provides a common environment for building secure, scalable, and platform-independent enterprise applications. Many businesses are now delivering goods and services to customers via the Internet by using such J2EE-based servers. The requirements of such an environment demand open standards on which to build applications, for example,

• Java 2 Platform, Standard Edition (J2SE), a platform independent language

• Components that deliver Web-based user interfaces

• Components to encapsulate business processes

• Access to data in corporate data stores

• Connectivity to other data sources and legacy systems

• Support for XML, the language of B2B e-commerce

Components and Containers

J2EE specifies that a compliant J2EE application server must provide a defined set of containers to house J2EE components. Containers supply a runtime environment for the components. As such, Java 2 Platform, Standard Edition (J2SE) is available in each container. Application Programming Interfaces (APIs) in J2EE are also made available to

03 0672323842 CH01 3/20/02 9:26 AM Page 21

The Challenge of N-Tier Development 21 provide communication between components, persistence, service discovery, and so on.

Containers are implemented by J2EE application server vendors and there should be a container available for each type of J2EE component:

• Applet Container

• Application Client Container

• Web Container

• EJB Container

There are two types of components deployed, managed, and executed on a J2EE Server:

Web components—A Web component interacts with a Web-based client, such as a

Web browser. There are two kinds of Web components in J2EE—Servlet

Component and Java Server Pages (JSP) Component. Both types handle the presentation of data to the user. Please see Days 12 and 13 for further details.

EJB components—There are three kinds of Enterprise JavaBean components—

Session beans, Entity beans, and Message-Driven Beans. Please see Day 4, Day 5,

“Session EJBs,” Day 6, “Entity EJBs,” and Day 10, respectively, for further information.

Figure 1.5 shows the overall relationships between the different containers and components in the J2EE environment.

F

IGURE

1.5

J2EE logical architecture.

Browser

J2EE Server

Web Container

Servlet JSP Page

Database

Application Client

Container

Application

Client

EJB Container

Enterprise

Bean

Enterprise

Bean

1

J2EE Standard Services

Containers must provide each type of component with a defined set of services that are covered in detail as you progress through the book. Briefly these services consist of

03 0672323842 CH01 3/20/02 9:26 AM Page 22

22 Day 1

Connectivity—Containers must support connectivity to other components and to application clients. One form of required connectivity is to distributed objects through both Java Remote Method Invocation (RMI) and CORBA (as implemented by the Java IDL package and RMI over IIOP). Internet connectivity must be provided both through the Hypertext Transport Protocol (HTTP) and its secure form

(HTTPS).

Directory services—J2EE servers are required to provide naming services in which components can be registered and discovered. The Java Naming and Directory

Interfaces (JNDI) provide a way of accessing these services.

Data access and persistence—Data access is provided through the Java Database

Connection API (JDBC). This API works both at the application level to interface with databases and also service providers who build drivers for specific databases.

Legacy connectivity—The Java Connector Architecture (JCA or Connectors) provides J2EE support in integrating Enterprise Information Servers and legacy systems, such as mainframe transaction processing and Enterprise Resource Planning

(ERP) systems. This support extends to J2EE service providers who are writing adapters to connect other systems to the J2EE enterprise architecture.

Security—Security is built into the J2EE model. APIs, such as the Java

Authentication and Authorization Service (JAAS), assist the J2EE enterprise application in imposing authentication and authorization security checks on users.

XML Support—The JAXP API supports the parsing of XML documents using

Document Object Model (DOM), SimpleAPI for XML documents (SAX), and the eXtensible Stylesheet Language Transformations (XSLT).

Transactions—A J2EE server must provide transaction services for its components.

The boundaries of transactions need to be specified by the container or the application. The container will usually take responsibility for transaction demarcation, although the Java Transaction API (JTA) allows the component to control its own transactions if required.

Messaging and e-mail—The Java Message Service (JMS) allows components to send and receive asynchronous messages, typically within an organizational boundary. The JavaMail API enables Internet mail to be sent by components and also provides functionality to retrieve e-mail from mailstores. JavaMail uses the

JavaBeans Activation Framework (JAF) to support various MIME types.

Figure 1.6 shows the J2EE architecture updated with the services available to its containers. All of these services are discussed in more detail tomorrow.

Every J2EE-compliant server must support the services defined in this section. To provide a concrete example of how services should work, the team working on the J2EE

03 0672323842 CH01 3/20/02 9:26 AM Page 23

The Challenge of N-Tier Development 23

JSR is responsible for providing a Reference Implementation (RI) of the J2EE APIs. This

RI is freely available from Sun and provides a convenient platform for prototyping applications and testing technologies.

F

IGURE

1.6

The J2EE platform with services available.

Applet

Container

Applet

Client

HTTP

J2EE Server

Web Container

Servlet JSP Page

1

J2SE

Applet

Container

Application

Client

HTTP

EJB Container

Enterprise

Bean

Enterprise

Bean

Database

J2SE

J2EE Blueprints

The J2EE Blueprints are a set of best practices that show how best to implement J2EE applications. The Blueprints provide a concrete implementation of Sun’s vision for 3-tier,

J2EE-based systems. There is a download available from the Sun Web site called Java

Pet Store Sample Application that shows these best practices. This can be found online at http://java.sun.com/j2ee/blueprints

.

The Java Pet Store is a typical online e-commerce application. The best practices cover application design and, in particular, the promotion of the following:

• Code reuse

• Logical functional partitioning

• The separation of areas of high maintenance

• Extensibility

• Modularity

• Security

• Simple and consistent user interface

03 0672323842 CH01 3/20/02 9:26 AM Page 24

24 Day 1

• Efficient network usage

• Data integrity

The J2EE Blueprints will show you step-by-step how to design multi-tier enterprise applications. There are explorations on the following topic areas:

• The Client Tier

• The Web Tier

• The Enterprise JavaBeans Tier

• The Enterprise Information Systems Tier

• Design Patterns

In addition, there are discussions on how to package and deploy your enterprise applications.

J2EE Compatibility Test Suite

Enterprise service providers will sell more if their enterprise servers meet with the J2EE specification requirements. To enable them to test their products against the specification,

Sun Microsystems Inc. offers a testing environment. Servers that pass all of the tests can be certified as J2EE compliant. Further details of the compatibility suite can be found online at http://java.sun.com/j2ee/compatibility.html

.

The following are some examples of application vendors and their servers that have been certified by Sun Microsystems as being compatible with the J2EE specification (some of these may not yet have attained J2EE 1.3 certification, so please check on the Sun Web site).

• Allaire ( www.macromedia.com/software/coldfusion

)—ColdFusion 5 comes with its own markup language (ColdFusion Markup Language) that integrates with all popular Web languages and technologies. ColdFusion works with multi-tier architectures through COM, CORBA, and EJB integration.

• BEA Systems ( www.bea.com

)—BEA Weblogic Server includes support for Web

Services, J2EE Connector Architecture, and updated J2EE services, with EJB 2.0,

Servlet 2.3, and JSP 1.2.

• IBM ( www.ibm.com

)—Websphere Commerce Business Edition supports EJB, JSP,

XML, HTTP, and wireless markup language technologies.

• iPlanet ( www.iplanet.com

)—iPlanet Application Server Enterprise Edition supports the J2EE platform and is integrated with transaction monitor, iPlanet Web

Server, and iPlanet Directory Server. It supports XML, wireless application protocols, Simple Network Management Protocol (SNMP), LDAP, CORBA, and JDBC.

03 0672323842 CH01 3/20/02 9:26 AM Page 25

The Challenge of N-Tier Development 25

• JBoss ( www.jboss.org

)—JBoss is a freeware server that houses an implementation of the EJB 1.1 (and parts of 2.0) specification. It is similar to Sun’s J2EE

Reference Implementation, but the JBoss core server provides only an EJB server.

JBoss does not include a Web container, but JBoss is available to download with a freeware Web server.

• Persistence ( www.persistence.com

)—PowerTier Release 7 for J2EE supports Java,

EJB deployment, and Rational Rose integration.

The Future of J2EE

Probably the major area for the future of J2EE is that of Web Services. There are a number of JSRs active at the time of this writing on the following topic areas:

JSR 67 Java APIs for XML Messaging (JAXM) 1.0—Message-based communication between Web Services and Web Service clients. Please refer to Day 21, “Web

Service Registries and Message-style Web Services,” for further details.

JSR 93 Java APIs for XML Registries 1.0 (JAXR)—Registry and naming service access for Web Services. Please refer to Day 21 for further details.

JSR 101 Java APIs for XML-RPC (JAX-RPC)—RPC-style interaction with Web

Services. Please refer to Day 20, “Using RPC-Style Web Services with J2EE,” for further details.

JSR 109 Implementing Enterprise Web Services—A model of how Web Services should work within J2EE.

These JSRs can be found through the Java Community Process (JCP) Web site at http://www.jcp.org

.

Summary

Enterprise application development has helped businesses provide Web-enabled, scalable, secure applications quickly. It has also enabled vendors to produce pluggable tools and services to augment the J2EE standard defined through the Java Community Process.

This chapter describes the journey towards the n-tier environment that underpins the architecture of enterprise application programming. You have investigated the basic services that should be available to an n-tier enterprise application, and examined a few of the enterprise application servers on the market.

Q&A

Q I have a monolithic program that I would like transition into an n-tier application. How do I do this?

1

03 0672323842 CH01 3/20/02 9:26 AM Page 26

26 Day 1

A First you need to identify what sort of target architecture is required. If your application is to be Web-enabled, you will need to provide Web-oriented functionality in the presentation layer. If you are working with persistent data, you will need data access through a data access layer. You should map out your target architecture based on the services available under the J2EE platform.

Next, you will need to sift through the monolithic code separating out the code belonging to the logical layers. This code might need to be rewritten in such a way as to make it maintainable and extensible. Introduce modularity by adopting object-oriented programming and design classes. Package these classes and design components to have maximum cohesion and loose coupling wherever possible.

To implement and deploy your J2EE application, read the rest of the book and follow the examples.

Q What is the difference between Microsoft’s .NET framework and J2EE?

A You can build enterprise applications with both platforms. Both J2EE and .NET

framework applications can provide good levels of scalability, availability and so forth. The essential difference is largely one of choice. J2EE lets you use any operating system, such as Windows, UNIX, or a mainframe. J2EE’s development environment can be chosen to suit developers from a variety of Integrated Development

Environment (IDE) and J2EE application server vendors. The .NET framework is essentially limited to the Windows family of operating systems. This allows it to be more cleanly integrated with the operating system, but reduces the choice of target platform.

Exercises

To extend your knowledge of n-tier development, try the following exercises:

1. Write a design for a monolithic application to provide a shopkeeper with data concerning stock information.

2. Redesign the application based on the contents of this chapter, so as to make it accessible over the Internet.

3. Visit http://java.sun.com/j2ee for further details of the J2EE programming tools and utilities.

4. Visit http://www.microsoft.com

for further details of the .NET framework.

Compare and contrast this with the facilities available under J2EE.

04 0672323842 CH02 3/20/02 9:37 AM Page 27

W

EEK

1

D

AY

2

The J2EE Platform and

Roles

Yesterday, you learned about enterprise computing and some of the problems facing developers of enterprise solutions. The day also introduced J2EE, a technology that can help you develop secure, scalable, and platform-independent solutions that meet the needs of today’s business.

Today, you will explore the J2EE platform and see what it can offer you to help solve your business problems. J2EE is a large framework that boasts of a widerange of components, services, and roles. It is these that you explore today, so that you’ll be eager and prepared to start writing code tomorrow. The following are the major topics today covers:

• Understanding how J2EE delivers solutions for today’s business

• Introducing the available Web-centric components

• Introducing the use of Enterprise JavaBeans

• Assessing platform roles

• Exploring the packaging and deployment of enterprise applications

04 0672323842 CH02 3/20/02 9:37 AM Page 28

28 Day 2

Revisiting the J2EE Platform

You learned a lot about enterprise computing yesterday. You learned specifically about how business needs force the evolution of application architectures; today, most applications are distributed across multiple machines. This approach, the n-tier model, gives rise to different ways of writing and structuring applications. Units of functionality— components—provide modularity that allow multiple developers to work more easily on different parts of the application. Use of a component framework also allows developers to apply third-party components to speed development. These loosely-coupled components may run as an application on a desktop client, within a Web server, or even on a server that connects to a legacy system. In addition, data has undergone a revolution.

Data sources now go beyond simple, relational databases containing tables to encompass databases that contain serialized objects or plain text files containing XML. Alternatively, data may take the form of user information in an LDAP directory or information in an

Enterprise Resource Planning (ERP) system.

Applications written in traditional programming languages that do not have supporting frameworks simply cannot perform the operations required by today’s environment.

Instead, you must employ component-aware programming languages together with frameworks dedicated to enterprise computing. As you have already seen, J2EE is such a framework. Although the environment within which J2EE operates might sound daunting, J2EE isn’t. When you write J2EE applications, you still write Java code, and you still get to use the J2SE classes with which you are familiar.

To successfully use J2EE, you must

• Install and configure your J2EE environment

• Understand J2EE roles

• Appreciate the purpose of containers

• Understand how you can use J2EE components

• Understand the services that containers supply to components

• Learn or explore a new set of APIs

Yesterday’s lesson introduced the first four points in the list. You will explore them in more depth today. After you understand these, you will be ready for tomorrow, when you will start to apply the new APIs and to code real applications against them.

Using Sun Microsystems’ J2EE SDK

Before you can start coding real J2EE applications, you need a J2EE implementation and a Java development environment, such as Sun Microsystems JDK or a Java Integrated

04 0672323842 CH02 3/20/02 9:37 AM Page 29

The J2EE Platform and Roles 29

Development Environment (IDE). This book uses the Sun Microsystems’ J2EE SDK, which is a complete reference implementation of J2EE. It includes all the classes, containers, and tools you need to learn J2EE.

To run the example code provided on the CD-ROM accompanying this book, you will also need to install a sample database. Installing the sample database is described in the

Exercise at the end of this day’s lesson. But now, to give you some hands-on work before you study the theory behind J2EE, you will install the J2EE SDK 1.3 on your workstation.

Note The J2EE SDK is free to download, use for learning J2EE, and use as a development tool. The Sun Microsystems license for this product states that you may not use it in a production environment. Be warned!

Installing J2EE SDK 1.3

Before you download the SDK, ensure that you have J2SE 1.3.1_01 (also known as JDK

1.3.1) or later correctly installed and are using one of the following supported platforms:

• Windows NT4 or 2000

• Solaris SPARC 7 or 8

• Linux Redhat v 6.0 or 6.1

You can use a Java IDE that supports J2SE 1.3 (or later) in preference to the Sun

Microsystem’s J2SE JDK 1.3.1.

Before installing J2EE SDK 1.3, you must uninstall any previous versions of the J2EE

SDK.

Finally, you must ensure that you have a

JAVA_HOME environment variable that points to the location of the directory where you installed J2SE SDK (or your preferred Java IDE).

This should have been defined when you (or your administrator) installed the J2SE SDK

(JDK). If the

JAVA_HOME variable is not defined, you must define it now as follows.

If you are using Windows NT or 2000 (remember J2EE SDK is not supported on other

Windows’ platforms) you should set the

JAVA_HOME variable in your system environment so that it is defined for all of the programs you run. Do this using the Control Panel as follows:

1. Within the Control Panel, select System.

2. The System Properties dialog appears, select the Advanced tab.

3. Click Environment Variables.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 30

30 Day 2

4. The Environment Variables dialog appears, click New.

5. The New System Variable dialog appears, enter the name and value of the variable.

Assuming that you installed the J2EE SDK 1.3 on the C: drive using the default directory name, you will set the

JAVA_HOME variable to

C:\j2sdkee1.3

6. Click OK to clear each of the dialogs.

You must have administrator privileges to edit or change system environment variables. If you do not have administrator privilege, you can still use the JDK, but you’ll have to define the variables in your user environment. Any other users of your workstation will also have to define the same variables in their environment.

If you are using Linux or Unix and the

JAVA_HOME environment variable does not exist, you can set it with the following command(you must be using the Bourne, Korn, Bash or compatible shell):

JAVA_HOME=/usr/local/jdk1.3.1

export JAVA_HOME

This example assumes you have installed the Sun Microsystems’ JDK 1.3.1 in

/usr/local

.

Typically, you will add these variable definitions to your login environment by adding the same two lines to the

.profile

file in your home directory.

Finally, you should ensure that the JDK bin directory is in your search path (again this should already be configured on your workstation).

For Windows users, if your search path does not contain the JDK bin directory, add the following directory to your

PATH via the Control Panel:

%JAVA_HOME%\bin

For Linux/Unix users, if your search path does not contain the JDK bin directory, add the following line to your

.profile

file:

PATH=$PATH:$JAVA_HOME/bin

Now download the J2EE SDK in the format appropriate to your system from http://java.sun.com/j2ee/sdk_1.3/index.html

. You should download the J2EE SDK to a temporary directory because the installation process will ask you where to install the

SDK.

The installation of the SDK is quite straightforward; just follow the instructions for your platform:

04 0672323842 CH02 3/20/02 9:37 AM Page 31

The J2EE Platform and Roles 31

Windows—Double-click the icon of the j2sdkee-1_3_01-win.exe

file and follow the onscreen instructions.

Solaris—Issue the following commands to make the download bundle executable and run the installation:: chmod a+x ./j2sdkee-1_3__01-solsparc.sh

./j2sdkee-1_3__01-solsparc.sh

Linux—Change directories to the required parent directory for the J2EE SDK (for example,

/usr/local

) and extract the download bundle using the following command: tar – xzvf <download_directory>/j2sdkee-1_3_01-linux.tar.gz

Now you must:

1. Define the

J2EE_HOME variable.

2. Add the J2EE SDK bin directory to your search path.

3. Add the J2EE classes to your

CLASSPATH

.

You have already been shown how to define variables and change your path for your environment (Windows users use the Control Panel and Linux/Unix users add variable definition lines to

.profile

), so making these changes will be straightforward.

Windows users must (assuming the J2EE SDK was installed on the

C: drive)

1. Define the following environment variable

J2EE_HOME=c:\j2eesdk1.3

2. Add the following directory to the end of the

PATH variable:

%J2EE_HOME%\bin

3. Add the J2EE JAR files to the

CLASSPATH variable:

%J2EE_HOME%\lib\j2ee.jar;%J2EE_HOME%\lib\locale

If your

CLASSPATH variable is not currently defined, you must ensure that it includes the current directory so the full setting will be

.;%J2EE_HOME%\lib\j2ee.jar;%J2EE_HOME%\lib\locale

Linux/Unix users must add the following to their

.profile

(assuming the J2EE SDK was installed in

/usr/local/j2eesdk1.3

):

1. Define the following environment variable:

J2EE_HOME=/usr/local/j2eesdk1.3

2. Update the

PATH variable for J2EE SDK:

PATH=$PATH:$J2EE_HOME/bin

2

04 0672323842 CH02 3/20/02 9:37 AM Page 32

32 Day 2

3. Add the J2EE JAR files to the

CLASSPATH variable:

CLASSPATH=$CLASSPATH:$J2EE_HOME/lib/j2ee.jar:$J2EE_HOME/lib/locale

If your

CLASSPATH variable is not currently defined, you must ensure that it includes the current directory so the full setting will be:

CLASSPATH=.:$J2EE_HOME/lib/j2ee.jar:$J2EE_HOME/lib/locale

That is it! You are now ready to start using the J2EE SDK.

Note

All the documentation for the J2EE utility programs and class files is contained in the J2EE SDK download bundle. You will find the documentation in the docs sub-directory of the J2EE installation directory.

Starting the J2EE Reference Implementation (RI)

The J2EE SDK includes a Reference Implementation of J2EE. This Reference

Implementation (RI) contains the following software programs:

• A J2EE server

• A relational database called Cloudscape

• An HTTP (Web) server

• A JNDI service implementation

• A JMS service implementation

• Class files for the J2EE APIs

• Various administration and support utilities

The server software components of the RI (database, JNDI, Web server, and J2EE server) are purely for development and are not designed for commercial use. The J2EE RI has been used for the code shown in this book because it is free of charge and conforms to the J2EE 1.3 specification.

To develop J2EE applications and run the example code presented in this book, you will need to start the J2EE server and the Cloudscape database server. Starting the J2EE server will also start the JNDI, JMS, and HTTP servers provided with the J2EE RI.

There are no graphic tools for managing the J2EE and Cloudscape servers, so you will have to start them from the command line. Each server should be started in a separate command window or Telnet window if you are not using a graphical console. It does not matter in which order you start the J2EE and Cloudscape servers.

04 0672323842 CH02 3/20/02 9:37 AM Page 33

The J2EE Platform and Roles 33

In the following examples, you must have configured your search path (the

PATH environment variable) to include the J2EE SDK bin directory as shown previously.

To start the J2EE RI server, create a new window with access to a command-line prompt

(command window for Windows NT/2000 or a terminal or shell window for Linux/Unix users). Enter the following command at the prompt: j2ee –verbose

This will start the J2EE server in the current window with diagnostic messages displayed in the window. If (or when) you have problems deploying your J2EE applications to the server, it is this window you should examine for error messages. If you put simple diagnostic messages in your EJBs that write to

System.out

or

System.err

, these messages will also appear in this window.

Listing 2.1 shows the diagnostic messages issued as the J2EE RI and associated servers startup.

L

ISTING

2.1

Successful J2EE Reference Implementation Startup Diagnostics

1: > j2ee -verbose

2: J2EE server listen port: 1050

3: Naming service started:1050

4: Binding DataSource, name = jdbc/DB2,

➥url = jdbc:cloudscape:rmi:CloudscapeDB;create=true

5: Binding DataSource, name = jdbc/DB1,

➥url = jdbc:cloudscape:rmi:CloudscapeDB;create=true

6: Binding DataSource, name = jdbc/InventoryDB,

➥url = jdbc:cloudscape:rmi:CloudscapeDB;create=true

7: Binding DataSource, name = jdbc/Cloudscape,

➥url = jdbc:cloudscape:rmi:CloudscapeDB;create=true

8: Binding DataSource, name = jdbc/EstoreDB,

➥url = jdbc:cloudscape:rmi:CloudscapeDB;create=true

9: Binding DataSource, name = jdbc/XACloudscape, url = jdbc/XACloudscape__xa

10: Binding DataSource, name = jdbc/XACloudscape__xa, dataSource = [email protected]

11: Starting JMS service...

12: Initialization complete - waiting for client requests

13: Binding: < JMS Destination : jms/Queue , javax.jms.Queue >

14: Binding: < JMS Destination : jms/firstQueue , javax.jms.Queue >

15: Binding: < JMS Destination : jms/Topic , javax.jms.Topic >

16: Binding: < JMS Cnx Factory :

➥QueueConnectionFactory , Queue , No properties >

17: Binding: < JMS Cnx Factory :

➥jms/QueueConnectionFactory , Queue , No properties >

18: Binding: < JMS Cnx Factory :

➥TopicConnectionFactory , Topic , No properties >

19: Binding: < JMS Cnx Factory :

2

04 0672323842 CH02 3/20/02 9:37 AM Page 34

34 Day 2

L

ISTING

2.1

Continued

➥jms/TopicConnectionFactory , Topic , No properties >

20: Starting web service at port: 8000

21: Starting secure web service at port: 7000

22: J2EE SDK/1.3

23: Starting web service at port: 9191

24: J2EE SDK/1.3

25: J2EE server startup complete.

If you start the J2EE server without the

-verbose option, all the diagnostic messages will be written to log files in the logs sub-directory of the J2EE SDK installation directory.

You will find additional logging information is also written to these log files even with the

-verbose option specified.

The J2EE log files are stored in a sub-directory named after your workstation in the logs sub-directory of the J2EE SDK installation directory. Further sub-directories are used to separate the log files for the J2EE, JMS, and HTTP servers.

To start up the Cloudscape database server, you must open a new window and enter the following command: cloudscape –start

Again, some simple diagnostic messages will be displayed as the server starts up, as shown in Listing 2.2.

L

ISTING

2.2

Successful Cloudscape Startup Diagnostics

> cloudscape -start

Thu Jan 10 10:52:18 GMT+00:00 2002:

➥ [RmiJdbc] Starting Cloudscape RmiJdbc Server

Version 1.7.2 ...

Thu Jan 10 10:52:25 GMT+00:00 2002:

➥ [RmiJdbc] COM.cloudscape.core.JDBCDriver registered in DriverManager

Thu Jan 10 10:52:25 GMT+00:00 2002: [RmiJdbc] Binding RmiJdbcServer

Thu Jan 10 10:52:25 GMT+00:00 2002:

➥ [RmiJdbc] No installation of RMI Security Manager

Thu Jan 10 10:52:26 GMT+00:00 2002:

➥ [RmiJdbc] RmiJdbcServer bound in rmi registry

Troubleshooting J2EE and Cloudscape

You should have no problems starting up either server. If you do have problems, check the error messages displayed in the relevant window. The most likely problems are discussed in the rest of this section.

04 0672323842 CH02 3/20/02 9:37 AM Page 35

The J2EE Platform and Roles 35

Read Only Installation Directory

You will not be able to run J2EE RI and Cloudscape unless you have installed the J2EE

SDK in a writeable directory. If you have installed the J2EE SDK as a privileged user

(Administrator, root, or whomever), make sure that you grant your normal login account read and write permission to the installation directory and all contained files and directories.

Server Port Conflicts

Although the J2EE SDK software uses TCP port numbers that are not normally used by other software, there is always a possibility that there will be a port number conflict.

If a port is used by another software server, a J2EE component will fail to start up and you will see an error message stating that the server “Could not connect to a required port.” The error will normally include a stack trace.

The most likely cause of a port conflict is where you (or another developer) have already started the J2EE server. Listing 2.3 shows the error message for this situation.

L

ISTING

2.3

Error Message Caused by Running the J2EE Server Twice

> j2ee -verbose

J2EE server listen port: 1050 org.omg.CORBA.INTERNAL: minor code: 1398079697 completed: No at com.sun.corba.ee.internal.iiop.

➥GIOPImpl.createListener(GIOPImpl.java:256) at com.sun.corba.ee.internal.iiop.

➥GIOPImpl.getEndpoint(GIOPImpl.java:205) at com.sun.corba.ee.internal.iiop.

➥GIOPImpl.initEndpoints(GIOPImpl.java:140) at com.sun.corba.ee.internal.POA.POAORB.

➥getServerEndpoint(POAORB.java:488) at com.sun.corba.ee.internal.POA.POAImpl.

➥pre_initialize(POAImpl.java:154) at com.sun.corba.ee.internal.POA.POAImpl.<init>(POAImpl.java:112) at com.sun.corba.ee.internal.POA.POAORB.makeRootPOA(POAORB.java:110) at com.sun.corba.ee.internal.POA.POAORB$1.evaluate(POAORB.java:128) at com.sun.corba.ee.internal.core.Future.evaluate(Future.java:21) at com.sun.corba.ee.internal.corba.ORB.

➥resolveInitialReference(ORB.java:2421) at com.sun.corba.ee.internal.corba.ORB.

➥resolve_initial_references(ORB.java:2356) at com.sun.enterprise.server.J2EEServer.run(J2EEServer.java:193) at com.sun.enterprise.server.J2EEServer.main(J2EEServer.java:913) java.lang.RuntimeException: Unable to create ORB.

➥ Possible causes include TCP/IP ports in use by another process

2

04 0672323842 CH02 3/20/02 9:37 AM Page 36

36 Day 2

L

ISTING

2.3

Continued at com.sun.enterprise.server.J2EEServer.run(J2EEServer.java:203) at com.sun.enterprise.server.J2EEServer.main(J2EEServer.java:913) java.lang.RuntimeException: Unable to create ORB.

➥Possible causes include TCP/IP ports in use by another process at com.sun.enterprise.server.J2EEServer.run(J2EEServer.java:203) at com.sun.enterprise.server.J2EEServer.main(J2EEServer.java:913) java.lang.RuntimeException: Unable to create ORB.

➥Possible causes include TCP/IP ports in use by another process at com.sun.enterprise.server.J2EEServer.run(J2EEServer.java:350) at com.sun.enterprise.server.J2EEServer.main(J2EEServer.java:913)

J2EE server reported the following error: Unable to create ORB.

➥Possible causes include TCP/IP ports in use by another process

Error executing J2EE server ...

You cannot run more than one J2EE RI server on the same workstation.

If your port conflict is with another piece of software, try to disable this software when running J2EE RI and Cloudscape. If this is not possible, you can change the port numbers used by each J2EE server by editing the file called server.xml

in the conf subdirectory of the J2EE SDK installation directory. The definitions of the default server port numbers are obvious if you are used to reading and editing XML files. Changing the

J2EE RI port numbers is something you should avoid if at all possible.

Applications Failing with a “

Connection Refused: no further information

” Exception

A common error when working with the J2EE RI is to forget to start up the Cloudscape database server in a separate window. If you fail to start the database and use a J2EE component, such as an EJB, that accesses the database, you will get the following error: java.sql.SQLException: Connection refused to host:

<hostname>; nested exception is: java.net.ConnectException: Connection Refused: no further information

To solve this problem, start up the Cloudscape server as described previously and stick a note to your monitor to remind you to start the database as well as J2EE. If you are confident with batch or shell scripts, you can write your own scripts to start both servers in separate windows.

The following simple scripts will work for the indicated platforms:

On Windows NT/2000, use start “J2EE” j2ee –verbose start “Cloudscape” cloudscape –start

04 0672323842 CH02 3/20/02 9:37 AM Page 37

The J2EE Platform and Roles 37

On Solaris, use dtterm –name j2ee –e “j2ee –verbose” & dtterm –name cloudscape –e “cloudscape –start” &

On Linux, use xterm –title j2ee –e “j2ee –verbose” & xterm –title cloudscape –e “cloudscape –start” &

Closing Down J2EE RI and Cloudscape

To close down J2EE RI and Cloudscape, you should use the following commands: j2ee –stop cloudscape –stop

These commands can be run from any command window. Remember that the J2EE and

Cloudscape server windows are busy and cannot accept additional commands while the servers are running.

Although Sun Microsystems do not recommend this approach, typing ^C (Ctrl+C) in the server window or simply closing the window will also shut down the servers.

Optional Software Used in this Book

A Java IDE (JDK) and a J2EE implementation (J2EE SDK) are all you require to learn how to develop J2EE applications. However, areas of this book look at using J2EE applications in a wider context and make use of additional (freely available) software.

So that you are aware of this software, Table 2.1 lists the optional software used in this book. Full instructions for downloading and configuring this software (should you want to do so) are included in the relevant day’s instructions. You do not need to download this software at the present time.

T

ABLE

2.1

Optional Software Used in Daily Lessons

Day Software Resource URL

3 OpenLDAP and a Unix system to run the Open

LDAP server.

http://www.openldap.org/ software/download/

14

17

JSPTL Java Standard Tag libraries from the

Apache Jakarta project.

XALAN from the Apache project.

http://jakarta.apache.

org/taglibs/index.html

http://xml.apache.org/ xalan-j/index.html

2

04 0672323842 CH02 3/20/02 9:37 AM Page 38

38 Day 2

T

ABLE

2.1

Continued

Day

20

Software

Apache Axis alpha2.

Apache Tomcat 4.0.1.

JAXM 1.0 reference implementation (part of the “JAX Pack Fall 01”).

Resource URL

http://xml.apache.org/ axis/index.html

http://jakarta.apache.org/ tomcat/index.html

http://java.sun.com/xml

Understanding Tiers and Components

The 3-tier model splits an application down so that business logic resides in the middle of the 3 tiers. This is often called the middle tier, business tier, or EJB tier. Throughout this section, this tier will be referred to as the business tier. The first tier has the role of providing the interface between the user and the application. Depending on your specific architecture, this can be known as the client tier or presentation tier. Throughout this section, the term presentation tier will be used to define this client-centric tier.

Most of the code written by J2EE application developers resides in the presentation and business tiers. The next two sections explore these tiers. You will see that different types of components are used in each tier to deliver particular application functionality. The components in different tiers should be loosely coupled. In other words, a component should not have dependencies on the client or other components. For example, imagine a business component that processes credit card payments. If the component is self contained, almost any application can pass it payment information and it, in turn, can return an appropriate response. Figure 2.1 shows such a business component communicating with a variety of clients:

As you can see, because this business component encapsulates all the payment process functionality, it is not tied to any component in the presentation tier, so it can serve multiple client types. Code that supports more than one type of client is only one advantage of component architecture; you will learn about some of the other advantages later in this chapter.

04 0672323842 CH02 3/20/02 9:37 AM Page 39

The J2EE Platform and Roles 39

F

IGURE

2.1

Multiple clients accessing the services of a business component.

Wireless

Client

Web

Browser

Desktop

Application

J2EE Server

JSP Container

JSP

Presentation

Tier

EJB Container

Enterprise

Bean

Containing

Payment

Process

Functionality

Business

Tier

The Business Tier

As you have just seen, business components sit in the business tier of a J2EE application.

These business components encapsulate business logic and are used by components in the presentation tier that deliver this functionality to users of the application.

Benefits of Business Components

Previously, you saw a simple credit card processing component that provided that service for a number of different clients. This demonstrated just one benefit of component architecture but, in fact, components offer many advantages over a composite architecture:

Increased efficiency—The division of labor helps a business to roll out applications quickly. The use of components allows presentation developers to develop GUIs, process programmers to focus on business logic, and data access experts to focus on data access.

Extensibility—You can simply add or remove components to an application so that it can offer further functionality, for example, if you want to expose application functionality through a Web Service. The component architecture allows you to simply add the additional units of functionality the system requires. By the way, if you don’t know about Web Services, don’t worry. Day 20, “Using RPC-Style Web

Services with J2EE,” and Day 21, “Web Service Registries and Message-Style Web

Services,” show you how to apply a J2EE application as a Web Service.

Language independence—A modularized system allows you to write code in one language that communicates with code written in another. For example, you can

2

04 0672323842 CH02 3/20/02 9:37 AM Page 40

40 Day 2 access the functionality of a J2EE application from a CORBA client by using RMI-

IIOP, or from a Microsoft COM client by using the Client Access Services COM

Bridge.

System upgrade—Inevitably, an organization’s business processes change, and so too must the application logic. The use of components allows you to change one component without affecting the other components in the system.

This list is not an exhaustive survey of the benefits of components. But it should make it clear that components offer both developers and business an ideal way of providing application functionality.

J2EE defines how various types of components perform specific roles in different tiers.

In the presentation tier, different types of components will be applied to provide functionality for different types of client (application components, applet components, servlet components, and JavaServer Page components). The business tier houses different types of business components. In J2EE terms, business components are embodied as Enterprise

JavaBeans. The following sections focus on how such components are applied for the most common application architecture currently—namely, a business system with a Webbased user interface.

Components: Enterprise JavaBeans

In a typical business application built on the J2EE platform, business logic will be encapsulated in Enterprise JavaBeans (EJBs). It is possible to use other types of component or plain Java objects to implement business logic, but EJBs provide a convenient way of encapsulating and sharing common business logic, as well as benefiting from the services provided by the EJB container.

Suppose you were tasked with designing and implementing a typical Web-based, e-commerce application. Although the precise analysis of the business problem would be specific to your own environment, you would probably end up with the following flow through your application:

1. Display your products to the customer.

2. Allow the customer to select one or more products.

3. Confirm the order and take shipping details.

4. Take payment for the items.

5. Deliver the order to your warehousing and distribution systems.

6. [optional] Authenticate the user to access previously stored personal information or preferences.

04 0672323842 CH02 3/20/02 9:37 AM Page 41

The J2EE Platform and Roles 41

7. [optional] Generate a report from the items purchased by a particular customer or on a particular day.

All of these steps involve a certain amount of business logic and data manipulation. For example, in step 1, your application will need to send pages of HTML to the customer’s browser containing product descriptions and pricing information. To do this, you will need to retrieve this product and pricing information from somewhere. The obvious choice is a database that stores your product catalog information together with pricing information.

As you will see, it would be quite possible to deliver the catalog functionality you require simply by using database access code from a Web component such as a

JavaServer Page. However, what if gathering this catalog information was not quite so straightforward?

• The product and pricing information may be spread across multiple databases.

Even worse, it may be that some of the information must be extracted from (or delivered to, in the case of submitting an order) a legacy system. This means that the user-interface component would have to know details about data access.

• There may be extra business processes that need to be applied during the creation of the catalog. These could range from custom pricing for a specific group or individual through cross-selling of related products and on to the suggested substitution of alternative products for any that are not in stock. This means that the user interface component must have knowledge of multiple business processes.

• If a customer is to be identified and their preferences reflected, authentication is required. Also, some information about them and their preferences must be maintained against a database somewhere. This means that the user interface component must know about authentication and mapping user identity to stored information.

As you can see, the user interface component rapidly becomes its own version of the monolithic applications you saw yesterday. The business logic associated with all of this processing should be devolved to EJBs in order that the user interface component can concentrate on what it does best, namely generating a compelling user interface and guiding the user through a particular interaction.

What is needed then is for the business logic in the EJB to be made available to other components that may want to use it. The mechanisms involved must be commonly available to the potential clients to impose minimal overhead on those clients. Hence, the EJB model makes use of two mechanisms found in J2SE—namely Remote Method

Invocation (RMI) and the Java Naming and Directory Interface (JNDI)—to facilitate interaction between the EJB and its client. When an EJB is written, the functionality it

2

04 0672323842 CH02 3/20/02 9:37 AM Page 42

42 Day 2 offers to clients is defined as an RMI remote interface. When an EJB is deployed, its location is registered in the naming service.

A client will then use JNDI to look up the location of the EJB. It will interact with a factory object, called the EJB’s home, to obtain an instance of the EJB. This is equivalent of using new to create an instance of a local object. When the client has a reference to the

EJB, it can use the business functionality offered by the EJB. This sequence is shown in

Figure 2.2.

F

IGURE

2.2

A client uses JNDI and

RMI to access an EJB.

Client Container

Client

3

Call business methods

RMI

EJB Container

Component

Enterprise

Bean

2

Obtain instance

RMI

1 Look up EJB

Home

Factory

Database

There is a more detailed look at how to use EJBs on Day 4, “Introduction to EJBs.”

Within the required business logic, certain components will be primarily concerned with data and the manipulation of that data, whereas others will focus on the sequencing of business logic and the associated workflow. Equally, components will interact in different ways, depending on the needs of the application. Some interactions will be synchronous in nature; there is no point in performing the next stage of the process until the current one has finished. Other interactions can be handled asynchronously; an appropriate message can be sent to another component and then the originator of the message can carry on to the next stage of the process. This means that clients using asynchronous interactions complete faster, but they may not be suitable for all applications if there must be a guarantee that an operation has completed or if return values are required. This difference between synchronous and asynchronous interactions is shown in Figure 2.3.

04 0672323842 CH02 3/20/02 9:37 AM Page 43

The J2EE Platform and Roles

F

IGURE

2.3

Synchronous interactions will result in the caller waiting for the function to complete, whereas asynchronous interactions allow callers to proceed without waiting.

Synchronous interaction

Client BusinessObject

Call function

Return value

Asynchronous interaction

Client BusinessObject1

BusinessObject2

Send message

Send message

43

2

Because different business components are called on to behave in different ways, there are multiple types of EJB defined that can be used to encapsulate different parts of an application’s business logic.

Components: Session Beans

Session EJBs, often just called session beans, are the simplest and probably most common type of EJB. A session bean is primarily intended to encapsulate a set of common business functions. When capturing the outputs of business analysis using the Unified

Modeling Language (UML), Use Cases are identified. A Use Case documents a particular interaction sequence between a user and a system (a common example is withdrawing money from an ATM). Such an interaction typically involves multiple, but related, steps.

The business logic associated with the steps from such a Use Case can typically be housed in a session bean. There are various analogies for and examples of session beans on Day 4 and Day 5, “Session EJBs.”

Session beans offer a synchronous interface through which the client can use the business logic. Session beans are not intended to hold essential business data. The session bean will either hold no data on an ongoing basis, or the data it does hold will tend to be temporary (only relevant to the current user session) rather than persistent. If a session bean wants to obtain data from a database, it can use JDBC calls. Such a bean may provide part of solution when providing an e-commerce catalog as described earlier.

Alternatively, application data can be obtained through entity EJBs.

Components: Entity Beans

An entity EJB, again often just called an entity bean, is a representation of some business data. During analysis, various business concepts will be discovered, such as “customer” or “account.” These business “objects,” sometimes called “entities,” represent the core data manipulated during the business processes. If such a business object contains

04 0672323842 CH02 3/20/02 9:37 AM Page 44

44 Day 2 dynamic data, has associated functionality, and can be shared between multiple clients at any one time, this business object would probably map to an entity bean.

Entity beans offer a synchronous interface through which the client can access its data and functionality. Entity beans will access underlying data sources (frequently, a database or possibly an ERP system) to collect all the business information they represent. The entity bean will then act as the dynamic representation of this business data—providing methods to update and retrieve it in various ways. As you will see later, entity beans are frequently used together with session beans to provide the business functionality of a system. Entity beans are discussed further on Day 6, “Entity EJBs.”

A message-driven EJB, or just message-driven bean, fulfills a similar purpose to a session bean, but is asynchronous in nature. There are times when it is inefficient to interact synchronously with a component. One example would be if you wanted to log the details of a particular transaction to an underlying data store. In many cases, it is not important that the logging is done immediately, just as long as it is done reliably. This type of operation can quickly become a performance bottleneck if performed synchronously. The same is true of “undoable” operations, such as credit card processing.

Components: Message Beans

Message-driven beans offer an asynchronous interface through which clients can interact with them. The bean is associated with a particular message queue, and any messages arriving on that queue will be delivered to an instance of the message-driven bean. As with session beans, message-driven beans are intended to house business logic rather than data, so they will access any data required through JDBC or entity beans. To use the services of a message-driven bean, a client will send a message to its associated message queue. If a response is required, another message queue will typically be used. Messagedriven beans and the Java Message Service with which they interact are discussed further on Day 9, “Java Message Service,” and Day 10, “Message-Driven Beans.”

The Presentation Tier

Given some business logic implemented as EJBs, you must provide clients with access to this functionality. What is needed is some presentation logic coupled with a way of displaying information to the user. The presentation logic will govern which screens are displayed to the user in which order and how the presentation logic will interact with the business logic in the business tier to work through the appropriate business process.

The way in which user input is received and information is displayed will depend on the type of client. The client can range from a WAP phone through to a standalone

Java application with Swing interface. Each type of client will require different

04 0672323842 CH02 3/20/02 9:37 AM Page 45

The J2EE Platform and Roles 45 presentation tier functionality to exchange and display information. The most common type of client for a J2EE application is a Web client, in the shape of a Web browser. In this case, the presentation tier will present a Web-based interface that produces HTML and consumes HTML form-based input.

Components: Web-Centric

To create a Web-based user interface, you need to apply Web-centric components. J2EE provides two types of Web-centric components—JavaServer Pages (JSP) and Java servlets. These components are applied in the presentation tier and provide services to clients that use HTTP as a means of communication. For example, Web-centric components can interact with clients such as the following:

• Standard HTML-oriented browsers, such as Microsoft Internet Explorer and

Netscape Navigator

• Java 2 Micro Edition (J2ME) enabled devices, connecting across a wireless network

• Desktop clients using raw HTTP or sockets functionality

• Wireless Markup Language (WML) browsers, such as those found on WAPenabled mobile phones

Figure 2.4 shows how you would typically use these components.

F

IGURE

2.4

Typical use of Webcentric.components.

Web-centric

Client

HTTP

Request

HTTP

Response

J2EE Server

Web Container

JSP or

Servlet

Presentation

Tier

EJB Container

Enterprise

Bean

Business

Tier

Figure 2.4 shows a Web-centric client making a request to either a servlet or JSP. The server-side component parses the client’s request and then calls the EJB. The EJB contains the application’s business logic. When the servlet or JSP receives a response from the EJB, it is responsible for presenting the data it receives. After the servlet or JSP has

2

04 0672323842 CH02 3/20/02 9:37 AM Page 46

46 Day 2 prepared the response, it passes it back to the client, completing the request-response cycle.

The previous illustration demonstrated a model where the Web-centric component was responsible for presenting data supplied by an EJB. The EJB was responsible for the execution of the business logic. However, you do not have to use EJBs as part of a Webcentric solution. A simpler application can consist of pages of markup—for example,

HTML—and servlets, or JSPs, or a combination of JSPs and servlets.

The next section of today’s lesson shows you the relationship between JSPs and servlets, so that you are in a position to choose which of these technologies best suit your needs.

JavaServer Pages (JSP)

JSPs allow you to dynamically create pages of markup, such as HTML, in response to a client’s request. If you are familiar with other Internet technologies, you can liken JSP to

Active Server Pages (ASP) or ColdFusion. But be aware that these technologies are similar to JSP—not the same.

A JSP consists of a combination of JSP tags and scriplets, which contain the executable code, and static markup, such as HTML or XML. The code contained in the JSP is identified and executed by the server, and the resulting page is delivered to the client. This means that the embedded code can generate additional markup dynamically that is delivered to the client alongside the original static markup. The client sees none of this processing, just the result.

The JSP tags delimit sections of executable code and form the basis of any JSP page.

Scriplets are delimited sections of script that allow a JSP further processing power.

Typically, you can write this script using the Java programming language, but different

JSP implementations may support additional languages.

Day 13, “JavaServer Pages,” shows you in detail how to write JSPs and how JSPs interact with other J2EE components. To whet your appetite today will introduce you to how

JSPs work and interact with other J2EE components. Figure 2.5 shows a scenario that depicts the typical interactions of client, JSP, and J2EE components.

Figure 2.5 shows a client making a HTTP request for a JSP. The first time a user makes a request the JSP container handles it by converting the JSP into a Java source file and compiling it. In most implementations, this file is a servlet. The servlet forwards the request to a business logic component, such as another servlet, a JavaBean, or an

Enterprise JavaBean. The component performs some action, such as accesses a database or processes the client’s input, and returns a response to the servlet. The servlet passes this response to a JSP that generates the markup language that the client will display.

Finally, the JSP container and the Web Server return the markup to the client.

04 0672323842 CH02 3/20/02 9:37 AM Page 47

The J2EE Platform and Roles 47

F

IGURE

2.5

The interactions between a client, JSP, and J2EE component.

Web-centric

Client

HTTP

Request

HTTP

Response

J2EE Server

JSP Container

JSP

EJB Container

Enterprise

Bean

Database

As you can see, JSPs provide a very powerful method for dynamically creating pages of markup. They also allow Web clients to indirectly access the application logic that other

J2EE components contain. Importantly, you have seen the relationship between a JSP and a servlet.

Java Servlets

Servlets add processing power to servers that employ a response-request model. Perhaps the most common of such servers is the Web server. In this instance in the past, CGI scripts would provide this kind of functionality; now you can use servlets. Although servlets are similar to CGI scripts, they are superior in many ways:

Speed—You deploy servlets as Java class files. The class file consists of Java byte codes, which means that they execute faster than interpreted scripting languages, such as Perl.

Platform Independence—Servlets are platform-independent classes, so they can be run under different servers on different operating systems.

Consistency—Servlets use a standard API (the Servlet API), so they enjoy the support of many Web servers.

Power—Servlets can access any of the Java APIs. For example, a servlet can use

JDBC to access a data store, or access remote objects such as EJBs over RMI.

Support—The Servlet API exposes a number of classes that greatly simplify many of the tasks a server-side programmer must perform. For example, the Servlet API provides direct access to response and request information (you do not have to explicitly parse request data), and it provides support for state management through a Session object and classes to manipulate cookies.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 48

48 Day 2

One of the greatest facets of the servlet is its ability to interact with other J2EE components. Servlets can interact with other servlets or EJBs in just the same way as a JSP.

For example, Figure 2.6 shows a client calling a servlet and then that servlet accessing an EJB.

F

IGURE

2.6

The interactions between a client, servlet, and EJB.

Web Browser

HTTP

POST

HTML Form

J2EE Server

Servlet Container

Servlet

HTML Page

HTTP

Response

EJB Container

Enterprise

Bean

Figure 2.6 shows a user completing an HTML form and then initiating a HTTP

POST to a servlet on a Web server. The Servlet Container on the Web Server invokes the servlet and passes it an object that represents the client’s request. The servlet calls on the services of the EJB to perform the applications logic. Finally, the servlet generates an HTML response, which it returns to the client via a response object. In this instance, the servlet used an EJB, but it could also perform the processing itself or call another servlet or

JavaBean.

Evaluation of Web-Centric Components

Now that you’ve been introduced to JSPs and servlets, you may wonder which component best suits a given scenario. In many instances, you can use JSPs and servlets interchangeably—remember that a servlet underlies a JSP. Both components dynamically create markup, operate on a request-response model, and can interact with other J2EE components.

However, Sun Microsystems provide guidance to help you develop applications using

Web-centric components. This guidance takes the form of BluePrints that offer guidelines on the best practice and recommended use of J2EE technologies. The following guidelines derive from these BluePrints.

Generally, the presentation tier should use JSP pages that are presentation-centric, making them ideally suited to the generation of markup. In addition, JSP pages consist of

04 0672323842 CH02 3/20/02 9:37 AM Page 49

The J2EE Platform and Roles 49

XML tags, which are familiar to Web content providers. This familiarity allows content providers to easily maintain a site’s content without altering code. Conversely, you should consider servlets as a programmatic tool that you don’t modify frequently. There are two main instances when you should use servlet in preference to JSPs:

Generating Binary Data—You should use servlets to output binary data, such as images.

Extending Web Server Functionality—For example, you could use a servlet to do the filtering of mail for a mail service.

As a guide, you should use JSPs in preference to servlets unless you require one of the previously mentioned items of servlet functionality.

The Client Tier

J2EE supports a wide range of clients. These clients range from thin clients, such as a

Web browser, to intelligent clients, such as mobile devices that run J2ME Midlets. Both of these clients communicate through HTTP, but other clients may use SOAP, sockets, or even CORBA. The factors that unite these disparate clients are that they all call and subsequently receive a response from J2EE middle tier components. This part of the day looks at a range of J2EE clients and focuses on which components interact with them.

HTTP Clients

Java technologies have a long history of providing a wide range of services to clients that communicate via HTTP. For example, is it possible that someone has never heard of applets? J2EE provides services to Web-based clients by using two components—JSPs and servlets. These technologies may not be as familiar to you as the applet. Previously, this chapter explained what these components are; now, it explains how they integrate with HTTP clients.

Static HTML Client

It’s hard to imagine a Web application that relies on static HTML pages, but, believe it or not, some do. Typically, sites that use static pages are small and require very little functionality. The type of functionality they do require includes the processing of user response forms, basic e-commerce capabilities, and automated navigation. In a nonenterprise environment, a developer can use a CGI script to provide this functionality.

With J2EE, you can use a servlet. For example, consider a customer feedback form written in HTML.

Figure 2.7 shows that the user completes the HTML Form and then initiates a HTTP

POST to the servlet. The servlet parses the POST data; at this point, it could pass the

2

04 0672323842 CH02 3/20/02 9:37 AM Page 50

50 Day 2 data to another component. However, in this instance, it simply uses classes in the

JavaMail API (see Day 11, “JavaMail”) to send the customer an e-mail response.

F

IGURE

2.7

Using a servlet to process an HTML form.

Web Browser

HTML

Form

HTML

Page

HTTP

(POST)

Request

HTTP

Response

J2EE Server

JSP Container

JSP

E-mail Client

E-mail

Message

JavaMail

Dynamic HTML Client

Many Web applications use HTML pages, which they generate when they receive a client request. These dynamic pages typically contain information that is context sensitive. For example, the page may contain a simple time stamp, a banner advertisement, or information retrieved from a database. With J2EE, you can use a JSP to create dynamic

HTML pages. Figure 2.8 shows how JSPs relate to a Web client.

F

IGURE

2.8

A Web-client interacting with a JSP.

Web Browser

HTTP

Request for index.jsp

HTTP

Response

HTML

Page

J2EE Server

JSP Container

JSP

EJB Container

Enterprise

Bean

04 0672323842 CH02 3/20/02 9:37 AM Page 51

The J2EE Platform and Roles 51

The client makes a HTTP request for the page index.jsp

. The JSP engine (on the server) interprets the tags within the JSP, calls the EJB, the EJB returns a response, and then the engine returns a page of HTML to the client. The functionality in this model is encapsulated in the EJB.

Java Applet Client

In case you do not know, an applet is a small GUI-based program that typically executes within the context of a Web browser. In the sphere of the Internet, a client requests an

HTML page that references a server-side Java class file (the applet). The Web server responds to the client by returning this file. The applet then executes within the Java

Virtual Machine (JVM) the client browser supplies.

In some respects, applets are an ideal way of providing remote access to J2EE applications. They are highly portable, run in an environment with strict security controls (the

Java 1.0 Sandbox as a minimum), enjoy wide industry support (for example, Netscape and Internet Explorer browsers), and offer a rich GUI. However, browsers do not keep pace with changing specifications; so many browsers only support the deprecated Java

1.0 event model. This may cause you a number of problems, especially when working with AWT, which underwent a major change in its event model between Java 1.0 and

Java 1.1. One of the best ways you can control the presentation tier is to deploy applets within a controlled network, such as a corporate intranet. In this instance, you can write code to work to the limitations of known browsers rather than working to the lowest common denominator.

Other HTTP Clients

The three previous clients are very desktop browser centric, but there are many other types of clients that communicate by using HTTP. These clients include mobile devices, such as cellular phones, smart phones, and PDAs. For example, Figure 2.9 shows a WAP device calling a JSP. The component-based architecture means that the only change is to the JSP because WAP devices don’t display HTML. All of the logic in business tier remains unaffected.

You can also write an application that uses HTTP to communicate and still communicate with the Web-centric J2EE components.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 52

52 Day 2

F

IGURE

2.9

A WAP device with a

WML browser interacting with a JSP.

WAP Device with

WML Browser

HTTP

Request

HTTP

Response

WML

Page

J2EE Server

JSP Container

JSP

EJB Container

Enterprise

Bean

Presentation

Tier

Business

Tier

Standalone Client

The HTTP clients all used a model that in essence was client—presentation tier— business tier—integration tier. However, there are other clients that may assume the responsibilities inherent in some of these tiers. For example, an application can connect to an EJB via its container, rather than route through a JSP in the presentation tier. Figure

2.10 shows such an example.

F

IGURE

2.10

A standalone client directly interacting with an EJB.

Standalone

Client

J2EE Server

Enterprise

Bean

EJB Container

Figure 2.10 shows a standalone client accessing an EJB via its container. In this example, the client would typically be another EJB. Because the client accesses the business tier, it becomes responsible for the provision of the presentation tier. Consequently, in this example, the client EJB can pass the data to a servlet, which will then generate the appropriate response to its client.

04 0672323842 CH02 3/20/02 9:37 AM Page 53

The J2EE Platform and Roles 53

Figure 2.11 shows another scenario where the standalone client bypasses both the presentation tier and business tier to access enterprise information system resources directly.

Typically, the client uses JDBC to access the resources.

F

IGURE

2.11

A standalone client interacting directly with an EIS resource.

Client

Application

Enterprise

Information

Service

2

In this scenario, the client takes responsibility for the presentation tier and business tier.

Interestingly, the client may not be another J2EE client: it might be a single application that encapsulates all presentation and business logic. If this is the case, the client will not gain the benefits of enterprise computing, especially scalability, because it is effectively working on the 2-tier client-server model.

Business to Business

The previous clients and scenarios worked on the premise of connecting to a component in a different tier—with the exception of the EJB to EJB example. However, many components do connect to components within the same level tier as themselves. For example, consider a small garage where mechanics order parts from a local catalogue via a handheld device. Figure 2.12 shows the architecture for the system. As you can see, a JSP handles their orders by accessing a catalogue via JDBC. With each order for a part, the

JSP decrements the count for that part. When the part count reaches zero (Just-in-Time stock management), the JSP connects to a JSP at the parts wholesaler and places an order for more parts.

The previous example showed two JSPs communicating, but this could just as easily be two servlets or two EJBs. Another interesting development in peer-to-peer communications such as these is the use of messaging. For instance, the previous example requires both JSPs to be available at the same time to complete an order. However, using the messaging facilities JMS provides, you may create a system where both JSPs can asynchronously send and receive orders. You can learn a little more about JMS later today and a lot more on Day 9, “Java Message Service.”

04 0672323842 CH02 3/20/02 9:37 AM Page 54

54

F

IGURE

2.12

JSP to JSP interaction.

Mechanic’s

Handheld

Device

Day 2

J2EE Server

JSP Container

JSP

J2EE Server

JSP Container

JSP

EJB Container

Enterprise

Bean

Catalogue Database

Non-Java Clients

It is possible to use non-Java clients to access J2EE applications and application components. One obvious solution is that non-Java clients can use HTTP to access the services offered by a servlet or JSP. This is particularly attractive when the servlet or JSP produces and consumes data-oriented information in XML rather than HTML. This could be done in the context of exposing the component as a Web Service, which is discussed in the next section of today’s lesson.

In the case of an EJB, things are a little trickier. However, the RMI-IIOP protocol used to communicate with EJBs allows them to interoperate with clients written using the

Common Object Request Broker (CORBA) standard. Hence, it is possible to expose an

EJB as a CORBA server that can be used by CORBA clients written in C++ or even

COBOL.

Another alternative is that EJBs can be accessed from a Microsoft COM client using the

J2EE Client Access Services (CAS) COM Bridge or Table Bridge.

Web Services

Web Services are XML-based middleware components that applications access over

HTTP and SOAP. They enjoy industry-wide support and are not a proprietary solution. In fact, because Web Services use XML and open communication standards, any client that

04 0672323842 CH02 3/20/02 9:37 AM Page 55

The J2EE Platform and Roles 55 can understand SOAP messages can consume Web Services. J2EE provides a rich framework that facilitates the building, deployment, and consumption of Web Services. You can learn more about J2EE and Web Services on Days 20 and 21.

Understanding Containers

When you previously installed J2EE, you also installed Sun Microsystems’ reference implementation of J2EE, which is a J2EE Product. All J2EE products must provide containers to house J2EE components. The role of the container is to provide a component with the resources it needs to operate and a runtime within which to execute; yet still provide a degree of protection (security) to the application host.

Containers provide a number of services for a component. These services include lifecycle management, threading, security, deployment, and communication with other components. In addition to these services, a container must provide components with Java compatible runtime that conforms to J2SE 1.3.

Different components perform different tasks, so it may come as no surprise that they require different containers. A Product Provider can supply any of the following containers:

• Applet container

• Application client container

• JSP container

• Servlet container

• Web container

• EJB container

It was mentioned previously that all containers must supply a J2SE 1.3-compatible runtime environment. Interestingly, many applet hosts (typically Web browsers) do not support such a high runtime version. To compensate for this, the J2EE specification states that an applet container can use a Java plug-in to provide an appropriate environment.

All of these containers must provide the components they house with certain services and communications protocols. You will learn more about these service and protocols later in this chapter in the “Understanding the Service Supply to their Components” section.

These containers must all also provide access to certain J2EE APIs; Table 2.2 shows these APIs.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 56

56 Day 2

T

ABLE

2.2

J2EE Required Standard Extension APIs

API

JDBC 2.0 Extension

JTA 1.0

Applet

N

N

Application Client

Y

N

JNDI 1.2

Servlet 2.2

JSP 1.1

EJB 1.1

RMI-IIOP 1.0

JMS 1.0

JavaMail 1.1

JAF 1.0

N

N

N

N

N

N

N

N

N

N

Y

Y

N

Y

Y

N

Y

Y

Y

Y

Y

Y

Y

Y

Web

Y

Y

N

Y

Y

N

EJB

Y

Y

Y

Y

Y

Y

For the sake of simplicity, Table 2.2 does not distinguish between servlet, JSP, and Web containers. You can consider these three containers as a stack where each builds on the functionality of the other. At the bottom of the stack is the servlet container, which must support HTTP; optionally, it might support other protocols. Above this is the JSP container, which provides the same functions as the servlet container and an engine to build servlets from JSP pages. Finally, the Web container provides all the services of the JSP container and access to J2EE service and communications APIs, which today’s lesson details next.

Understanding the Services Containers

Supply to Components

Previously, you learned that there are a variety of J2EE containers, and that these containers house J2EE components and provide methods and protocols that allow components to communicate with each other and with platform services. You also learned that a

J2EE server underlies the container. This server is often known as the J2EE Product; there are other possible J2EE products that you will learn about later today in the “J2EE

Product Provider” section of today’s lesson.

J2EE products must provide components with certain standard services. Yesterday you were introduced to some of these services; today you will explore them in a little more detail and see how they work in conjunction with J2EE components.

04 0672323842 CH02 3/20/02 9:37 AM Page 57

The J2EE Platform and Roles 57

Hypertext Transfer Protocol (HTTP)

A W3C specification defines HTTP 1.0, which is a protocol that allows the exchange of data of various formats in a widely distributed network. Both JSPs and servlets allow clients to access a J2EE application through the use of HTTP 1.0. Clients that have a

Java runtime communicate with J2EE applications by using the java.net

package, which is part of J2SE.

Note The J2EE Specification states that containers need only provide support for

HTTP 1.0. However, in practice, the majority of containers, including those in the RI, support HTTP 1.1.

HTTP over Secure Sockets Layer (HTTPS)

A Netscape specification defines SSL 3.0, which is a protocol that manages the secure transfer of data over a network. HTTPS uses SSL 3.0 as a sub-layer to HTTP to provide secure data transfer over the Internet. In common with HTTP, the JSP and Java Servlet

APIs define the server-side API, and java.net

defines the client-side.

Java Database Connectivity (JDBC)

JDBC is an API that allows you to access any tabular data source including relational databases, spreadsheets, and text files. The API allows you to connect to a database via a driver and then execute Structured Query Language (SQL) statements against that database. Appendix B, “SQL Reference,” provides an SQL reference. The API consists of two packages— java.sql

(ships with J2SE) and javax.sql

(ships with J2EE). The javax.sql

package provides many of the features an enterprise application requires, such as transaction support and connection pooling.

Day 8, “Transactions and Persistence,” describes JDBC, so today’s lesson provides only a quick overview of JDBC architecture. Typically, an EJB uses the API, but any other component can use it, for example a servlet. To connect to a given database, you must load a JDBC driver for that database. However, in the case of an Open Database

Connectivity (ODBC) data source, you may optionally use a JDBC-ODBC bridge where no driver exists. After the appropriate driver loads, you can make a connection to the database and then create and execute SQL statements. If the statement is a query, a

ResultSet is returned, which contains the results of the query. You can then manipulate these query results.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 58

58 Day 2

Java Transaction API (JTA)

A transaction is an atomic group of operations. For example, a banking application may debit one account and credit another. The transaction is considered complete when both the debit and credit are complete. If one operation fails, the other must roll back. A distributed system makes transaction management complex. In such a system, a transaction manager must coordinate transactions across the system.

The JTA API allows you to work with transactions independently of the transaction manager. You work directly with the methods JTA exposes via an instance of

UserTransaction

. In a simple scenario, you can use the begin()

, commit() and rollback() methods—which might be familiar SQL commands if you are a database programmer—to manage the transaction. Day 8 explores JTA and its use with JDBC.

Java Authentication and Authorization Service (JAAS)

Anyone who has an interest in security knows that Java technologies have a rich history of providing a strong security framework. JAAS is a new supplement to this existing security framework. It provides both authorization and authentication services that the

Pluggable Authentication Module (PAM) provides. In common with the Java 2 security framework, JAAS provides access control based on code location and code signers. In addition, JAAS provides access control to a specific user or group of users.

JAAS allows you to simply swap at runtime between encryption algorithms when authenticating users. This is because you interact with JAAS through a login context, so you effectively work with an abstraction of the authentication mechanisms.

Note Because JAAS does not actually contain classes that encrypt data, it is not subject to U.S. export control restrictions. This means that developers outside of the U.S. are free to download JAAS.

JAAS is an optional package to J2SE 1.3.x, and it ships with sample authentication modules that use JNDI, Solaris, and Windows NT. You can download the current version of

JAAS from http://java.sun.com/products/jaas/

. You can learn more about using

JAAS with JNDI and J2EE components on Day 15, “Security.”

Java API for XML Parsing (JAXP)

XML is a text-based markup language that describes data. It provides a platformindependent and language-independent method for exchanging data between applications.

04 0672323842 CH02 3/20/02 9:37 AM Page 59

The J2EE Platform and Roles 59

Because XML consists of plain text, it is human-readable. However, you very rarely read

XML: you use an application that implements an XML API to read the data.

The JAXP API allows you to parse XML documents using the Document Object Model

(DOM) or the Simple API for XML (SAX). One very useful feature of JAXP is that you can swap between XML parsers without making changes to your code. For example, if speed suddenly becomes very important, you can use the SAX parser because it reads a very large document in a fraction of the time of the DOM parser. Another useful feature of JAXP is that it provides support for Extensible Stylesheet Language Transformations

(XSLT). For example, the J2EE Reference Implementation (RI) provides a transformation engine that supports XSLT. This allows you to dynamically transform an XML document into either another XML document, HTML, or plain text.

There are many ways that you might use XML within a J2EE application. For example, you can store content in XML and then transform the XML using XSLT so that a JSP can render content to devices that support different markup languages. Another typical use of XML is in the arena of business-to-business applications, where organizations can exchange data independently of their system architectures. Finally, one very important use of XML is within Web Services, which you will learn about on Days 20 and 21.

Java Naming and Directory Interface (JNDI)

JNDI provides an API for working with naming and directory services. A naming service simply associates names with objects, for example, the Domain Name System (DNS). A directory service also associates names with objects, but it also provides additional information through attributes, for example, a Lightweight Directory Access Protocol (LDAP) directory.

Although JNDI provides access to a wide array of naming and directory services, each service must provide a Service Provider. This is similar to JDBC and drivers, but in this instance, it is a naming or directory service and a Service Provider. For example, an

LDAP directory must provide an LDAP Service Provider, which JNDI hides from you as a developer.

Whenever you need to access naming or directory services, you can use JNDI. More specifically, you use JNDI in J2EE in three main instances—to access or register EJBs or objects in an RMI registry or to access the CORBA Common Object Services (COS) naming service. You can learn more about JNDI in Day 4.

JavaBeans Activation Framework (JAF)

Typically, you use JAF in the context of JavaBeans (that’s JavaBeans, not Enterprise

JavaBeans!). However, a J2EE product must provide JAF for the JavaMail API to use

2

04 0672323842 CH02 3/20/02 9:37 AM Page 60

60 Day 2

MIME types. JAF allows you to send e-mails that are not simply plain text. Instead, you can use different MIME types or send attachments. You can learn more about the use of

JAF in the context of JavaMail on Day 11.

JavaMail

The JavaMail API provides classes that allow you to work with e-mail. Specifically, it allows you to send and receive e-mails by using a wide variety of protocols, including

POP, SMTP, and IMAP. You can create e-mails that conform to a large number of MIME types, because the API uses JAF to provide support for a number of MIME types. For example, you can create HTML messages that contain embedded graphics and even have attachments.

Most Internet applications require the ability to send e-mail messages. You can use this

API together with JAF to send e-mails from a JSP, a servlet, or an EJB. You can learn more about the API on Day 11.

Java Message Service (JMS)

Messaging is the process of communication between applications or components; it does not include application to human communications, such as e-mail. The JMS API allows you to create, read, and store messages.

JMS support two messaging models—point-to-point and publish-subscribe. Point-topoint messaging is where an application sends a message to a queue (a prearranged destination for messages), and then a client application collects that message. For example, leaving a voicemail message is a real-world example of this model. The publishsubscribe model requires client applications to subscribe to a topic with a message broker that again acts as a prearranged message destination. When an application sends a message to the message broker, the message broker immediately forwards the message to all current subscribers.

You can send and receive JMS messages by using both session and entity beans.

However, you can only do this synchronously; the sender must suspend execution until the receiver receives the message. Alternatively, you can use a message-driven bean to send messages asynchronously. You can learn more about message-driven beans on Day

10 and JSM on Day 9.

Java Interface Definition Language (Java IDL)

Java IDL provides a way for you to access and deploy remote objects that comply with the Common Object Request Broker Architecture (CORBA) defined by the Object

Management Group (OMG). CORBA Interface Definition Language (IDL) provides a

04 0672323842 CH02 3/20/02 9:37 AM Page 61

The J2EE Platform and Roles 61 language-independent means of defining object interfaces. OMG provides mappings between various languages and IDL. A client written in any language that has an IDL binding can access objects you export by using CORBA. For example, a Java client can consume objects written in other languages, such as C++, C, Smalltalk, COBOL, and Ada.

In terms of J2EE applications, you can look up CORBA remote objects in the COS naming service through JNDI. The main reason you would want to use Java IDL, as a Java developer, is to allow your J2EE application to access legacy systems. For example, you might have a legacy Integration tier where a COBOL application manages data access.

Java IDL allows an EJB in the business tier to communicate with the COBOL object, thus negating the need to rewrite the entire backend legacy code.

Remote Method Invocation over Internet Inter-Orb

Protocol (RMI-IIOP)

RMI is a distributed object system that allows Java objects to interact with other Java objects running in a different virtual machine (usually on a remote host). In practice, you can access these remote objects almost as if they were local; you simply get a reference to the remote object and invoke its methods as if it were running in the same virtual machine. The seamless nature of this access is due, in part, to the fact that RMI is a Javaonly distributed object system that relies on a proprietary transport protocol, namely,

Java Remote Method Protocol (JRMP), to exchange information between client and server. However, this means that you can only use it to access other Java objects—not non-

Java objects.

The actual process of performing a remote method invocation is similar to that of

CORBA, namely, RMI utilizes client-side stubs and server-side skeletons. A client invokes a remote method by making a request on the stub, and this forwards to the server where the skeleton converts the request into an actual method call on the remote object.

Any arguments for the remote method are marshaled by the stub into a serialized form before they are forwarded to the skeleton, which, in turn, unmarshalls the arguments.

This marshalling allows objects to transport across a network.

Unfortunately, this protocol is not suitable for the type of enterprise-level interactions required by EJBs, where transaction and security context must be propagated across remote method calls. To this end, Sun Microsystems created a new implementation of

RMI called RMI-IIOP. This keeps the same semantics as RMI (remote interfaces, passing serialized objects, and so on) but uses the CORBA Internet Inter-ORB Protocol (IIOP) as its transport mechanism. IIOP already contains all of the necessary hooks to propagate security and transaction context, so this new protocol can act as the core transport for

EJBs in the J2EE architecture.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 62

62 Day 2

RMI-IIOP is used by default as the transport mechanism when generating stubs and skeletons for EJBs. You can also explicitly create RMI-IIOP clients and servers for your own applications by using flags on the RMI compiler (rmic) to create RMI-IIOP stubs and skeletons rather than the default JRMP stubs and skeletons.

You can also use RMI-IIOP as a mechanism for exposing your EJB components to

CORBA clients without having to learn IDL. You can specify a flag to the RMI compiler that gets it to generate CORBA IDL on your behalf. After you have the CORBA IDL, this can be used together with an alternative language binding to create a client for the

EJB that is written in another language.

Connector Architecture

The J2EE Connector Architecture allows your J2EE application to interact with

Enterprise Information Systems (EIS), such as mainframe transaction processing,

Enterprise Resource Planning (ERP) systems, and legacy non-Java applications. It does this by allowing EIS vendors to produce a resource adapter that product providers can plug into their application servers. The J2EE developer can then obtain connections to these EIS resources in a similar way to obtaining a JDBC connection.

The J2EE Connector Architecture defines a set of contracts that govern the relationship between the EIS and the application server. These contracts determine the interaction between server and EIS in terms of the management of connections, transactions, and security. You can learn more about Connector Architecture on Day 19, “Integrating with

External Resources.”

Introducing Platform Roles

To create, package, and deploy any J2EE application—other than the simplest application—requires the effort of more than one person or organization. For example, in the development arena, a team of developers will write the J2EE components and someone else will assemble the finished application. In the production environment, someone will configure the J2EE environment and deploy the application, and yet another person will monitor the running application and its physical environment. In smaller organizations, there may be no physical distinction between these roles, but they will still be logically separate. Based on this premise, it is no surprise that Sun Microsystems suggest a named team whose responsibility it is to perform these tasks.

This team, together with Product Providers and Tool Providers constitute the J2EE platform roles. It is these roles that this section explores.

04 0672323842 CH02 3/20/02 9:37 AM Page 63

The J2EE Platform and Roles 63

J2EE Product Provider

A J2EE product must include the component containers and J2EE APIs the J2EE specification states; today’s lesson has introduced all of these containers and APIs. Examples of

J2EE products include operating systems, database systems, application servers, and Web servers. An organization that supplies a J2EE product is known as the J2EE Product

Provider.

The J2EE Product Provider is also responsible for mapping application components to the network protocols the J2EE specification defines. In addition, the Product Provider must provide deployment tools for the Deployer and management tools for the System

Administrator. The end of this section provides an explanation of these tools.

The Product Provider is free to provide implementation-specific interfaces that the J2EE specification does not define. Hence, you will occasionally see a warning in a lesson that highlights a vendor-specific piece of functionality.

Application Component Provider

As you have already seen, a J2EE application consists of components, but it also may consist of other resources, such as HTML files or XML files. The Application

Component Provider creates both these resources and components. Almost all organizations will use several component providers. They may exist in-house, or the organization may outsource component creation or buy in components. Whichever is the case, specialists in the different tiers (presentation, business, and data access) will write the components that relate to that tier. For example, a business tier specialist will write EJBs, whereas a presentation tier expert may write JSPs. Regardless of the Application

Component Provider’s specialist area, the Tools Provider will supply them with tools to write components.

Application Assembler

After the Application Component Providers write an application, the Application

Assembler assembles the application into a J2EE application. The Application Assembler packages the application into an EAR file that must conform to the J2EE specification.

Other than assembly, the Application Assembler is responsible for providing instructions that state the external dependencies of the application. Typically, the Application

Assembler uses tools the Tools Provider or Product Provider provides to perform these tasks.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 64

64 Day 2

Application Deployer

The Application Deployer is the first person who requires knowledge of the production environment. This is because he or she must deploy the application into that environment. Specifically, the Application Deployer must install, configure, and start the execution of the application. Typically, the Product Provider provides tools that help perform these tasks.

The installation process is where the Application Deployer moves the application to the server and installs any classes the container requires to perform its duties. During the configuration process, the Application Deployer satisfies any external dependencies the

Application Assembler stipulates and configures any local security settings, for example, modifies a policy file. The final stage, starting execution, is where the Application

Deployer starts the application in readiness to service clients.

Systems Administrator

The Systems Administrator configures and maintains the enterprise network, and monitors and maintains the application the Application Deployer deployed. The Product

Provider supplies tools that assist the Systems Administrator in the monitoring and maintenance of the application.

This concludes the list of platform roles that specifically work with the application; only the Tool Provider remains. Figure 2.13 shows the interactions between each of these roles and their interactions with the J2EE application.

F

IGURE

2.13

J2EE roles.

Product Provider

Development Environment

Application

Component

Provider

Application

Assembler

Production Environment

Application

Deployer

System

Administrator

Tools Provider

04 0672323842 CH02 3/20/02 9:37 AM Page 65

The J2EE Platform and Roles 65

Tool Provider

As you have seen, many of the platform roles use tools the Tool Provider supplies. These tools assist people with the creation, packaging, deployment, and maintenance of J2EE applications. Currently, the J2EE specification only defines that the Product Provider must supply deployment and maintenance tools; it does not stipulate what these tools should be. Future releases of the specification are likely to provide further guidelines, so that Tool Providers can supply platform-independent standardized tools sets.

To offer you a typical overview of the tools that Tools Providers and Product Providers supply, this section concludes with a brief survey of the tools that ship with the J2EE reference implementation. For use guidance, please refer to the J2EE documentation.

J2EE Administration Tool—Enables the addition and removal of resources, such as

JDBC drivers and data sources.

Cleanup Tool—Removes all J2EE applications from the server.

Cloudscape Server—Starts and stops the Cloudscape relational database.

Deployment Tool—Enables you to package and deploy J2EE applications.

J2EE Server—Launches and stops the J2EE server.

Key Tool—Enables you to generate public and private keys and X.509 certificates.

Packager—Allows you to package J2EE applications if you are not packaging them using the deployment tool (see above). You can create EJB JAR, Web WAR,

Application Client JAR, J2EE EAR, and Resource Adapter RAR files.

Realm Tool—Allows the administration of J2EE users and also the import certificates.

Runclient script—Enables you to run a J2EE application client.

Verifier—Verifies the integrity of EAR, WAR, and JAR files.

Future of J2EE tools

There are 3 Java Specification Requests underway that will affect the future of J2EE tools:

• JSR 77—A new management model for tools that will provide a single management tool to configure the J2EE platform. You can read more about the JSR at http://www.jcp.org/jsr/detail/077.jsp

.

• JSR 88—A description of the APIs that enable the deployment tool. You can read more about the JSR at http://www.jcp.org/jsr/detail/088.jsp

.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 66

66 Day 2

• JSR 127—This defines the architecture that simplifies the creation and maintenance of Java Server application graphical user interfaces. You can read more about the JSR at http://www.jcp.org/jsr/detail/127.jsp

.

Packaging and Deploying J2EE Applications

The installation of a typical desktop application, such as a word processor, is usually a straightforward affair. The installation program will ask you a few questions about the functionality you require and where it should install its files. It will also examine parts of your desktop machine (such as the Windows registry) to discover whether any components that it relies on are already installed. Normally, such an installation takes in the order of a few minutes—half an hour at most.

The installation of a distributed enterprise application is unlike that of a packaged desktop application. The installation of a desktop application is reasonably straightforward because

• The concept of word processing is well understood by most people, so they can make an appropriate judgment on whether they need particular parts of the package or not. There is little in the way of personal tailoring involved.

• All of the installation takes place on a single machine. The installation program knows where to find existing configuration information and where it should install the different parts of the application.

Compared with this, a distributed enterprise application will require a lot more information about the environment in which it is to be installed. This includes, but is not limited to, the following:

• The location of the servers on which the server-side components will be deployed.

The Web and business components for an application could be distributed across multiple servers.

• The appropriate level of security must be enforced for the application. The application must carry with it information about the security roles it expects and the access each role has to the functionality of the application. These security roles must be mapped onto the underlying security principals used in the distributed environment.

• Components that access data and other resources must be configured to use appropriate local data sources.

04 0672323842 CH02 3/20/02 9:37 AM Page 67

The J2EE Platform and Roles 67

• The names of components and resources must be checked and potentially changed to avoid clashes with existing applications or to conform to a company-wide naming standard.

• Web-components must be configured so that they integrate with any existing Web sites of which they will form a part.

As you can see, this is currently a far more specialist task, requiring knowledge about the application and the environment in which it is being deployed. The application must carry with it information about the requirements it has of the environment. The application assembler defines these requirements when the application is created. The application Deployer must examine these requirements and map them onto the underlying environment.

J2EE applications

A J2EE application will consist of the following:

• Zero or more Web components packaged as Web Archives (WAR files)

• Zero or more EJB components packaged as EJB-JAR files

• Zero or more client components packaged as JAR files

• Zero or more connectors packaged as Resource Archives (RAR files)

Naturally, there must be at least one component for there to be an application! The components that constitute an application must be packaged together so that they can be transported and then deployed. To this end, all of the components in a J2EE application are stored in a particular type of JAR file called an Enterprise Application Archive or

EAR.

Given the previous scenario, it should be clear that a J2EE application needs to carry with it information about how its different parts interrelate and its requirements of the environment in which it will be deployed. This information is carried in a series of XML documents called deployment descriptors. There is an overall application deployment descriptor that defines application-level requirements. This application deployment descriptor is also stored in the EAR file.

Each individual component will have its own deployment descriptor that defines its own configuration and requirements. These component deployment descriptors are carried in the individual component archives described in the “Breaking Modules Down into

Components” section of today’s lesson. Figure 2.14 shows the structure of an EAR file and how the application deployment descriptor, the component archives, and the component deployment descriptors fit within this structure.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 68

68 Day 2

F

IGURE

2.14

Structure of an

Enterprise Archive

(EAR).

EJB-JAR

EJB-JAR

EJB-JAR

DD

DD

DD

WAR

WAR

WAR

DD

DD

DD

JAR

JAR

JAR

DD

DD

DD

RAR

RAR

RAR

DD

DD

DD

EAR

Application DD

Container DD

EJB

Modules

Web

Modules

Client

Modules

Resource

Modules

The application deployment descriptor contains application-wide deployment information and can potentially supersede information in individual component deployment descriptors.

Note

The EAR file can also contain a container-specific deployment descriptor that holds information that is useful to the container but falls outside the scope of the J2EE application deployment descriptor.

The application is split into modules, each of which represents a component. If necessary, a module can contain an additional deployment descriptor to override some or all settings in the deployment descriptor provided in the component archive file.

Breaking Modules down into Components

As you can see from Figure 2.14, components are represented in an EAR file by component archive files. Each module will point to its associated component archive file. Each type of component archive file is a JAR-format file that contains the component’s classes and resources together with a component-specific deployment descriptor.

04 0672323842 CH02 3/20/02 9:37 AM Page 69

The J2EE Platform and Roles 69

The two most common types of component archive are EJB-JAR files and WAR files.

EJB Component

An EJB-JAR file contains all of the classes that make up an EJB. It also contains any resource files required by the EJB. The properties of the component are described in its associated deployment descriptor, called ejb-jar.xml

, which is also included in the

EJB-JAR file.

The deployment descriptor described the main class files contained in the EJB-JAR file.

The deployment descriptor specifies which external resources are required by the component. It also contains extra information about the security and transaction settings. This resource and extra information is often referred to as metadata. Figure 2.15 shows a subset of the contents of an EJB deployment descriptor.

F

IGURE

2.15

An EJB deployment descriptor indicates the main classes in the

EJB-JAR file together with the component’s metadata.

Deployment

Descriptor (XML) specifies

Home interface

Remote interface

EJB implementation

Security requirements

Transaction requirements

AgencyHome.class

Agency.class

AgencyBean.class

<security-role>

<role-name>admin</role-name>

</security-role>

<container-transaction>

<trans-attribute>Required</trans-attribute>

</container-transaction>

All of the component’s metadata can be altered or replaced by the application assembler as they bind the component into the application. The application Deployer can also customize some of the metadata.

2

Note

An EJB-JAR file can contain more than one EJB.

EJB-JAR files and their deployment descriptors are discussed in more detail on Days 4 and 5. Other aspects of EJB deployment information, such as security and transactions, are covered later.

04 0672323842 CH02 3/20/02 9:37 AM Page 70

70 Day 2

Web Component

Servlets and JSPs can also be packaged together into a component archive file. The archive file is a JAR-format file that contains the class files, JSP files, and resources required by the Web component. In this case, the resources can include static HTML files that form part of the application. This Web Archive (WAR) file also contains a deployment descriptor that indicates the Web components contained in the WAR.

Just as with the EJB deployment descriptor, the WAR deployment descriptor indicates the main classes in the WAR file and the resources required by the components.

However, the WAR deployment descriptor also contains Web-specific information, such as the URLs onto which servlets and JSPs should be mapped, and which is the front page of the application.

WAR files and their deployment descriptors are discussed in more detail on Day 12,

“Servlets,” and Day 13. Other aspects of WAR deployment information, such as security, are covered later.

Summary

Today, you looked in more detail at J2EE and the facilities that it provides. You saw how the different J2EE technologies fit into the 3-tier model, and how it provides component frameworks for different types of functionality.

You have seen that the EJBs provide a robust, scalable home for business logic and that servlets (and JSPs) provide a flexible way of delivering application functionality to clients. There are many different types of client, ranging from simple, markup-based clients that work over HTTP to sophisticated and powerful clients that use GUIs and

RPC.

You have seen that the creation and deployment of an enterprise application requires many different roles. You have also seen that an enterprise application is assembled from many different parts, and that it must carry with it information about how all of those parts fit together.

Q&A

Q Can a J2EE application be written without using any Enterprise JavaBeans?

A Certainly. You can write a client application that connects to a servlet in a Web container and have that servlet connect directly to a back-end database. You don’t

04 0672323842 CH02 3/20/02 9:37 AM Page 71

The J2EE Platform and Roles 71 need to add an EJB. An EJB can add value by providing persistent conversational state if that is required. It can also provide transactional security and roll back to a previous state should there be an interruption in the flow of data for any reason.

Therefore, you can use servlets and JSPs on their own if a database is simply read, but any updates or new records to be added will more safely be done using

Enterprise JavaBeans.

Q What type of EJB should I typically use to encapsulate business logic? And which type would I use to contain data and its associated operations?

A For pure business logic, you would typically use a session bean (or a messagedriven bean). If the EJB is to represent underlying application data, you would probably use an entity bean.

Q Why are JSPs generally faster than other server-side scripting environments?

A When a JSP is accessed, it is compiled into Java bytecodes. Every subsequent access will use the bytecodes rather than processing the page again. This means that JSPs will run as fast as standard Java classes such as servlets.

Q What are the consequences of producing a J2EE application with vendor specific APIs?

A The application you produce will become specific to a particular container, server, or vendor. You will not be able to easily move the application from platform to platform.

Q How do you package an EJB? What should be in the package?

A An EJB is packaged in an EJB-JAR file. The EJB-JAR file contains the classes for the EJB, any other resources, and a deployment descriptor that contains EJB metadata and describes the external resource requirements of the EJB.

Q What is an EAR file?

A This is an Enterprise Application Resource file that houses the application’s JAR,

WAR, and XML files. The Assembler takes on the responsibility of packaging the

EAR file, while the Deployer authenticates that the file conforms to the J2EE specification, adds the file to the J2EE server, and deploys the application.

Exercises—Case Study

To help understand the role of each of the technologies in the J2EE specification, a single case study will be followed throughout the daily exercises. As you work through the 21 days, a functional implementation of a simple enterprise application will be developed.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 72

72 Day 2

The Job Agency

The chosen case study is a simple Job Agency. Jobs are categorized by Location and

Skills required for the job. Customers advertise jobs, and Applicants register their location and skills so they can be matched to jobs. Customers and applicants will be notified of job matches by e-mail.

To illustrate the relationships between the different components in the data model for the

Agency, a traditional database ERD diagram is shown in Figure 2.16.

F

IGURE

2.16

Case Study ERD.

Applicant login: name:

VARCHAR(16) - PK

VARCHAR(64) email: location:

VARCHAR(64)

FK-Location summary: VARCHAR(512) applicant: skill:

ApplicantSkill

FK-Applicant

FK-Skill /

\PK

Location name: VARCHAR(16) - PK description: VARCHAR(64)

Skill name: VARCHAR(16) - PK description: VARCHAR(64)

JobSkill job: FK-Job \ customer: FK-Customer >PK skill: FK-Skill /

Job ref: VARCHAR(16) \PK customer: FK-Customer / description: VARCHAR(512) location: FK-Location

Customer login: name:

VARCHAR(16) - PK

VARCHAR(64) email: VARCHAR(64) address1: VARCHAR(64) address2: VARCHAR(64)

Matched applicant: FK-Applicant \ customer: FK-Customer >PK job: exact:

FK-Job boolean

/

04 0672323842 CH02 3/20/02 9:37 AM Page 73

The J2EE Platform and Roles 73

Today’s exercise, which is described later, will be to create the database and register it as a javax.sql.DataSource

to the J2EE RI.

The case study has a front office part with the following components:

• Maintaining the location and job lookup tables

• Adding customers and advertising jobs

• Registering job applications

The back office part consists of the following:

• Matching a new job against existing applicants

• Matching a new applicant against existing jobs

• Generating e-mails

Using the Agency Case Study

The example code shown in each day’s lesson will use the Customer functionality (jobs and invoices) from the case study. At the end of each day’s work, you will be asked to enhance the case study by adding the Applicant functionality (registering jobs) to the system. A fully-worked solution for the exercises is provided on the CD-ROM included with this book, so that you will have a working case study if you choose to omit any day’s exercise.

2

Note

Material for many days exercises, particularly JNDI, JavaMail, JMS and Java

Connectors, will primarily use examples and exercises not related to the case study. The last two days’ work discuss J2EE in the context of the wider context of Web applications and do not refer to the Agency case study.

Table 2.3 shows roughly what will be covered on each day. Don’t worry if some of the terminology is new to you; all will become clear as you work your way through the book.

T

ABLE

2.3

Daily Workout

Day

1

2

Lesson

Introduce multi-tiered application architectures

Exercise

No exercise

Introduce the J2EE platform, technologies and roles Install J2EE RI and case study database.

3 Using JNDI naming and directory services Write a JNDI namespace browser.

04 0672323842 CH02 3/20/02 9:37 AM Page 74

74 Day 2

T

ABLE

2.3

Continued

Day

4

5

6

7

8

9

10

Lesson

Using data sources, environment entries, and EJB references

Using Session EJBs to implement business logic

Using Entity EJBs to encapsulate access to persistent data

Exercise

Build and deploy a simple EJB and client application with J2EE

RI.

Add a Session bean to register job applicants.

Add Entity beans for the appli cant data and refactor the register

Session bean.

Using Container Managed Persistence (CMP) Refactor the applicant Entity and Container Manage Relationships (CMR) with bean to use CMP.

entity EJBs

Adding transaction management to Session and

Entity EJBs

Add transaction management to the Applicant processing.

Using JMS topics and queues

Using Message-driven beans to implement back office functionality

Develop a simple chat room service.

Use Message-driven beans to match new or changed applicants to advertised jobs.

11

12

13

14

15

16

17

Adding e-mail capabilities to back office functionality

Use e-mail to send matched jobs to applicants.

Developing Web-based applications using servlets Develop a servlet front end to create a new applicant for the

Agency case study.

Developing Web-based applications using Java

Server Pages

Using custom Tag Libraries with JSPs

Use JSPs to register job applicants.

Refactor the register job JSP to use Tag Libraries.

Adding security to restrict access to J2EE application functionality and data

Understanding XML and writing simple XML documents

Using XSL to transform XML documents into different data formats

Add security to control access to the job skills data.

Refactor the messages sent to the back office job/applicant matching functionality to use XML.

Transforming an XLD document into HTML for viewing in a Web browser.

04 0672323842 CH02 3/20/02 9:37 AM Page 75

The J2EE Platform and Roles 75

T

ABLE

2.3

Continued

Day

18

Lesson

Understanding design patterns and recognizing patterns present (and absent) from the case study

19

20

21

Working with legacy systems using the

Connector architecture

Exposing J2EE components as Web Services

Using XML-based registries and asynchronous

Web Services

Exercise

Identify which design patterns can be applied to the case study to improve maintainability.

Identify how the case study could be linked into a legacy invoicing system.

Create a simple Java stock quote class and expose it as a Web

Service. Create a client to retrieve the stock quote information from the server.

Create a Web Service

JobPortal that will take a SOAP message containing new agency customer information and return a generated customer login ID.

By the end of the course, you will have a simple, but working, job agency enterprise application. The agency will have both a GUI-based front end and a Web-based interface and will have given you a good grounding in the relative strengths of each J2EE technologies and how to apply them.

Practice Makes Perfect

Developing J2EE architectures requires two disciplines:

• Good analysis and design skills

• Practical hands-on experience with the J2EE technologies

The first comes with time and experience, but the last few days lessons will help point you in the right direction to becoming a J2EE designer.

The second discipline comes with practice. If you read this book and attempt all the exercises, you will learn a lot more than if you just read the book and simply study the example code shown.

The case study exercises are not complex. They have been designed to take between 30 minutes and 2 hours to complete. The exercises only use information presented and your existing knowledge: you will need to know something about JDBC, Swing, and HTML,

2

04 0672323842 CH02 3/20/02 9:37 AM Page 76

76 Day 2 but you certainly don’t need to be an expert in these technologies. The book “Teach

Yourself Java in 21 Days” from SAMS Publishing is a good source for improving your knowledge of JDBC and Swing should you require a little refresher course. More importantly, the exercises will give you hands-on coding experience using J2EE.

The Case Study Directory on the CD-ROM

On the CD-ROM included with this book, you will find all the Java software required to develop all of the code shown in this book.

The CD-ROM contains a directory called

CaseStudy

. All the example code solutions to the exercises are included in this directory.

There are 21 sub-directories corresponding to each days work. Each day will have one or more of the following directories:

Agency

The complete Agency case study so far. This includes code from the examples in the book and the completed exercise if this is based on the case study.

Examples

The code for all the example programs show in the book where these examples are not part of the case study.

Exercise

Any existing code to be used as a starting point for the exercise.

Typically, this will be the Agency case study, including all the example code in the book but excluding the code the reader needs to provide as part of the exercise.

Solution

A solution to the set problem if the exercise does not enhance the

Agency case study.

Installing the Case Study Database

The Job Agency case study requires a small database for storing information about customers, jobs, applicants, and invoices. A Java program to create the database has been provided in the Day 2 exercises on the accompanying CD-ROM. The program uses the

Cloudscape database provided with the J2EE RI and can easily be adapted to work with any JDBC compatible database.

Find the directory on the CD called

CaseStudy\Day02\Exercise

.

Inside this directory is a Java source file, class file, and two script files:

CreateAgency.java

A source file for a program to create the

Agency database under J2EE RI Cloudscape database.

CreateAgency.class

The compiled Java class file for

CreateAgency.java

04 0672323842 CH02 3/20/02 9:37 AM Page 77

The J2EE Platform and Roles 77

CreateAgency.bat

A Windows NT/2000 batch file to run the application to create the database

CreateAgency.sh

Unix/Linux Bourne shell script to run the application to create the database

To create the Agency database, you will need write permission to the J2EE installation directory.

Follow the instructions shown earlier in today’s lesson for stopping the Cloudscape and

J2EE, servers and stop these servers if they are currently running. If you have installed

J2EE as suggested, you simply have to enter the following commands from a command

(or shell) window: j2ee –stop cloudscape -stop

The Java

CreateAgency program provided in the Day 2 exercises will create the necessary database files in a sub-directory called

Agency

. To create and install the database, you will need to do the following:

1. Copy all the files from the Day 2 exercises directory CD-ROM to the cloudscape sub-directory of the J2EE SDK installation directory.

2. Change directories to the cloudscape sub-directory of the J2EE SDK home directory and then run the appropriate script program as follows:

Under Windows, type cd %J2EE_HOME%\cloudscape

CreateAgency

Under Linux/Unix, type cd $J2EE_HOME/cloudscape

./CreateAgency.sh

A new sub-directory called

Agency will be created under the current cloudscape directory.

3. Having created the database, you must now add a data source called

Agency to

J2EE (data sources are discussed on Day 4, “Introduction to EJBs”). Run the following command to add the data source (the same command is used for both windows and Linux/Unix): j2eeadmin -addJdbcDatasource jdbc/Agency

➥jdbc:cloudscape:rmi:Agency;create=true

If you have not included the J2EE bin directory in your program search path, you will have to run the command as shown below:

2

04 0672323842 CH02 3/20/02 9:37 AM Page 78

78 Day 2

Under Windows, enter the following command:

%J2EE_HOME%\bin\j2eeadmin -addJdbcDatasource

➥jdbc/Agency jdbc:cloudscape:rmi:Agency;create=true

Under Linux/Unix, enter the following command:

$J2EE_HOME/bin/j2eeadmin -addJdbcDatasource

➥jdbc/Agency jdbc:cloudscape:rmi:Agency;create=true

Finally, restart the Cloudscape and J2EE servers as described in the earlier section,

“Starting the J2EE Reference Implementation (RI).” Normally, you would start the J2EE server and Cloudscape database server in separate windows by using the following commands: j2ee –verbose cloudscape -start

If you start the J2EE server as shown with the

-verbose option, you will see the diagnostic output shown in Listing 2.4 (the line showing the

Agency data source configuration is highlighted in bold).

L

ISTING

2.4

The J2EE Reference Implementation Startup Diagnostics

1: > j2ee -verbose

2: J2EE server listen port: 1050

3: Naming service started:1050

4: Binding DataSource, name = jdbc/DB2, url =

➥jdbc:cloudscape:rmi:CloudscapeDB;create=true

5: Binding DataSource, name = jdbc/DB1, url =

➥jdbc:cloudscape:rmi:CloudscapeDB;create=true

6: Binding DataSource, name = jdbc/Agency, url =

jdbc:cloudscape:rmi:Agency;create=true

7: Binding DataSource, name = jdbc/InventoryDB, url =

➥jdbc:cloudscape:rmi:CloudscapeDB;create=true

8: Binding DataSource, name = jdbc/Cloudscape, url =

➥jdbc:cloudscape:rmi:CloudscapeDB;create=true

9: Binding DataSource, name = jdbc/EstoreDB, url =

➥jdbc:cloudscape:rmi:CloudscapeDB;create=true

10: Binding DataSource, name = jdbc/XACloudscape, url = jdbc/XACloudscape__xa

11: Binding DataSource, name = jdbc/XACloudscape__xa,

➥dataSource = [email protected]

12: Starting JMS service...

13: Initialization complete - waiting for client requests

14: Binding: < JMS Destination : jms/Queue , javax.jms.Queue >

15: Binding: < JMS Destination : jms/firstQueue , javax.jms.Queue >

16: Binding: < JMS Destination : jms/Topic , javax.jms.Topic >

17: Binding: < JMS Cnx Factory :

➥QueueConnectionFactory , Queue , No properties >

18: Binding: < JMS Cnx Factory :

➥jms/QueueConnectionFactory , Queue , No properties >

04 0672323842 CH02 3/20/02 9:37 AM Page 79

The J2EE Platform and Roles 79

L

ISTING

2.4

Continued

19: Binding: < JMS Cnx Factory :

➥TopicConnectionFactory , Topic , No properties >

20: Binding: < JMS Cnx Factory :

➥jms/TopicConnectionFactory , Topic , No properties >

21: Starting web service at port: 8000

22: Starting secure web service at port: 7000

23: J2EE SDK/1.3

24: Starting web service at port: 9191

25: J2EE SDK/1.3

26: J2EE server startup complete.

You will test the

Agency database configuration on Day 4 when you learn how to create and deploy a simple EJB.

Congratulations, you have installed J2EE successfully and completed today’s exercise to configure the Agency database for use with the other exercises in this book.

2

04 0672323842 CH02 3/20/02 9:37 AM Page 80

05 0672323842 CH03 3/20/02 9:31 AM Page 81

W

EEK

1

D

AY

3

Naming and Directory

Services

The previous days have discussed the background to enterprise computing concepts and introduced J2EE technologies such as EJBs and Servlets. This chapter will show how the Java Naming and Directory Interface (JNDI) supports the use of many of the J2EE components.

In its simplest form, JNDI is used to find resources (such as EJBs) you have registered via the J2EE server. Advanced use of JNDI supports sophisticated storage and retrieval of Java objects and other information.

This day’s work will include

• Using Naming and Directory Services

• JNDI and X.500 names

• Obtaining a JNDI Initial Context

• Binding and looking up names

• Name attributes

• Objects and References

• JNDI events and security

05 0672323842 CH03 3/20/02 9:31 AM Page 82

82 Day 3

Naming and Directory Services

A Naming Service provides a mechanism for giving names to objects so that you can retrieve and use those objects without knowing the location of the object. Objects can be located on any machine accessible from your network, not necessarily the local workstation.

A real-world example is a phone directory. It stores telephone numbers against names and addresses. To find someone’s phone number is simply a matter of using his or her name (and possibly address) to identify the entry in the phone book and obtain the stored phone number. There are a few complications, such as finding the right phone book to look in, but it is essentially fairly simple.

Incidentally, naming services have a similar problem to that of finding the right phone book. This is known as obtaining a context. A name can only be found if you examine the right context (phone book).

A Directory Service also associates names with objects but provides additional information by associating attributes with the objects.

The yellow pages phone directory is a simple form of a directory service. Here, businesses often include advertisements with additional information such as a list of products sold, professional qualifications, affiliated organizations, and even location maps for their premises. These attributes add value to the name entry. A directory service will normally provide the ability to find entries that have particular attributes or values for attributes.

This is similar to searching the yellow pages phone book for all plumbers running a 24hour emergency service within a certain area.

Yellow page style phone books also store names under different categories—for example, plumbers or lawyers. Categorizing entries can simplify searching for a particular type of entry. These categorized entries are a form of sub-context within the directory context of the local phone book.

Why Use a Naming Service?

Naming Services provide an indispensable mechanism for de-coupling the provider of a service from the consumer of the service. Naming services allow a supplier of a service to register their service against a name. Users, or clients, of the service need only know the name of the service to use it.

Think of the phone book once more, and how difficult it would be to find someone’s phone number without the phone book. Obtaining your friend’s phone number means going to their home and asking, or waiting until you meet up with them again—which may be difficult to organize because you can’t phone them to arrange the meeting.

05 0672323842 CH03 3/20/02 9:31 AM Page 83

Naming and Directory Services 83

The phone book is a directory service. In fact, a phone book is often referred to as a phone directory. The phone directory service lets you look up a person or company’s phone book using their name as a key.

At the end of the day, it is very difficult to imagine a world without naming services.

What is JNDI?

JNDI is a Java API that defines an interface to Naming and Directory Services for Java programs. JNDI is just an API and not, in itself, a Naming and Directory Service. To use

JNDI, an implementation of a Naming and Directory service must be available. JNDI provides a service-independent interface to the underlying Service Provider implementation.

Figure 3.1 shows how the JNDI layer interfaces between the Java program and the underlying naming services. Additional naming services can be plugged into the JNDI layer by implementing the Service Provider Interface (SPI) for JNDI.

F

IGURE

3.1

JNDI Architecture.

Java Program

JNDI Application programming Interface (API)

Naming Manager

LDAP

JNDI Service Provider Interface (SPI)

DNS NDS CORBA RMI NIS

???

Common Naming Services

Figure 3.1 shows that JNDI supports several well-known naming services, including the following:

• Domain Name System (DNS) is the Internet naming service for identifying machines on a network.

• Novell Directory Services (NDS) from Novell provides information about network services, such as files and printers. NDS is found primarily in environments where the main networking software is Novell.

• Network Information Service (NIS) from Sun Microsystems provides system-wide information about machines, files, users, printers, and networks. NIS is primarily found on Solaris systems, but Linux and some other Unix platforms support it.

3

05 0672323842 CH03 3/20/02 9:31 AM Page 84

84 Day 3

• Lightweight Directory Access Protocol (LDAP) is the approved standard for an

Internet naming service. LDAP is a true directory service and supports attributes as well as names for objects. LDAP is fast becoming the de-facto directory service for the enterprise.

JNDI also supports some more specialized naming systems. For example, CORBA for distributed component programming and RMI for distributed Java programming.

Although there is no named service provider for Windows Active Directory, it is supported. Windows Active Directory supports an LDAP interface, and you can access it via the

JNDI LDAP Service Provider Interface.

Naming Conventions

Each naming service has its own mechanism for supplying a name. Perhaps the most familiar naming convention is that of DNS, where every machine connected to the

Internet has a unique name and address. Most readers should recognize the following as a host name used by DNS: www.samspublishing.com

In contrast, LDAP names are based on the X.500 standard and use distinguished names that look like the following fictitious example: cn=Martin Bond, ou=Authors, o=SAMS, c=us

This format will also be familiar to users of Microsoft’s Active Directory service, whose naming system is also based on X.500 but uses a forward slash to separate the various name components: cn=Martin Bond/ou=Authors/o=SAMS/c=us

These last two naming conventions have similarities in that they are both hierarchically structured with the most specific name occurring first and the most general name (or context) occurring last.

JNDI provides classes that support creating and manipulating structured names; but most programmers will use simple strings that JNDI passes on to the underlying service with minimal interpretation.

Some JNDI Service Providers may use names that are case sensitive, and some service providers may not, it all depends on the underlying technology and environment. To maintain portability of your applications, it is always best to avoid names that differ only by letter case and also ensure that names are always spelled in a consistent manner.

05 0672323842 CH03 3/20/02 9:31 AM Page 85

Naming and Directory Services 85

Using JNDI

JNDI is a standard component of JDK 1.3 and is, therefore, also part of J2EE 1.3. JNDI is also included in J2EE 1.2 and is available as a standard Java extension for JDK 1.2

and earlier.

While developing code, the program’s

CLASSPATH must include the location of the JNDI class libraries. As long as the

JAVA_HOME environment variable has been set up, the JNDI classes will be available to the Java compiler.

Running a JNDI-aware program requires a JNDI service to be running and the classes for that service to be available to the program. Typically, this requires the

CLASSPATH to include one or more JAR files provided by the JNDI provider or a J2EE server vendor.

For implementation-specific details, see the vendor’s documentation.

By default, running a J2EE server starts a naming service on the same machine. If the default behavior isn’t required, you must change the J2EE server configuration to use an existing JNDI server.

Using Sun Microsystems’ J2EE Reference

Implementation

Using JNDI with Sun Microsystems’ J2EE Reference Implementation (RI) is straightforward. Ensure that

• The

J2EE_HOME variable exists

• The

CLASSPATH variable includes the j2ee.jar

file from the lib directory of the

J2EE home directory

Examples of how to do this both for Windows and for Unix are shown in this section.

J2EE RI for Windows

Under systems running Microsoft Windows NT or 2000, you can set the class path interactively with the following:

Set CLASSPATH=%J2EE_HOME%\lib\j2ee.jar;%CLASSPATH%

Typically, it is better to set the class path as a system-wide environment variable (via the

My Computer properties dialog). A suitable value is as follows:

.;%J2EE_HOME%\lib\j2ee.jar

The class path can also include additional JAR files and directories for other Java components.

3

05 0672323842 CH03 3/20/02 9:31 AM Page 86

86 Day 3

It is important to define the current directory (

.

) in the class path; otherwise, the Java compiler and runtime systems will not find the classes for the program being developed.

J2EE RI for Linux and Unix

Under Linux and Unix, set the class path with the following:

CLASSPATH=$J2EE_HOME/lib/j2ee.jar:$CLASSPATH

Starting the JNDI Server

Startup the J2EE RI server, as Day 2, “The J2EE Platform and Roles,” described, and the

JNDI server will start at the same time. You start the J2EE server by entering the following command from a command-line window: j2ee –verbose

The J2EE server will run in that window until you close the window down or enter the following shutdown command from another command-line window: j2ee -stop

Obtaining an Initial Context

The first step in using the JNDI name service is to get a context in which to add or find names. The context that represents the entire namespace is called the Initial Context and is represented by a class called javax.naming.InitialContext

and is a sub-class of the javax.naming.Context

class.

A

Context object represents a context that you can use to look up objects or add new objects to the namespace. You can also interrogate the context to get a list of objects bound to that context.

The javax.naming

package contains all the simple JNDI classes. Sub-packages within the javax.naming

package provide additional JNDI functionality, such as directorybased features like attributes.

The following code creates an initial context using the default JNDI service information:

Context ctx = new InitialContext();

If something goes wrong when creating the initial context, a

NamingException is thrown.

Initial Context Naming Exceptions

The runtime system reports errors in using JNDI as a subclass of

NamingException

. The exceptions most likely to occur for accessing the initial context are as follows:

05 0672323842 CH03 3/20/02 9:31 AM Page 87

Naming and Directory Services 87 javax.naming.CommunicationException: Can’t find SerialContextProvider

This exception usually means the JNDI Server is not running, or possibly the JNDI properties for the server are incorrect (see the next section, “Defining the JNDI Service”).

javax.naming.NoInitialContextException:

➥Need to specify class name in environment or system property, or as an applet

➥parameter, or in an application resource file: java.naming.factory.initial

This exception occurs when the

IntialContext class does not have default properties for the JNDI Service Provider, and the JNDI server properties have not be configured explicitly (see the next section, “Defining the JNDI Service”).

javax.naming.NoInitialContextException: Cannot instantiate class: XXX

[Root exception is java.lang.ClassNotFoundException: XXX]

This exception occurs when the class path defined for the JNDI program does not include the JNDI server classes (see the next section, “Defining the JNDI Service”). javax.naming.ServiceUnavailableException:

➥Connection refused: no further information

[Root exception is java.net.ConnectException:

➥Connection refused: no further information]

This exception occurs when the JNDI properties for the program fail to match the JNDI

Service Provider currently in use (see the next section, “Defining the JNDI Service”).

Defining the JNDI Service

During program development, it is reasonable to use a JNDI service running on the local machine that uses the default service provider supplied with the J2EE server. When you deploy the program, you must make use of the enterprise-wide naming service for your site. You will need to configure the program to use a specific naming server and not the default one provided with your test J2EE server.

The parameters that you usually need to define for the JNDI service are as follows:

• JNDI service classname

• Server’s DNS host name

• Socket port number

A particular server vendor’s implementation may require additional parameters.

There are several ways of defining the JNDI service properties for a program, but you only need to use one of them. You can either

• Add the properties to the JNDI properties file in the Java runtime home directory

• Provide an application resource file for the program

3

05 0672323842 CH03 3/20/02 9:31 AM Page 88

88 Day 3

• Specify command-line parameters to be passed to an application

• Specify parameters to be passed into an applet

• Hard-code the parameters into the program

The last option is the weakest approach, because it restricts the program to working with one type of JNDI service provider on one specific host.

The first two options are the most suited to production environments. They both require that you distribute simple text configuration files with the program.

JNDI Properties Files

An application resource file called jndi.properties defines the JNDI service. The

JNDI system automatically reads the application resource files from all components in the program’s

CLASSPATH and from lib/jndi.properties

in the Java runtime home directory (this is the jre sub-directory of the Java JDK home directory).

The following example from Sun Microsystems’ J2EE RI shows a typical jndi.properties file: java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory

java.naming.provider.url=localhost:1099 java.naming.factory.url.pkgs=com.sun.enterprise.naming

Each entry in the property file defines a name value pair. The

InitialContext object uses these properties to determine the JNDI service provider.

The J2EE server vendor usually supplies a sample jndi.properties

file defining the properties that need to be configured with their server. You can find the J2EE RI jndi.properties

file in the lib/classes directory in the J2EE RI installation directory.

Normally, any given JNDI service will require the following named properties: java.naming.provider.url

This defines the DNS host name of the machine running the JNDI service and the service port number. This is the only property that the network administrator needs to customize.

The property value is a machine name with an optional colon and port number. By default, JNDI uses port 1099, and most sites do not change this value. The default server is usually localhost

.

Consider a host called nameserver in the Sams Publishing domain

( samspublishing.com

), the full URL including port number for a default JNDI server on this host would be as follows: nameserver.samspublishing.com:1099 java.naming.factory.initial

05 0672323842 CH03 3/20/02 9:31 AM Page 89

Naming and Directory Services 89

You set this property to the classname (including the package) of the Initial Context

Factory for the JNDI Service Provider. This value effectively determines which JNDI

Service Provider you use. To use the default Naming Service supplied with the J2EE RI, you would specify this property as follows: java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory

java.naming.factory.url.pkgs

This property defines prefix package names the

InitialContext class uses for finding other classes JNDI requires. The J2EE RI uses the following value for this property: java.naming.factory.url.pkgs=com.sun.enterprise.naming

More information on these and other JNDI properties can be found in the API documentation for the

Context class and in the JNDI Tutorial from Sun Microsystems.

The simplest way to define the JNDI Service Provider is to configure every client’s Java home directory to include the necessary JNDI properties. This approach suits an intranet where all machines are centrally managed.

Another approach is to include a suitable JNDI properties file with the client program and distribute everything as a JAR file (program class files and the jndi.properties

file). This suits Web-based intranets or extranets, where applets are used or where you can distribute the client JAR file to users.

Application Properties

Using the

-D option, you can supply the JNDI properties on the java command line of an application. This has the disadvantage of requiring long command lines that are hard to remember and easy to mistype. A way around this problem is for you to provide script files to run the application on the target platforms; typically, you will supply batch files for Windows and shell scripts for Linux and Unix.

The following is an example of a command line that defines the JNDI factory classes and server: java

➥-D java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory

➥–D java.naming.provider.url=localhost:1099 MyClass

Providing a jndi.properties

file in the application JAR file is a cleaner solution than providing command-line parameters. However, using command-line parameters makes the JNDI properties more apparent when customizing the application for a local site. It is easy to overlook a jndi.properties

file in a JAR file.

3

05 0672323842 CH03 3/20/02 9:31 AM Page 90

90 Day 3

Applet Parameters

An applet can accept the JNDI properties as parameters, for example

<applet code=”MyApplet.class” width=”640” height=”480”>

<param name=”java.naming.factory.initial” value= “com.sun.enterprise.naming.SerialInitContextFactory” >

<param name=”java.naming.provider.url”

“value= localhost:1099”>

</applet>

Using parameters with the applet HTML file makes the JNDI properties more apparent when customizing the applet for a local site. A jndi.properties

file in the jar file is easily overlooked.

Hard-Coded Properties

The least desirable way to specify the JNDI properties is via hard-coded values in the program. Hard coding the properties means including the JNDI classnames and the server name in the source code. This is undesirable because it means that should the network architecture change, you must edit, recompile, and redistribute the program. Obviously, you want to avoid this maintenance overhead if you can. The network architecture may change if the JNDI service moves to a different server or you install a new JNDI Service

Provider.

The mechanism for defining the service in code is via a hash table of properties passed into the

InitialContext constructor:

Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY,

“com.sun.enterprise.naming.SerialInitContextFactory”); env.put(Context. PROVIDER_URL,

“localhost:1099”);

Context ctx = new InitialContext(env);

Notice how the code uses symbolic constants from the

Context class rather than using strings representing the properties (such as

“java.naming.factory.initial”

). This approach makes the code more portable should the property names change in future versions of Java or JNDI.

Binding JNDI Objects

After the initial JNDI context has been obtained, a program can look up existing objects and bind new objects to the context.

When working with EJBs, the main JNDI activity is to look up existing bound objects; the J2EE server does most of the binding of the objects automatically.

05 0672323842 CH03 3/20/02 9:31 AM Page 91

Naming and Directory Services 91

Because this section discusses the binding of objects, you can skip it if your primary purpose for using JNDI is to obtain EJB and other references within a J2EE application.

Binding Objects

Binding an object means adding a name to the JNDI service and associating that name with a Java object. The name and object are bound to a context. Listing 3.1 shows how a text message can be bound to the name sams/book

.

L

ISTING

3.1

Full Text of

JNDIBind.java

1: import javax.naming.*;

2: public class JNDIBind

3: {

4: private final static String JNDI = “sams/book”;

5:

6: public static void main(String[] args) {

7: try {

8: Context ic = new InitialContext();

9: ic.bind(JNDI,”Teach Yourself J2EE in 21 Days”);

10: System.out.println(“Bound “+JNDI);

11: }

12: catch (NamingException ex) {

13: System.err.println(ex);

14: System.exit(1);

15: }

16: }

17: }

The object to be bound must implement the

Serializable interface so that the name server can store a copy of the object.

The

Context.bind() method will fail with a

NameAlreadyBoundException

(which extends

NamingException

) if an object is already bound to that name. Another subclass of

NamingException is thrown if there is some other form of error, such as an invalid name. Remember that different Service Providers may have different naming conventions.

Binding Problems

A Service Provider may not support binding of all types of objects. If the service cannot bind a particular object, it will throw an exception.

Using the default naming service for J2EE RI that uses a transient CORBA naming service, the class of the object must be in the

CLASSPATH used by the J2EE RI JNDI server.

For now, this means using standard J2SE and J2EE classes or configuring the J2EE RI services to include your class files. The recommended approach is to edit the user

3

05 0672323842 CH03 3/20/02 9:31 AM Page 92

92 Day 3 configuration file ( userconfig.sh

or userconfig.bat

) in the bin directory of the J2EE

RI home directory, and add the required class directories or JAR files to the

J2EE_CLASS-

PATH variable defined in the configuration file.

An alternative is to use a Web Service to dynamically upload the required class files.

Dynamic uploading of class files is dicussed in the “Loading Classes from a Code Base” section, later in this chapter.

Some Naming Services (such as LDAP) may use security features to ensure that only authorized programs can bind new objects. The bind() method can also fail if it violates any security features of the underlying naming service. The “Security” section of today’s lesson covers this in more detail.

Name Persistence

A bound object normally remains in the namespace until it is unbound. If the bound name remains across server restarts, it is said to be persistent. Commercial servers, such as NDS, Active Directory, and LDAP, are persistent name servers and store the bound names and ancilliary information on disk (typically in a database).

The default naming service for Sun Microsystems’ J2EE RI is a transient service; it reloads bound objects from configuration files in the SDK home directory whenever it is restarted. This naming service will not retain objects bound with the

Context.bind() method across server restarts.

Rebinding Objects

You can use the rebind() method to solve the problem of bind() failing if a name is already bound. For example, ic.rebind(“sams/book”,”Teach Yourself J2EE in 21 Days”);

The code unbinds any existing object bound to that name and binds the new object in its place.

Using rebind() is a good design technique when a programmer is sure the name will not be in use by another component. The alternative is to explicitly unbind the old name first if it is in use as discussed in the next section on “Unbinding Objects.”

Unbinding Objects

You can remove an object from a namespace by using the

Context.unbind() method. A program uses this method when it is closing down and needs to remove its advertised service because a bound name is not automatically unbound when the program shuts down.

05 0672323842 CH03 3/20/02 9:31 AM Page 93

Naming and Directory Services 93

Another common use for unbind() is to test if a name is already in use and unbind the old object before binding a new object. The advantage of using unbind() in preference to rebind() is that you can verify that the object to be unbound is at least of the same type as the new object to be bound.

String JNDI = “sams/book”; try {

Object o = ic.lookup(JNDI); if (o instanceof String) ic.unbind (JNDI);

} catch (NameNotFoundException ex) {} ic.bind(JNDI,”Teach Yourself J2EE in 21 Days”);

This example rebinds a string bound to the name sams/book, but will fail with a

NameAlreadyBoundException if the name is bound to another class of object. This is a better design approach than that of using the rebind() method.

Renaming Objects

You can rename objects using

Context.rename() by specifying the old name and then the new name as parameters. The new name must specify a name in the same context as the old name. An object must be bound to the old name, and the new name must not have a bound object; otherwise, a

NamingException is thrown.

ic.rename(“sams/book”,”sams/teachyourself”);

JNDI Name Lookup

The most common use of JNDI is to look up objects that have been bound to a name. To do this, you require two items of information:

• The JNDI name

• The class of the bound object

With this information in hand, object lookup is the simple matter of using the

Context.lookup() method to find the object and then to cast that object to the required class.

Listing 3.2 shows a simple program to look up the name sams/book that was bound by the program in Listing 3.1.

L

ISTING

3.2

Full Text of

JNDILookup.java

1: import javax.naming.*;

2: public class JNDILookup

3

05 0672323842 CH03 3/20/02 9:31 AM Page 94

94 Day 3

L

ISTING

3.2

Continued

3: {

4: private final static String JNDI = “sams/book”;

5: public static void main(String[] args) {

6: try {

7: Context ic = new InitialContext();

8: String name = (String)ic.lookup(JNDI);

9: System.out.println(JNDI+”=”+name);

10: }

11: catch (NamingException ex) {

12: System.err.println(ex);

13: System.exit(1);

14: }

15: catch (ClassCastException ex) {

16: System.err.println(ex);

17: System.exit(1);

18: }

19: }

20: }

You can run the

JNDIBind program in Listing 3.1 and then run this

JNDILookup program to print out the value of the string bound against sams/book

.

Note

When casting an object that the lookup() method returns, that object’s class must be in the client program’s class path. If this is not the case, the program throws an exception.

Changing Contexts

The example name sams/book used in Listings 3.1 and 3.2 is an example of a Composite

Name. If you need to look up many names in the same context of a composite name

(names of the form sams/...

), it is better to change to sub-context and look up the simple name within that context.

With this information in hand, the sub-context is a name entry just like any other name, and you look it up in just the same way. The retrieved object is another

Context object.

Listing 3.3 shows code that retrieves a name from a sub-context.

L

ISTING

3.3

Full Text of

JNDILookupSAMS.java

1: import javax.naming.*;

2: public class JNDILookupSAMS

3: {

05 0672323842 CH03 3/20/02 9:31 AM Page 95

Naming and Directory Services 95

L

ISTING

3.3

Continued

4: public static void main(String[] args) {

5: try {

6: Context ic = new InitialContext();

7: Context ctx = (Context)ic.lookup(“sams”);

8: String name = (String)ctx.lookup(“book”);

9: System.out.println(name);

10: }

11: catch (NamingException ex) {

12: System.err.println(ex);

13: System.exit(1);

14: }

15: catch (ClassCastException ex) {

16: System.err.println(ex);

17: System.exit(1);

18: }

19: }

20: }

Narrowing RMI-IIOP Objects

There is only one additional twist to the lookup tale, and that is when dealing with RMI over IIOP objects.

The implementation of J2EE requires the use of RMI-IIOP to implement the remote interfaces to EJB components. Consequently, when a lookup is for an EJB name (more on this on Day 4, “Introduction to EJBs”), you cannot cast the returned object to the required class; instead, you must narrow it.

RMI-IIOP uses a portable remote object to encapsulate information about the real remote object. A portable remote object contains information about the real bound object in a portable format that can be interogated by the recipient to find the real remote object.

The process of obtaining the real object from the portable remote object is called nar-

rowing.

You use the

PortableRemoteObject.narrow() method in the javax.rmi

package to narrow a protable remote object to obtain the actual remote object. The narrow() method takes two parameters:

• The object to narrow

• A java.lang.Class

object defining the real remote object’s class

Listing 3.4 previews the discussion on Day 4 about EJB objects, but also serves to illustrate the use of the narrow() method.

3

05 0672323842 CH03 3/20/02 9:31 AM Page 96

96 Day 3

L

ISTING

3.4

Narrowing an EJB Home Object

1: InitialContext ic = new InitialContext();

2: Object lookup = ic.lookup(“java:comp/env/ejb/Agency”);

3: AgencyHome home = (AgencyHome)

➥PortableRemoteObject.narrow(lookup, AgencyHome.class);

If your primary purpose for understanding JNDI is to enable the lookup and use of EJBs and other J2EE technologies (such as JDBC data sources and Message queues), you can skip the rest of this day’s material and return to it at a later date.

Contexts

Contexts provide a hierarchical structure to JNDI names, and composite names group together related names. The initial context provides a top-level view of the namespace and any sub-contexts reflect the hierarchical composite name structure.

Listing Contexts

The namespace represents contexts as names, and you can look these up just like any other name. You can obtain a listing of the names in a context by using

Context.list()

.

This method provides a list of name and class bindings as a javax.naming.NamingEnumeration

, where each element in the enumeration is a javax.naming.NameClassPair

object. Listing 3.5 shows a simple program to list the names and classes for the example sams sub context.

L

ISTING

3.5

Full Text of

JNDIListSAMS.java

1: import javax.naming.*;

2: public class JNDIListSAMS

3: {

4: public static void main(String[] args)

5: {

6: try {

7: Context ctx = new InitialContext();

8: NamingEnumeration list = ctx.list(“sams”);

9: while (list.hasMore()) {

10: NameClassPair item = (NameClassPair)list.next();

11: String cl = item.getClassName();

12: String name = item.getName();

13: System.out.println(cl+” - “+name);

14: }

15: }

16: catch (NamingException ex) {

17: System.out.println (ex);

18: System.exit(1);

05 0672323842 CH03 3/20/02 9:31 AM Page 97

Naming and Directory Services 97

L

ISTING

3.5

Continued

19: }

20: }

21: }

You must run the

JNDIBind program from Listing 3.1 before running this program; otherwise, the “ sams

” sub context will not exist. Running the program in Listing 3.5 will produce a single line of output: java.lang.String – book

The parameter to the list() method defines the name of the context to list. If this is the empty string, the method lists the current context.

If the initial context of the J2EE RI namespace is listed, you must have the J2EE RI classes in your search path; otherwise, you will get an org.omg.CORBA.BAD_PARAM

exception caused by another CORBA exception: org.omg.CORBA.MARSHAL: Unable to read value from underlying bridge :

➥ Serializable readObject method failed internally

➥ minor code: 1398079699 completed: Maybe

Don’t believe the completed: Maybe tagged on to the end of the error message. It didn’t complete.

The easiest solution to this problem is to run the setenv script in the bin directory of

J2EE RI. This script creates a variable

CPATH that you can use as the

CLASSPATH for running J2EE RI client programs.

Under Windows use

%J2EE_HOME%\bin\setenv java –classpath .;%CPATH% JNDIList

Under Linux and Unix use

$J2EE_HOME/bin/setenv java –classpath .:$CPATH JNDIList

The CD-ROM accompanying this book includes the

JNDIList program, which is the same as the program in Listing 3.5 but without the parameter to the list() method.

The list() method returns the name and the bound object’s classname, but not the object itself. It is a lightweight interface designed for browsing the namespace.

A second method, called

Context.listBindings()

, retrieves the object itself. The listBindings() method returns a

NamingEnumeration

, where each element is of type

3

05 0672323842 CH03 3/20/02 9:31 AM Page 98

98 Day 3 javax.naming.Binding

. Access methods in the

Binding class support retrieval of the information of the bound object. Listing 3.6 shows a simple recursive tree-walking program that is a useful diagnostic tool for examining JNDI namespaces.

L

ISTING

3.6

Full Text of

JNDITree.java

1: import javax.naming.*;

2: public class JNDITree

3: {

4: public static void main(String[] args) {

5: Context ctx=null;

6: try {

7: ctx = new InitialContext();

8: listContext (ctx,””);

9: }

10: catch (NamingException ex) {

11: System.err.println (ex);

12: System.exit(1);

13: }

14: }

15:

16: private static void listContext (Context ctx, String indent) {

17: try {

18: NamingEnumeration list = ctx.listBindings(“”);

19: while (list.hasMore()) {

20: Binding item = (Binding)list.next();

21: String className = item.getClassName();

22: String name = item.getName();

23: System.out.println(indent+className+” “+name);

24: Object o = item.getObject();

25: if (o instanceof javax.naming.Context)

26: listContext ((Context)o,indent+” “);

27: }

28: }

29: catch (NamingException ex) {

30: System.err.println (“List error: “+ex);

31: }

32: }

33: }

Creating and Destroying Contexts

Binding a composite name will automatically create any intermediate sub-contexts required to bind the name. Binding the name com/sams/publishing/book/j2ee in 21 days creates the following sub-contexts if they don’t already exist: com com/sams com/sams/publishing com/sams/publishing/book

05 0672323842 CH03 3/20/02 9:31 AM Page 99

Naming and Directory Services 99

You can explicitly create contexts with the

Context.createSubcontext() method. The single method parameter is the name of the context. If this is a composite name, all intermediate contexts must already exist. The createSubContext() method will throw a

NameAlreadyBoundException if the name already exists.

Note Contrary to the API documentation, the J2EE RI naming service will automatically create any intermediate contexts.

Listing 3.7 shows a simple program for creating arbitrary contexts.

L

ISTING

3.7

Full Text of

JNDICreate.java

1: import javax.naming.*;

2: public class JNDICreate

3: {

4: public static void main(String[] args) {

5: try {

6: if (args.length != 1) {

7: System.out.println (“Usage: JNDICreate context”);

8: System.exit(1);

9: }

10: Context ic = new InitialContext();

11: ic.createSubcontext(args[0]);

12: }

13: catch (NamingException ex) {

14: System.err.println(ex);

15: System.exit(1);

16: }

17: }

18: }

The

Context.destroySubcontext() method can destroy contexts. Again, the single method parameter is the name of the context. The context does not have to be empty, because the method will remove from the namespace any bound names and sub-contexts with the destroyed context.

Listing 3.8 shows a simple program for deleting arbitrary contexts.

Use this program with caution, because destroying the wrong context will render your

J2EE server unusable. If you are using the J2EE RI, restarting the J2EE server can rectify a mistake; this might not be the case with other servers.

3

05 0672323842 CH03 3/20/02 9:31 AM Page 100

100 Day 3

L

ISTING

3.8

Full Text of

JNDIDestroy.java

1: import javax.naming.*;

2: public class JNDIDestroy

3: {

4: public static void main(String[] args) {

5: try {

6: if (args.length != 1) {

7: System.out.println (“Usage: JNDIDestroy context”);

8: System.exit(1);

9: }

10: Context ic = new InitialContext();

11: ic.destroySubcontext(args[0]);

12: }

13: catch (NamingException ex) {

14: System.err.println(ex);

15: System.exit(1);

16: }

17: }

18: }

The destroyContext() method can throw a

NameNotFoundException if the name doesn’t exist and a

NotContextException if the bound name is not a context.

More on JNDI Names

JNDI has to support different naming conventions for different Service Providers in the most transparent manner possible. Generally, programmers will specify JNDI names as strings, but a little understanding of how JNDI interprets bound names will help circumvent many simple problems that can occur when using names.

Special Characters

JNDI applies minimal interpretation to names specified as

String objects. JNDI uses the forward slash character (

/

) as a name separator to provide a simple name hierarchy called a Composite Name. It is conventional for these composite names to be used to group related names (such as plumbers in the phone book). As an example, JDBC data sources take names of jdbc/XXX and EJBs the form ejb/XXX

. While this is only a convention, it does help separate different sorts of named objects within the JNDI name space.

Composite and Compound Names

Composite names can span different naming systems. An LDAP name can combine with a file system name to get a composite name: cn=Martin Bond, ou=Authors, o=SAMS, c=us/agency/agency.ldif

05 0672323842 CH03 3/20/02 9:31 AM Page 101

Naming and Directory Services 101

Here a filename ( agency/agency.ldif

) is appended to an LDAP name. How JNDI interprets this is up to the individual Service Provider.

Incidentally, JNDI calls structured names like the DNS and LDAP compound names.

JNDI does not interpret compound names, but simply passes them through to the Service

Provider.

Besides forward slash (

/

), JNDI also treats backslash (

\

), single quote (

), and double quote (

) characters as special. If a compound name or a component of a name contains any of these characters, they must be escaped using the backslash character (

\

).

If the underlying Service Provider uses the forward slash as a name separator (for example, the CORBA name service), there appears to be a conflict between JNDI and the

Service Provider. In practice, there is unlikely to be a problem because JNDI recognizes two sorts of name separation—weak and strong. JNDI always passes the entire name to the Service provider. A strong name separation implementation (such as LDAP or DNS) simply processes the first part of the composite name and returns the remainder to the

JNDI Naming Manager to pass on to other name services. A weak name separation implementation will simply process the entire composite name.The

COSNaming server used in the J2EE RI uses weak separation, as does the

RMIRegistry naming service.

For those programmers who need to do more that use names to look up and bind objects,

JNDI provides several classes for manipulating and parsing composite and compound names. The JNDI name support classes in the javax.naming

package are

Name

,

CompositeName

, and

CompoundName

.

URLs

In certain contexts, JNDI recognizes a URL (Uniform Resource Locator). The primary use of URLs is to identify the JNDI server usually through the java.naming.provider.url

property, as shown in the following: java.naming.provider.url=ldap://localhost:389

You can also specify a URL as a parameter to the lookup() and bind() methods in the

Context class. For example,

Context ic = new InitialContext();

Object obj = ic.lookup(“ldap://localhost:389/cn=Winston,dc=my-domain,dc=com”);

This overrides the default context and forces JNDI to perform the lookup against the specified server. You need to take care with this approach, because the class path must contain the necessary Service Provider classes, and these must be able to process the request bind or lookup operation. In practice, this means that the URL must use the same

Service Provider classes as the initial context.

3

05 0672323842 CH03 3/20/02 9:31 AM Page 102

102 Day 3

Attributes

Attributes are a feature of a Directory service and are not available with simple name servers. Typically, you use attributes with an LDAP server. The J2EE RI JNDI server is a

CORBA name server, and it does not support attributes.

An attribute is additional information stored with a name. Storing full name, address, phone number, and e-mail with a person’s name is a common use of a directory service.

NDS uses attributes to control access to shared network drives and to configure a user’s login environment.

A directory service stores attributes as values against a keyword (LDAP calls them IDs).

Directory services usually support searching for names (objects) that have certain attributes defined (or not defined). Searching often supports looking for names with certain attributes that have a specific value (often wildcard pattern matching is supported). A simple search of a personnel database under an LDAP server might be to find all names whose surname is Washington.

LDAP uses a schema system to control which attributes an object must define and those that it may define. Any attributes that you add or delete must not break the schema’s requirements. LDAP servers may be able to disable schema checking, but this is usually a bad idea because the schema was created for a purpose.

If you want to see the capabilities of attributes, you must have access to a directory server. The rest of this section is based on using an LDAP Directory Server.

Overview of LDAP X.500 Names

LDAP names conform to the X.500 standard that requires a hierarchical namespace. A

Distinguished Name (DN) unambiguously identifies each entry in the directory. The DN consists of the concatenation of the names from the root of the directory tree down to the specific entry.

X.500 focuses on the interoperability of different directory services rather than specifying how a directory service should define the DN. Consequently, different implementations of X.500 can each use a different syntax for representing object names (as shown earlier when we compared LDAP and Microsoft Active Directory names).

The official specification for the X.500 Directory Service is available from the

International Telecommunications Union (ITU) Web site at http://www.itu.int/rec/recommendation.asp?type=items&lang=e&parent=T-REC-

X.500-199708-S

.

LDAP uses a comma-separated list of names with the names specified from the lowest entry up the tree to the higher entries.

05 0672323842 CH03 3/20/02 9:31 AM Page 103

Naming and Directory Services 103

Names consist of name and value pairs with the names typically being those in the following list. Each name has a short code and a full name; it is usual to only use the short code because the Distinguished Names are fairly long.

• c

( countryName

) — ISO two letter code for county such as us

, uk

, and so forth.

• o

( organizationName

) — Organization or company name, such as samspublishing

• ou

( organizationUnitName

) — Organizational unit, typically a division or department within an organization

• l

( localityName

) — Typically defines a location within an organizational unit

• cn

( commonName

) — Common name (sometimes called personal name), usually the name of the user or client

• dc

( domainComponent

) — A component part of a domain name (such as DNS names)

• uid

( userid

) — Typically represents a login name

An example LDAP DN looks like the following: cn=Martin Bond, ou=Authors, o=SAMS, c=us

This will be a familiar structure if you work with digital certificates whose names conform to the X.509 standard.

Obtaining an LDAP Server

Using an LDAP Directory Service requires the JNDI properties to specify the JNDI

Service provider from Sun Microsystems and to have an LDAP server running.

The J2EE RI does not include an LDAP server, so you will have to obtain one from elsewhere. Only certain operating systems provide LDAP servers. Windows NT, 2000, and

XP users will have to purchase the enterprise (or server) editions of the operating system, which are typically significantly more expense than the usual desktop or professional editions. Sun Microsystems’ Solaris 8 Operating Environment includes an LDAP server.

Linux and Unix users can download and install the OpenLDAP implementation, which is an open source server available free of charge for personal use. The Open LDAP server can be downloaded from http://www.openldap.org/software/download/

.

Users of Microsoft Windows will have to make other arrangements as OpenLDAP is not available for the platform. If an Active Directory server is accessible on the network or you use the Enterprise (or Server) edition of the Operating System, this is a simple solution. Otherwise, Windows users are well advised to find a spare PC and install Linux on that and use OpenLDAP.

3

05 0672323842 CH03 3/20/02 9:31 AM Page 104

104 Day 3

Note Interestingly, with today’s low cost of hardware, it may be cheaper for the home user to purchase a second system to run Linux rather then purchase the additional license required to run the Enterprise (or Server) versions of

Windows NT/2000/XP.

Using OpenLDAP

Given that many users experimenting with LDAP will probably use OpenLDAP on a

Linux server, a brief digression on configuring Linux/LDAP for the programs in the rest of this section is useful. Other users must adapt the configuration for their own LDAP servers.

Note

You only need to install Open LDAP if you want to evaluate the directory service features of JNDI and do not have access to a suitable directory service on your network. The following discussion of LDAP is not necessary for your understanding and use of J2EE.

The rest of this sub-section assumes that you have some knowledge of Linux and

OpenLDAP (at the very least, that you have successfully installed OpenLDAP on a Unix server). The following OpenLDAP dicussion assumes that you have installed OpenLDAP in the default location of

/usr/local

(for example OpenLDAP v2.0.15 will be in the directory

/usr/local/openldap- 2.0.15

).

If you build and install OpenLDAP according to the supplied instructions, the process results in an empty LDAP directory namespace. The following steps will populate that namespace with a few sample entries. Make sure the slapd

(LDAP) server is not running before making the following changes (if you have just installed OpenLDAP then slapd will not be running).

If you install OpenLDAP 2.0 with the recommended Berkeley database, it creates a default empty database with the DN suffix of dc=my-domain,dc=com

.

To create a new DN suffix of o=Agency,c=US

, you must add the following lines to the end of the slapd configuration file (

/usr/local/etc/openldap/slapd.conf

): database ldbm suffix “o=Agency,c=US” directory /usr/local/var/openldap-ldbm rootdn “cn=Manager,o=Agency,c=US” rootpw secret index objectclass eq index cn,sn pres,eq,sub,subany

05 0672323842 CH03 3/20/02 9:31 AM Page 105

Naming and Directory Services 105

Having defined the DN, you must add some sample data to the database. Listing 3.9

shows a configuration file for populating the database with sample data for use with the example programs shown in this section.

L

ISTING

3.9

Full Text of agency.ldif

1: dn: o=Agency,c=us

2: objectclass: top

3: objectclass: organization

4: o: Agency

5: description: Job Agency

6:

7: dn: ou=Customers,o=Agency,c=us

8: objectclass: top

9: objectclass: organizationalUnit

10: ou: Customers

11:

12: dn: cn=All Customers,ou=Customers,o=Agency,c=us

13: objectclass: top

14: objectclass: groupofnames

15: member: cn=Winston,ou=Customers,o=Agency,c=us

16: member: cn=George,ou=Customers,o=Agency,c=us

17: cn: Customers

18:

19: dn: cn=Manager,o=Agency,c=us

20: objectclass: top

21: objectclass: person

22: cn: Manager

23: sn: Manager

24:

25: dn: cn=George,ou=Customers,o=Agency,c=us

26: objectclass: top

27: objectclass: person

28: cn: George

29: cn: George Washington

30: description: President

31: sn: Washington

32:

33: dn: cn=Abraham,ou=Customers,o=Agency,c=us

34: objectclass: top

35: objectclass: person

36: cn: Abraham

37: cn: Abraham Lincoln

38: description: President

39: sn: Lincoln

40:

41: dn: cn=Winston,ou=Customers,o=Agency,c=us

42: objectclass: top

43: objectclass: person

3

05 0672323842 CH03 3/20/02 9:31 AM Page 106

106 Day 3

L

ISTING

3.9

Continued

44: cn: Winston

45: cn: Winston Churchill

46: description: Prime Minister

47: sn: Churchill

Copy this file to the Linux server and call it agency.ldif

. Ensure that the slapd

LDAP server is not running and, using the following slapdadd commandto install the sample names in the OpenLDAP configuration: slapadd -b “o=Agency,c=US” –l agency.ldif

You can check the database configuration by using the slapcat command as follows: slapcat | more

If you make a mistake or want to change the database configuration, you must delete the existing entries ( slapadd will not replace an entry that already exists in the database).

You can delete the existing OpenLDAP database by removing all of the files in the database directory specified in the slapd.conf

configuration file. The following command will delete the default ldbm database used by slapd

: rm /usr/local/var/openldap-ldbm/*

You can now use slapadd to create the new database as shown previously.

You can start the slapd

(OpenLDAP) server using the following command:

/usr/local/openldap-2.0.15/services/slapd/slapd –d 1

This will run the server in debug mode with diagnostic messages being displayed to the screen.

In the next section, you will test your LDAP server setup. You will find that the

OpenLDAP database now has three entries for the

Customer

Organizational Unit: cn=Abraham,ou=Customers,o=Agency,c=us cn=George,ou=Customers,o=Agency,c=us cn=Winston,ou=Customers,o=Agency,c=us

You must leave the slapd server running while you evaluate the examples shown in the rest of this section. When you want to stop the server, simply use Ctrl+C to interrupt the server or use the kill command to send the server a terminate signal.

Configuring JNDI to use LDAP

After an LDAP server is available for use, you must configure JNDI to use that server.

This requires that you to obtain a JNDI Service Provider for LDAP (this isn’t part of the

LDAP server) and configure the JNDI properties accordingly.

05 0672323842 CH03 3/20/02 9:31 AM Page 107

Naming and Directory Services 107

JDK1.3 and J2EE RI 1.3 include an LDAP Service Provider from Sun Microsystems and all you have to do to use LDAP is to configure the JNDI properties to use the LDAP service. The simplest way to do this is to create an empty text file in the current directory called jndi.properties

and add the following lines to this file.

java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory

java.naming.provider.url=ldap://localhost:389

If the LDAP server is not running on the current machine, replace the name localhost with the name or IP address of the actual LDAP server. Port number 389 is the default

LDAP port number, and you can omit it if LDAP is running on the default port (or replace it by the actual port number if a non-standard port is being used).

Testing the LDAP Server

Before looking at attributes, it is worth checking that the LDAP server is up and running with the sample data. Verify that you can look up one of these sample names by using the simple

JNDILoopAny script shown in Listing 3.10. The code reads the JNDI name from the command-line arguments.

L

ISTING

3.10

Full Text of

JNDILookupAny.java

1: import javax.naming.*;

2: public class JNDILookupAny

3: {

4:

5: public static void main(String[] args) {

6: if (args.length != 1) {

7: System.err.println (“Usage: JNDILookupAny JNDIname”);

8: System.exit(1);

9: }

10: try {

11: Context ic = new InitialContext();

12: Object o = ic.lookup(args[0]);

13: System.out.println(args[0]+”=”+o);

14: }

15: catch (NamingException ex) {

16: System.err.println(ex);

17: System.exit(1);

18: }

19: catch (ClassCastException ex) {

20: System.err.println(ex);

21: System.exit(1);

22: }

23: }

24: }

3

05 0672323842 CH03 3/20/02 9:31 AM Page 108

108 Day 3

Run the following command to check access to the LDAP server.

java JNDILookupAny “cn=Manager, o=Agency,c=us “

This will display an entry similar to the following: ou=Customers,o=Agency,[email protected]

If the test doesn’t work, you must check your local LDAP configuration. The most likely problem will relate to security. You may need to provide security credentials to the

LDAP server. The “Security” section at the end of today’s lesson briefly covers this.

Obtaining a Directory Context

Attributes are only supported by Directory Services and cannot be accessed through the ordinary

Context object. Instead, you must use a javax.naming.directory.DirContext

class

. The

DirContext is a sub-class of

Context

, and you can use it in place of a

Context when dealing with a Directory Service where you require directory functionality

(such as attributes). For example,

DirContext ic = new InitialDirContext();

The javax.naming.directory

package contains the other attribute classes discussed next.

Reading Attributes

Attributes are read from the context just like a name is looked up from the context. The

DirContext.getAttributes() method returns a

NamingEnumeration that contains a collection of

Attribute objects. Each

Attribute has an ID (or key) and a list of values (an attribute can have more than one value for the same key). Listing 3.11 shows how all the attributes for an object can be listed.

L

ISTING

3.11

Full Text of

JNDIAttributes.java

1: import javax.naming.*;

2: import javax.naming.directory.*;

3: public class JNDIAttributes

4: {

5: public static void main(String[] args) {

6: if (args.length != 1) {

7: System.err.println (“Usage: JNDIAttributes JNDIname”);

8: System.exit(1);

9: }

10: try {

11: DirContext ctx = new InitialDirContext();

12: Attributes attrs = ctx.getAttributes(args[0]);

05 0672323842 CH03 3/20/02 9:31 AM Page 109

Naming and Directory Services 109

L

ISTING

3.11

Continued

13: NamingEnumeration ae = attrs.getAll();

14: while (ae.hasMore()) {

15: Attribute attr = (Attribute)ae.next();

16: System.out.println(“ attribute: “ + attr.getID());

17: NamingEnumeration e = attr.getAll();

18: while (e.hasMore())

19: System.out.println(“ value: “ + e.next());

20: }

21: System.out.println(“END of attributes for “+args[0]);

22: }

23: catch (NamingException ex) {

24: System.out.println (ex);

25: System.exit(1);

26: }

27: }

28: }

Running this program against the sample data produces the following result:

> java JNDIAttributes “cn=George,ou=Customers,o=Agency,c=us” attribute: description value: President attribute: objectClass value: person attribute: sn value: Washington attribute: cn value: George value: George Washington

END of attributes for cn=George,ou=Customers,o=Agency,c=us

A second form of the getAttributes() method allows you to provide an array of attribute names, and it only returns the values for those attributes. It is not an error to query an attribute that isn’t defined; it simply doesn’t return a value for that attribute.

The following fragment shows how to find the cn and sn attributes for a name:

String[] IDs = {“sn”, “cn”};

Attributes attrs = ctx.getAttributes(“cn=George,ou=Customers,o=Agency,c=us”,IDs);

Searching for Objects

A powerful and useful feature of attributes is the ability for you to search for names that have specific attributes or names that have attributes of a particular value.

You use the

Context.search() method to search for names. There are several overloaded forms of this method, all of which require a DN to define the context in the name

3

05 0672323842 CH03 3/20/02 9:31 AM Page 110

110 Day 3 tree where the search should begin. The simplest form of search() takes a second parameter that is an

Attributes object that contains a list of attributes to find. Each attribute can be just the name or the name and a value for that attribute.

Listing 3.12 shows a simple program to find all names that have a surname ( sn

) defined and a description of

President

.

L

ISTING

3.12

Full Text of

JNDISearch.java

1: import javax.naming.*;

2: import javax.naming.directory.*;

3: public class JNDISearch

4: {

5: private final static String JNDI = “ou=Customers,o=Agency,c=us”;

6:

7: public static void main(String[] args) {

8: try {

9: DirContext ctx = new InitialDirContext();

10: // create case insensitive search attributes

11: Attributes match = new BasicAttributes(true);

12: match.put(new BasicAttribute(“sn”));

13: match.put(new BasicAttribute(“description”,”president”));

14: NamingEnumeration enum = ctx.search(JNDI, match);

15: while (enum.hasMore()) {

16: SearchResult res = (SearchResult)enum.next();

17: System.out.println(res.getName()+”,”+JNDI);

18: }

19: }

20: catch (NamingException ex) {

21: System.out.println (ex);

22: System.exit(1);

23: }

24: }

25: }

The search() method returns a

NamingEnumeration containing objects of class

SearchResult

(a sub-class of

NameClassPair discussed earlier). The

SearchResult encapsulates information about the names found. The example program simply prints out the names (the names in the

SearchResult object are relative to the context that was searched).

Running the program in Listing 3.12 will return the following values from the sample data: cn=George,ou=Customers,o=Agency,c=us cn=Abraham,ou=Customers,o=Agency,c=us

The

SearchResult class also has a getAttributes() method that returns the attributes for the found name. The simple search shown in Listing 3.12 returns all of the name’s attributes.

05 0672323842 CH03 3/20/02 9:31 AM Page 111

Naming and Directory Services 111

A second form of the search() method takes a third parameter that is an array of String objects specifying the attributes for the method to return. The following code fragment shows how to search and return just the surname and common name attributes:

NamingEnumeration enum = ctx.search(JNDI, match, new String[]{“sn”,”cn”});

Another form of the search() method takes a String parameter specifying a search filter.

The filter uses a simple prefix notation for combining attributes and values. The JNDI

API documentation and the JNDI Tutorial from Sun Microsystems provides full details of the search filter syntax. Listing 3.13 shows a search for names with a description or

President or

Prime Minister

.

L

ISTING

3.13

Full Text of

JNDIFilter.java

1: import javax.naming.*;

2: import javax.naming.directory.*;

3: public class JNDIFilter

4: {

5: private final static String JNDI = “ou=Customers,o=Agency,c=us”;

6:

7: public static void main(String[] args) {

8: try {

9: DirContext ctx = new InitialDirContext();

10: SearchControls sc = new SearchControls();

11: String filter =

➥”(|(description=President)(description=Prime Minister))”;

12: NamingEnumeration enum = ctx.search(JNDI, filter, sc);

13: while (enum.hasMore()) {

14: SearchResult res = (SearchResult)enum.next();

15: System.out.println(res.getName()+”,”+JNDI);

16: }

17: }

18: catch (NamingException ex) {

19: System.out.println (ex);

20: System.exit(1);

21: }

22: }

23: }

You can use the javax.naming.directory.SearchControls

argument required by search() to

• Specify which attributes the method returns (the default is all attributes)

• Define the scope of the search, such as the depth of tree to search down to

• Limit the results to a maximum number of names

• Limit the amount of time for the search

3

05 0672323842 CH03 3/20/02 9:31 AM Page 112

112 Day 3

Running the program in Listing 3.13 with the sample data produces the following output: cn=George,ou=Customers,o=Agency,c=us cn=abraham,ou=Customers,o=Agency,c=us cn=Winston,ou=Customers,o=Agency,c=us

Manipulating Attributes

The

DirContext. ModifyAttributes() method supports the addition, modification, and deletion of attributes for a name. To manipulate an attribute, the program must have write permission to entries in the LDAP name server. On a live system, the program must supply valid user credentials when obtaining the initial context (see the “Security” section later in this lesson). If you attempt to modify a name’s attributes without the requisite permissions, a javax.naming.NoPermissionException is thrown.

If you are using the OpenLDAP server purely for evaluating JNDI, you can easily change the permissions so that all users have write permission. Find the slapd configuration file

(by default, this is

/usr/local/etc/openladap/slapd.conf

) and replace the following line: access to * by * read with access to * by * write

Stop and restart the slapd server for this change to take effect.

You can manipulate attributes in one of two ways. The first, and most functional, is to create an array of javax.naming.directory.ModificationItem

objects. Each entry in the array specifies an attribute ID and an operation (one of

DirContext.REPLACE_ATTRIBUTE

,

DirContext.ADD_ATTRIBUTE

, and

DirContext.REMOVE_ATTRIBUTE

). To modify or add a new attribute, the

ModifyAttributes() method requires an additional parameter for the value of the attribute.

Listing 3.14 shows how the entry for

Abraham can update the description attribute and add a new seeAlso attribute.

L

ISTING

3.14

Full Text of

JNDIModify.java

1: import javax.naming.*;

2: import javax.naming.directory.*;

3: public class JNDIModify

4: {

5: private final static String JNDI =

➥”cn=Abraham,ou=Customers,o=Agency,c=us”;

05 0672323842 CH03 3/20/02 9:31 AM Page 113

Naming and Directory Services 113

L

ISTING

3.14

Continued

6:

7: public static void main(String[] args) {

8: try {

9: DirContext ctx = new InitialDirContext();

10: SearchControls sc = new SearchControls();

11: ModificationItem[] mods = new ModificationItem[2];

12: mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,

13: new BasicAttribute(“description”, “Assasinated

President”));

14: mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE,

15: new BasicAttribute(“seeAlso”,

➥”cn=George,ou=Customers,o=Agency,c=us”));

16: ctx.modifyAttributes(JNDI, mods);

17: }

18: catch (NamingException ex) {

19: System.out.println (ex);

20: System.exit(1);

21: }

22: }

23: }

After running this program, you can view the changes by using the

JNDIAttributes program shown in listing 3.11.

> java JNDIAttributes “cn=abraham,ou=Customers,o=Agency,c=us” attribute: seeAlso value: cn=George,ou=Customers,o=Agency,c=us attribute: description value: Assasinated President attribute: objectClass value: top value: person attribute: sn value: Lincoln attribute: cn value: Abraham value: Abraham Lincoln

END of attributes for cn=abraham,ou=Customers,o=Agency,c=us

The second method for manipulating attributes is to define the operation and an

Attributes list of

Attribute objects to be manipulated (with values if appropriate). The next code fragment shows how to delete the seeAlso entry just added (which probably should have referred to John F. Kennedy and not George Washington).

Attributes attrs = new BasicAttributes(“seeAlso”,null); ctx.modifyAttributes(JNDI, DirContext.REMOVE_ATTRIBUTE, attrs);

3

05 0672323842 CH03 3/20/02 9:31 AM Page 114

114 Day 3

The next example shows how to change the description back to

President

.

Attributes attrs = new BasicAttributes(“description”,”President”); ctx.modifyAttributes(JNDI, DirContext.REPLACE_ATTRIBUTE, attrs);

More on Objects

The early part of today’s lesson covered binding objects into a JNDI namespace. To recap, a bound object must implement the

Serializable interface, and the object’s class file must be available to the JNDI server.

The obvious means of making an object’s class file available to the JNDI server is to set the server’s class path to include the necessary directory or JAR file. However, this isn’t always convenient, and the JNDI specification recognizes this situation and supports dynamic loading of classes when using a directory service.

Loading Classes from a Code Base

Provided the JNDI Service Provider is a Directory Service and that service supports

Internet RFC 2713, the JNDI server can obtain necessary class files dynamically from any HTTP server.

RFC2713 defines an interoperable way of storing Java objects in an LDAP server. By defining how Java objects are stored and retrieved, the JNDI Naming Manager in Sun

Microsystems’ LDAP Service Provider can retrieve Java objects from the directory server and recreate them on the client’s system.

You must configure the LDAP server to support the Java Schema defined in RFC2713. If you are using the OpenLDAP server, as previously configured in the “Attributes” section, supporting the Java Schema is a relatively simple change to the configuration:

1. Stop the OpenLDAP slapd daemon.

2. Edit the slapd configuration file (

/usr/local/etc/openldap/slad.conf

). After the existing line starting with include

, add the following line: include /usr/local/etc/openldap/schema/java.schema

3. Save the file and restart the OpenLDAP server.

Defining a Code Base

From a programming point of view, Java class files must be made available via a Web server. A javaCodebase attribute supplies the details of the Web server location when binding the object into the JNDI directory namespace.

The following example uses the Sun Microsystems’ LDAP Service Provider and an

LDAP server, as discussed in the “Attributes” section earlier in today’s lesson.

05 0672323842 CH03 3/20/02 9:31 AM Page 115

Naming and Directory Services 115

This example also requires an HTTP server to be running. Starting up the J2EE RI server will also start an HTTP server on port 8000. The J2EE RI stores its HTTP pages in the public_html directory in the J2EE RI home directory.

Listing 3.15 shows a simple class representing a book.

L

ISTING

3.15

Full Text of

Book.java

1: import java.io.*;

2: public class Book implements Serializable

3: {

4: String title;

5: public Book(String title) {

6: this. title = title;

7: }

8: public String toString() {

9: return title;

10: }

11: }

A Web server must make the book.class

file available for download for the Sun

Microsystems’ LDAP Service provider to bind and look up

Book objects. It is conventional to store downloadable class files in a separate sub-directory under the HTTP server’s home directory. You normally call this sub-directory classes

.

Use the following commands to copy the book.class

file to the J2EE RI Web server. In both cases, you require appropriate permission to write to the J2EE RI home directory.

Under Windows mkdir %J2EE_HOME%\public_html\classes copy Book.class %J2EE_HOME%\public_html\classes

Under Linux and Unix mkdir $J2EE_HOME/public_html/classes cp Book.class $J2EE_HOME/public_html/classes

With the class files in place, a

Book object can be bound to the LDAP namespace as

Listing 3.16 shows. The code uses the javaCodebase attribute to specify the URL of the directory containing the Java class files and not the class file itself.

L

ISTING

3.16

Full Text of

JNDICodebase.java

1: import javax.naming.*;

2: import javax.naming.directory.*;

3: public class JNDICodebase

4: {

3

05 0672323842 CH03 3/20/02 9:31 AM Page 116

116 Day 3

L

ISTING

3.16

Continued

5: private final static String JNDI = “cn=book,o=Agency,c=us”;

6: private final static String codeURL =

➥”http://localhost:8000/classes”;

7:

8: public static void main(String[] args) {

9: try {

10: DirContext ic = new InitialDirContext();

11: Book book = new Book(“Teach Yourself J2EE in 21 Days”);

12: Attributes attrs = new BasicAttributes();

13: attrs.put(“javaCodebase”, codeURL);

14: attrs.put(“cn”, “book”);

15: ic.rebind(JNDI, book, attrs);

16: System.out.println(“Bound “+JNDI);

17: }

18: catch (NamingException ex) {

19: System.err.println(ex);

20: System.exit(1);

21: }

22: }

23: }

Note that the example uses the code base URL of localhost:8000

, which is required because the J2EE web server uses a non-standard HTTP port (the standard HTTP port is 80).

Note The http.port

entry in the web.properties

file in the J2EE RI config directory defines the default port for the Web server.

With the name registered and the HTTP server running, the client can look up the bound object without having the

Book class file in the search path. The Sun Microsystems’

LDAP Service Provider automatically loads the class file. Listing 3.17 shows a simple client program.

L

ISTING

3.17

Full Text of

JNDILookupBook.java

1: import javax.naming.*;

2: public class JNDILookupBook

3: {

4: private final static String JNDI = “cn=book, o=Agency,c=us “;

5:

6: public static void main(String[] args) {

7: try {

05 0672323842 CH03 3/20/02 9:31 AM Page 117

Naming and Directory Services 117

L

ISTING

3.17

Continued

8: Context ic = new InitialContext();

9: Book book = (Book)ic.lookup(JNDI);

10: System.out.println(JNDI+”=”+book);

11: }

12: catch (NamingException ex) {

13: System.err.println(ex);

14: System.exit(1);

15: }

16: catch (ClassCastException ex) {

17: System.err.println(ex);

18: System.exit(1);

19: }

20: }

21: }

References

Sometimes, storing a serialized copy of an object in the Directory Service is inappropriate. Perhaps the object is too large or you must instantiate it dynamically because its construction depends on information that can vary from one client to another.

JNDI references provide a mechanism for storing an object by reference rather than by value. This mechanism only works if the underlying JNDI Service Provider supports

Referenceable objects. The LDAP Service Provider from Sun Microsystems supports

Referenceable objects.

Without going into too much detail, a reference to an object requires that a

Factory class is available to build the object from the information the reference stores. From a design perspective, this requires two related classes:

• The

Object class that must implement the javax.naming.Referenceable

interface

• A

Factory class that can create the required objects

The

Referenceable interface requires an object to implement the getReference() method, which returns a

Reference object. The

Reference object defines the name of the class referred to and a

Factory class that can be used to build the referenced object.

Listing 3.18 shows a simple

Book reference class.

L

ISTING

3.18

Full Text of

BookRef.java

1: import java.io.*;

2: import javax.naming.*;

3: public class BookRef implements Referenceable

4: {

3

05 0672323842 CH03 3/20/02 9:31 AM Page 118

118 Day 3

L

ISTING

3.18

Continued

5: String title;

6: public BookRef(String title) {

7: this.title = title;

8: }

9: public String toString() {

10: return title;

11: }

12: public Reference getReference() throws NamingException {

13: return new Reference(

14: BookRef.class.getName(),

15: new StringRefAddr(“book”, title),

16: BookFactory.class.getName(),

17: null);

18: }

19: }

The second parameter to the

Reference constructor uniquely defines the object. This requires a key to define the address type of the object (in this case, a

String set to the value book

) and a value for this specific object (in this case, the book’s title). When the object is reconstructed, this address type and value will pass to the

Factory object.

The

Factory class must implement either javax.naming.spi.ObjectFactory

or javax.naming.spi.DirObjectFactory

, depending whether you use a Name Service or a Directory Service. Both classes require the

Factory class to implement a getObjectInstance() method for creating reference objects. Listing 3.19 shows the

BookFactory class used in the

BookRef class shown in Listing 3.16.

L

ISTING

3.19

Full Text of

BookFactory.java

1: import javax.naming.*;

2: import javax.naming.spi.*;

3: import java.util.Hashtable;

4: public class BookFactory implements ObjectFactory {

5: public Object getObjectInstance(Object obj, Name name,

6: Context ctx, Hashtable env) throws Exception {

7: if (obj instanceof Reference) {

8: Reference ref = (Reference)obj;

9: if (ref.getClassName().equals(BookRef.class.getName())) {

10: RefAddr addr = ref.get(“book”);

11: if (addr != null) {

12: return new BookRef((String)addr.getContent());

13: }

14: }

15: }

16: return null;

17: }

18: }

05 0672323842 CH03 3/20/02 9:31 AM Page 119

Naming and Directory Services 119

The factory getObjectInstance() method checks that it is passed a

Reference object and then checks that the class of the reference is a

BookRef

. If both of these conditions are true, the factory uses the address type book to look up the value of the object and then uses this to create a new

BookRef object.

As far as name binding and object lookup are concerned, the client is unaware of the use of references. Listings 3.20 and 3.21 show how your code can use the

BookRef class.

L

ISTING

3.20

Full Text of

JNDIBindBookRef.java

1: import javax.naming.*;

2: import javax.naming.directory.*;

3: public class JNDIBindBookRef

4: {

5: private final static String JNDI = “ book”;

6:

7: public static void main(String[] args) {

8: try {

9: DirContext ic = new InitialDirContext();

10: BookRef book = new BookRef(“Teach Yourself J2EE in 21 Days”);

11: Attributes attrs = new BasicAttributes();

12: attrs.put(“cn”, “book”);

13: ic.rebind(JNDI,book,attrs);

14: System.out.println(“Bound BookRef “+JNDI);

15: }

16: catch (NamingException ex) {

17: System.err.println(ex);

18: System.exit(1);

19: }

20: }

21: }

3

L

ISTING

3.21

Full Text of

JNDILookupBookRef.java

1: import javax.naming.*;

2: import javax.naming.directory.*;

3: public class JNDILookupBookRef

4: {

5: private final static String JNDI = “book”;

6:

7: public static void main(String[] args) {

8: try {

9: DirContext ic = new InitialDirContext();

10: BookRef name = (BookRef)ic.lookup(JNDI);

11: System.out.println(JNDI+”=”+name);

12: }

13: catch (NamingException ex) {

14: System.err.println(ex);

05 0672323842 CH03 3/20/02 9:31 AM Page 120

120 Day 3

L

ISTING

3.21

Continued

15: System.exit(1);

16: }

17: catch (ClassCastException ex) {

18: System.err.println(ex);

19: System.exit(1);

20: }

21: }

22: }

What Else Can JNDI Do?

JNDI is a large subject, and some of the previous discussion has been quite brief (there is a lot more to attributes, searching, and references than has been shown). Today, the additional features, such as naming events and security, have been presented in a very superficial manner.

JNDI Events

JNDI supports an event model similar to the event listeners in the Java AWT and Swing classes. However, the underlying JNDI Service Provider must also provide support for the event model for a client to register event handlers.

The javax.naming.event

package supports two types of JNDI event listener (both are sub-classes of

NamingListener

):

NamespaceChangeListener reports on changes to the namespace objects that are added, removed, or renamed.

ObjectChangeListener reports on changes to an object when its binding is replaced or attributes are added, removed, or replaced.

Both interfaces define appropriate methods that are called when changes occur in the

JNDI namespace. A javax.naming.event.NamingEvent

object is passed to the listener method to define:

• The type of event (for example, name added or object changed)

• The name binding before the event occurred

• The name binding after the event occurred

You use the

EventContext.addNamingListener() method to register a

NamingListener object against a context. Adding and removing a listener requires the context to implement the

EventContext

(or

EventDirContext for Directory Services). The code to look up and register a

NamingListener is similar to that shown in Listing 3.22 (please use the

API documentation for further details):

05 0672323842 CH03 3/20/02 9:31 AM Page 121

Naming and Directory Services 121

L

ISTING

3.22

Sample Code to Add a

NamingListener

1: Context ic = new InitialContext();

2: EventContext ctx = (EventContext) ic.lookup(“sams”);

3:

4: NamingListener listener = new MyNamingListener();

5:

6: ctx.addNamingListener(“book”, EventContext.ONELEVEL_SCOPE, listener);

Listing 3.22 registers a listener against the sams context and listens for events on the book name. Depending on the underlying Service Provider, the book name need not exist when you register the listener.

You can register a

NamingListener to listen for several objects either by listening on a context (rather than an object) or by using attribute filters to specify the required objects.

JNDI event handling provides an effective means for monitoring changes to a namespace to maintain up-to-date information about the registered objects.

Security

JNDI security depends on the underlying Service Provider. Simple services, such as RMI and the CORBA name service (both of which the J2EE RI implementation supplies), do not support security. These services allow any client to perform any operation.

In a production environment, security is paramount to ensuring the integrity of the data in the JNDI server. Most live J2EE implementations will make use of LDAP to provide a naming service that supports security.

LDAP security is based on three categories:

Anonymous—No security information is provided.

Simple—The client provides a clear text name and password.

Simple Authentication and Security Layer (SASL)

The client and server negotiate an authentication system based on a challenge and response protocol that conforms to RFC2222.

If the client does not supply any security information (as in all the examples shown today), the client is treated as an anonymous client.

The following JNDI properties provide security information:

• java.naming.security.authentication

is set to a

String to define the authentication mechanism used (one of none

, simple

, or the name of an SASL authentication system supported by the LDAP server).

• java.naming.security.principal

is set to the fully qualified domain name of the client to authenticate.

3

05 0672323842 CH03 3/20/02 9:31 AM Page 122

122 Day 3

• java.naming.security.credentials

is a password or encrypted data (such as a digital certificate) that the implementation uses to authenticate the client.

If you do not define any of these properties, the implementation uses anonymous

( java.naming.security.authentication=none

) authentication.

You can use a JNDI properties file to supply client authentication information, but more usually you code the information within the client program. Usually, your application must obtain the client authentication dynamically.

If you use strong (not simple or anonymous

) authentication, the java.naming.security.authentication

value can consist of a space-separated list of authentication mechanisms. Depending on the LDAP service provider, JNDI can support the following authentication schemes:

External—Allows JNDI to use any authentication system. The client must define a callback mechanism for JNDI to hook into the client’s authentication mechanism.

GSSAPI (Kerberos v5)

A well-known, token-based security mechanism.

Digest MD5

Uses the Java Cryptography Extension (JCE) to support client authentication using the MD5 encryption algorithm, which has no known decryption technique.

Day 15, “Security,” discuses the whole topic of J2EE and JNDI security.

Summary

JNDI provides a uniform API to an underlying naming or driectory service. A Naming

Service provides a means of storing simple information against a name so the information can be retrieved using the name as a key. A Directory Service stores additional attribute information, as well as values against a name. Directory Services use attributes to categorize names so that powerful searching of the directory tree structure can be supported.

JNDI supports any naming service provided a Service Provider implementation is available for the service. Standard services supported by JNDI include the following:

• Lightweight Directory Access Protocol (LDAP)

• Novel Directory Services (NDS)

• CORBA

• Active Directory is supported via its LDAP interface

05 0672323842 CH03 3/20/02 9:31 AM Page 123

Naming and Directory Services 123

Using JNDI from within a Java program is a simple matter of creating a context and looking up names within that context. The

Context class supports naming services, and the

DirContext class supports directory services.

After a context has been defined, the lookup() method is used to retrieve the object stored against a name. The bind() and rebind() methods are used to add or changes bound objects, and the unbind() method is used to remove a bound object.

Within J2EE, JNDI is used to advertise components such as the following:

• EJBs

• Data sources (databases)

• JMS message queues and topics

Q&A

Q Why is a Name Service so important?

A Without JNDI, it would be a lot harder to provide services such as those implemented using J2EE objects like data sources, message queues, and EJBs. Each vendor would choose its own mechanism for defining how a client program should gain access to the J2EE objects. Some might do this by distributing configuration files, others by using TCP/IP broadcast network packets. Using a Name Service provides a consistent means of providing network services in a portable and platform-independent manner. Not only that, you can move an implementation of a service from one machine to another. In this instance, the server simply updates the

Name Service entry to reflect its new location, and the whole process is transparent to the client.

Q Why is JNDI so large? Surely all I need to do is map a name onto a Java object?

A If all you want to do is support J2EE objects, JNDI could be as simple as a name to object mapping service. But Sun Microsystems designed JNDI to interoperate with established Directory Services, such as NDS, LDAP (Active Directory), and

DNS. By providing Java programming support for these services, the designers of

JNDI have ensured it will not be used as a proprietary product with J2EE servers, but as a general interface to fully -functional directory services. This design philosophy also provides programmers with a mechanism for developing interfaces to

NDS and LDAP in Java rather than some other language, such as C++ or C#.

3

05 0672323842 CH03 3/20/02 9:31 AM Page 124

124 Day 3

Exercise

You have been shown a simple program to display a JNDI namespace as a command line program (

JNDITree.java

in Listing 3.6). Today’s exercise is to write a GUI version of this program using the Swing

JTree class.If you already know Swing, you can use

JNDITree.java

as a guide for your program and go ahead and write your own JNDI browser.

If you do not know Swing, the exercise directory for Day 3 on the accompanying CD-

ROM includes a template program called

JNDIBrowser.java

for you to enhance. The

JNDIBrowser program handles all of the Swing initialization, all you have to do is get a list of the names in the JNDI namespace and create a new javax.swing.tree.DefaultMutableTreeNode

representing the name and add this to the

JTree

. When you add a name that is also a context, you need to add all the names in this sub-context.

Comments have been added to the

JNDIBrowser.java

file to show you where to add your code.

Don’t worry if this sounds complex—it isn’t. You only have to write about 12 lines of code (most of which you can adapt from

JNDITree.java

).

Before you rush off and write your first piece of Java code for this book, remember that you need to set up your

CLASSPATH to include the J2EE Reference Implementation classes using the setenv script, as discussed in today’s lesson. You can run the supplied solution using the following commands.

Under Windows, use

%J2EE_HOME%\bin\setenv java –classpath .;%CPATH% JNDIBrowser

Under Linux and Unix, use

$J2EE_HOME/bin/setenv java –classpath .:$CPATH JNDIBrowser

If you complete this exercise or simply run the provided solution, you will see that the

JNDI names are listed in the order they were added to the context. As a second exercise, change your program to display the names in alphabetical order. The solution called

JNDIBrowserSort.java

program shows how this can be achieved using the java.util.TreeMap

class.

You will find these programs useful for browsing the JNDI namespace when you are developing J2EE applications.

06 0672323842 CH04 3/20/02 9:25 AM Page 125

W

EEK

1

D

AY

4

Introduction to EJBs

J2EE provides different types of components for different purposes. Today, you will start to look at one of the principal types of component in J2EE—

Enterprise JavaBeans (EJBs).

The study of EJBs is continued on Day 5, “Session EJBs,”, Day 6, “Entity

EJBs”, Day 7, “CMP and EJB QL”, Day 8, “Transactions and Persistence”, and

Day 10, “Message-Driven Beans”. As you can see, there is a lot to learn about

EJBs, so today serves as a first step on the road to all of this EJB knowledge.

Today, you will

• Examine the different types of EJB available

• Take a look at how EJBs are applied

• Explore the structure of one of the EJBs that forms part of the case study to see how the different parts fit together

• Deploy and use some of the EJBs from the case study

• Write a simple client for an EJB

First, you need to understand why you would use EJBs.

06 0672323842 CH04 3/20/02 9:25 AM Page 126

126 Day 4

What Is an EJB?

In a typical J2EE application, Enterprise JavaBeans (EJBs) contain the application’s business logic and live business data. Although it is possible to use standard Java objects to contain your business logic and business data, using EJBs addresses many of the issues you would find by using simple Java objects, such as scalability, lifecycle management, and state management.

Beans, Clients, Containers, and Servers

An EJB is essentially a managed component that is created, controlled, and destroyed by the J2EE container in which it lives. This control allows the container to control the number of EJBs currently in existence and the resources they are using, such as memory and database connections. Each container will maintain a pool of EJB instances that are ready to be assigned to a client. When a client no longer needs an EJB, the EJB instance will be returned to the pool and all of its resources will be released. At times of heavy load, even EJB instances that are still in use by clients will be returned to the pool so they can service other clients. When the original client makes another request of its EJB, the container will reconstitute the original EJB instance to service the request. This pooling and recycling of EJB instances means that a few EJB instances, and the resources they use, can be shared between many clients. This maximizes the scalability of the EJBbased application. The EJB lifecycle is discussed further on Days 5 and 6.

The client that uses the EJB instance does not need to know about all of this work by the container. As far as the client is concerned, it is talking to a remote component that supports defined business methods. How those methods are implemented and any magic performed by the container, such as just-in-time instantiation of that specific component instance, are entirely transparent to the client part of the application.

The EJB benefits from certain services provided by the container, such as automatic security, automatic transactions, lifecycle management, and so on. To do this, the EJB must conform to certain rules and implement an appropriate interface that allows the container to manage the component. The EJB is packaged with configuration information that indicates the component’s requirements, such as transaction and security requirements. The container will then use this information to perform authentication and control transactions on behalf of the component—the component does not have to contain code to perform such tasks.

The primary purpose of the container is to control and provide services for the EJBs it contains. When it needs to use some underlying functionality, such as creating a transaction on behalf of a bean, it uses the facilities of the underlying EJB server. The EJB server is the base set of services on top of which the container runs. Different types of

06 0672323842 CH04 3/20/02 9:25 AM Page 127

Introduction to EJBs 127

EJB will run in different containers, but many different EJB containers can run on a single EJB server. EJB servers are generally delivered as part of a J2EE-compliant application server (examples include BEA WebLogic and IBM WebSphere). You will install and run the application server, which will provide the underlying services required of an EJB server and will host EJB containers.

The EJB Landscape

As you have seen, the J2EE Blueprints ( http://java.sun.com/blueprints/enterprise/index.html

) define a target architecture for a typical J2EE-based application. In this architecture, EJBs live in the middle tier and are used by other application components that live in the presentation tier. Although it is possible that both of these logical tiers will reside on the same computer, it is most likely that they will reside on different machines. This means that an EJB will usually have to be made available to remote clients.

To offer services to remote clients, EJBs will export their services as RMI remote interfaces. RMI allows you to define distributed interfaces in Java. There are certain caveats on doing this, not only at the implementation level (such as declaring that

RemoteExceptions may be thrown when calling a method on an EJB) but also at the design level. Designing remote interfaces is a skill in itself, which will be explored as you progress through topics in this book, such as EJBs and J2EE Patterns.

Because they must use an RMI-based interface to access the functionality of the EJB, the clients of an EJB must have some programming functionality. This means that they are typically either “thick” clients that provide a GUI interface or Web-server components that deliver HTML interfaces to “thin” clients. The different types of client are explored in more detail shortly.

In the other direction, EJBs themselves will make use of data sources, such as databases and mainframe systems, to perform the required business logic. Access to such data and services can be through a JDBC database connection, a J2EE Connector, another EJB, or a dedicated server or class of some form.

Discovering EJBs

While it is quite easy to draw pictures of a 3-tier system containing boxes labelled

“EJB,” it is important to identify what application functionality should go into an EJB.

At the start of application development, regardless of the precise development process used (Rational Unified Process (RUP), eXtreme Programming (XP), and so on), there is generally some analysis that delivers a Unified Modelling Language (UML) domain model (this identifies the main elements of the business problem to be solved). This can

4

06 0672323842 CH04 3/20/02 9:25 AM Page 128

128 Day 4 then form the basis of a solution model where the business concepts are mapped into appropriate design-level artefacts, such as components. This is where EJBs come into the design.

The UML model will consist of a set of classes and packages that represent single or grouped business concepts. A class or package can be implemented as an EJB. Generally, only larger individual classes will become EJBs in themselves, because EJBs are intended to be fairly coarse-grained components that incorporate a reasonably large amount of functionality and/or data.

There are generally two types of functionality discovered during analysis—data manipulation and business process flow. The application model will usually contain data-based classes such as

Customer or

Product

. These classes will be manipulated by other classes or roles that represent business processes, such as

Purchaser or

CustomerManager

. There are different types of EJB that can be applied to these different requirements.

Types of EJB

There are three different types of EJB that are suited to different purposes:

Session EJB—A Session EJB is useful for mapping business process flow (or equivalent application concepts). There are two sub-types of Session EJB — stateless and stateful— that are discussed in more detail on Day 5. Session EJBs commonly represent “pure” functionality that is created as it is needed.

Entity EJB—An Entity EJB maps a combination of data (or equivalent application concept) and associated functionality. Entity EJBs are usually based on an underlying data store and will be created based on that data within it.

Message-driven EJB—A Message-driven EJB is very similar in concept to a

Session EJB, but is only activated when an asynchronous message arrives.

As an application designer, you should choose the most appropriate type of EJB based on the task to be accomplished.

Common Uses of EJBs

So, given all of this, where would you commonly encounter EJBs and in what roles?

Well, the following are some examples:

• In a Web-centric application, the EJBs will provide the business logic that sits behind the Web-oriented components, such as servlets and JSPs. If a Web-oriented application requires a high level of scalability or maintainability, use of EJBs can help to deliver this.

06 0672323842 CH04 3/20/02 9:25 AM Page 129

Introduction to EJBs 129

• Thick client applications, such as Swing applications, will use EJBs in a similar way to Web-centric applications. To share business logic in a natural way between different types of client applications, EJBs can be used to house that business logic.

• Business-to-business (B2B) e-commerce applications can also take advantage of

EJBs. Because B2B e-commerce frequently revolves around the integration of business processes, EJBs provide an ideal place to house the business process logic. They can also provide a link between the Web technologies frequently used to deliver B2B and the business systems behind.

• Enterprise Application Integration (EAI) applications can incorporate EJBs to house processing and mapping between different applications. Again, this is an encapsulation of the business logic that is needed when transferring data between applications (in this case, in-house applications).

These are all high-level views on how EJBs are applied. There are various other EJBspecific patterns and idioms that can be applied when implementing EJB-based solutions.

These are discussed more on Day 18, “Patterns.”

Given this context, common types of EJB client include the following:

• A servlet or JSP that provides an HTML-based interface for a browser client

• Another EJB that can delegate certain of its own tasks or can work in combination with other EJBs to achieve its own goals

• A Java/Swing application that provides a front-end for the business processes encapsulated in the EJB

• A CORBA application that takes advantage of the EJB’s business logic

• An applet that takes advantage of the business logic in a remote EJB so that this business logic does not need to be downloaded to the client

These are common ways that EJBs are applied. What benefits does the use of EJBs give to you as a developer?

Why Use EJBs?

Despite the recommendations of the J2EE Blueprints, the use of EJBs is not mandatory.

You can build very successful applications using servlets, JSPs or standalone Java applications.

As a general rule of thumb, if an application is small in scope and is not required to be highly scalable, you can use J2EE components, such as servlets, together with direct

JDBC connectivity to build it. However, as the application complexity grows or the number of concurrent users increases, the use of EJBs makes it much easier to partition and scale the application. In this case, using EJBs gives you some significant advantages.

4

06 0672323842 CH04 3/20/02 9:25 AM Page 130

130 Day 4

Hiding Complexity

Early middleware environments, such as “raw” CORBA, require the application developer to write a lot of code that interacts with the CORBA environment and facilitates the connectivity and registration process. Such code can be likened to the plumbing that pipes water around a house. It needs to be there but, as the user of a sink or shower, you do not want to be intimately involved with it. In J2EE application terms, business developers want to write business code, not “plumbing” code. The EJB model tries to reduce such interaction to a minimum by using the following mechanisms:

• Each bean conforms to a defined lifecycle and set of rules. This provides a distinct boundary between system code and application code.

• Declarative attributes allow a developer to specify, say, the transactional behavior of the component without having to write code to control such functionality.

• The deployment information provided with the deployable J2EE application provides information about the relationships between multiple EJBs and also defines the resources required by an EJB.

Separation of Business Logic from UI and Data Access

One of the key facets of applying EJBs is that they allow business functionality to be developed and then deployed independently of the presentational layer. You might, for example, create an application with a user interface built using Java’s Swing API. This application might then provide access to some business functionality for the employees working on the company’s internal network. If the underlying business functionality is implemented using EJBs, a different user interface could take its place without having to redevelop the entire application. A Web-based interface that used servlets would make the application available from the Internet without having to change a single line of code in the business functionality. Figure 4.1 is a UML component diagram that shows this.

(More information on UML can be found in Appendix A, “An Introduction to UML,” on the accompanying CD-ROM.)

It can sometimes be difficult to distinguish between the functionality that an application provides and the user interface that is used to invoke that functionality. This is not unexpected because many common applications—such as a word-processor—are single-tier; the presentational logic and the business functionality are a single entity. On the other hand, consider programming a video recorder. Most modern video recorders can be programmed either directly on the console or through a remote control unit. Either user interface will accomplish the task of recording your favorite TV show, but there is only a single “application.”

06 0672323842 CH04 3/20/02 9:25 AM Page 131

Introduction to EJBs

F

IGURE

4.1

An application implemented using EJBs can have more than one user interface.

«swing» user interface

«servlet» user interface

«session EJB» business functionality

«database» persistence layer

131

Consider another example. In most supermarkets, a cashier is responsible for scanning the items in your shopping cart and then requesting a payment for the total. However, some supermarkets also offer a trust system, whereby the customer scans the items with a mobile scanner as they place the item into the shopping cart. To pay for the goods in the shopping cart, the customer simply swipes his or her own card, and then leaves with the goods. Again, there is a single application (to purchase shopping items) but two different interfaces—the cashier’s till and the customer’s mobile scanner.

To implement a distributed application using EJBs, make sure you have distinguished between the user interface and the underlying business function. The EJB itself is concerned only with the latter of these.

Container Services

The container provides various services for the EJB to relieve the developer from having to implement such services, namely

Distribution via proxies—The container will generate a client-side stub and serverside skeleton for the EJB. The stub and skeleton will use RMI over IIOP to communicate.

Lifecycle management—Bean initialization, state management, and destruction is driven by the container, all the developer must do is implement the appropriate methods.

Naming and registration—The EJB container and server will provide the EJB with access to naming services. These services are used by local and remote clients to look up the EJB and by the EJB itself to look up resources it may need.

Transaction management—Declarative transactions provide a means for the developer to easily delegate the creation and control of transactions to the container.

Security and access control—Again, declarative security provides a means for the developer to easily delegate the enforcement of security to the container.

4

06 0672323842 CH04 3/20/02 9:25 AM Page 132

132 Day 4

Persistence (if you want)—Using the Entity EJB’s container-managed persistence mechanism, state can be saved and restored without having to write a single line of code.

All of these container services are covered in more detail as the book progresses.

Now that you know why you would want to use an EJB and how to apply it, you can examine the inner workings of an EJB to understand how all the parts fit together.

What’s in an EJB?

So far, you have been presented with a “black box” view of an EJB; it provides business functionality via an RMI remote interface, and it cooperates with its container to perform its duties. To understand, use, and ultimately write EJBs, you will need to know more in concrete terms about the Java programming artefacts that make up an EJB. In other words, what’s in one?

The Business Interface

The primary purpose of an EJB is to deliver business or application logic. To this end, the bean developer will define or derive the business operations required of the bean and will formalize them in an RMI remote interface. This is referred to as the bean’s business or remote interface as opposed to the home interface you will look at in a moment.

Note

You may see references to local EJB interfaces and wonder how these relate to the current discussion. Don’t worry about local interfaces for the moment; they are covered on Day 6 when you examine entity EJBs.

The actual methods defined on the remote interface will depend on the purpose of the bean, but there are certain general rules concerning the interface:

• As with any RMI-based interface, each method must be declared as throwing java.rmi.RemoteException

in addition to any business-oriented exceptions. This allows the RMI subsystem to signal network-related errors to the client.

• RMI rules also apply to parameters and return values, so any types used must either be primitive,

Serializable

, or

Remote

.

• The interface must declare that it extends the javax.ejb.EJBObject

interface. This provides a handful of basic methods that you will encounter as you progress.

06 0672323842 CH04 3/20/02 9:25 AM Page 133

Introduction to EJBs 133

Caution Failure to conform to the rules about extending javax.ejb.EJBObject

and throwing RemoteException will cause the interface to be rejected by tools that manipulate EJBs. Additionally, if you use parameter or return types that do not conform to the rules, your bean will compile and even deploy, but will fail with runtime errors.

The issue regarding object parameters and return values is worth considering for a moment. When you pass a parameter into a local method call, a reference to the original object is provided to be used within the method. Any changes to the state of the object are seen by all users of that object because they are sharing the same object. Also, there is no need to create a copy of the object—only a reference is passed.

On the other hand, when using RMI remote methods, objects that are serializable (implement the

Serializable interface) are passed by value, whereas objects that are remote

(that is, EJBs) are passed by reference. Pass by value means that a copy of the object is sent. This has several implications. First, users of a serializable object passed across a remote interface will no longer share the same object. Also, there may now be some performance costs associated with invoking a method through a bean’s remote interface. Not only is there the cost of the network call, but also there is the cost of making a copy of the object so that it can be sent across the network. Most of the time, it will be serializable objects that are passed.

You can see an example of an EJB remote interface in Listing 4.1—in this case, the one for the

Agency

EJB used in the case study.

L

ISTING

4.1

Remote Interface for the

Agency

EJB package agency; import java.rmi.*; import java.util.*; import javax.ejb.*; public interface Agency extends EJBObject

{

String getAgencyName() throws RemoteException;

Collection findAllApplicants() throws RemoteException; void createApplicant(String login, String name, String email) throws RemoteException, DuplicateException, CreateException; void deleteApplicant (String login)

4

06 0672323842 CH04 3/20/02 9:25 AM Page 134

134 Day 4

L

ISTING

4.1

Continued throws RemoteException, NotFoundException;

Collection findAllCustomers() throws RemoteException; void createCustomer(String login, String name, String email) throws RemoteException, DuplicateException, CreateException; void deleteCustomer (String login) throws RemoteException, NotFoundException;

Collection getLocations() throws RemoteException; void addLocation(String name) throws RemoteException, DuplicateException; void removeLocation(String code) throws RemoteException, NotFoundException;

Collection getSkills() throws RemoteException; void addSkill(String name) throws RemoteException, DuplicateException; void removeSkill(String name) throws RemoteException, NotFoundException;

List select(String table) throws RemoteException;

}

The interface lives in a package called agency

, which will be common to all the classes that comprise the EJB. The definition imports java.rmi.* and javax.ejb.* for

RemoteException and

EJBObject

, respectively. The rest of the interface is much as you would expect from any remote Java interface—in this case, passing

Strings and returning serializable

Collections

.

Notice that all the methods must be declared as throwing

RemoteException

. This means that the client will have to handle potential exceptions that may arise from the underlying distribution mechanism. However, your application will probably want to employ exceptions itself to indicate application-level errors. These exceptions should be declared as part of the remote interface, as shown by the use of

NotFoundException and

DuplicateException in the

Agency interface.

The Business Logic

After an interface is defined, there is the none-too-trivial task of implementing the business logic behind it. The business logic for an EJB will live in a class referred to as the bean. The bean consists of two parts:

06 0672323842 CH04 3/20/02 9:25 AM Page 135

Introduction to EJBs 135

• The business logic itself, including implementations of the methods defined in the remote interface

• A set of methods that allow the container to manage the bean’s lifecycle.

Note

Although the bean itself must contain these elements, note that it is possible, indeed common, for non-trivial beans to delegate some or all of their business functionality to other, helper, classes.

Drilling down into these areas reveals more about the structure of an EJB.

Implementing the Business Interface

The first thing to note is that the bean itself does not implement the remote interface previously defined. This may seem slightly bizarre at first sight, because the equivalent RMI server would have to implement the associated remote interface. However, there is a very good reason for this.

As you will see later, it is possible to ask the container to apply services, such as access control, on behalf of the EJB simply by setting attributes in the EJB configuration information. To do this, the container must have some way of intercepting the method call from the client. When it receives such a method call, the container can then decide if any extra services need to be applied before forwarding the method call on to the bean itself.

Sticking with the security example, the container would examine security information configured for the EJB before deciding whether to forward the method call to the bean or to reject it. The details about access control are covered on Day 15, “Security,” but you can see that it is necessary to interpose between the client and the bean to “automagically” deliver such services.

The interception is performed by a server-side object called the EJBObject (not to be confused with the interface of the same name). The EJBObject acts as a server-side proxy for the bean itself, and it is the EJBObject that actually implements the EJB’s remote interface. Figure 4.2 shows the relationship between the client, the bean, and the

EJBObject.

As shown in Figure 4.2, the client calls the business methods on the EJBObject implementation. The EJBObject applies the required extra services and then forwards the method calls on to the bean itself. The EJBObject is separate from the RMI stub and skeleton that provide the remote procedure call capability.

4

06 0672323842 CH04 3/20/02 9:25 AM Page 136

136 Day 4

F

IGURE

4.2

The EJBObject acts as a server-side proxy for the bean itself.

Location transparency

Home

RMI

Stub

Client

Security and transactions

Home

RMI

Skeleton

Bean

RMI

Stub

1

Bean

RMI

Skeleton

Classes marked with

1

implement the EJB’s remote interface

EJB Home

Implementation

EJB

Object

1

Bean

So, your bean must implement the business methods defined in the remote interface. The container uses the method signatures defined in the interface, together with the Java reflection API, to find the appropriate methods on the bean, so you must ensure that you use the correct method signatures. Despite this, the bean should not implement the remote interface itself (the reasons for this are discussed later). However, if you are using a developer tool that supports the creation of EJBs, it will generally generate empty methods for you to populate. Listing 4.2 contains the outlines of the business methods in the example

AgencyBean

.

L

ISTING

4.2

Business Method Implementation Signatures for the

AgencyBean package agency; import java.rmi.*; import java.util.*; import javax.ejb.*;

// Remaining imports removed for clarity public class AgencyBean implements SessionBean

{ public String getAgencyName() {

// Code removed for clarity

} public Collection findAllApplicants() {

// Code removed for clarity

} public void createApplicant(String login, String name, String email) throws DuplicateException, CreateException {

// Code removed for clarity

}

06 0672323842 CH04 3/20/02 9:25 AM Page 137

Introduction to EJBs

L

ISTING

4.2

Continued

} public void deleteApplicant (String login) throws NotFoundException {

// Code removed for clarity

} public Collection findAllCustomers() {

// Code removed for clarity

} public void createCustomer(String login, String name, String email) throws DuplicateException, CreateException {

// Code removed for clarity

} public void deleteCustomer (String login) throws NotFoundException {

// Code removed for clarity

} public Collection getLocations() {

// Code removed for clarity

} public void addLocation(String name) throws DuplicateException {

// Code removed for clarity

} public void removeLocation(String code) throws NotFoundException {

// Code removed for clarity

} public Collection getSkills() {

// Code removed for clarity

} public void addSkill (String name) throws DuplicateException {

// Code removed for clarity

} public void removeSkill (String name) throws NotFoundException {

// Code removed for clarity

} public List select(String table) {

// Code removed for clarity

}

// Remaining methods removed for clarity

137

4

06 0672323842 CH04 3/20/02 9:25 AM Page 138

138 Day 4

The detail of the method implementations have been removed for clarity, because the main area of interest here is how the method signatures match up with those on the remote interface. The contents of the methods are largely the creation and dispatch of

JDBC statements and handling the results from the queries.

Note that the bean does not implement the

Agency interface. You can also see that various of the methods, such as addSkill()

, declare that they throw an application-specific exception—in this case,

DuplicateException

.

Note Note that your bean methods will only throw business exceptions or standard Java exceptions. They should not throw java.rmi.RemoteException

, because such exceptions should only be generated by the RMI subsystem.

Providing Lifecycle Hooks

Remember that the intention of the EJB environment is that you will spend most of your time writing business logic rather than network and database “plumbing.” Beyond writing the business logic, the only additional thing the bean writer needs to do is to provide lifecycle “hooks” that allow the container to manage the bean.

Each of the different types of EJB discussed earlier has a slightly different lifecycle, but the common parts are as follows:

• Bean creation and initialization

• Bean destruction and removal

• The saving and restoring of the bean’s internal state (if applicable)

The details associated with each type of bean lifecycle will be discussed as they are covered. For now, all you need to know is that

• An EJB will implement one or more lifecycle interfaces depending on its type. The interfaces (

SessionBean

,

EntityBean

,

MessageDrivenBean

, and

SessionSynchronization

) are defined in the javax.ejb

package.

• The lifecycle methods will generally begin with ejb so that they can be easily distinguished from the business methods around them, for example, ejbCreate()

.

Listing 4.3 contains the lifecycle methods in the example

AgencyBean

.

06 0672323842 CH04 3/20/02 9:25 AM Page 139

Introduction to EJBs

L

ISTING

4.3

Lifecycle Methods on the

AgencyBean package agency; import java.rmi.*; import java.util.*; import javax.ejb.*;

// Remaining imports removed for clarity public class AgencyBean implements SessionBean

{ private DataSource dataSource; private String name = “”; private void error (String msg, Exception ex) {

String s = “AgencyBean: “ + msg + “\n” + ex;

System.out.println(s); throw new EJBException(s);

} public void ejbCreate () throws CreateException { try {

InitialContext ic = new InitialContext(); dataSource = (DataSource)ic.lookup(“java:comp/env/jdbc/Agency”); name = (String)ic.lookup(“java:comp/env/AgencyName”);

} catch (NamingException ex) { error(“Error connecting to java:comp/env/Agency:”, ex);

}

} public void ejbActivate() {

} public void ejbPassivate() {

} public void ejbRemove() { dataSource = null;

} private SessionContext ctx; public void setSessionContext(SessionContext ctx) { this.ctx = ctx;

}

// Remaining methods removed for clarity

}

139

4

06 0672323842 CH04 3/20/02 9:25 AM Page 140

140 Day 4

As you can see, the example

AgencyBean implements the

SessionBean interface. This means that it must implement the ejbCreate()

, ejbRemove()

, ejbActivate()

, ejbPassivate()

, and setSessionContext() methods. The ejbCreate() method takes on the role of constructor in that most of the bean initialization will take place in there.

The context passed in setSessionContext() provides a way for the bean to communicate with the container.

This concludes the examination of the bean internals for the time being. You will discover more as you learn about the specific types of EJB later.

Factory Information

For an EJB to be used by a client, the client must create a new instance or discover an existing one. Finding and gaining access to the services of a traditional remote server is relatively simple. Such a server will tend to start when the machine boots, reside in a well-know location, and carry on running until the machine shuts down. However, EJBs are far more dynamic than that. It is the ability to dynamically create and reuse beans that provides the scalability inherent in the EJB model.

To facilitate the creation and discovery of EJBs, each type of EJB provides a home interface. The bean developer will provide an EJB home interface that acts as a factory for that particular EJB. A home interface will extend the javax.ejb.EJBHome

interface and will contain the necessary methods identified by the bean developer that allow a client to create, find, or remove EJBs.

There are two ways for a client to get hold of the EJB itself, depending on the type of

EJB (Session, Entity, or Message-driven) and the way it is intended to be used. The EJB

Home interface can contain one or more create() methods to create a new instance of an EJB. So, for example, you will create a new instance of a Session bean before using it. On the other hand, when you interact with Entity EJBs, you will frequently find existing EJBs using one or more findXXX() methods. The home interface may or may not allow you to remove the bean, depending on bean type and usage.

Listing 4.4 shows the home interface for the example

Agency

EJB.

L

ISTING

4.4

Home Interface for the

Agency

Bean package agency; import java.rmi.*; import javax.ejb.*; public interface AgencyHome extends EJBHome

{

06 0672323842 CH04 3/20/02 9:25 AM Page 141

Introduction to EJBs 141

L

ISTING

4.4

Continued

Agency create () throws RemoteException, CreateException;

}

Because the

Agency

EJB is just a simple wrapper around some JDBC-based functionality and does not maintain any business state, all that is required is a simple creation method— create()

. This maps onto the ejbCreate() seen in Listing 4.3. The client will call create() to create an instance of the

Agency bean.

The code underlying the home interface will work with the container to create, populate, and destroy EJBs as requested by the client. The effects of the method calls will vary depending on the type of EJB being manipulated. As a result, a request to remove a

Session EJB will just result in the EJB being thrown away, while the same request on an

Entity EJB may cause underlying data to be removed. The types and effects of different home interface methods are discussed in more detail on subsequent days.

Bean Metadata

The final piece of the EJB jigsaw lies in the provision of configuration information, or metadata, for the EJB. This provides a way of communicating the EJB’s requirements and structure to the container. If an EJB is to be successfully deployed, the container will have to be provided with extra information, including

• An identifier or name for the EJB that can be used to look it up.

• The bean type (Session, Entity, or Message-driven).

• Which class is the EJB’s remote interface. This interface will typically just be named according to the EJB’s functionality, for example,

Agency or

BankTeller

.

• Which class is the EJB’s home interface. The name for an EJB’s home interface will typically be derived from its remote interface name. So, for example, the

Agency

EJB has a home interface called

AgencyHome

. However, because this is a convention rather than being mandatory, the metadata explicitly indicates the name of the home interface.

• Which class is the bean itself. Again, the name for the bean will typically be derived from the associated remote interface name. So, for example, the

Agency bean is called

AgencyBean

. However, because this is a convention rather than being mandatory, the metadata explicitly indicates the name of the bean.

• Any name/value pairs to be provided as part of the bean’s environment.

• Information about any external resources required by the EJB, such as database connections or other EJBs.

4

06 0672323842 CH04 3/20/02 9:25 AM Page 142

142 Day 4

All of this essential information is bundled into a deployment descriptor that accompanies the EJB classes. As you might expect, given its recent rise as the most ubiquitous way to define data, the deployment descriptor is defined as an XML document. The deployment descriptor is discussed in more detail soon when examining the packaging of an EJB.

In addition to the essential information, the deployment descriptor can also carry other metadata that you will encounter as you progress:

• Declarative attributes for security and transactions

• Structural information bean relationships and dependencies

• Persistence mapping (if applicable)

You are now nearing the conclusion of this whistle-stop tour of the structure of an EJB.

After you have examined how an EJB is created and packaged, you will be ready to deploy and use one.

How Do I Create an EJB?

You will create specific types of EJB as you progress through the book. However, the creation of EJBs follows the same steps and principals for all types of EJB.

The Creation Mechanism

As you may have gathered from the previous discussion on EJB contents, the EJB developer must go through the following cycle:

1. Design and define the business interface. This may involve mapping from a UML model of the solution into Java.

2. Decide on a bean type appropriate to the task in hand. Entity, Session, and

Message-driven beans all have their own pros and cons. If you choose to use a

Session bean, another question is whether to use a stateful Session bean or a stateless Session bean. Choice of the appropriate type is discussed in more detail on

Days 5, 6, 7, and 10.

3. Decide which home interface methods are appropriate for the bean type and define the home interface for the EJB.

4. Create (or generate) a “boilerplate” bean with correct lifecycle methods.

5. Create your business logic by filling out the business methods.

6. Fill out lifecycle methods to control creation, destruction and to manage state (if applicable).

06 0672323842 CH04 3/20/02 9:25 AM Page 143

Introduction to EJBs 143

If your EJB classes are written correctly, all that remains is to wrap them up as a deployable unit. However, there are certain caveats you should bear in mind while creating your bean.

Caveats on Code Creation

Due to the managed nature of the bean lifecycle, the EJB container imposes certain restrictions on the bean including:

• EJBs cannot perform file I/O. If you need to log messages or access files, you must find an alternative mechanism.

• EJBs are not allowed to start threads. All threading is controlled by the container.

• EJBs cannot call native methods.

• EJBs cannot use static member variables.

• There is no GUI available to an EJB, so it must not attempt to use AWT or JFC components.

• An EJB cannot act as a network server, listening for inbound connections.

• An EJB should not attempt to create classloaders or change factories for artifacts, such as sockets.

• An EJB should not return this from a method. Although not strictly a restriction

(the container will not prevent you from doing it), it is identified as being a very bad practice. This relates to the earlier discussion that a bean should not implement its associated remote interface. This would potentially give a client a direct remote reference to the bean rather than the EJBObject. Instead, the bean should query its

EJB context for a reference to its associated EJBObject and return that to the caller.

For a full list of restrictions, see section 24.1.2 of the EJB 2.0 specification (available online at http://java.sun.com/products/ejb/docs.html

).

Create the Deployable Component

One alternative definition of a component is “a unit of deployment.” Following this theme, a component should

• Contain all the information required to deploy it, above and beyond the classes.

This is the metadata discussed earlier.

• Be bound up in such a way that it can easily be transported and deployed without losing any parts along the way.

Consequently, after the classes and interfaces for an EJB have been created, the following steps must be performed:

4

06 0672323842 CH04 3/20/02 9:25 AM Page 144

144 Day 4

1. Capture the EJB’s metadata in a universally understood format. This takes the form of an XML-based deployment descriptor (DD).

2. Bundle the classes and deployment descriptor up in a deployable format, namely a

JAR file.

The Deployment Descriptor

The EJB specification defines a standard format of an XML deployment descriptor document that can house EJB metadata. The exact format of a deployment descriptor is usually hidden behind tools that manipulate them on your behalf. However, it is worth examining some of the contents of a deployment descriptor to see how the EJB fits together and how extra information and metadata is provided.

Listing 4.5 shows the deployment descriptor for the example Agency EJB.

L

ISTING

4.5

Agency

Bean EJB Deployment Descriptor

1: <?xml version=”1.0” encoding=”UTF-8”?>

2:

3: <!DOCTYPE ejb-jar PUBLIC

➥ ‘-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN’

➥ ‘http://java.sun.com/dtd/ejb-jar_2_0.dtd’>

4:

5: <ejb-jar>

6: <display-name>Simple</display-name>

7: <enterprise-beans>

8: <session>

9: <display-name>Agency</display-name>

10: <ejb-name>Agency</ejb-name>

11: <home>agency.AgencyHome</home>

12: <remote>agency.Agency</remote>

13: <ejb-class>agency.AgencyBean</ejb-class>

14: <session-type>Stateless</session-type>

15: <transaction-type>Bean</transaction-type>

16: <env-entry>

17: <env-entry-name>AgencyName</env-entry-name>

18: <env-entry-type>java.lang.String</env-entry-type>

19: <env-entry-value>J2EE in 21 Days Job Agency</env-entry-value>

20: </env-entry>

21: <security-identity>

22: <description></description>

23: <use-caller-identity></use-caller-identity>

24: </security-identity>

25: <resource-ref>

26: <res-ref-name>jdbc/Agency</res-ref-name>

27: <res-type>javax.sql.DataSource</res-type>

28: <res-auth>Container</res-auth>

29: <res-sharing-scope>Shareable</res-sharing-scope>

06 0672323842 CH04 3/20/02 9:25 AM Page 145

Introduction to EJBs 145

L

ISTING

4.5

Continued

30: </resource-ref>

31: </session>

32: </enterprise-beans>

33: </ejb-jar>

The essential parts of the deployment descriptor in Listing 4.5 are

• The

<session> tag delimits the definition of the

Agency

EJB and indicates that it is a Session EJB (lines 8 and 31).

• The

<ejb-name> tag defines the name of the EJB, in this case

Agency

(line 10).

• The home and remote interface types (as defined by their fully-qualified class filenames) are specified by the

<home> and

<remote> tags, respectively (lines 11–12).

The type of the bean itself is defined by the

<ejb-class> tag (line 13).

In addition, two other parts are of particular note at this point in time:

• An environment entry is defined between lines 16 and 20 by using the

<enventry> tag. This indicates that a

String property called

AgencyName should be made available to the bean. The value of the property is

J2EE in 21 Days Job

Agency

. The environment defined in the deployment descriptor is made available through JNDI under the name java:comp/env

. In this case, the agency name can be retrieved by looking up the name java:comp/env/AgencyName

. This lookup can be seen in the ejbCreate() method of Listing 4.3.

• An external resource is defined in lines 25–30 using the

<resource-ref> tag. This defines that a

DataSource should be made available to this EJB under the name jdbc/Agency

. As with the environment entry for the agency name, this resource is made available through JNDI under java:comp/env

, so the EJB can retrieve the

DataSource by looking up the name java:comp/env/jdbc/Agency

. Again, this lookup can be seen in the ejbCreate() method of Listing 4.3.

4

Note It is important to realize that the name used for a

<resource-ref> is only a logical name. In other words, it is just a text string used by a component to reference an external resource. In theory, the resource name used by the EJB to refer to the data source could be anything ( foo

, for example) as long as it ties in with the information in the deployment descriptor. However, by convention, such names are kept in line with the name you would expect to use under JNDI. As a result, in this example, the data source resource is referred to by the bean as jdbc/Agency and will be registered under JNDI with the same name.

06 0672323842 CH04 3/20/02 9:25 AM Page 146

146 Day 4

All of the EJB classes and the deployment descriptor should then be bundled up in a JAR file. The deployment descriptor should be named ejb-jar.xml

. If there are multiple

EJBs packaged in the same JAR file, the deployment descriptor will have multiple EJB definitions in it. This JAR file is then termed an EJB-JAR file to denote its payload. The

JAR file itself can be called anything (within reason) and has a

.jar

file extension.

The EJB-JAR file can also contain any extra resources required by the EJB, such as application-specific configuration information that does not fit in a deployment descriptor environment entry.

Enterprise Applications

Although the EJB-JAR file is now complete, it must form part of an application to serve a useful purpose. J2EE defines that enterprise applications can be built from components

(Web, EJB, and application components). The key is how to define the relationships between the different parts of the application—there must be some way of plugging things together.

The answer is that there must be a description of the application itself, which components it uses, how those components relate to each other, and which specific resources they use. This is the information provided by the Application Assembler and Deployer roles.

To provide this information to the target J2EE platform, another level of deployment descriptor is used —the J2EE deployment descriptor. The J2EE deployment descriptor provides the following:

• A list of the components in the application

• Security role information

• Web root information for Web components

This information is stored in an XML file called application.xml

. All of the constituent component JAR files (such as EJB-JARs) and the J2EE deployment descriptor are then bundled up in another JAR file, this time called an Enterprise Archive (EAR) file, which has a

.ear

extension. The contents of the J2EE deployment descriptor will be covered in more detail as you examine the different parts of the example enterprise application.

Is the application now ready to deploy? Unfortunately, the answer is “Not quite yet.” The

J2EE deployment descriptor does not cover information about how to map the application onto a specific J2EE application server, specifically

06 0672323842 CH04 3/20/02 9:25 AM Page 147

Introduction to EJBs 147

• The JNDI name under which the application server will make the EJB available. In the case of the

Agency bean, this would mean that an entry was required to map the bean name of

Agency to the JNDI name under which the EJB is registered, for example, ejb/Agency

.

• Information about how the security roles defined map to underlying security principals (this is covered on Day 15).

So, yet another XML-based deployment descriptor is required to contain this information, this time an application server-specific one. This file contains extra mapping information, as previously described, and also any other container-specific information required for a smooth deployment in that environment. This extra deployment descriptor is also stored in the EAR file, ready to be accessed when the application is deployed.

Note The specific deployment descriptor for the J2EE Reference Implementation

(RI) server is called sun-j2ee-ri.xml

.

How Do I Deploy an EJB?

After an EJB is packaged, it can be deployed in an appropriate J2EE server. There is no limit to the number of times an EJB can be deployed as a part of different applications.

Remember that J2EE defines a separate role for the application deployer. It may be that for particular installations, databases, or other resource names need to be changed to match the local environment. When configuring the application, the deployer can alter this EJB or enterprise application metadata.

Plugging into the Container

When an EJB is deployed into a particular EJB container, the EJB must be plugged into that container. To do this, an EJBObject must be generated based on the EJB’s remote interface. This EJBObject will be specific to that EJB container and will contain code that allows it to interface with that container to access security and transaction information. The container will examine the metadata supplied with the EJB to determine what type of security and transaction code is required in the EJBObject.

The container will also generate the home interface implementation so that calls to create, find, and destroy EJB instances are delegated to container-defined methods.

The container will examine the EJB and enterprise application metadata and hook up resource references. It will also provide an environment for the application components.

4

06 0672323842 CH04 3/20/02 9:25 AM Page 148

148 Day 4

Finally, the container will register the home interface of the EJB with JNDI. This allows other application components to create and find EJBs of this type.

Performing the Deployment

As mentioned previously, when deploying an EJB or enterprise application, the application developer taking on the J2EE role of deployer can choose to alter certain of the metadata relating to the configuration of the application. Although this can be done manually, it is usually done through a GUI tool to make things easier and to keep things consistent.

After the EJB has been deployed, any subsequent changes to its functionality will mean that the EJB must be re-deployed. If the enterprise application or EJB is no longer needed, it should be undeployed from the container.

How Do I Use an EJB?

Given that EJBs are middle-tier business components, they are of little use without a client to drive them. As mentioned earlier, those clients can be Web components, standalone Java clients, or other EJBs.

Regardless of the type of client, using an EJB requires the same set of steps—namely, discovery, retrieval, use, and disposal. These steps are covered in the next three sections.

Discovery

To create or find an EJB, the client must call the appropriate method on the EJB’s home interface. Consequently, the first step for the client is to get hold of a remote reference to the home interface. On Day 3, you looked at naming services and how these can be used to register information in a distributed environment. In a J2EE environment, such a naming service is accessible through JNDI and can be used to store references to EJB home interfaces.

The EJB container will have registered the home interface using the JNDI name specified during deployment (as part of the deployment descriptor). This is the name that the client should use to look up the home interface. Recall from the EJB deployment descriptor shown in Listing 4.5 that the EJB name specified was

Agency

. When deploying the EJB, the deployer has a chance to set the JNDI name by which clients will find this EJB. In this case, you would expect the deployer to simply set a JNDI name of ejb/Agency so that the client could find the home interface by looking up java:comp/env/ejb/Agency

.

The following code shows the initial lookup required:

06 0672323842 CH04 3/20/02 9:25 AM Page 149

Introduction to EJBs 149 try

{

InitialContext ic = new InitialContext();

Object lookup = ic.lookup(“java:comp/env/ejb/Agency”);

AgencyHome home =

(AgencyHome)PortableRemoteObject.narrow(lookup, AgencyHome.class);

...

} catch (NamingException ex) { /* Handle it */ } catch (ClassCastException ex) { /* Handle it */ }

As you can see, because the reference returned from JNDI is just an object, you must narrow it to the home interface type you expect—in this case,

AgencyHome

. If there are any problems with the JNDI access or if the wrong object type is returned, a

NamingException or

ClassCastException will be thrown.

There is no magic here. The object returned by the JNDI is simply an RMI remote object stub. This stub represents the home interface remote object created by the container when the EJB was deployed. This can be seen in Figure 4.2.

Now that you have a reference to the home interface, you can create the EJB you want to use.

Retrieval and Use

You can now call the create() method you saw defined on the

AgencyHome interface in

Listing 4.4 as follows: try

{

...

Agency agency = home.create();

System.out.println(“Welcome to: “ + agency.getAgencyName());

...

} catch (RemoteException ex) { /* Handle it */ } catch (CreateException ex) { /* Handle it */ }

The create() method returns a remote reference to the newly-created EJB. If there are any problems with the EJB creation or the remote connection, a

CreateException or

RemoteException will be thrown.

CreateException is defined in the javax.ejb

package, and

RemoteException is defined in the java.rmi

package, so remember to import these packages at the top of your client class.

Now that you have a reference to an EJB, you can call its methods. The previous code sample shows the getAgencyName() method being called on the returned

Agency reference. Again, whenever you call a remote method that is defined in an EJB remote interface, you must be prepared to handle

RemoteException s.

4

06 0672323842 CH04 3/20/02 9:25 AM Page 150

150 Day 4

Note You will see later that some types of EJB are found rather than created. In this case, all steps are the same except that the create() method is replaced by the appropriate finder method and find-related exceptions must be handled. You still end up with a remote reference to an EJB. All of this is covered later when Entity EJBs are discussed on Day 6.

Disposing of the EJB

You have now created and used an EJB. What happens now? Well, if you no longer need the EJB, you can get rid of it in exactly the same way that you would get rid of a local

Java object or a remote Java object defined using RMI—by setting its reference to null as follows:

// No longer need the agency EJB instance agency = null;

When the local RMI runtime detects that the remote object no longer has any local references, it will trigger remote garbage collection for that object, which means that its remote reference will time out. This will result in the object being de-referenced at the server-side. In the case of the simple

Agency bean (a stateless Session bean), this will cause the bean to be destroyed.

Although it is possible to use the remove() method to get rid of the EJB, you would not normally use this for such a simple bean. Use of this method is discussed in more detail on Days 5 and 6.

Running the Client

You are now in a position to write a simple application client for the

Agency

EJB. After you have written it, you will want to compile and run it.

Before compiling your client, you should ensure that you have j2ee.jar

on your classpath. This JAR file lives in the lib directory under

J2EE_HOME

. If you are using an enterprise IDE, you may find that all the relevant classes are already in your classpath.

To compile and run the client, you will need the following:

• The J2EE classes. These must be accessible through the classpath.

• Access to the EJB’s home and remote interface class files via the classpath.

• RMI stubs for the home and remote interfaces. These can either be installed on the local classpath or downloaded dynamically from the EJB server.

06 0672323842 CH04 3/20/02 9:25 AM Page 151

Introduction to EJBs 151

• If the client does not have the JNDI name of the EJB compiled in, you may want to provide this on the command line or through a system property.

When you deploy the EJB, you should be able to ask the container for a client JAR file.

This client JAR file will contain all of the classes and interfaces needed to compile the client (as defined in the previous bulleted list). You should add this client JAR file to your classpath when compiling your client.

In theory, this should be it. However, you will find that any form of security definition on the server will require you to authenticate yourself before you can run the application. In this case, you must explicitly use the client container to provide the required security mechanism.

Note

The client container is called runclient under the J2EE RI.

Deploying and Using an EJB in the J2EE

Reference Implementation

You should now be in a position to write and test an EJB client. However, before you can do that, you must deploy an EJB that it can use. In this section, you will look at how to deploy an EJB in the J2EE Reference Implementation (RI) and how to then use it from a simple client.

The J2EE on which your EJB is deployed will provide a complete server-side environment. It houses any EJBs, runs a Web Server for JSP/servlets, runs a naming server for storing component location information, and provides database access. All J2EEcompliant application servers will do this—even a non-commercial version, such as the

J2EE RI. The RI also provides you with a ready-to-use database so you do not have to concern yourself with hooking up to an existing database or installing a separate one.

To deploy and test your EJBs (and servlets/JSPs later), you only need a single machine.

Both the J2EE client and the J2EE server (and its EJBs, servlets and JSPs) can run on the same machine. No connection to the Internet is required. The J2EE RI is available on multiple platforms (Win32, Solaris, and Linux) and should be consistent across these platforms, so that J2EE applications created on one platform can be deployed on another.

If you encounter problems at any stage, try referring to the troubleshooting section just before today’s Summary.

4

06 0672323842 CH04 3/20/02 9:25 AM Page 152

152 Day 4

Note Before running any of the tools described in this section, you will need to set the

J2EE_HOME environment variable to the location on your hard drive where you deployed the J2EE reference implementation. You should also add the bin directory below

J2EE_HOME to your executable search path

(

%PATH% under Windows or

$path under Unix/Linux) so that you can run J2EE tools and batch files from the command line.

Opening the Case Study EAR File

To deploy and manipulate EJBs under the RI, you will use a graphic tool called deploytool

. Before you start using this, you will need to do two things:

1. Ensure that you have created and configured your database environment as described on Day 2.

2. Start the J2EE RI runtime environment and the associated

Cloudscape database. To do this, run the cloudscape and j2ee scripts/batch files found in the bin directory under

J2EE_HOME as follows: cloudscape -start j2ee -verbose

The use of the

-verbose flag for J2EE is not strictly necessary, but you may find it useful to help you understand what the J2EE server does when it starts up.

Now you are ready to run the deploytool

. Again, this is a script/batch file found in the bin directory under

J2EE_HOME

. When you run it, the GUI screen will appear as shown in

Figure 4.3.

You should now be able to open the initial agency enterprise archive provided in the JAR subdirectory of the Day 4 exercise code on the CD-ROM ( agency.ear

). Do this through the menus by selecting File, Open and then browsing for the file in the subsequent Open

Object dialog box. Select the EAR file and click the Open Object button. The agency application will now be displayed in the list of applications, as shown in Figure 4.4.

All of the code for the Agency EJB that is contained in the

Agency application can be found in the agency subdirectory of the src directory under the Day 4 Exercise part of the CD-ROM.

Now that the enterprise application is loaded in deploytool

, you can examine its settings.

06 0672323842 CH04 3/20/02 9:25 AM Page 153

Introduction to EJBs

F

IGURE

4.3

The initial screen shown by deploytool

.

153

4

F

IGURE

4.4

The

Agency application has now been loaded by deploytool

.

06 0672323842 CH04 3/20/02 9:25 AM Page 154

154 Day 4

Examining the Case Study Application

You can use deploytool to examine and alter deployment descriptor information for the application and, if necessary, for individual components, such as EJBs.

If you select the JNDI Names tab for the agency application, you will see information about the resources that the application exports and consumes. This is largely based on information defined in the application deployment descriptor and the container-specific deployment descriptor described earlier in the “Enterprise Applications” section.

In Figure 4.5, you can see in the Application box that there is a single EJB in this initial form of the application. That EJB can be referenced through JNDI using the name ejb/Agency

.

F

IGURE

4.5

deploytool displays the JNDI information from the

Agency application deployment descriptor.

In the References box, you can see that two of the components in the application use external resources. First, you can see that the component named

Agency

(the EJB) uses a resource called jdbc/Agency that is registered under JNDI as jdbc/Agency

.

The References box also indicates that the application client,

SimpleClient

, references the

Agency

EJB by using the name ejb/Agency that appears under JNDI as ejb/Agency

.

06 0672323842 CH04 3/20/02 9:25 AM Page 155

Introduction to EJBs

You can also examine the settings of the EJB through deploytool

. Click the icon next to the

Simple

JAR file symbol to show the EJBs contained in the

Simple

EJB-JAR file.

There is a single EJB in the JAR file called

Agency

. If you select the

Agency

EJB, you will see the properties defined in the deployment descriptor for that EJB. Select the

Resource Refs tab to see what external resources this EJB uses, as shown in Figure 4.6.

F

IGURE

4.6

You can examine the deployment descriptor information for a single EJB, such as the external resources it expects.

155

4

Figure 4.6 shows that the

Agency

EJB expects one resource called jdbc/Agency that is of type javax.sql.Datasource

. This is the EJB deployment descriptor information you saw in Listing 4.5.

Figure 4.7 shows the environment entries for the

Agency

EJB. If you want to alter the

AgencyName defined there, you can just double-click the Value field and type in an alternative name. If you make any changes to the configuration of the application or any of its components, the suffix

(changed) will be added to the application name in the title bar.

06 0672323842 CH04 3/20/02 9:25 AM Page 156

156

F

IGURE

4.7

Environment entries can be viewed or edited through

deploytool

.

Day 4

Deploying the Case Study Application

You can deploy the server-side components of the agency application (in this case, a single EJB) using deploytool

. To deploy them, select the agency application item (indicated by a blue diamond under the

Applications folder in the explorer area on the left side), and select Tools, Deploy from the deploytool menu. This will display the initial deployment screen shown in Figure 4.8.

As you can see from Figure 4.8, the default target host is localhost

. This is fine, presuming that your copy of the J2EE RI is running on the local machine. If not, you should

Cancel and add the appropriate server name through the File, Add Server menu item before proceeding.

The other point to note for this screen is that it allows you to obtain a client JAR file.

Recall that this client JAR file will contain all of the classes required by a client of the application being deployed. A pre-prepared client JAR file is provided in the jar subdirectory of the Day 4 exercise code on the CD-ROM ( agencyClient.jar

), but you will need to obtain a client JAR file for any new applications or components you deploy. If you check the Return Client Jar box, you can browse and select an appropriate location to store the returned JAR file.

06 0672323842 CH04 3/20/02 9:25 AM Page 157

Introduction to EJBs

F

IGURE

4.8

You can select a server on which to deploy an enterprise application.

157

Caution

If you are deploying the agency application on an other machine (not localhost

), you should not use the pre-provided agencyClient.jar

file.

Instead, select the option to return the client JAR file and add this JAR to the classpath when you run the client later.

Click Next to move on to the

JNDI Names screen. This gives the deployer another chance to provide server-specific deployment information. Note that this is the same information you saw earlier when examining the application’s JNDI information. For now, just click the Next button. At the last screen, click Finish to deploy the application to the selected server.

The progress of the deployment is shown in a separate window, as seen in Figure 4.9.

The blue and green bars are progress bars that will increase as the deployment proceeds.

Click OK when the deployment is complete.

When you are done, the agency enterprise application should have been deployed, as shown in Figure 4.10. To view the applications deployed on a server, expand the Servers folder in the explorer area on the left side and then expand the particular server, such as localhost

.

4

06 0672323842 CH04 3/20/02 9:25 AM Page 158

158

F

IGURE

4.9

deploytool will show you the progress of the deployment.

Day 4

F

IGURE

4.10

You can list the applications deployed on a server.

Testing the Case Study Application

After you have successfully deployed the server-side components of the application under the J2EE RI, you can run the test client to check that everything is okay.

06 0672323842 CH04 3/20/02 9:25 AM Page 159

Introduction to EJBs 159

The test client, client.SimpleClient

, is provided pre-compiled in the classes subdirectory. To run this, use the script/batch file runSimple that can be found in the run subdirectory. When you run the client, you will need to provide a username and password.

Use the username guest with a password of guest123

. Your interaction with the client should look something like the following:

Initiating login ...

Username = null

Enter Username:guest

Enter Password:guest123

Binding name:`java:comp/env/ejb/Agency`

Welcome to: J2EE in 21 Days Job Agency

Customer list: J2EE in 21 Days Job Agency abraham alfred george winston

Unbinding name:`java:comp/env/ejb/Agency`

If you are not able to run this test, refer to the “Troubleshooting the Case Study

Application,” section next.

As you can see, this has used the

Agency

EJB to list all of the customers in the job agency database. The code for the client is provided in the client sub-directory of the src directory. If you examine it, you will see that it is identical to the code shown earlier, but with some extra code to invoke the customer listing methods on the EJB and display the results.

If you examine the runSimple script/batch file, you will see that it uses the runclient utility provided by the J2EE RI as follows: runclient -client agency.ear -name SimpleClient -textauth

4

Note

The precise command line used will differ between platforms to define the location of the agency EAR file using the correct direction of slashes (backslash or forwardslash). However, this makes no difference in how the command works.

This runs the client application directly from the EAR file without having to unpack it.

The command line specifies that the client is called

SimpleClient and that it lives in the agency.ear

archive. It also specifies that simple, text-based authentication should be used between the client and the server.

06 0672323842 CH04 3/20/02 9:25 AM Page 160

160 Day 4

The other thing to note in the script/batch file is that the environment variable

APPCPATH is set before runclient is called. The runclient utility uses this environment variable to find the client JAR file for the application (in this case, it points to agencyClient.jar

).

This information will be needed at runtime, not only because the EJB’s interface class files are needed, but also because it also contains RMI stubs that are targeted at the correct server and deployment information that is used to map resource names to the target server’s JNDI names.

Should you need to re-compile the client, or to compile your own client, you will need to add this client JAR file to your classpath.

Initial Naming Context for the Client

When creating a JNDI

InitialContext

, the client runtime must get hold of information regarding the content of this initial context. In the case of runclient

, it will find mappings in the deployment descriptors contained in the client JAR file. These mappings will be used to set up the initial context for the client. Consequently, when the client gets a new initial context and looks up java:comp/env/ejb/Agency

, for example, the local naming service runtime will be able to use the pre-provided mapping information to match this text string to the appropriate JNDI name.

Other application servers may use different mechanisms to set up this initial context, such as passing a

Properties object to the naming runtime containing the appropriate information.

Troubleshooting the Case Study Application

If you have problems running the case study application, check out the following possible issues:

• Have you started the J2EE RI ( j2ee -verbose

)? Make sure by locating its console window or looking for it in the list of processes or applications on your machine.

• Have you started the cloudscape database ( cloudscape -start

)? Try running the initial database test at the end of Day 2 to ensure that the data is present and that the database server is running.

• Have you deployed the EJBs? By opening the EAR file, you are simply loading the enterprise application into the deploytool environment. You must explicitly deploy the application to the server you are using through the Tools, Deploy menu.

• Have you set the classpath correctly for your client? The client will need access to the J2EE libraries in order to run.

06 0672323842 CH04 3/20/02 9:25 AM Page 161

Introduction to EJBs 161

• Try re-creating the client JAR file when you deploy the J2EE application to your server. Make sure that this client JAR file is on the classpath when you compile and run the client application.

• Check the J2EE RI console window to see if exceptions or errors are shown there.

• Check the J2EE log files under the logs directory in

J2EE_HOME

. There is a directory below logs that is named after the machine on which the server is running.

Below this, there are two nested j2ee directories. In the lower of these, you will find various log files that you can examine for errors.

If you still have problems and you suspect that there is a problem with the configuration of your J2EE RI, you can either re-install the RI or you could try deleting the serverspecific repository directory and then re-starting your server. You will lose all of your deployed J2EE applications, but you may find this easier than re-installing. Under the

J2EE_HOME directory, you will find a directory called repository

, and below this there will be a directory named after the server on which you are running this instance of the RI (for example, if your hostname is “fred”, there will be a fred directory below repository

). Stop the J2EE RI, remove the directory that is named after your server, and then start the J2EE RI again.

Summary

Today, you have seen common ways that EJBs are used in applications and why you would want to use them. You have seen that an EJB will have a home interface, a business or remote interface, and an implementation. You have seen how the EJB container will provide much of the underlying code to support the EJB, and that it relies on detailed deployment information that defines the EJB’s requirements.

You have also seen that a J2EE application consists of components and deployment information and how the server-side part of such an application can be deployed. You have seen a client that is able to use such server-side components and the code required to write such a client.

Q&A

Q How many Java classes and interfaces must I write to create an EJB?

A The EJB writer must define a remote (or business) interface, a home interface, and the bean implementation itself.

4

06 0672323842 CH04 3/20/02 9:25 AM Page 162

162 Day 4

Q Why does an EJB run inside a container?

A The container provides many services to the EJB, including distribution, lifecycle, naming/registration, transaction management, security/authentication, and persistence. If the container did not exist, you would have to write all the code to interact with these services yourself.

Q What issues surround passing an object as part of a remote method call?

A To be passed as an argument or return type, an object must be either serializable or remote. If it is neither of these, an error will occur at runtime. If an object is defined as serializable, a new copy will be created and passed/returned. This can add to the overhead of making the method call, but it is a very useful tool when trying to cut down the amount of network traffic between clients and EJBs (as you will see later on Day 18).

Q Most of the deployment descriptor information is straightforward, but what is the difference between a

<resource-ref> and an

<env-entry>

, and what sort of information is contained in each type of entry?

A A

<resource-ref> is part of a deployment descriptor that defines an external resource used by a J2EE component. The

<resource-ref> will define a name and type for a resource together with other information for the container. The information in a

<resource-ref> is really for the container rather than for the EJB itself.

To access a resource defined in a

<resource-ref>

, you would use JNDI to look up its name java:comp/env/jdbc/Agency

.

On the other hand, an

<env-entry> contains information that is intended for the

EJB itself rather than the container. It will define a name, a class type and a value.

The contents of

<env-entry> elements are usually strings. Again, you would use

JNDI to look up its name, java:comp/env/AgencyName

.

Exercises

The intention of this day is for you to familiarise yourself with the EJB environment and the use of EJBs. To ensure that you are comfortable with these areas, you should attempt the following tasks.

1. If you have not already done so, follow the steps to deploy the example

Agency

EJB from the Day 4

Exercise directory on the CD-ROM.

2. Examine the information displayed by deploytool and make sure that you can identify where the resource reference for the

Agency

JDBC connection is set, where the environment reference for the agency name is set, and where the JNDI name of the

Agency

EJB itself is set.

06 0672323842 CH04 3/20/02 9:25 AM Page 163

Introduction to EJBs

3. Use the runSimple script/batch file provided under the Day 4

Exercise directory on the CD-ROM to run the test client. Make sure that this client runs without errors and successfully lists all the customers in the agency database.

4. Without referring to the example client (but referring to the material you have covered today), create your own simple test client for the

Agency

EJB from scratch.

This should just consist of a command-line client that creates an instance of an

Agency

EJB and asks it for its name.

5. Try changing the name under which the EJB is registered in JNDI using deploytool

. Change the JNDI name used by your client to find the

Agency

EJB and make sure that it still works.

163

4

06 0672323842 CH04 3/20/02 9:25 AM Page 164

07 0672323842 CH05 3/20/02 9:36 AM Page 165

W

EEK

1

D

AY

5

Session EJBs

On Day 4, “Introduction to EJBs,” you learned that business functionality can be implemented using Session beans, and you deployed a simple Session bean into the EJB container. Today, you will learn

• The uses of Session beans in more detail

• The different Session bean types and how to specify, implement, and deploy both stateless and stateful Session beans

• About common practices and idioms when using Session beans

Overview

Session beans are a key technology within the J2EE platform because they allow business functionality to be developed and then deployed independently of the presentational layer.

For example, you might create an application with a user interface built using

Java’s Swing API. This application might then provide access to some business functionality for the employees working on the company’s internal network.

07 0672323842 CH05 3/20/02 9:36 AM Page 166

166 Day 5

If the underlying business functionality is implemented as Session beans, a different user interface could take its place without having to redevelop the entire application. A Webbased interface would make the application available from the Internet at a single stroke.

There are two types of Session beans, and a couple of analogies help explain the differences between them. You almost certainly will have used the so-called wizards—helpers to guide you through some task—in any modern word-processing program or IDE. A wizard encapsulates a conversation between you the user and the application running on the computer. The steps in that conversation are dictated by the Next and the Back buttons. The wizard remembers the answers from one page, and these sometimes dictate the choices for the next. When you are done, you select the Finish button and the wizard goes away and does its stuff.

The wizard is analogous to a stateful Session bean. The wizard remembers the answers from each page, or put another way, it remembers the state of the conversation. It also provides some service, as characterized by the Finish button. This is precisely what a stateful session bean does.

Here is another analogy, this time with databases. You may well have had cause to write stored procedures. These are named routines (methods and functions) that are written in a database vendor’s version of the SQL language (for example, PL/SQL for Oracle and

Transact-SQL for Microsoft SQL Server) and stored in the database. They provide a way to implement business rules on the database.

To invoke a stored procedure, a client-side application needs to know just the name of the stored procedure and the parameters it requires. No knowledge of the underlying database schema is needed, so to call a stored procedure called find_jobs_by_ advertiser written in Transact-SQL, the client would use the following: exec find_jobs_by_advertiser “winston”

Behind the scenes, this would probably run a query against the

Job table, but the important thing is that the client does not need to know this detail.

A stored procedure is analogous to a stateless Session bean. The stored procedure just provides a service and can be invoked by any client. You may be wondering why have

Session beans at all if stored procedures—which are a tried-and-trusted technology— already solve the problem. But Session beans do have a number of advantages.

Implementing stateful conversations is cumbersome using stored procedures, but trivial with Session beans. Also, stored procedures are written in some database vendor’s proprietary dialect of SQL, so they are not portable across RDBMS. Session beans are, of course, written in Java, so they will be portable across any compliant EJB container.

Session beans provide a service to a client application. In other words, Session beans are an extension of a client’s business functionality into the middle tier.

07 0672323842 CH05 3/20/02 9:36 AM Page 167

Session EJBs 167

Note The Unified Modeling Language offers a number of useful diagrams to help specify an application, one of which is the use case diagram (see Appendix

A, “An Introduction to UML,” found on the CD-ROM accompanying this book, for further details).

Each use case represents an item of business functionality that is required to fulfill an end-user’s goal. However, while use case diagrams indicate the system boundary of the application being developed, the use cases themselves typically say nothing about the actual look-and-feel of the system. In other words, the presentational layer or user interface is not directly specified.

It is quite possible to directly relate UML use cases to Session beans. A given use case specifies an item of business functionality, while the Session bean implements that functionality. Neither are concerned with the detail of how that functionality is presented to the user.

This direct correspondence of the logical design (as characterized here in

UML use cases) to the physical implementation (in this case, Session beans) is one of the reasons that the J2EE platform is so appealing, quickly allowing designs to be realized into working code.

The javax.ejb

Package for Session Beans

Now it is time to add a little more detail. EJBs are written by implementing various interfaces of the javax.ejb

package. Some of these are implemented by the bean itself. In other words, this is the code that you, the developer must write. Others are implemented either directly by the EJB container or are implemented by classes generated by the tools provided by your EJB container vendor, such as the J2EE RI.

Figure 5.1 shows a UML class diagram of the interfaces in javax.ejb

that support

Session beans.

Central to the EJB architecture are the javax.ejb.EJBHome

and javax.ejb.EJBObject

interfaces, common to both Session beans and Entity beans. These both extend the java.rmi.Remote

interface, meaning that the classes that implement them (not shown) are available through RMI stubs across the network.

The javax.ejb.EJBLocalHome

and javax.ejb.EJBLocalObject

interfaces are local equivalents, and the classes that implement these are accessible only locally (that is, by clients that reside within the same EJB container itself). Because local interfaces are most often used with Entity beans, and also because there’s plenty for you to learn about today already, there’s no major discussion of them until tomorrow.

5

07 0672323842 CH05 3/20/02 9:36 AM Page 168

168 Day 5

F

IGURE

5.1

The

javax.ejb

package defines remote and local interfaces, as well as an interface for the Session bean itself to implement.

interface

EJBMetaData

getEJBHome getHomeInterfaceClass getRemoteInterfaceClass getPrimaryKeyClass isSession isStatelessSession interface

java.rmi.Remote

!

interface

HomeHandle

getEJBHome interface

EJBHome

remove remove getEJBMetaData getHomeHandle interface

EJBLocalHome

remove interface

EJBContext

getEJBHome getEJBLocalHome getEnvironment getCallerIdentity getCallerPrincipal isCallerInRole isCallerInRole getUserTransaction setRollbackOnly getRollbackOnly interface

Handle

getEJBObject interface

EnterpriseBean

interface

EJBObject

getEJBHome getPrimaryKey remove getHandle isIdentical interface

EJBLocalObject

getEJBLocalHome getPrimaryKey remove isIdentical interface

SessionContext

getEJBLocalObject getEJBObject interface

SessionBean

setSessionContext ejbRemove ejbActivate ejbPassivate

The javax.ejb.EJBContext

interface provides access to the home interfaces and, as you can see from its method list, also provides security and transaction control. The javax.ejb.SessionContext

subclass is used only by Session beans and provides a reference to the bean’s

EJBObject

, that is, its interface for remote clients. Every EJB must have a remote interface (or a local interface, discussed on Day 6, “Entity EJBs”).

The javax.ejb.HomeHandle

and javax.ejb.Handle

interfaces provide a mechanism to serialize a reference to either a home or a remote interface for use later. This capability is not often used, so isn’t discussed further.

The Session bean itself implements the javax.ejb.SessionBean

interface that defines the bean’s lifecycle methods and has an implementation for all of the methods defined in the remote or the home interface.

Stateless Session Bean Lifecycle

You already know that there are two different types of bean—stateful and stateless.

You’ll be learning about both types today, first, the simpler stateless bean. The

Agency bean from the case study will be used for the example code.

07 0672323842 CH05 3/20/02 9:36 AM Page 169

Session EJBs

Stateless beans hold no state for any particular client, but they do have a lifecycle—and thus different states—imposed on them by the EJB architecture. Specifically, these are the interactions between the bean and the container in which it has been deployed.

This is a recurrent theme throughout the EJB architecture, so it is important to fully understand it. The methods you define in your bean will be invoked either by its client or by the EJB container itself. Specifically, the methods invoked by the client will be those defined in the remote interface, whereas the methods invoked by the container are those defined by the javax.ejb.SessionBean

interface. The bean must also provide methods that correspond to the create method of the bean’s home interface.

Figure 5.2 shows the

SessionBean interface and its super-interfaces.

F

IGURE

5.2

The javax.ejb.

SessionBean interface defines certain lifecycle methods that must be implemented by

Session beans.

interface

java.io.Serializable

interface

EnterpriseBean

!

169 interface

SessionBean

+ setSessionContext(sessioncontext:SessionContext):void

+ ejbRemove():void

+ ejbActivate():void

+ ejbPassivate():void

In the case study, the

AgencyBean class indicates that it is a Session bean implementation by implementing this interface: package agency; import javax.ejb.*;

// some import statements omitted public class AgencyBean implements SessionBean

{

// code omitted

}

5

07 0672323842 CH05 3/20/02 9:36 AM Page 170

170 Day 5

The lifecycle for Session beans, as perceived by the Session bean and as likely to be enacted by the EJB container, is as shown in the UML state chart diagram in Figure 5.3.

F

IGURE

5.3

Stateless Session beans have a lifecycle managed by the EJB container.

[pool too small]/ setSessionContext

Context Set

[context set]/ejbCreate remove^ejbobject.finalize() create^ejbobject.new()

Pooled

[surplus]/ejbRemove

[business method invoked]

[method completes]

Bound to client business method

The lifecycle is as follows:

• If the EJB container requires an instance of the stateless Session bean (for example, because the pool of instances is too small), it instantiates the bean and then calls the lifecycle method setSessionContext()

. This provides the bean with a reference to a

SessionContext object, providing access to its security and transaction context.

• Immediately after the context has been set, the container will call ejbCreate()

.

This means that the bean is now ready to have methods invoked. Note that the ejbCreate() method is not part of the

SessionBean interface, but nevertheless must be declared in the bean.

• When a client invokes a business method, it is delegated by the bean’s

EJBObject proxy to the bean itself. During this time, the bean is temporarily bound to the client. When the method completes, the bean is available to be called again.

The binding of the bean to the client lasts only as long as the method takes to execute, so it will typically be just a few milliseconds. The EJB specification specifies this approach so that the bean developer does not need to worry about making the bean thread-safe.

07 0672323842 CH05 3/20/02 9:36 AM Page 171

Session EJBs 171

To support the case where two (or more) clients need to invoke the service of some stateless Session bean at the same time, most EJB containers hold a pool of Session beans. In general, the pool will never be larger than the maximum number of concurrent clients. If a container decides that the pool is too large or that some Session bean instances are surplus, it will call the bean’s ejbRemove() method.

Note As you will see on Day 12, “Servlets,” servlets have similarities with stateless

Session beans. However, in the servlet specification, they are defined to work in precisely the opposite way; by default, a servlet must be threadsafe, and there is only one instance of it.

If the client calls create() or remove()

, the bean itself is not necessarily affected.

However, the client’s reference to the bean will be initialized (or destroyed). The client is not aware of the complexities of this lifecycle, so the client’s perception of a stateless bean is somewhat simpler, as shown in Figure 5.4.

F

IGURE

5.4

The client’s perception of the bean’s lifecycle is simple.

deploy/setSessionContext

Context Set create/ejbCreate

Ready remove/ejbRemove business method

From the client’s perspective, the bean is simply instantiated when the client calls create() on the bean’s home interface, and is removed when the bean calls remove() on the bean itself.

5

07 0672323842 CH05 3/20/02 9:36 AM Page 172

172 Day 5

Note The EJB specification does not attempt to prescribe too closely the implementation of EJB containers, and correctly focuses instead on their specification. Unfortunately, it does not always identify the only realistic implementation.

For example, the EJB specification suggests that the EJB container is at liberty to adopt any appropriate pooling policy for Session beans. In Figure 5.3, you saw the state chart for a container using an eager instantiation policy, pre-instantiating beans before they are necessarily used. However, the fact that beans can throw

CreateException exceptions from their ejbCreate() method seems to imply that only a lazy instantiation policy—instantiating beans only as they are required—could be used.

In fact, it is the case that some EJB containers do not maintain a pool of

Session bean references and, instead, simply instantiate beans as required. In other words, the actual lifecycle for the bean matches that perceived by the client. While this might seem a wasteful approach, in fact it is not; modern

JVMs are becoming so efficient that maintaining a pool of beans is more expensive than simply instantiating beans as needed.

Figures 5.3 and 5.4 show how the methods of the

SessionBean interface are invoked through the bean’s lifecycle. You will have noticed that the ejbActivate() and ejbPassivate() methods are not mentioned; this is because these methods are only called for stateful Session beans, a topic covered later today. However, given that these methods are in the

SessionBean interface, they do require an implementation. For stateless Session beans, this implementation will be empty.

The implementation of the lifecycle methods is covered later today in the “Implementing a Stateless Session Bean” section.

Specifying a Stateless Session Bean

As you will by now have gathered, the responsibilities of Session beans (and indeed,

Entity beans) are specified through its remote and home interfaces. These are what the

EJB container makes available to the remote clients.

To define a home interface for a stateless Session bean, extend javax.ejb.EJBHome

. To define a remote interface, extend javax.ejb.EJBObject

. Because both

EJBHome and

EJBObject extend the java.rmi.Remote

interface, the rules for remote objects (in the

Java sense of the word) must be followed.

The following is the home interface for the

Agency session bean. If it looks familiar, it should be—you saw this for the first time just yesterday.

07 0672323842 CH05 3/20/02 9:36 AM Page 173

Session EJBs 173 package agency; import java.rmi.*; import javax.ejb.*; public interface AgencyHome extends EJBHome

{

Agency create() throws RemoteException, CreateException;

}

The

AgencyHome interface defines a single no-arg method called create()

. This method returns an

Agency

, which is the remote interface for the

Agency bean. Because this remote interface is remote (that is, extends java.rmi.Remote

), what the client that calls this interface will receive is a reference to the remote

Agency object. In other words, the client will obtain an RMI stub to the

Agency

.

The EJB specification requires that stateless Session beans must define this single no-arg version of the create() method. The bean can perform any initialization it requires there. The create() method throws java.rmi.RemoteException

, as required for remote objects, and also throws javax.ejb.CreateException

. This is an exception that the bean can throw to indicate that it was unable to initialize itself correctly.

The create() method in the home interface implies a corresponding ejbCreate() method in the bean class itself. This delegation to a method with an ejb prefix is prevalent throughout the EJB specification, so you will become quite familiar with it over the next few days. The corresponding code in the

AgencyBean class is as follows: package agency;

// some import statements omitted import java.rmi.*; import java.util.*; import javax.ejb.*; public class AgencyBean implements SessionBean

{ public void ejbCreate() throws CreateException {

// implementation omitted

}

// code omitted

}

Note that the ejbCreate() method also takes no arguments because the argument list must match. The throws clause includes javax.ejb.CreateException

, because that was defined in the home interface, but does not include java.rmi.RemoteException

. This is because the bean itself is not remote; it is the code generated by the vendor’s deployments tools that is remote. The EJB specification requires also that ejbCreate() method returns void

.

5

07 0672323842 CH05 3/20/02 9:36 AM Page 174

174 Day 5

Listing 5.1 shows the remote interface for the

Agency session bean. Again, you saw this yesterday:

L

ISTING

5.1

Remote Interface for the Stateless

Agency

Bean

1: package agency;

2:

3: import java.rmi.*;

4: import java.util.*;

5: import javax.ejb.*;

6:

7: public interface Agency extends EJBObject

8: {

9: String getAgencyName() throws RemoteException;

10:

11: Collection findAllApplicants() throws RemoteException;

12: void createApplicant(String login, String name, String email)

13: throws RemoteException, DuplicateException, CreateException;

14: void deleteApplicant (String login)

➥throws RemoteException, NotFoundException;

15:

16: Collection findAllCustomers() throws RemoteException;

17: void createCustomer(String login, String name, String email)

18: throws RemoteException, DuplicateException, CreateException;

19: void deleteCustomer(String login)

➥throws RemoteException, NotFoundException;

20:

21: Collection getLocations() throws RemoteException;

22: void addLocation(String name)

➥throws RemoteException, DuplicateException;

23: void removeLocation(String code)

➥throws RemoteException, NotFoundException;

24:

25: Collection getSkills() throws RemoteException;

26: void addSkill(String name)

➥throws RemoteException, DuplicateException;

27: void removeSkill(String name)

➥throws RemoteException, NotFoundException;

28:

29: List select(String table) throws RemoteException;

30: }

You can see that the

Agency

Session bean provides a number of sets of functionality, managing applicants, customers, locations, and skills. These services manipulate data within the database, but there is no underlying state for the bean itself. In each case, the methods throw java.rmi.RemoteException

as required and also throw various other exceptions.

The

DuplicateException and

NotFoundException are user-defined exception classes that simply extend java.lang.Exception

. You encountered these classes yesterday.

07 0672323842 CH05 3/20/02 9:36 AM Page 175

Session EJBs 175

For each of these methods in the remote interface, there is a corresponding method in the Session bean. As was noted before, this is not because the bean has implemented the remote interface (it hasn’t) but because the EJB specification requires it so that the

EJBObject proxy (the vendor-generated implementation of the remote interface) can delegate to the bean. The business methods for the

AgencyBean have the same signature as those in the remote interface, with the exception that they do not throw java.rmi.RemoteException

. Those are the steps to specifying a stateless Session bean’s interface. Indeed, as you will see later today and tomorrow, specifying the interface of stateful Session beans and of Entity beans follows along very similar lines. In the next section, “Implementing a Stateless Session Bean,” you will see the implementation of some of these methods.

Implementing a Stateless Session Bean

Implementing a Session bean involves providing an implementation for the methods of the javax.ejb.SessionBean

, corresponding methods for each method in the home interface, and a method for each method in the remote interface.

Implementing javax.ejb.SessionBean

The implementation of the methods of the SessionBean interface is often boilerplate. The setSessionContext() method usually just saves the supplied SessionContext object: private SessionContext ctx; public void setSessionContext(SessionContext ctx) { this.ctx = ctx;

}

Although ejbRemove() method is part of the

SessionBean interface, you’ll learn about its implementation in the next section. As already noted, for a stateless Session bean, the ejbActivate() and ejbPassivate() methods should have a null implementation: public void ejbActivate() { } public void ejbPassivate() { }

Implementing the Home Interface Methods

The home interface has a single method create()

. The ejbCreate() method in the bean corresponds to this method. It makes sense to look up JNDI references in the ejbCreate() method and store them in instance variables. This is shown in Listing 5.2.

L

ISTING

5.2

AgencyBean.ejbCreate()

Method

1: private DataSource dataSource;

2: private String name = “”;

3: public void ejbCreate () throws CreateException {

5

07 0672323842 CH05 3/20/02 9:36 AM Page 176

176 Day 5

L

ISTING

5.2

Continued

4: InitialContext ic = null;

5: try {

6: ic = new InitialContext();

7: dataSource = (DataSource)ic.lookup(“java:comp/env/jdbc/Agency”);

8: }

9: catch (NamingException ex) {

10: error(“Error connecting to java:comp/env/jdbc/Agency:”,ex);

11: return;

12: }

13: try {

14: name = (String)ic.lookup(“java:comp/env/AgencyName”);

15: }

16: catch (NamingException ex) {

17: error(“Error looking up java:comp/env/AgencyName:”,ex);

18: }

19: }

Tip On Day 18, “Patterns,” you will learn about a design pattern that simplifies

JNDI lookups. It can also speed up your beans; some EJB containers are not particularly efficient at obtaining references from within JNDI.

In this case, the ejbCreate() method makes two lookups from JNDI; the first is to obtain a

DataSource

(you will see how this is used shortly) and the other is to obtain environment configuration information—that is, the name of the agency—from the deployment descriptor. You will learn about the deployment descriptor in the following section.

Incidentally, this is a good place to note that stateless Session bean does not mean that the bean has no state; just that it has no state that is specific to any given client. In the case of the

Agency bean, it caches a

DataSource and its name in instance variables.

The home interface inherits a remove(Object o) from

EJBHome

. This corresponds to the ejbRemove() method of the bean. The implementation is pretty simple; it should just reset state: public void ejbRemove(){ dataSource = null;

}

07 0672323842 CH05 3/20/02 9:36 AM Page 177

Session EJBs 177

Note The ejbRemove() method is mandated by the EJB specification in three different ways! It appears in the

SessionBean interface, and it is required as the method corresponding to the home method of remove(Object)

(inherited from javax.ejb.EJBHome

) and is also required as the method corresponding to the remote method of remove()

(inherited from javax.ejb.EJBObject

). It is covered here because it fits best along side the coverage of ejbCreate()

.

Implementing the Remote Interface Methods

The remaining methods correspond to the business methods defined in the remote interface. The

Agency session bean manipulates the data in the

Applicant

,

Customer

,

Location

, and

Skill tables, providing methods to return all the data in a table, to insert a new item, or to delete an existing item. When deleting rows, rows in dependent tables are also removed.

The methods that manipulate the database all require a java.sql.Connection

to submit

SQL to the database. In regular “fat client” applications, the idiom is to create a database connection at application startup and to close the connection only when the user quits the application. This idiom exists because making database connections is expensive in performance terms. When writing EJBs, however, the idiom is the precise opposite. You should obtain the database connection just before it is needed, and close it as soon as your processing is complete. In other words, “acquire late, release early.” This is because the EJB container has already made the database connections and holds them in a pool.

When your bean obtains its connection, it is simply being “leased” one from the pool for a period of time. When the connection is “closed,” in reality it is simply returned back to the pool to be used again.

The getLocations() method shows this principle clearly, as shown in Listing 5.3.

L

ISTING

5.3

AgencyBean.getLocations()

Method

1: public Collection getLocations() {

2: Connection con = null;

3: PreparedStatement stmt = null;

4: ResultSet rs = null;

5: try {

6: con = dataSource.getConnection();

7: stmt = con.prepareStatement(“SELECT name FROM Location”);

8: rs = stmt.executeQuery();

9:

5

07 0672323842 CH05 3/20/02 9:36 AM Page 178

178 Day 5

L

ISTING

5.3

Continued

10: Collection col = new TreeSet();

11: while (rs.next()) {

12: col.add(rs.getString(1));

13: }

14:

15: return col;

16: }

17: catch (SQLException e) {

18: error(“Error getting Location list”,e);

19: }

20: finally {

21: closeConnection(con, stmt, rs);

22: }

23: return null;

24: }

25: private void closeConnection (Connection con,

26: PreparedStatement stmt, ResultSet rslt) {

27: if (rslt != null) {

28: try {

29: rslt.close();

30: }

31: catch (SQLException e) {}

32: }

33: if (stmt != null) {

34: try {

35: stmt.close();

36: }

37: catch (SQLException e) {}

38: }

39: if (con != null) {

40: try {

41: con.close();

42: }

43: catch (SQLException e) {}

44: }

45: }

In this method, you can see the

DataSource object obtained in the ejbCreate() method in use. The

Connection object is obtained from this

DataSource object. Another advantage of this approach is that the user and password information does not need to be embedded within the code; rather, it is set up by the deployer who configures the

DataSource using vendor-specific tools. As you remember from Day 2, “The J2EE

Platform and Roles,” in the J2EE RI, the

DataSource object is configured by editing the resource.properties

file in the

%J2EE_HOME%\config directory.

The other business methods all access the database in a similar manner.

07 0672323842 CH05 3/20/02 9:36 AM Page 179

Session EJBs 179

Exceptions

Your bean needs to be able to indicate when it hits an exception. The EJB specification lays out certain rules as to the types of exceptions your bean can throw, because the client does not call your bean directly. For remote clients, there is also the possibility of network problems.

The EJB specification categorizes exceptions as either application exceptions or system exceptions. These correspond quite closely to the regular Java categories of checked exceptions and runtime exceptions.

Figure 5.5 shows the exceptions in the javax.ejb

package, indicating which are application and which are system exceptions.

F

IGURE

5.5

Exceptions are either system exceptions or application exceptions.

System

Exceptions java.lang.RuntimeException

!

0..1

caused by

java.lang.Exception

!

java.io.IOException

!

EJBException

-causeException:Exception

+EJBException()

+EJBException(message:String)

+EJBException(ex:Exception)

+EJBException(message:String,ex:Exception)

+getCausedByException():Exception

+getMessage():String

+printStackTrace(ps:PrintStream):void

+printStackTrace():void

+printStackTrace(pw:PrintWriter):void

java.rmi.RemoteException

!

DuplicateKeyException

FinderException

CreateException

ObjectNotFoundException

RemoveException

NoSuchEntityException AccessLocalException

Application

Exceptions

NoSuchObjectLocalException

5

So, what do these categorizations mean? If a bean throws an application exception, the

EJB container will propagate this back to the application client. As you shall see on Day

8, “Transactions and Persistence,” any ongoing transaction is not terminated by an application exception. In other words, the semantics of an application exception are pretty similar to a checked exception; generally, the client can recover if desired.

However, if a bean throws a system exception, that indicates a severe problem that will not be recoverable by the client. For example, if the bean has been incorrectly deployed such that the database connection fails, there is very little that the client can do about it.

07 0672323842 CH05 3/20/02 9:36 AM Page 180

180 Day 5

In such a case, the EJB container will take steps to terminate any ongoing transaction because it is unlikely to complete. Moreover, the EJB container will discard the bean instance that threw the exception. In other words, there is no need to code any clean up logic in your bean after having thrown a system exception.

Although all runtime exceptions are classified as EJB system exceptions, the javax.ejb.EJBException

is a

RuntimeException provided for your use. This class allows the underlying cause to be wrapped through one of its constructors. The error() helper method in

AgencyBean does precisely this: private void error (String msg, Exception ex) {

String s = “AgencyBean: “+msg + “\n” + ex;

System.out.println(s); throw new EJBException(s,ex);

}

In Figure 5.5, you can see that there is one checked exception, namely java.rmi.RemoteException

, that is classified as an EJB system exception rather than as an EJB application exception. Your bean code should never throw this exception; instead, it is reserved for the EJB container itself to throw. If your bean has hit a system exception, it should throw an

EJBException rather than

RemoteException

.

Configuring and Deploying a Stateless

Session Bean

With the code compiled, the next step is to deploy the bean onto the EJB container.

As you learned yesterday, EJBs are designed to be portable across EJB containers, and the configuration information that defines the bean’s name, interfaces, class(es), characteristics, dependencies, and so on is stored in an XML document called a deployment descriptor. This is provided along with the bean code itself.

As you appreciate from Day 2, there are several EJB roles involved in building the deployment descriptor. The bean provider specifies the information about a given bean

(“intra-bean” configuration information, if you like), and the application assembler specifies the information about all the beans in an application (“inter-bean” configuration information). When both the bean deployer and application assembler have specified their information, the deployment descriptor is complete.

However, that’s not the end of the story, because the deployment descriptor does not define every piece of configuration information necessary to deploy a bean. In effect, the deployment descriptor defines only the logical relationships and dependencies between the beans. There will also be additional configuration information that maps the logical

07 0672323842 CH05 3/20/02 9:36 AM Page 181

Session EJBs 181 dependencies of the deployment descriptor to the physical environment. Performing this mapping is the role of the deployer.

EJB container vendors are free to capture this additional mapping in any way they want, although most use auxiliary deployment descriptors, again usually XML documents. In the case of the J2EE RI, the auxiliary deployment descriptors are indeed XML documents. The EJB specification explicitly disallows vendors from storing their auxiliary mapping information in the standard deployment descriptor itself.

Thus, to port an EJB from one EJB container to another, all that should be required is to recreate an auxiliary deployment descriptor. In other words, the deployer has to redeploy the application, but the bean provider and the application assembler should not have to get involved.

Because manipulating XML documents can be somewhat error prone, most EJB container vendors provide graphical tools to do the work. As you saw yesterday, this is the deploytool

GUI in the case of the J2EE RI. Unfortunately, many such tools do not distinguish between information that is being saved in the standard deployment descriptor and that which is being saved in the vendor’s own auxiliary deployment descriptors.

Also, many tools do not explicitly support the EJB architecture’s concept of roles, making it possible for a bean provider to start specifying information that might more correctly be decided only by the application assembler or even the deployer. The J2EE RI deploytool is guilty on both counts.

Because you may not be using J2EE RI in your own projects, this section presents the task of deployment by looking at both the J2EE RI deploytool and also the underlying

XML deployment descriptor. Having a firm understanding as to how these relate should make it much easier for you to deploy if you aim to deploy to some other EJB container.

It also has to be said that understanding the XML deployment descriptor makes the deploytool

GUI easier to comprehend.

Using deploytool

This section shows how to deploy the Day 5 version of the case study application to the J2EE

RI. It’s best if you follow along (but if you’re on a train, just read the text and make do).

As usual, start up the Cloudscape RDBMS and J2EE RI using two console windows.

Then, start up a third console window and start deploytool

.

By choosing File, Open, load up the day05\Examples\agency.ear

enterprise application archive. This defines two groups of Session beans—

Agency and

Advertise

. Their contents have already been configured to contain the appropriate code. Click either of these in the explorer on the left side of the deploytool

GUI and their contents will be shown on the right side. Note that for both of these beans, some supporting classes (application exception classes) also constitute part of the bean.

5

07 0672323842 CH05 3/20/02 9:36 AM Page 182

182 Day 5

To deploy the Session beans, select the Examples item (under the Applications folder in the explorer area on the left side) and choose the Tools, Deploy menu option. Click Next twice and then click Finish.

As you saw yesterday, the deployment descriptor holds configuration information. This is accessible within deploytool as follows. Select the

Agency element from the explorer, and then choose Tools, Descriptor Viewer from the menu. This will display the XML deployment descriptor for all of the beans in that EJB JAR file (in this case, just the one

Agency bean). Figure 5.6 shows this screen.

F

IGURE

5.6

The deploytool lets you view the underlying deployment descriptor.

In the following sections, you’ll see how this information is structured and built up.

Structural Elements

XML documents provide a mechanism to store data in a hierarchical format, and are similar in style to HTML documents. You shouldn’t have too much trouble following the coming discussion, but if you want to do some additional background reading, skip forward to Day 16, “Integrating XML with J2EE,” which covers XML documents in more detail.

The format of the EJB deployment descriptor is defined by a document type definition

(DTD) file called ejb-jar_2_0.dtd

in the

%J2EE_HOME%\lib\dtds\ directory. The root of an EJB deployment descriptor is the ejb-jar element, whose definition is as follows:

07 0672323842 CH05 3/20/02 9:36 AM Page 183

Session EJBs 183

<!ELEMENT ejb-jar (description?, display-name?, small-icon?, large-icon?, enterprise-beans, relationships?, assembly-descriptor?, ejb-client-jar?)>

This indicates that an ejb-jar element may (the

?

sign suffix) contain one each of a description

, display-name

, small-icon

, large-icon

, relationships

, assemblydescriptor and ejb-client-jar elements, and must (no suffix) contain one enterprise-beans element.

The enterprise-beans element’s definition is as follows:

<!ELEMENT enterprise-beans (session | entity | message-driven)+>

This states that an enterprise-beans element consists of one or many (the

+ sign suffix) elements that are either session

, entity

, or message-driven elements. In other words, the enterprise-beans element contains one or more session

, entity

, or messagedriven elements. You will learn about the session element shortly.

In Figure 5.6, you can see this structure in the deployment descriptor, and you can also see the same hierarchy in deploytool

’s explorer pane on the left side of the explorer.

The

Advertise ejb-jar element consists of two beans.

Presentational Elements

In many of the definitions within the DTD, you will see the display-name

, description

, small-icon

, and large-icon elements defined. These are used by vendor deployment tools when managing your beans, so you will certainly want to define a display-name to distinguish the beans in the tool’s GUI. Whether you choose to provide the remaining elements is up to you. Their presence is primarily so that third- party companies can develop EJBs to sell as “off-the-shelf” business logic components.

In Figure 5.6, you can see that the display-name element for the two groups of EJB

JARs have been set to

Advertise and

Agency

. This is shown in the explorer on the left side of deploytool

GUI. It is also presented as the (read-only) JAR Display Name on the right side when the

Advertise node is selected. There doesn’t appear to be any good reason why this is read-only in deploytool

, that’s just the way the tool works.

If you wanted to add another EJB to the enterprise application, (using the File, New,

Enterprise Bean menu option) it would either be in an existing ejb-jar or a new ejbjar could be defined. When choosing the second option, the display-name for your new collection of EJBs can be specified. This is shown in Figure 5.7.

5

07 0672323842 CH05 3/20/02 9:36 AM Page 184

184

F

IGURE

5.7

The deploytool allows

EJBs to be defined in either their own ejbjar

(with attendant deployment descriptor) or in an existing ejbjar

.

Day 5

For your own custom applications, it really is up to you whether you choose to use one ejb-jar or several. In the case study example, the latter has been used. Certainly, if you wanted to use some off-the-shelf component EJBs bought from a third-party vendor, the

EJBs will already have been bundled into an EJB JAR file. To add these to your enterprise application, you would use File, Add to Application, EJB JAR menu option. When the selected JAR file is read, the display-name element from the associated XML deployment descriptor would then be used.

Session Element

The configuration information for Session beans is defined—not surprisingly—in the session element of the DTD. Its definition is as follows:

<!ELEMENT session (description?, display-name?, small-icon?, large-icon?, ejb-name, home?, remote?, local-home?, local?, ejb-class, session-type, transaction-type, env-entry*, ejb-ref*, ejb-local-ref*, security-role-ref*, security-identity?, resource-ref*, resource-env-ref*)>

This indicates that a session element may contain the presentational elements just discussed, and also will consist of a number of other mandatory and optional elements. The mandatory elements are as follows:

07 0672323842 CH05 3/20/02 9:36 AM Page 185

Session EJBs

• The ejb-name element is the mandatory logical name for the EJB. This must be unique within all the EJBs defined within the ejb-jar element.

• The home

, remote

, local-home

, and local elements define the remote and local interfaces for the bean. They are all marked as being optional, although the specification also requires that either the home and remote and/or the local-home and local elements are defined. As was noted previously, you’ll be learning about local interfaces in detail tomorrow.

• The ejb-class element defines the class that has the bean’s actual implementation.

• The session-type element indicates whether the bean is stateless or stateful.

• The transaction-type element indicates how the EJB container should manage transactions. You will be learning about this in detail on Day 8, so until then, just specifying that transactions are Required will be sufficient.

This information is available graphically in deploytool

. Select the

Agency

Session bean in the explorer pane (under

Agency ejb-jar

). You should see something similar to that shown in Figure 5.8.

F

IGURE

5.8

The deploytool

represents the underlying deployment descriptor graphically.

185

5

You should be able to see a pretty close resemblance between the information portrayed in deploytool and the mandatory information required by the underlying deployment descriptor. The only mandatory item not shown is transaction-type

; that is on the

Transactions tab of the GUI.

07 0672323842 CH05 3/20/02 9:36 AM Page 186

186 Day 5

The (fragment of the) underlying deployment descriptor for the

Agency bean that is represented in Figure 5.8 is as follows:

<session>

<display-name>AgencyBean</display-name>

<ejb-name>AgencyBean</ejb-name>

<home>agency.AgencyHome</home>

<remote>agency.Agency</remote>

<ejb-class>agency.AgencyBean</ejb-class>

<session-type>Stateless</session-type>

… lines omitted …

</session>

This should tie in with the code that was presented earlier today.

Thus far, you have only seen the EJB standard deployment descriptor, but there is also the vendor-specific deployment descriptor for J2EE. The structure of this auxiliary deployment descriptor is defined by

%J2EE_HOME%\lib\dtds\sun-j2ee-ri_1_3.dtd

, but it is not so necessary to learn its structure in detail because it all vendor specific, and deploytool allows it to be configured through its GUI.

The auxiliary information in this descriptor maps the logical names to the physical runtime environment. For the Session bean itself, a mapping is required from its logical ejb-name to its JNDI name. The following fragment from the auxiliary deployment descriptor shows this:

<j2ee-ri-specific-information>

// lines omitted

<enterprise-beans>

// code omitted

<ejb>

<ejb-name>AgencyBean</ejb-name>

<jndi-name>ejb/Agency</jndi-name>

Figure 5.9 shows how deploytool portrays this information. Figure 5.9 also shows the

JNDI mappings for references; you will learn about these shortly.

The remaining optional items of the EJB deployment descriptor ( env-entry

, resourceref

, and so on) also correspond to the different tabs of the deploytool window shown in

Figure 5.8. These each indicate different types of dependencies that the bean may have with its runtime environment. The following sections discuss each in turn.

07 0672323842 CH05 3/20/02 9:36 AM Page 187

Session EJBs

F

IGURE

5.9

Behind the scenes, deploytool stores the

JNDI mappings to an auxiliary vendorspecific deployment descriptor.

187

Environment Entries

Environment entries allow general configuration information—as might be found in a

.ini

file or in the Windows Registry—to be made available to the bean. Such entries are represented by the env-entry element in the deployment description and—not surprisingly—are configured on the Env. Entries tab within deploytool

.

The

Agency bean uses an environment entry to look up its name. The relevant code is in the ejbCreate() method:

InitialContext ic = new InitialContext();

// code omitted name = (String)ic.lookup(“java:comp/env/AgencyName”);

The EJB specification requires that the EJB container makes the environment entries available under the well-defined context of java:comp/env

. Therefore, this is needed in the JNDI lookup. However, this prefix is not required in the deployment descriptor itself.

The DTD defines the env-entry element as follows:

<!ELEMENT env-entry (description?, env-entry-name, env-entry-type, env-entryvalue?)>

5

07 0672323842 CH05 3/20/02 9:36 AM Page 188

188 Day 5

The type of the environment entry (

String

,

Integer

, and so on) is indicated through the env-entry-type element. The actual value ( env-entry-value

) is optional, meaning that the bean provider/application assembler does not need to define it. If the actual value isn’t specified by these roles, the deployer will need to define the value.

Figure 5.10 shows this information being configured within deploytool

.

F

IGURE

5.10

Environment entries can be managed graphically by deploytool

.

In the underlying deployment descriptor for the

Agency bean, this corresponds to

<env-entry>

<env-entry-name>AgencyName</env-entry-name>

<env-entry-type>java.lang.String</env-entry-type>

<env-entry-value>J2EE in 21 Days Job Agency</env-entry-value>

</env-entry>

To re-emphasize, note that the entry name is

AgencyName

, not java:comp/env/AgencyName

.

EJB References

Declaring EJB references for an EJB indicates that the bean being deployed depends on other EJBs. Generally speaking, when there are inter-bean dependencies, both the dependent and dependee will have been written by the same bean provider. However, there will be cases when this isn’t so. The issue then arises that the dependent EJB may not know the deployed name of the EJB upon which it depends.

07 0672323842 CH05 3/20/02 9:36 AM Page 189

Session EJBs 189

For example, a vendor might provide some sort of business-oriented bean (perhaps an

Invoice

EJB) that can optionally perform logging through a

Logging

EJB. The same vendor might well provide an implementation of a

Logging

EJB, but would also allow for application assemblers to use some other EJB that implements the appropriate home and remote interfaces. In this way, the application assembler could ensure that all logging from beans within its application was consistent.

Now the

Invoice

EJB will have a reference to a

Logging

EJB in its JNDI lookup. This might be something like the following:

InitialContext ic = new InitialContext();

Object lookup = ic.lookup(“ejb/Logger”);

LoggerHome home = (AgencyHome)PortableRemoteObject.narrow(lookup,

LoggerHome.class);

Logger logger = home.create();

The

“ejb/Logger” string is hard-coded into the

Invoice

EJB source code and cannot be changed by the application assembler or by the deployer. This is what is sometimes referred to as the coded name or coded entry. However, the deployment descriptor allows this logical name to be associated with an actual physical name through the ejb-ref elements.

The following is the ejb-ref element, as defined by the DTD:

<!ELEMENT ejb-ref (description?, ejb-ref-name, ejb-ref-type, home, remote, ejblink?)>

The ejb-ref-name element is the coded name— ejb/Logger in the previous example.

The names of the home and remote interfaces must be specified. Finally, the ejb-link element specifies the actual ejb-name of the EJB that implements these interfaces. This is a way for the application assembler to constrain the reference in the dependent EJB to a particular EJB within the enterprise application.

In the Day 5 version of the case study, there are no EJB references between EJBs (you will see some such references between EJBs tomorrow), but there are EJB references from the clients and the EJBs. These are shown in Figure 5.11.

When EJB references are defined, they must also be mapped to the physical environment.

Figure 5.9 showed the mapping of EJBs and of references to JNDI names. These are shown in the bottom half of the window of Figure 5.9. As an experiment, try temporarily creating a EJB (remote) reference between the beans themselves, and then return to the JNDI tab in deploytool

(as shown in Figure 5.9). Here, the deployer should indicate the JNDI name for the EJB that implements the remote interfaces. As required by the EJB specification, deploytool validates that the JNDI name that is mapped by the deployer to the EJB reference is compatible with any ejb-link that may have been specified by the application assembler. When you have finished experimenting, you should delete these EJB references.

5

07 0672323842 CH05 3/20/02 9:36 AM Page 190

190

F

IGURE

5.11

There are EJB references from the clients to the EJBs.

Day 5

Resource References

Yesterday, you learned that the EJB container allows

DataSource s to be obtained via

JNDI. Within the deployment descriptor, a resource reference is used to define that dependency of the EJB. The term resource reference is used instead of database refer-

ence because an EJB can depend on data resources other than databases. It could also depend on an e-mail session (Day 11, “JavaMail”), on a URL, on a message topic or queue factory (Day 10, “Message-Driven Beans”), or on a general resource as defined by the Connector architecture (Day 19, “Integrating with External Resources”).

The

Agency bean has a dependency on a

DataSource reference that it refers to as jdbc/Agency

. This can be seen in the ejbCreate() method of the

AgencyBean code. As with the environment entries, note that resource reference has been bound by the EJB container under the context java:comp/env

:

InitialContext ic = new InitialContext(); dataSource = (DataSource)ic.lookup(“java:comp/env/jdbc/Agency”);

Resource references are defined in the DTD as follows:

<!ELEMENT resource-ref (description?, res-ref-name, res-type, res-auth, ressharing-scope?)>

Going through these in turn

• The res-ref-name element is the coded name of the referenced resource.

• The res-type element is the fully-qualified interface or class name of the resource.

07 0672323842 CH05 3/20/02 9:36 AM Page 191

Session EJBs

• The res-auth element indicates whether the container will provide the authentication information (username and password) or the application itself. In other words, it indicates which overloaded version of

DataSource.getConnection() will be called—the one where username and password are supplied (Application authentication) or where they are not (Container authentication).

• The res-sharing-scope element indicates whether this resource can be shared among beans (the default) or whether a separate resource will be set up for this bean’s exclusive use.

So, in the deployment descriptor for the

Agency bean, you will see the following:

<resource-ref>

<res-ref-name>jdbc/Agency</res-ref-name>

<res-type>javax.sql.DataSource</res-type>

<res-auth>Container</res-auth>

<res-sharing-scope>Shareable</res-sharing-scope>

</resource-ref>

This information is also shown in deploytool

, as shown in Figure 5.12.

F

IGURE

5.12

Resource references can be managed graphically by deploytool

.

191

5

Again, resource references must be mapped to physical resources. You defined the physical resources for the case study on Day 2, when you edited the resource.properties

file within

%J2EE_HOME%\config

. The entries that are relevant to this discussion are as follows: jdbcDataSource.5.name=jdbc/Agency jdbcDataSource.5.url=jdbc:cloudscape:rmi:Agency

07 0672323842 CH05 3/20/02 9:36 AM Page 192

192 Day 5

This instructs J2EE RI to create a

DataSource using a URL of jdbc:cloudscape:rmi:Agency and bind it into JNDI with a name of jdbc/Agency

.

From the deployment descriptor, you can see that the declared resource reference in the

AgencyBean

Session bean code is jdbc/Agency and is also mapped to a JNDI name of jdbc/Agency

. The fact that the logical and physical names are the same string strings can be a source of confusion; the point is that both are needed. Moreover, the logical resource reference could have been anything at all, so long as it is the same string that appears in the code, in the standard ejb-jar.xml

deployment descriptor and the vendorspecific auxiliary deployment descriptor.

The mapping between these two names is performed on the JNDI tab of deploytool

, as shown in Figure 5.9. You should be able to see there that, for example, the resource reference in the

Agency bean called jdbc/Agency maps onto the JNDI name of jdbc/Agency

.

The auxiliary deployment descriptor has the following entries:

<j2ee-ri-specific-information>

// lines omitted

<enterprise-beans>

// lines omitted

<ejb>

<ejb-name>AgencyBean</ejb-name>

// lines omitted

<resource-ref>

<res-ref-name>jdbc/Agency</res-ref-name>

<jndi-name>jdbc/Agency</jndi-name>

</resource-ref>

</ejb>

// remaining lines omitted

Resource Environment References

Resource environment reference entries allow access to so-called “administered objects.”

In J2EE RI, this means JMS queues or JMS topics. In future versions of J2EE, other administered objects may well be specified.

Resource references and resource environment references sound very similar, and, indeed, they are related. However, a resource reference is access to some sort of factory object, used to manufacture (variously) database connections, URLs, JMS sessions, and so on. On the other hand, an administered object must be defined up-front by an administrator and is persistent. The EJB specification doesn’t define RDBMS tables as administered objects, but it might well have. If it had, a resource reference would be to a database connection, and a resource environment reference might be to a table on the database to which you have connected.

The DTD defines resource environment references as follows:

<!ELEMENT resource-env-ref ( description?, resource-env-ref-name, resource-env-ref-type)>

07 0672323842 CH05 3/20/02 9:36 AM Page 193

Session EJBs 193

The resource-env-ref-name is the name of the reference in the code, less the java:comp/env prefix, and the resource-env-ref-type is either javax.jms.Queue

or javax.jms.Topic

.

The

Agency session bean does not have any dependencies on resource environment references, so there is no screen shot of the deploytool

. However, you will be using resource environment references on Day 10 when you work with the Java Messaging Service and

Message-driven beans. But (again) as an experiment, try adding an resource environment reference on the Resource Env. Refs tab. On the JNDI tab (as shown in Figure 5.9), you should be able to indicate the JNDI name for the administered object. When you have finished experimenting, delete the resource environment reference.

Deploying the Enterprise Application

The enterprise application can be deployed from deploytool by using the Tools, Deploy menu option. This causes the bean’s code components to be compiled and the proxy and home interfaces to be generated and then packaged up into an ejb-jar using information from the underlying deployment descriptor and auxiliary deployment descriptor. Finally, the package is deployed across the network to the J2EE RI.

Once deployed, you can run your application. To do this, use day05\agency\run\runAll

.

Stateful Session Bean Lifecycle

Now that you have learned how to specify, implement, and deploy stateless Session beans, it is time to look at stateful Session beans. As you shall see, there are many similarities (especially with regard to the deployment), but the lifecycle is different and warrants close attention.

The client’s view of the lifecycle of a stateful Session bean is identical to that of a stateless Session bean and, in truth, more closely matches the actual lifecycle as managed by the container. Figure 5.13 shows this lifecycle.

From the client’s perspective, the bean is simply instantiated when the client calls create() on the bean’s home interface, and it’s removed when the bean calls remove() on the bean itself.

The bean’s viewpoint of its lifecycle is as shown in Figure 5.14.

The principle difference between stateful and stateless Session beans is the duration of the time that the bean is bound to the client. With a stateless Session bean, the duration was only as long as the time needed to execute the business method. With a stateful

Session bean, however, the bean stays bound until the client releases it. In this way, there is a quite close correspondence between the client’s and the bean’s perspectives.

5

07 0672323842 CH05 3/20/02 9:36 AM Page 194

194 Day 5

F

IGURE

5.13

The client’s view of the lifecycle of stateful beans is identical to that of stateless

Session beans.

deploy/setSessionContext

Context Set create/ejbCreate

Ready remove/ejbRemove business method

F

IGURE

5.14

A stateful Session bean’s view of its lifecycle includes passivation and timeouts.

[timeout] pool too small/setSessionContext

Pooled

[surplus] create/ejbCreate remove/ejbRemove

Bound to client

Passivated

[too many active]

/ejbPassivate

Ready

[business method or remove invoked]/ejbActivate business method

When the client calls create() on the home interface, a Session bean instance is obtained. Most EJB containers maintain a pool of unbound Session bean instances, so any unused instance will be chosen. This is then bound to the client. The client can call business methods on the bean, and because the bean will remain bound, these can legitimately save to instance variables the state pertaining to the client. When the client is done with the bean, it calls remove() which releases the bean back to the pool of unbound instances.

07 0672323842 CH05 3/20/02 9:36 AM Page 195

Session EJBs 195

The EJB specification uses the analogy of a shopping cart, and it is easy to see that this is a natural fit. In such a case, the client would obtain a shopping cart bean using create()

, call methods such as addItem()

, removeItem()

, and checkout()

, and then release the bean using remove()

.

If there are many concurrent clients, the amount of memory to manage all of the clients’ state can become significant. Moreover, there is nothing to prevent a client from acquiring a bean, and then not using it—an abandoned shopping cart in the aisles, if you like.

The EJB specification addresses these issues by defining the notions of passivation and of timeouts. Passivation allows the EJB container to release the memory used by a bean, first writing out its internal state to some secondary storage. This is transparent to the bean’s client; when the bean is next used, the EJB container first activates the bean. How the EJB container actually implements passivation is not specified, but the specification does require that the Session bean is serializable, so many implementations will take this route and serialize the bean to a file. If a bean is not used for longer than its timeout, it can be timed out and its memory released.

Note Perhaps surprisingly, the EJB specification does not define how the timeout for a Session bean reference is specified. Section 7.6.3 of the specification indicates clearly that its definition is specific to the EJB container. Usually, the information will be captured in a vendor deployment tool and stored in an auxiliary deployment descriptor.

Equally, the EJB specification does not indicate how the EJB container should decide when to passivate beans (though it does suggest that a “least recently used” strategy can be employed, see section 7.6).

5

Figure 5.14 showed the bean’s viewpoint of its lifecycle, but the actual lifecycle as managed by the EJB container is likely to be different again. After all, the whole point of passivation is to reduce the number of bean instances; if the bean was merely “put to sleep,” that wouldn’t have been accomplished, so Figure 5.15 shows the actual lifecycle used by many EJB container implementations.

When the EJB container passivates a bean, its state is written out to secondary storage.

The bean instance is then destroyed. If a client whose bean instance has been passivated invokes a method, the EJB container first re-instantiates the bean by deserializing it from secondary storage. The business method is then invoked.

07 0672323842 CH05 3/20/02 9:36 AM Page 196

196 Day 5

F

IGURE

5.15

The actual lifecycle of stateful Session beans as managed by the

EJB container is somewhat more complex.

[surplus] create/ejbCreate

[state written]

Passivating do/'write out state to secondary storage'

Pooled

[pool too small]

/setSessionContext

[timeout]

[business method invoked && !

timed out]

[too many active]

/ejbPassivate

Activating do/'restore state from secondary storage' remove/ejbRemove

Bound to client

[state restored]/ejbActivate business method

Specifying a Stateful Session Bean

Specifying a stateful Session bean is similar to specifying a stateless Session bean. The remote interface defines access to the bean by remote clients, and every method of the remote interface must throw an

RemoteException

. The primary difference (from a specification viewpoint) is that there can be multiple create() methods in the home interface.

You will recall that a stateless Session bean allows only for a single create() method in the home interface, and this corresponds to the ejbCreate() method of the bean itself.

For a stateful Session bean, the create() method can be overloaded, so that the stateful bean can be given some initial state. From the client’s viewpoint, this is somewhat analogous to invoking a constructor on the bean.

For example, the

Advertise bean in the case study is stateful. It represents an advertiser of job positions. The home interface for this bean is as follows: package agency; import java.rmi.*; import javax.ejb.*; public interface AdvertiseHome extends EJBHome

07 0672323842 CH05 3/20/02 9:36 AM Page 197

Session EJBs 197

{

Advertise create (String login) throws RemoteException, CreateException;

}

Obviously, the create() method has a corresponding ejbCreate() method in the

AdvertiseBean class itself. This ejbCreate() method must instantiate the bean with any appropriate state, as shown in listing 5.4.

L

ISTING

5.4

AdvertiseBean.ejbCreate()

Method

1: package agency;

2: // imports omitted

3:

4: public class AdvertiseBean implements SessionBean

5: {

6: private String login;

7: private String name;

8: private String email;

9: private String[] address;

10: private Collection jobs = new TreeSet();

11:

12: public void ejbCreate (String login) throws CreateException {

13: this.login = login;

14:

15: // database detail not shown

16: name = …;

17: email = …;

18: address[0] = …;

19: address[1] = …;

20: jobs = new TreeSet();

21: while(…) {

22: jobs.add( … );

23: }

24: }

25: }

Alternatively, the EJB specification allows for methods named createXXX() to be defined in the home interface, with corresponding methods ejbCreateXXX()

. These methods can take parameters if required. Whether you choose to use this facility or just use regular overloaded versions of create()

/ ejbCreate() is up to you.

Other than this one change of being able to pass in state in the create() method, there really is little difference in the specification of a stateful bean compared to that of a stateless Session bean. The remote interface of the stateful

Advertise

Session bean is shown in Listing 5.5.

5

07 0672323842 CH05 3/20/02 9:36 AM Page 198

198 Day 5

L

ISTING

5.5

The Remote Interface for the Stateful

Advertise

Bean

1: package agency;

2:

3: import java.rmi.*;

4: import javax.ejb.*;

5:

6: public interface Advertise extends EJBObject

7: {

8: void updateDetails (String name, String email, String[] Address)

➥throws RemoteException;

9: String getName() throws RemoteException;

10: String getEmail() throws RemoteException;

11: String[] getAddress() throws RemoteException;

12: String[] getJobs() throws RemoteException;

13:

14: void createJob (String ref) throws RemoteException,

➥DuplicateException, CreateException;

15: void deleteJob (String ref) throws RemoteException, NotFoundException;

16: }

The ejbCreate() method from the home interface supplies the information to the bean so that it can retrieve the data about the advertiser from the database. The remote interface allows this information to be accessed and be updated.

Implementing a Stateful Session Bean

When implementing a stateful Session bean, there are a number of issues that you must keep in mind. They are discussed in this section.

Passivation

Unlike stateless Session beans, stateful Session beans are at liberty to store clientspecific state information in instance variables. However, because your bean may be passivated, any instance variables you define must be either primitives, references to serializable objects, or—failing that—be transient

.

Of course, any transient variables will be reset to null if the bean is passivated and then re-activated, so your implementation will need to deal with this. Classes that are not serializable often depend in some way on the environment, such as an open java.net.Socket

, so that your bean can act as a network client to some service. The general approach to dealing with this is to store other data that is serializable in instance variables during the ejbPassivate() method. Then, the non-serializable reference can be re-instantiated in the ejbActivate() method using this other data.

07 0672323842 CH05 3/20/02 9:36 AM Page 199

Session EJBs 199

For example, in the case of a

Socket

, your bean could hold a

String and an int representing the hostname and the port number of the socket. The instance variables and ejbActivate() method could would be something like the following: import java.net.*;

// code omitted.

private transient Socket clientSocket; private String socketHost; private int socketPort; public void ejbActivate() { this.clientSocket = new Socket(socketHost, socketPort);

}

Although your Session bean must itself be serializable, it is not necessary to explicitly implement the java.io.Serializable

interface. This is because the javax.ejb.SessionBean

interface extends from

Serializable

(by way of its superinterface, javax.ejb.EnterpriseBean

).

Tip Given that passivation causes quite a few implementation headaches, some commentators have asked why the EJB specification goes to such lengths to define a passivation mechanism. After all, operating systems are very good at paging memory to disk, and this is all that the “secondary storage” is really accomplishing.

These are good questions, with no ready answers. But if you would rather have the operating system do the work and keep your beans simple, just configure your beans with a very high passivation threshold.

You may be wondering how to handle passivation in a stateful Session bean that has a reference to a home or remote interface of another EJB, a javax.sql.DataSource

or some other resource connection factory. After all, none of these references may be serializable. Luckily, though, the EJB specification puts the onus for worrying about these references on the EJB container (section 7.4.1). In other words, your bean is at liberty to hold references to any of these types of objects, and you don’t need to care whether they are serializable. Of course, most EJB container vendors are likely to comply with this part of the specification by making sure that these objects are serializable.

Timeouts

Another difference between stateful and stateless Session beans is that stateful Session beans may timeout. If a bean is timed out, the client’s reference is no longer valid, and any further invocations on that reference will result in a java.rmi.NoSuchObjectException

for remote clients.

5

07 0672323842 CH05 3/20/02 9:37 AM Page 200

200 Day 5

You should note from Figure 5.14 that if a bean times out, its ejbRemove() method will not be called. This means that you shouldn’t adopt a convention of acquiring external resources in ejbCreate() with a view to releasing them in ejbRemove()

. Even releasing the resources in ejbPassivate() is not enough, because a bean can be timed out even from its ready state.

Caution Don’t confuse passivation and timeout. An EJB container might implement passivation using an LRU strategy and allow a bean timeout to be specified in seconds. If the EJB container is not busy, a bean will not be passivated according to the LRU strategy, but it may hit its timeout nevertheless.

Chaining State

It is generally a bad idea to have more than one stateful Session bean involved in any conversation, because no matter which way you cut it, there’s always the chance that one of them will time out, preventing the other from completing.

To see this, suppose the client calls stateful Session A, which, in turn, uses the services of stateful Session B. There are two cases—the timeout of A is larger than that of B, or the timeout is less than that of B. Taking each in turn

• Suppose that the timeout of Session bean A is 30 minutes and that of Session bean

B is 20 minutes. The client makes a call on A at time t1=0, which then calls B. If the client calls A at time t2 = t1 + 25 minutes, A’s call to B will fail because B will have timed out.

• Suppose now that the timeout of Session bean A is 20 minutes, and that of Session bean B is 30 minutes. The client makes a call on A at time t1=0, which then calls

B. The client then calls A again at time t2 = t1 + 19 minutes, although for this call,

A does not need to call B to service the request. If the client calls A again at time t3 = t2 + 19 minutes = t1 + 38 minutes, A’s call to B will fail because B was last invoked more than 30 minutes ago and will have timed out.

A similar problem can occur with session beans and servlets; this is discussed on Day 12.

Configuring and Deploying a Stateful

Session Bean

Configuring and deploying a stateful Session bean is just the same as deploying a stateless Session bean. The only difference is that the session-type element in the deployment descriptor will be set to

Stateful

.

07 0672323842 CH05 3/20/02 9:37 AM Page 201

Session EJBs 201

Client’s View

Yesterday, you saw how to use JNDI to obtain a reference to a Session bean home and how to obtain a Session bean by calling the appropriate create() method. Now that you have a full understanding of how Session beans work, there are a few other points that are worth appreciating.

First, if your client has a reference to a stateless Session bean, although it should call remove() when it is finished with the EJB, this method call doesn’t actually do particularly much. In particular, it won’t release any bean resources itself, as shown clearly by the state chart diagrams in Figure 5.3. What this will do is allow the EJB container to remove the

EJBObject proxy for the bean.

Conversely, calling create() for a stateless Session bean doesn’t necessarily cause ejbCreate() to be called on the underlying bean, although the client will have a reference to an

EJBObject after making this call.

One benefit of stateless beans over stateful is that they are more resilient. That is, if the client invokes a method on a stateless bean and it throws an exception, the client can still use their reference to try again. The client does not need to discard the reference and obtain a new one from the home interface. This is because, behind the scene, the EJB container will have discarded the bean that threw the exception, but can simply select another bean from the pool to service the client’s retry attempt. This is safe to do because the stateless Session beans have no state. Of course, it is possible that the retry attempt might fail; it would depend on the underlying cause of the exception.

In contrast, if a stateful Session bean throws an exception, the client must obtain a new

Session bean reference and start its conversation over again. This is because the EJB container will have discarded the Session bean that threw the exception, discarding all the client’s conversational state in the process.

Sometimes, a client may end up having two references to a Session bean. It may have obtained them both from other method calls, for example. More precisely, the client will have two RMI stubs to Session beans. If the client wants to determine whether these two stubs refer to the same Session bean, it should not call the equals() method. This almost certainly will not return true

. Instead, the client should call isIdentical(EJBObject) on the reference. This indicates whether both stubs refer to the same Session bean. Note that two references to the same stateless Session bean will always return true

, because—at least conceptually—there is only a single instance (see EJB specification, section 7.5.6).

Earlier today, you saw the different types of exceptions that a bean can throw. If a bean throws an application exception, the EJB container will propagate it back to the client. If the bean throws an

EJBException

(representing a system exception), the EJB container will throw a java.rmi.RemoteException

in turn.

5

07 0672323842 CH05 3/20/02 9:37 AM Page 202

202 Day 5

For the client, any

RemoteException represents a severe problem. It doesn’t really matter to the client if the

RemoteException has arisen because of a network problem or because of a problem with a bean. Either way, it will be unable to recover the problem.

Table 5.1 lists the system exceptions shown in Figure 5.5 and indicates how each is raised and thrown. As you will see, the majority are raised when the EJB container itself has detected a problem with either transactions or security. You will learn more about transactions on Day 8, and more about security a week later on Day 15, “Security.”

T

ABLE

5.1

System Exceptions Are Thrown in a Variety of Situations

What Event Client Receives

Any bean Throws javax.ejb.EJBException

(or any subclass) java.rmi.

RemoteException

BMP Entity bean Throws

NoSuchEntityException java.rmi.NoSuchObject

Exception

Container When client invokes method on a reference to a Session bean that no longer exists

When client calls a method without a transaction context java.rmi.NoSuchObject

Exception javax.transaction.

TransactionRequired

Exception

When client has insufficient security access java.rmi.

AccessException

When transaction needs to be rolled back javax.transaction.

TransactionRolledBack

Exception

If you are wondering what BMP Entity beans are, the phrase is an abbreviation of “beanmanaged persistence Entity beans.” You’ll be learning about those tomorrow.

Patterns and Idioms

You now know all the important theory behind writing Session beans, but it’s always helpful to have one or two real-world insights into how to write them in practice.

Patterns (or more fully, “design patterns”) are documented pearls of wisdom. Idioms are much the same thing, although they tend to be more lower-level and code-oriented.

On Day 18, many of the design patterns discussed piecemeal throughout the book will be brought together to see how they apply to all aspects to the J2EE environment. Some of

07 0672323842 CH05 3/20/02 9:37 AM Page 203

Session EJBs 203 those given here will be presented more fully. But for now, there are patterns and idioms specific to writing session EJBs. Reading this section might save you some time.

Business Interface

One of the peculiarities of the EJB architecture is that there is no direct link between the defined remote interface and the bean that provides the implementation. For example, the remote interface

Advertise is not implemented by

AdvertiseBean

. However, no link is needed because the EJB specification declares that it is the vendor’s deployment tools that are responsible for ensuring that every method defined in the remote interface has a corresponding method in the bean, and that the required methods for the home interfaces also exist.

However, this means that any mismatches between the interface and the bean’s implementation will be picked up not during compilation, but during deployment. From a practical point of view, this can make debugging the problem harder. After all, you are probably accomplished at reading compile errors and figuring out what the cause of the problem is. But you won’t (at least initially) be familiar with the errors that the vendor’s deployment tool throws up when it announces that your bean does not comply with the

EJB specification.

One idiom that solves this is to create an additional interface that defines just the business methods. This interface is sometimes called the business interface. Then, the bean implements the business interface, while the remote interface for the bean extends that business interface.

This hasn’t been done in the case study, so as not to complicate and confuse. However, it would be simple enough to introduce a business interface. Figure 5.16 shows a UML class diagram that illustrates this for the

Advertise bean.

With this idiom, if there is a mismatch between the interface and the bean, it will be picked up during compile time.

There is just one subtlety of which you must be aware. When applying this technique to the remote interface of a bean, the methods in the business interface must all throw java.rmi.RemoteException

. This is because the vendor-generated

EJBObject for the remote interface must follow the rules of remote objects, so that every one of its public methods throws the

RemoteException

. This applies also to the inherited methods of the business interface. The

AdvertiseBus interface is shown in Listing 5.6.

5

07 0672323842 CH05 3/20/02 9:37 AM Page 204

204 Day 5

F

IGURE

5.16

Defining a business interface means that the bean can implement that interface.

interface

EJBHome

interface

com.mycompany.agency.AdvertiseHome

interface

java.rmi.Remote

!

interface

EJBObject

interface

AdvertiseBus

updateDetails getName getEmail getAddress getJobs createJob deleteJob interface

com.mycompany.agency.Advertise

create interface

SessionBean com.mycompany.agency.AdvertiseBean

com.mycompany.agency.Advertise

com.mycompany.agency.AdvertiseHome

dataSource login name email address jobs ctx loadJobList error closeConnection ejbPassivate ejbActivate ejbRemove setSessionContext ejbCreate updateDetails getLogin getName getEmail getAddress getJobs createJob deleteJob

L

ISTING

5.6

AdvertiseBus

Interface

1: package agency;

2: import javax.ejb.*;

3: import java.rmi.RemoteException;

4:

5: public interface AdvertiseBus {

6: void updateDetails (String name, String email, String[] address)

➥ throws RemoteException;

7: String getName() throws RemoteException;

8: String getEmail() throws RemoteException;

9: String[] getAddress() throws RemoteException;

10: String[] getJobs() throws RemoteException;

11: void createJob (String ref) throws RemoteException,

➥ DuplicateException, CreateException;

12: void deleteJob (String ref)

➥throws java.rmi.RemoteException, NotFoundException;

13: }

Adapter

As you write your EJBs, you will quickly find yourself writing reams of “boilerplate” code. For example, the setSessionContext() method almost always just saves the

07 0672323842 CH05 3/20/02 9:37 AM Page 205

Session EJBs 205 session context to an instance variable. The ejbActivate() and ejbPassivate() methods often do nothing at all.

If you have written any GUI applications using AWT or Swing, you almost certainly will have used the various

Adapter classes in the java.awt.event

package. For example, the java.awt.event.WindowAdapter

class provides empty implementations of the seven methods in the java.awt.event.WindowListener

interface.

Adapter classes can also provide common default functionality. For example, the

AbstractList class acts as an adapter to the

List interface in the java.util

package, providing the majority of the implementation required. Although the

List interface defines 25 methods in total, the

AbstractList class implements all but two of them.

Creating an adapter for your Session beans can save you time in the long run. You can provide default implementations for many of the lifecycle methods, and can also provide additional methods. For example, you might decide to provide a log() method that will forward any log messages to some remote URL or to a logging database.

Coarse-Grained

Remote Session beans should offer coarse-grained services. In other words, the services offered by a remote Session bean should do large(-ish) chunks of work. The overhead of the network to use these beans then becomes much less significant.

There are a number of approaches for arranging this. One approach is to create value object classes. These are serializable objects that encapsulate enough information for the

Session bean to provide some service. The client populates these value objects and then sends them across the wire as part of the remote method call. The Session bean then interrogates its copy of the value object to accomplish its work. You will learn about the value object pattern and some related patterns more fully on Day 18.

The value object idea as described is to encapsulate enough data in an object such that the Session bean can do a reasonable chunk of work, but the responsibility for figuring out what that chunk of work is still resides with the Session bean. A natural extension to this concept is to place that responsibility into the value object itself. In effect, the value object represents the action or command to be invoked. Indeed, the name of this design pattern is the Command design pattern.

Gotchas

As you start to implement your own Session beans, there’s bound to be a couple of aspects that will trip you up. The following quick checklist of such “gotchas” should keep you on the straight-and-narrow:

5

07 0672323842 CH05 3/20/02 9:37 AM Page 206

206 Day 5

• When you look up resources from JNDI, you should use a string of the form java:comp/env/XXX

. However, in the deployment descriptor, only the

XXX

is needed; the java:comp/env prefix is implicit.

• Perhaps obvious, but don’t use ejb as a prefix for naming your business methods.

Names of that format are reserved for the EJB architecture callback methods.

• Don’t implement the remote interface in your bean! If you do so, your bean could inadvertently return itself (Java keyword this

) as a return type. If a client starts invoking methods on this reference, it will bypass all of the EJB container’s transaction and security control that is managed within the

EJBObject proxy to the bean. Instead, use the business interface idiom mentioned earlier today.

• The

EJBObject interface defines a getPrimaryKey() method; the

EJBHome interface defines a remove(Object primaryKey) method. Attempting to call either of these for a Session bean will immediately throw a

RemoteException

, so don’t do it. They are there only for Entity beans, discussed tomorrow.

• You’ll learn more about transactions on Day 8, but for now, remember that you should not perform work that updates a database in the ejbCreate or ejbRemove method, or indeed the other ejbXXX() lifecycle methods. This is because the transaction context is undefined. See section 7.5.7 of the EJB specification for more details.

• Don’t try to have a Session bean call itself through its own

EJBObject

; it won’t work. This is prevented so that the bean developer does not need to worry about multiple threads. In other words, Session beans are non-reentrant. Of course, your bean can call methods on itself directly through its implicit this reference.

• An ejbCreate() is required for stateless Session beans. It isn’t in the javax.ejb.SessionBean

interface because stateful Session beans won’t necessarily have a no-arg create() method.

Summary

You’ve covered a lot of ground today. You’ve learned that there are stateless and stateful

Session beans, and each has their own lifecycle. You’ve seen in detail how to specify a

Session bean by defining its home and remote interfaces and how to implement a bean by providing corresponding implementations for the methods in the home and remote interfaces, as well as how to implement the lifecycle methods as defined in the javax.ejb.SessionBean

interface.

You’ve also learned in detail how the deployment descriptor provides configuration information describing the bean’s characteristics and dependencies to the EJB container.

07 0672323842 CH05 3/20/02 9:37 AM Page 207

Session EJBs 207

Additionally, you’ve seen that those dependencies are logical dependencies that must be mapped by the EJB deployer role to the physical resources defined through vendorspecific auxiliary deployment descriptor.

Finally, you’ve learned about some common techniques, design patterns, and idioms that can simplify your coding and that represent best practice.

Q&A

Q What sort of state can stateless Session beans have?

A Somewhat surprisingly, stateless Session beans can store state, but it must be independent of the client.

Q What is the prefix that will appear in all JNDI lookups?

A The java:comp/env context is guaranteed to exist in an J2EE environment.

Q How are EJB system exceptions different from regular Java exceptions?

A

RemoteException s can be caused by network problems, which, in the context of distributed J2EE enterprise applications, represent a system-level rather than application-level exception.

Q How is the timeout for a stateful Session bean defined?

A Surprisingly, the mechanism for specifying the timeout interval for a stateful

Session bean is not mandated in the EJB specification.

Exercises

The exercise starts with a version of Day 5’s job agency case study that already provides a number of beans:

• There is a stateless

Agency bean that returns lists of all applications, customers, locations and skills in the database.

• There is a stateful

Advertise bean that allows advertisers (of jobs) to update their name, e-mail, and address, and to manage the jobs they have posted to the job agency.

• There is a stateful

AdvertiseJob bean that represents an advertised job. This allows the description, location, and required skills to be maintained.

However, it does not define any bean for the potential job applicants at this point. What is required is a

Register bean that allows applicants to register themselves with the job agency. The exercise is to implement the

RegisterBean

, define this new bean within the supplied agency.ear

enterprise application, configure the bean, deploy your bean to the

J2EE RI, and finally test with either

RegisterClient or

AllClients

(supplied).

5

07 0672323842 CH05 3/20/02 9:37 AM Page 208

208 Day 5

Under the day05\exercise directory, you will find a number of subdirectories, including the following:

• src

The source code for the EJBs and clients.

• classes

Directory to hold the compiled classes; empty.

• build

Batch scripts (for Windows and Unix) to compile the source into the classes directory. The scripts are named compileXXX

.

• jar

Holds agency.ear

—the agency enterprise application. Also holds agencyClient.jar

, the client-side JAR file optionally generated when deploying

EAR. This directory also holds some intermediary JAR files that are used only to create the previous two JAR files.

• run

Batch scripts (for Windows and Unix) to run the JARs. Use the files in the jar directory.

The

Register and

RegisterHome interfaces have been provided for you, under the src directory. For example, the

Register interface is as follows: package agency; import java.rmi.*; import javax.ejb.*; public interface Register extends EJBObject

{ void updateDetails (String name, String email,

String location, String summary, String[] skills) throws RemoteException;

String getLogin() throws RemoteException;

String getName() throws RemoteException;

String getEmail() throws RemoteException;

String getLocation() throws RemoteException;

String getSummary() throws RemoteException;

String[] getSkills() throws RemoteException;

}

Today’s exercise is to implement the

RegisterBean

, configure an appropriate deployment descriptor, deploy your bean to the J2EE RI, and then test with the

RegisterClient

. The bean will need to be stateful.

If you need some pointers as to how to go about this, read on.

1. Create a

RegisterBean.java

file and place this in day05\exercise\src\agency

.

2. Implement

RegisterBean to support the

Register and

RegisterHome interfaces supplied. Base your implementation on that of

AdvertiseBean

, if you want.

07 0672323842 CH05 3/20/02 9:37 AM Page 209

Session EJBs 209

3. Compile the

RegisterBean code and the other interfaces, using the build\compileAgencySessionEjbs script. Note that this expects the

JAVA_HOME and

J2EE_HOME environment variables to be set.

4. In deploytool

, open up the existing enterprise application

( day05\exercise\jar\agency.ear

). Then, add the your

Register bean to the existing

Agency ejb-jar by using File, New, Enterprise Bean. Specify the contents to include all the required class files.

5. Configure the deployment descriptor for the

RegisterBean appropriately. The bean will need to be stateful. You will need to specify resource references and JNDI names for the

RegisterBean

; bind the bean to a name of ejb/Register

.

6. For the

RegisterClient application client, configure the EJB reference appropriately. This has a coded name of java:comp/env/ejb/Register to refer to the

RegisterBean

.

7. Deploy your bean by selecting it and using Tools, Deploy. As you do this, you will need to define the appropriate JNDI mappings (mapping the logical EJB references to the physical runtime environment). Request a client JAR file to be created, called agencyClient.jar

, to reside in the JAR directory.

8. To test out your bean, compile

AllClients using the build\buildAllClientsClient script. Then run the client using run\runAll

.

You may also have noticed that in the build directory there are several other scripts apart from those to compile the source. In fact, these can be used to recreate the agency.ear

file using the deployment descriptors held in the dd directory. You will be learning more about this approach tomorrow. For now, all that you need to know is that the agency.ear

file can be created automatically by running the bat\buildall script. It requires that the

RegisterBean class exist to run successfully. You can then use deploytool to manually define and configure the

RegisterBean within the EAR.

The solution to the exercise is under day05\agency

.

5

07 0672323842 CH05 3/20/02 9:37 AM Page 210

08 0672323842 CH06 3/20/02 9:30 AM Page 211

W

EEK

1

D

AY

6

Entity EJBs

Yesterday, you learned about Session beans, and how they provide a service to a specific client.

The major topics that you will be covering today are

• How Entity beans represent domain objects, providing services that can be used by all clients

• Two types of Entity beans—bean-managed persistence (BMP) and container-managed persistence (CMP)

• How EJBs can provide a local interface in addition to their remote interface

• Specifying, implementing, configuring, and deploying BMP Entity beans

• Configuring and deploying EJBs from the command line rather than using a GUI

Overview

When building IT systems, the functionality required of the application must be specified and the business objects within the domain must be identified.

08 0672323842 CH06 3/20/02 9:30 AM Page 212

212 Day 6

In “traditional” client/server systems, the application’s functionality can be implemented in the front-end application or perhaps using database stored procedures, and the domain objects are usually tables within an RDBMS. In building an EJB-based system, the application’s functionality corresponds to Session beans, and the domain objects correspond to Entity beans.

You learned yesterday that Session beans take on the responsibility of implementing the application’s business functionality. There will still be a presentation layer to display the state of those Session beans, but its detail is unimportant in the larger scheme of things.

In the same way, Entity beans take on the responsibility of representing the domain data.

There will still a persistent data store to manage the data, almost certainly an RDBMS, but the Entity beans abstract out and hide the detail of the persistence mechanism.

The N-tier Architecture Revisited

On the very first day, you were introduced to n-tier architectures, with the business logic residing in its own tier. With an EJB-based system, both Session and Entity beans are objects, so the business logic could be reside in either of them. In practice, the business logic will be split over both, but to make the correct decision, it is worthwhile analyzing what is meant by that phrase “business logic.”

Business logic refers to the collection of rules, constraints, procedures and practices put in place by the business users to conduct their business. Some of the rules and constraints cannot be changed by the business, due to the domain in which the business is performed. For example, there could be legal constraints and obligations. The procedures and practices represent the (one particular) way in which business users have chosen to conduct business.

Rules and constraints generally apply across all applications. In other words, it doesn’t matter what the business is trying to accomplish, they will still need to comply with such rules and constraints. This sort of business logic is best implemented through Entity beans, because Entity beans are domain objects that can be reused in many different applications.

In the business world, procedures and practices are usually the expression of some sort of application, so Session beans are the best vehicle to implement this type of business logic. Indeed, introducing computerized systems often changes these procedures and practices (hopefully for the better, sometimes for the worse) because computers make available new ways of accomplishing tasks.

• Session beans should have the business logic of a specific application—in other words, application logic. The functionality provided should allow the user to accomplish some goal.

08 0672323842 CH06 3/20/02 9:30 AM Page 213

Entity EJBs 213

• Entity beans represent domain objects and should have business logic that is applicable for all applications—in other words, domain logic. Usually, this logic will be expressed in terms of rules and constraints.

If there is any doubt as to where the functionality should be placed, it is safer to place it with the Session bean. It can always be moved later if it is found to be truly re-usable across applications.

Figure 6.1 shows a UML component diagram to illustrate that there are at least four logical layers in an EJB-based system. Normally, at least some of these layers will be on the same physical tier.

F

IGURE

6.1

EJBs separate out business logic into application and domain logic.

«swing» user interface

«servlet» user interface

«session EJB» application logic

«entity EJB» domain logic

«database» persistence layer

«servlet» user interface

«session EJB» application logic

Comparison with RDBMS Technology

It’s natural to compare Entity beans with relational databases, because there is a significant overlap in the objectives of both technologies.

If you like to think in client/server terms, you could think of Session beans as being an extension of the “client”, and Entity beans as being an extension of the “server”. It’s important to realize that many clients can share a given Entity bean instance at the same time, just as many database clients can read some row from a database table at the same time.

You can also think of Entity beans as a high-performance data cache. Most RDBMS’ store data pages or blocks in a cache so that the most commonly used rows in tables can be read directly from memory rather than from disk. Although the EJB specification does not require it, many EJB containers adopt a strategy such that Entity beans are also cached, so the data that they represent can also be read directly from memory. The advantage of the Entity bean cache over an RDBMS’ data cache is that the Entity beans already have semantic meaning and can be used directly. In contrast, data read from an

RDBMS’ data cache needs to be reconstituted in some way before it can be used.

6

08 0672323842 CH06 3/20/02 9:30 AM Page 214

214 Day 6

Identifying Entities

At their simplest, Entity beans can correspond to nothing more complex than a row in a database; any data that might reasonably be expected to exist in a relational database table is a candidate. This makes examples of Entity beans easy to come by:

• A

Customer

Entity bean would correspond to a row in a customer table keyed by customer_num

. The list of contact phone numbers for that

Customer

(in a customer_phone_number detail table keyed on ( customer_num

, phone_num

) would also be part of the

Customer

Entity bean.

• An

Invoice

Entity bean might correspond to data in the order and order_detail tables.

• An

Employee

Entity bean could be persisted in an employee table. The employee’s salary history might also be part of the Entity bean.

Identifying entities can be made easier if a proper discipline is adopted with relational modeling of the database. Of course, many databases just evolve over time as developers add tables to support new requirements. Ideally, though, there should be a logical database model and a physical database model. The former is usually captured as an Entity relationship diagram (ERD) with entities, attributes, and relationships. Relational database theory defines a process called normalization and different normal forms that aim to eliminate data redundancy. It is this stage at which the normalization rules are applied, to get to third normal form (at least).

Tip This isn’t a book on relational database design, but here’s a cute phrase that you can use to get you to third normal form: “every non-key attribute depends upon the key, the whole key, and nothing but the key (so help me

Codd!).” If you are wondering who Codd is, that’s Dr. Codd who in the early

1970s laid down the mathematical foundations for relational theory.

Converting a logical database model to a physical model is in many ways mechanical.

Every entity becomes a table, every attribute becomes a column, and every relationship is expressed through a foreign key column in the “child” table.

These entities identified in logical data modeling are the very same concepts that should be expressed as Entity beans. Moreover, one of the key “deliverables” from performing relational analysis is the selection of the primary key—the attribute or attributes that uniquely identify an instance. Entity beans also require a primary key to be defined, and this is manifested either as an existing class (such as java.lang.String

or java.lang.Integer

) or a custom-written class for those cases where the key is composite.

08 0672323842 CH06 3/20/02 9:30 AM Page 215

Entity EJBs 215

The name often given to such primary key classes is something like

BeanPK

, although it can be anything. You can think of the primary key as some object that identifies the bean.

Note The requirement of a primary key class to identify Entity beans has led to criticism —in particular, by vendors of object-oriented DBMS—that the technology is not particularly object-oriented. In an OODBMS, the object does not need a primary key identifier; it is identified simply by its reference.

Nevertheless, there are some differences between relational entities and Entity beans.

Whereas relational modeling requires that the data is normalized, object modeling places no such constraints. Indeed, not even first normal form (where every attribute is scalar) needs to be honored. For example, a

Customer

Entity bean might have a vector attribute called phoneNumbers

, with a corresponding accessor method getPhoneNumbers() that returns a java.util.List

. In a physical data model, there would need to be a separate table to hold these phone numbers.

Even with a solid logical data model to guide you, selecting Entity beans is not necessarily straightforward. In particular, choosing the granularity of the entities can be problematic. With the customer example given earlier, the customer_phone table doesn’t really seem significant enough to be an Entity. It’s just the way in which vector attributes have to be modeled in relational databases. But what of the invoices? After all, invoices are sent to customers, and any given invoice relates only to the orders placed by a single customer. So perhaps invoices should be considered as just vector attributes of customers, with a getInvoices() accessor method? On the other hand, many modelers would argue that the concept of

Invoice is significant enough in itself—with its own state, behavior, and lifecycle—to warrant being represented as its own Entity bean.

Specifying the interfaces should help you decide which is the correct approach. If the invoice entity really is significant, you will find that the customer’s interface will be bloated with lots of invoice-related methods. At this point, you can tease the two entity objects apart.

6

Caution

If you read old text books on EJB design, you will find that the traditional

(pre EJB 2.0) advice for Entity beans is that they should be coarse-grained— in other words, that data from several tables correspond to a single entity.

This advice arose because of a combination of factors relating to pre EJB 2.0

Entity beans, one in particular being that Entity beans had to be remote

(implement the java.rmi.Remote interface).

These factors are no longer true, so the advice is out of date. Fine-grained

Entity beans are perfectly feasible for an EJB container that supports the EJB

2.0 specification.

08 0672323842 CH06 3/20/02 9:30 AM Page 216

216 Day 6

The javax.ejb

Package for Entity Beans

Yesterday, you saw the interfaces and classes in the javax.ejb

package that related to

Session beans. Figure 6.2 shows the interfaces and classes relevant to Entity beans.

F

IGURE

6.2

The javax.ejb

interfaces and classes pertaining to Entity beans.

interface

EJBMetaData

getEJBHome getHomeInterfaceClass getRemoteInterfaceClass getPrimaryKeyClass isSession isStatelessSession interface

java.rmi.Remote

!

interface

HomeHandle

getEJBHome interface

EJBHome

remove remove getEJBMetaData getHomeHandle interface

EJBLocalHome

remove interface

EJBContext

getEJBHome getEJBLocalHome getEnvironment getCallerIdentity getCallerPrincipal isCallerInRole isCallerInRole getUserTransaction setRollbackOnly getRollbackOnly interface

Handle

getEJBObject interface

EnterpriseBean

interface

EJBObject

getEJBHome getPrimaryKey remove getHandle isIdentical interface

EJBLocalObject

getEJBLocalHome getPrimaryKey remove isIdentical interface

EntityContext

getEJBLocalObject getEJBObject getPrimaryKey interface

EntityBean

setEntityContext unsetEntityContext ejbRemove ejbActivate ejbPassivate ejbLoad ejbStore

As you can see, many of the supporting classes are common, which is good news because that means there’s less to learn. The principle differences are as follows:

• The Entity bean implements javax.ejb.EntityBean

rather than javax.ejb.SessionBean

, so there is a different lifecycle.

• The Entity bean is initialized with an

EntityContext rather than a

SessionContext

. An

EntityContext exposes a primary key to the Entity bean, a concept not applicable to Session beans.

Other details of the javax.ejb

interfaces are the same as for Session beans. Briefly, the home and remote interfaces for the Entity bean are defined by extending

EJBHome and

EJBObject

, respectively, and the local-home and local interfaces by extending

EJBLocalHome and

EJBLocalObject

. You will be learning more about local interfaces later today, because they are highly relevant to implementing Entity beans.

08 0672323842 CH06 3/20/02 9:30 AM Page 217

Entity EJBs 217

The

EJBMetaData class provides access to the constituent parts of the Entity bean component, and the

Handle and

HomeHandle interfaces provide the ability to serialize a reference to a remote bean or home and then to re-instantiate this instance by deserializing the handle. None of these interfaces is discussed further.

Entity Bean Types

Entity beans represent shared persistent data stored in an RDBMS or other persistent data store. If the data store is relational, the responsibility for actually performing the

JDBC can be placed either with the bean itself or with the EJB container.

The term for the former is bean-managed persistence (BMP), and for the latter it is

container-managed persistence (CMP).

Note The EJB specification is very much oriented around relational data stores.

Certainly, container-managed persistence can only be performed through

JDBC javax.sql.DataSource

objects, and JDBC is based around ANSI SQL 92.

If using bean-managed persistence, any API can be used to save the bean’s state to a persistent data store, but even then, the methods that the bean is required to provide, such as findByPrimaryKey()

, still have a relational nature to them.

Container-managed persistence was part of the EJB 1.1 specification (the predecessor to the current EJB 2.0), but attracted much criticism in that release. However, it has been radically overhauled in EJB 2.0, and now works in a fundamentally different way. This is so much so that the deployment descriptor even has the cmp-version element to indicate whether the Entity bean has been written under the 1.1 or 2.0 contract. Tomorrow, you will learn more about CMP 2.0. For the rest of today, however, you will be focusing on

BMP. That way, you’ll have a pleasant surprise when you realize how much of the coding can be automated using CMP.

Remote Versus Local Interfaces

One of the most significant improvements in the EJB 2.0 specification over previous versions is the inclusion of local interfaces as well as remote interfaces.

All beans that you have seen on previous days have provided only a remote interface.

That is, both their home and remote interfaces have extended from javax.ejb.EJBHome

and javax.ejbEJBObject

, both of which, in turn, extend the java.rmi.Remote

interface.

6

08 0672323842 CH06 3/20/02 9:30 AM Page 218

218 Day 6

This ability to invoke methods on a bean without regard for its location is crucial for

Session beans, but for Entity beans it is less useful, even positively harmful. Very often, a client must deal with many Entity beans to transact some piece of work, and if each of those Entity beans is remote, this will incur substantial network traffic. There is also the cost of cloning any serializable objects to enforce the required “pass-by-value” semantics.

Even more frustratingly, the client of the Entity bean may well be a Session bean.

Indeed, it is generally considered bad practice to use anything other than a Session bean to interact with Entity beans. More often than not, this Session bean will be co-located

(running in the same JVM) as the Entity beans that it is using. The EJB container is obligated to make all Session-to-Entity bean calls via the network and to clone all serializable objects, just because the Entity beans are remote.

By now, you probably have guessed what local interfaces are. They are alternative nonremote interfaces that the Entity bean can specify. Again, the home and proxy idea is used, with the home interface being extended from javax.ejb.EJBLocalHome

and the proxy for the bean extending from javax.ejb.EJBLocalObject

. Otherwise though, these are regular Java interfaces, and the normal “pass by reference” semantics for objects passed across these interfaces apply.

Note

“Pass by reference” is a simpler way of saying “object references are passed by value.”

An Entity bean can provide a regular home/remote interface, or it can provide a localhome/local interface. Indeed, there is nothing to prevent an Entity bean from offering both interfaces, although any clients using the remote interface would incur the performance costs already noted. Local interfaces are not specific to Entity beans either;

Session beans can also provide local interfaces. For Session beans (especially stateless

Session beans), there might well be reason to offer both a remote and a local interface. In general, it would be expected for the two interfaces to offer the same sorts of capabilities, although there is nothing in the EJB specification that enforces this.

Figure 6.3 shows the two sets of interfaces that a bean can provide.

In both cases, the EJB home/local-home and proxy objects take responsibility for security (Day 15, “Security”) and transactions (Day 8, “Transactions and Persistence”), while home/remote interfaces also make the physical location of the bean transparent to the remote client.

Local interfaces are more than just a performance boost for EJBs though, they are the cornerstone on which container-managed persistence and also container-managed rela-

tionships (CMR) are founded. You will learn about these in detail tomorrow.

08 0672323842 CH06 3/20/02 9:30 AM Page 219

Entity EJBs

F

IGURE

6.3

EJBs can have local and remote interfaces.

home stub remote stub remote stub location transparency home local client remote security and transactions local home local bean

219

In the case study and examples for today and tomorrow, you will see that the Entity beans define only a local interface.

BMP Entity Bean Lifecycle

The lifecycle of both BMP and CMP Entity beans is dictated by the

EntityBean interface that the bean must implement. This is shown in Figure 6.4.

F

IGURE

6.4

The javax.ejb.Entity-

Bean interface defines certain lifecycle methods that must be implemented by Entity beans.

interface

java.io.Serializable

interface

EnterpriseBean

!

6

interface

EntityBean

+ setEntityContext(entitycontext:EntityContext):void

+unsetEntityContext():void

+ ejbRemove():void

+ ejbActivate():void

+ ejbPassivate():void

+ejbLoad():void

+ejbStore():void

08 0672323842 CH06 3/20/02 9:30 AM Page 220

220 Day 6

However, although the method names are the same, the obligations of BMP versus CMP

Entity beans for each of those methods are different. This section discusses just those lifecycle methods for BMP Entity beans. The

Job

Entity bean from the case study will be predominantly be used for example code.

To start with, the Entity bean must implement the javax.ejb.EntityBean

interface, as demonstrated with the

JobBean class: package data;

// imports omitted import javax.ejb.*; public class JobBean implements EntityBean

{

// implementation omitted

}

The lifecycle as perceived by the Entity bean and as managed by the container is as shown in Figure 6.5.

F

IGURE

6.5

The javax.ejb.EntityBean

lifecycle allows Entity beans to be pooled.

[pool too small]

/setEntityContext

Pooled

[pool too large]

/unsetEntityContext

/ejbFindAll

/ejbFindByPrimaryKey

/ejbActivate

/ejbCreate

Creating exit/^ejbLocalObject.new()

/ejbPostCreate

/ejbRemove

/ejbPassivate

Cached ejbLoad

/'business method' ejbStore

The lifecycle is as follows:

• If the EJB container requires an instance of an Entity bean (for example, if the pool is too small), it will instantiate the bean instance and call its setEntityContext() method.

08 0672323842 CH06 3/20/02 9:30 AM Page 221

Entity EJBs 221

• Pooled instances can service finder methods to locate data within the persistent data store that represents existing beans. More on these finder methods shortly.

• A bean can be associated with an

EJBLocalObject proxy (or

EJBObject proxy if the remote interface is in use) in one of two ways.

First, it can be activated by the container via ejbActivate()

. The proxy for the bean exists but has no associated bean. This could occur if the bean had previously been passivated and a business method has now been invoked on the proxy. It could also occur if the bean’s proxy was just returned as the result of a finder method.

Alternatively, the client may be requesting to create an Entity bean via ejbCreate() and then ejbPostCreate()

. This usually means that the corresponding data has been inserted into the persistent data store.

• When the bean has been associated with its proxy, business methods can be invoked on it. Before the business method is delegated by the proxy to the bean, the ejbLoad() lifecycle method will be called, indicating that the bean should reload its state from the persistent data store. Immediately after the business method has completed, the ejbStore() method is called, indicating that the bean should update the persistent data store with any change in its state.

• Beans can return to the pooled state in one of two ways.

First, they can be passivated via ejbPassivate()

. There is usually little to be done in the lifecycle, because the bean’s state will already have been saved to the persistent data store during the earlier ejbStore() method. So passivation simply means that the link from the

EJBLocalObject proxy to the bean has been severed.

Alternatively, the client may be requesting to remove the create bean via ejbRemove()

. This usually means that the corresponding data in the persistent data store has been deleted.

• Finally, if the EJB container wants, it can reduce the size of its pool by first calling unsetEntityContext()

.

6

Note Most commercial EJB containers provide mechanisms to suppress unnecessary ejbLoad() and ejbStore() calls. None of these mechanisms are in the

EJB specification, however.

Unlike Session beans, there is no binding of the Entity beans to a specific client; the bean can be shared by all clients.

08 0672323842 CH06 3/20/02 9:30 AM Page 222

222 Day 6

As Figure 6.5 indicated, there are two methods called during the creation of a bean. The ejbCreate() method is called prior to the

EJBLocalObject proxy being made available, the ejbPostCreate() method is called after the proxy is available. This is shown in the sequence diagram in Figure 6.6.

F

IGURE

6.6

Both the ejbCreate() and ejbPostCreate() lifecycle methods are called when an Entity bean is created.

jobHome

JobHomeImpl

localClient

1: create(ref, customer):JobLocal

1.1: pk:=ejbCreate(ref, customer):JobPk

jobBean

JobBean

ctx

EntityContext

1.2: proxy:=<constructor>(pk)

jobProxy

JobImpl

1.3: set local object

1.4: ejbPostCreate(ref, customer):void

1.2.1: set primary key

1.4.1:[if required] getEJBLocalObject():EJBLocalObject

Under BMP, the bean has several tasks to perform when its ejbCreate() method is called. It should:

• Calculate the value of its primary key (if not passed in as an argument).

• Persist itself to a data store. For a RDBMS, this will most likely be in the form of an SQL

INSERT statement or statements.

• Save the supplied arguments and its primary key to fields.

• Return the primary key.

As Figure 6.6 shows, the returned primary key is passed to the bean’s proxy, and the proxy continues to hold that primary key, even if the bean is subsequently passivated.

The proxy for the bean is also associated with the context object of the bean.

Caution

You can see that the EJBLocalObject proxy holds onto the primary key for the bean. This allows the bean to be transparently re-loaded if it is passivated. However, because the EJB container is using primary keys for lookups, it also means the EJB does not allow primary keys to be modified by the application; they must be immutable.

08 0672323842 CH06 3/20/02 9:30 AM Page 223

Entity EJBs

The ejbRemove() method is the opposite of the ejbCreate() method; it removes a bean’s data from the persistent data store. The implementation of ejbCreate() and ejbRemove() is given in the “Implementing javax.ejb.EntityBean

” section later today.

That takes care of creating and removing beans, but what about when a bean is queried or updated? The most significant of the Entity bean lifecycle methods are the ejbLoad() and ejbStore() methods. Together, these methods ensure that the Entity bean is kept in sync with the persistent data store. The ejbLoad() method is called immediately prior to any business method (so that a query access the most up-to-date data). The ejbStore() is called after the business method completes (so that if the method updated the bean’s state, this is reflected in the persistent data store). Figure 6.7 shows this as a UML sequence diagram.

F

IGURE

6.7

The ejbLoad() and ejbStore() methods keep the bean in sync with the persistent data store.

EjbContainer localClient

1: businessMethod

jobProxy

JobImpl

jobBean

JobBean

DatabaseDriver

1.1:[if passivated] ejbActivate():void

1.2: ejbLoad():void

1.3: businessMethod

1.2.1: select … where PK = …

1.4: ejbStore():void

1.4.1: update … where PK = …

223

2:[if required] ejbPassivate():void

Again, the actual implementation for these methods is given in the “Implementing javax.ejb.EntityBean

” section later today.

As you will recall, Session beans have ejbActivate() and ejbPassivate() methods, and so do Entity beans. If the EJB container wants to reduce the number of bean instances, it can passivate the bean. This is only ever done after an ejbStore()

, so the data represented by the bean is not lost. Also, the proxy for the bean continues to hold the bean’s primary key, meaning that if the client interacts with the bean (through the proxy) in the future, the appropriate data can be loaded from the persistent data store.

Generally, then, there is little or nothing to be done when an Entity bean is passivated or activated.

6

08 0672323842 CH06 3/20/02 9:30 AM Page 224

224 Day 6

These lifecycle methods allow new beans (and data in the persistent data store) to be created or removed and updating existing beans, but what about actually finding beans that already exist? In other words, in JDBC terms, you have seen the lifecycle methods that correspond to SQL

INSERT

,

DELETE

, and

UPDATE statements, but what of an SQL

SELECT statement? Well, this is accomplished by the finder methods. The EJB specification requires at least one finder method, whose name must be ejbFindByPrimaryKey()

, and allows other finder methods, whose names must begin ejbFind

. These methods have corresponding methods in the local-home interface, so you’ll be learning about them shortly as part of specifying and implementing the bean.

One obvious question arises, “When the client invokes the finder method on the home interface, which bean actually performs the ejbFindXxx() method?” The answer is perhaps a little unexpected; any unused (that is, pooled) bean will be used by the EJB container.

Learning all these lifecycle methods for both Entity and Session beans can be somewhat overwhelming at first, made all the more complicated because some method names appear for both bean types but imply different responsibilities. To clarify matters, Table

6.1 compares the two sets of lifecycle methods and identifies those responsibilities.

T

ABLE

6.1

Responsibilities of Session and Entity Beans Sit in Different Lifecycle Methods

Lifecycle Method

setXxxContext() unsetXxxContext()

Session Bean

Set context

N/A

Entity Bean

Set context

Unset context ejbFindByPrimaryKey() ejbCreate() ejbPostCreate() ejbActivate() ejbPassivate() ejbLoad() ejbStore() ejbRemove()

N/A

Acquire reference to proxy

Acquire reference to proxy a) Insert data to persistent data store b) Acquire reference to proxy

N/A Access proxy if necessary a) Loaded from (temporary) Obtain environmental resources data store b) Obtain environmental resources

Release environmental resources a) Saved to (temporary) data store; b) Release environmental resources

N/A Load from (persistent) data store

N/A

Release reference to proxy

Save to (persistent) data store a) Delete data from persistent data store b) Release reference to proxy

08 0672323842 CH06 3/20/02 9:30 AM Page 225

Entity EJBs 225

Specifying a BMP Entity Bean

Following the pattern of Session beans, specifying an Entity bean involves defining the local-home and the local interface:

• The local-home interface extends javax.ejb.EJBLocalHome

.

• The local interface extends javax.ejb.EJBLocalObject

.

A discussion on each of these interfaces follows.

Local-Home Interface

Listing 6.1 shows the complete

JobLocalHome interface as an example.

L

ISTING

6.1

JobLocalHome

Interface

1: package data;

2:

3: import java.rmi.*;

4: import java.util.*;

5: import javax.ejb.*;

6:

7: public interface JobLocalHome extends EJBLocalHome

8: {

9:

10:

11:

12:

13:

14: }

JobLocal create (String ref, String customer) throws CreateException;

JobLocal findByPrimaryKey(JobPK key) throws FinderException;

Collection findByCustomer(String customer) throws FinderException;

Collection findByLocation(String location) throws FinderException; void deleteByCustomer(String customer);

Each of these methods has a corresponding method in the bean class itself. Taking the

JobBean code as an example:

• The create(String ref, String customer) method in

JobBean corresponds to ejbCreate(String ref, String customer) in the

JobLocalHome interface.

• The ejbFindByPrimaryKey(String name) method in JobBean corresponds to findByPrimaryKey(String name) in the

JobLocalHome interface.

• The ejbFindByCustomer(String customer) method in

JobBean corresponds to findbyCustomer(String customer) in the

JobLocalHome interface.

• The ejbHomeDeleteByCustomer(String customer) in

JobBean corresponds to deleteByCustomer(String customer) in the

JobLocalHome interface.

6

08 0672323842 CH06 3/20/02 9:30 AM Page 226

226 Day 6

Note

Note that for home methods discussed shortly, the convention is to append ejbHome

, not just ejb, to the bean’s method name.

This seems straight-forward enough, but note that the return types for the bean’s ejbCreate() and ejbFindXxx() methods are different from the return types of the methods in the local-home interface. Specifically, while the bean returns (to the EJB container) either primary key objects or

Collection s of primary key objects, the local-home interface methods return (to the client) either local proxies (that is, instances of objects that implement the

JobLocal interface, for the example) or

Collection s of such.

Create and Remove Methods

The list of exceptions thrown by the local-home methods and the bean’s corresponding methods should match in each case. For the createXXX() method, the list should be the union of the exceptions thrown by both ejbCreateXXX() and ejbPostCreateXXX()

. If a home and a remote interface are being provided for the Entity bean, the java.rmi.RemoteException

must be declared for the methods of the home interface.

As well as the create() method, the local-home interface inherits a remove(Object o) method from javax.ejb.EJBLocalHome

. This corresponds to the ejbRemove() lifecycle method of the bean itself.

Finder Methods

Finder methods in the bean return either a single primary key (if a single bean matches the underlying query) or a java.util.Collection

of primary keys (if there is more than one matching bean). The ejbFindByPrimaryKey() method is always required to be one of the bean’s methods, although it is not part of the

EntityBean interface. This is because the argument type and return type will depend upon the bean.

Note It is also possible for finder methods to return java.util.Enumeration

s. This dates from EJB 1.0 before the Java Collections API was introduced in J2SE

1.2 and should not be used.

Obviously, to specify the findByPrimaryKey() method, the primary key of the Entity bean must have been identified. As was noted earlier today, if persisting to an RDBMS, identifying the primary key is probably quite easy, because the primary key will correspond to the columns of the primary key of the underlying RDBMS table. A customdeveloped primary key class is needed when two or more fields identify the bean; otherwise, the type of the single field of the bean that represents the key is used.

08 0672323842 CH06 3/20/02 9:31 AM Page 227

Entity EJBs 227

Note If a single field of the bean is used as the primary key, that field must not be a primitive type (such as an int or long

). Primary key fields must be actual classes, such as a java.lang.String

. Furthermore, the EJB specification does not allow primary keys to change once assigned, so it is best if the class chosen is immutable.

Custom Primary Key Classes

As noted earlier, the primary key can be either a field of the bean (in which case, the primary key class is just the class of that field) or can be a custom-developed class. The latter is required if more than one field is needed to identify the bean (and can be used even for single field keys).

For the

JobBean

, the primary key is a combination of the customer and the job reference

(the customer and ref fields, respectively). Because the primary key is composite, a custom primary key class is needed; this is the

JobPK class.

Custom primary key classes are required to follow a number of rules. Specifically

• The class must implement java.io.Serializable

or java.io.Externalizable

.

• The values of the class must all be primitives or be references to objects that, in turn, are serializable.

• The equals() method and the hashCode() methods must be implemented.

• There must be a no-arg constructor (there can also be other constructors that take arguments, but they would only be provided for convenience).

In other words, the class must be what is sometimes referred to as a value type.

Note At least conceptually, value types are immutable (there should be no setter methods; they cannot be changed). The requirement for a no-arg constructor does prevent this from actually being the case.

Listing 6.2 shows the

JobPK primary key class.

L

ISTING

6.2

JobPK

Class Identifies a

Job

1: package data;

2:

3: import java.io.*;

4: import javax.ejb.*;

5:

6

08 0672323842 CH06 3/20/02 9:31 AM Page 228

228 Day 6

L

ISTING

6.2

Continued

6: public class JobPK implements Serializable {

7: public String ref;

8: public String customer;

9:

10: public JobPK() {

11: }

12: public JobPK(String ref, String customer) {

13: this.ref = ref;

14: this.customer = customer;

15: }

16:

17: public String getRef() {

18: return ref;

19: }

20: public String getCustomer() {

21: return customer;

22: }

23:

24: public boolean equals(Object obj) {

25: if (obj instanceof JobPK) {

26: JobPK pk = (JobPK)obj;

27: return (pk.ref.equals(ref) && pk.customer.equals(customer));

28: }

29: return false;

30: }

31: public int hashCode() {

32: return (ref.hashCode() ^ customer.hashCode());

33: }

34: public String toString() {

35: return “JobPK: ref=\”” + ref + “\”, customer=\”” +

➥ customer + “\””;

36: }

37: }

Note that the ref and customer fields have public visibility. This is a requirement of the

EJB specification. Each field must correspond—in name and type—to one of the fields of the bean itself. This might seem like a strange requirement, but is needed by the EJB container to manage CMP beans.

To implement the equals() method, test that all fields of the object have the same value as the fields in the provided object. For primitive values, the regular

== operator should be used, but for object references, the equals() method must be called.

To implement the hashCode() method, generate an int value that is based entirely and deterministically on the value of the fields, such that if

A.equals(B) then

A.hashCode() == B.hashCode()

.

08 0672323842 CH06 3/20/02 9:31 AM Page 229

Entity EJBs 229

There are a couple of ways to accomplish this. A quick way to do this is to convert all the values of the primary key class’ fields to

String s, concatenate them to a single

String

, and then invoke the hashCode() on this resultant string. Alternatively, the hashCode() values for all of the fields could be or

’d together using the

^ operator. At runtime, this will execute more quickly than the concatenation approach, but it does mean that the distribution of hashcodes may be less good for primary keys with many fields. This is the approach used in Listing 6.2.

Tip

Creating these primary key classes can be somewhat tedious. But remember that if there is a single (non-primitive) field in the bean that identifies that bean, this can be used instead.

Failing that, a single primary key class can be used for multiple beans. For example, you could create a

IntPK class that just encapsulates an int primitive value.

Home Methods

In addition to finder, create, and remove methods, it is also possible to define home methods within the local-home interface. These are arbitrary methods that are expected to perform some business-type functionality related to the set of beans. In other words, they are an EJB equivalent of Java class methods (defined with the static keyword).

Some common uses for home methods include defining a batch operation to be performed on all bean instances (such as decreasing the price of all catalogue items for a sale), or various utility methods, such as formatting a bean’s state for a toString() method.

Caution

One question that sometimes arises is whether all database updates should be performed through Entity bean methods. One example given for a home method of a bean would be to decrease the price of all catalogue items for a sale. Iterating over perhaps 10,000 catalogue items and invoking setPrice( getPrice() * 0.9 ) is clearly going to cause massive amounts of

SQL hitting the back-end RDBMS (or equivalent persistent data store).

In J2SE programs, a simple update, such as

UPDATE catalogue set price = price * 0.9

is clearly the way to go. The ejbLoad() lifecycle method will ensure that any catalog item Entity bean will re-sync its state with the RDBMS.

6

08 0672323842 CH06 3/20/02 9:31 AM Page 230

230 Day 6

Local Interface

Just as for Session beans with their remote interfaces, the local interface defines the capabilities of the Entity bean. Because first and foremost an Entity bean represents data, it is entirely to be expected that many of the methods exposed through the local interface will be simple getter and setter methods. Listing 6.3 shows the local interface for the

Job bean.

L

ISTING

6.3

JobLocal

Interface

1: package data;

2:

3: import java.rmi.*;

4: import javax.ejb.*;

5:

6: public interface JobLocal extends EJBLocalObject

7: {

8: String getRef();

9: String getCustomer();

10: CustomerLocal getCustomerObj(); // derived

11:

12: void setDescription(String description);

13: String getDescription();

14:

15: void setLocation(LocationLocal location);

16: LocationLocal getLocation();

17:

18: Collection getSkills();

19: void setSkills(Collection skills);

20: }

Note that the setLocation() method accepts a

LocationLocal reference rather than, say, a

String containing the name of a location. In other words, the

Job bean is defining its relationships to other beans, in this case the

Location bean directly, effectively enforcing referential integrity. The client of the

Job

Entity bean is thus required to supply a valid location or none at all.

This is not to say that Entity beans cannot provide further processing. An example often quoted might be for a

SavingsAccountBean

. This might provide withdraw() and deposit() methods. The withdraw() method might well ensure that the balance can never go below zero. The bean might also provide an applyInterest() method, but it almost certainly would not provide a setBalance() method (if only!).

Each of these methods has a corresponding method in the bean itself, and the exceptions list matches exactly. The implementation is shown in the “Implementing the Local-

Interface Methods” section later today.

08 0672323842 CH06 3/20/02 9:31 AM Page 231

Entity EJBs 231

Note Actually, such an

SavingsAccountBean might well provide a setBalance() method, but would restrict access to administrators. You will learn more about security on Day 15, “Security.”

Implementing a BMP Entity Bean

Implementing an Entity bean involves providing an implementation for the methods of the javax.ejb.EntityBean

, corresponding methods for each method in the local-home interface, and a method for each method in the local interface.

Implementing javax.ejb.EntityBean

The setEntityContext() method is a good place to perform JNDI lookups, for example to acquire a JDBC DataSource reference. Listing 6.4 shows how this is done for the

JobBean code.

L

ISTING

6.4

JobBean.setEntityContext()

Method

1: package data;

2:

3: import javax.ejb.*;

4: import javax.naming.*;

5: import javax.sql.*;

6: // imports omitted

7:

8: public class JobBean implements EntityBean

9: {

10: public void setEntityContext(EntityContext ctx) {

11: this.ctx = ctx;

12: InitialContext ic = null;

13: try {

14: ic = new InitialContext();

15: dataSource = (DataSource)

➥ic.lookup(“java:comp/env/jdbc/Agency”);

16: skillHome = (SkillLocalHome)

➥ic.lookup(“java:comp/env/ejb/SkillLocal”);

17: locationHome = (LocationLocalHome)

➥ic.lookup(“java:comp/env/ejb/LocationLocal”);

18: customerHome = (CustomerLocalHome)

➥ic.lookup(“java:comp/env/ejb/CustomerLocal”);

19: }

20: catch (NamingException ex) {

21: error(“Error looking up depended EJB or resource”,ex);

22: return;

23: }

24: }

25:

6

08 0672323842 CH06 3/20/02 9:31 AM Page 232

232 Day 6

L

ISTING

6.4

Continued

26: private Context ctx;

27: private DataSource dataSource

28:

29: // code omitted

30: }

The unsetEntityContext() method (not shown) usually just sets these fields to null

.

The ejbLoad() and ejbStore() methods are responsible for synchronizing the bean’s state with the persistent data store. Listing 6.5 shows these methods for

JobBean

.

L

ISTING

6.5

JobBean

’s ejbLoad() and ejbStore()

Methods

1: package data;

2:

3: import javax.ejb.*;

4: import java.sql.*;

5: // imports omitted

6:

7: public class JobBean implements EntityBean

8: {

9: public void ejbLoad(){

10: JobPK key = (JobPK)ctx.getPrimaryKey();

11: Connection con = null;

12: PreparedStatement stmt = null;

13: ResultSet rs = null;

14: try {

15: con = dataSource.getConnection();

16: stmt = con.prepareStatement(

➥”SELECT description,location

➥ FROM Job

➥ WHERE ref = ? AND customer = ?”);

17: stmt.setString(1, key.getRef());

18: stmt.setString(2, key.getCustomer());

19: rs = stmt.executeQuery();

20: if (!rs.next()) {

21: error(“No data found in ejbLoad for “ + key, null);

22: }

23: this.ref = key.getRef();

24: this.customer = key.getCustomer();

25: this.customerObj =

➥customerHome.findByPrimaryKey(this.customer); // derived

26: this.description = rs.getString(1);

27: String locationName = rs.getString(2);

28: this.location = (locationName != null) ?

➥locationHome.findByPrimaryKey(locationName) : null;

29: // load skills

08 0672323842 CH06 3/20/02 9:31 AM Page 233

Entity EJBs 233

L

ISTING

6.5

Continued

30: stmt = con.prepareStatement(

➥”SELECT job, customer, skill

➥ FROM JobSkill

➥ WHERE job = ? AND customer = ?

➥ ORDER BY skill”);

31: stmt.setString(1, ref);

32: stmt.setString(2, customerObj.getLogin());

33: rs = stmt.executeQuery();

34: List skillNameList = new ArrayList();

35: while (rs.next()) {

36: skillNameList.add(rs.getString(3));

37: }

38: this.skills = skillHome.lookup(skillNameList);

39: }

40: catch (SQLException e) {

41: error(“Error in ejbLoad for “ + key, e);

42: }

43: catch (FinderException e) {

44: error(“Error in ejbLoad (invalid customer or location) for “

➥+ key, e);

45: }

46: finally {

47: closeConnection(con, stmt, rs);

48: }

49: }

50:

51: public void ejbStore(){

52: Connection con = null;

53: PreparedStatement stmt = null;

54: try {

55: con = dataSource.getConnection();

56: stmt = con.prepareStatement(

➥”UPDATE Job

➥ SET description = ?, location = ?

➥ WHERE ref = ? AND customer = ?”);

57: stmt.setString(1, description);

58: if (location != null) {

59: stmt.setString(2, location.getName());

60: } else {

61: stmt.setNull(2, java.sql.Types.VARCHAR);

62: }

63: stmt.setString(3, ref);

64: stmt.setString(4, customerObj.getLogin());

65: stmt.executeUpdate();

66: // delete all skills

67: stmt = con.prepareStatement(

➥”DELETE FROM JobSkill

➥ WHERE job = ? and customer = ?”);

6

08 0672323842 CH06 3/20/02 9:31 AM Page 234

234 Day 6

L

ISTING

6.5

Continued

68: stmt.setString(1, ref);

69: stmt.setString(2, customerObj.getLogin());

70: stmt.executeUpdate();

71: // add back in all skills

72: for (Iterator iter = getSkills().iterator(); iter.hasNext();){

73: SkillLocal skill = (SkillLocal)iter.next();

74: stmt = con.prepareStatement(

➥”INSERT INTO JobSkill (job,customer,skill)

➥ VALUES (?,?,?)”);

75: stmt.setString(1, ref);

76: stmt.setString(2, customerObj.getLogin());

77: stmt.setString(3, skill.getName());

78: stmt.executeUpdate();

79: }

80: }

81: catch (SQLException e) {

82: error(“Error in ejbStore for “ + ref + “,” + customer, e);

83: }

84: finally {

85: closeConnection(con, stmt, null);

86: }

87: }

88: // code omitted

89: }

In the ejbLoad() method, the

JobBean must load its state from both the

Job and

JobSkill tables, using the data in the

JobSkill table to populate the skills field. In the ejbStore() method, the equivalent updates to the

Job and

JobSkill tables occur.

Of course, there is the chance that when the bean comes to save itself, the data could have been removed. This would happen if some user manually deleted the data; there is nothing in the EJB specification to require that an Entity bean “locks” the underlying data. In such a case, the bean should throw a javax.ejb.NoSuchEntityException

; in turn, this will be returned to the client as some type of java.rmi.RemoteException

. This was mentioned briefly yesterday, so look back to refresh your memory if needed. And remember, you will be learning more about exception handling and transactions on Day 8.

Note

To keep the case study as small and understandable as possible, the error handling in JobBean is slightly simplified. In Listing 6.5, the code will throw an EJBException (rather than NoSuchEntityException) from ejbLoad() if the data has been removed. In ejbStore(), it doesn’t actually check to see if any rows were updated, so no exception would be thrown.

08 0672323842 CH06 3/20/02 9:31 AM Page 235

Entity EJBs 235

More complex beans can perform other processing within the ejbLoad() and ejbStore() methods. For example, the data might be stored in some denormalized form in a relational database, perhaps for performance reasons. The ejbStore() method would store the data in this de-normalized form, while the ejbLoad() methods would effectively be able to re-normalize the data on-the-fly. The client need not be aware of these persistence issues.

Another idea: these methods could be used to handle text more effectively. The EJB specification suggests compressing and decompressing text, but they could also perhaps do searches for keywords within the text, and then redundantly store these keywords separately, or the data might be converted into XML format.

As noted earlier today, there is usually very little or nothing to be done when an Entity bean is passivated or activated. Listing 6.6 shows this.

L

ISTING

6.6

JobBean

’s ejbActivate() and ejbPassivate()

Methods

1: package data;

2:

3: import javax.ejb.*;

4: // imports omitted

5:

6: public class JobBean implements EntityBean

7: {

8: public void ejbPassivate(){

9: ref = null;

10: customer = null;

11: customerObj = null;

12: description = null;

13: location = null;

14: }

15:

16: public void ejbActivate(){

17: }

18:

19: // code omitted

20: }

Implementing the Local-Home Interface Methods

The implementation of ejbCreate() and ejbPostCreate() for the

JobBean is shown in

Listing 6.7.

L

ISTING

6.7

JobBean

’s ejbCreate() and ejbPostCreate()

Methods

1: package data;

2:

6

08 0672323842 CH06 3/20/02 9:31 AM Page 236

236 Day 6

L

ISTING

6.7

Continued

3: import javax.ejb.*;

4: import javax.sql.*;

5: // imports omitted

6:

7: public class JobBean implements EntityBean

8: {

9: private String ref;

10: private String customer;

11: private String description;

12: private LocationLocal location;

13: private CustomerLocal customerObj; // derived

14: private List skills; // vector field; list of SkillLocal ref’s.

15:

16: public String ejbCreate (String ref, String customer)

➥throws CreateException {

17: // validate customer login is valid.

18: try {

19: customerObj = customerHome.findByPrimaryKey(customer);

20: } catch (FinderException ex) {

21: error(“Invalid customer.”, ex);

22: }

23: JobPK key = new JobPK(ref, customer);

24: try {

25: ejbFindByPrimaryKey(key);

26: throw new CreateException(“Duplicate job name: “ + key);

27: }

28: catch (FinderException ex) { }

29: Connection con = null;

30: PreparedStatement stmt = null;

31: try {

32: con = dataSource.getConnection();

33: stmt = con.prepareStatement(

➥”INSERT INTO Job (ref,customer)

➥ VALUES (?,?)”);

34: stmt.setString(1, ref);

35: stmt.setString(2, customerObj.getLogin());

36: stmt.executeUpdate();

37: }

38: catch (SQLException e) {

39: error(“Error creating job “ + key, e);

40: }

41: finally {

42: closeConnection(con, stmt, null);

43: }

44: this.ref = ref;

45: this.customer = customer;

46: this.description = description;

47: this.location = null;

48: this.skills = new ArrayList();

08 0672323842 CH06 3/20/02 9:31 AM Page 237

Entity EJBs 237

L

ISTING

6.7

Continued

49: return key;

50: }

51:

52: public void ejbPostCreate (String name, String description) {}

53: }

This particular implementation validates that the customer exists (jobs are identified by customer and by a unique reference), and it also makes sure that the full primary key does not already exist in the database. If it does, the BMP bean throws a

CreateException

. If it doesn’t (represented by the ejbFindByPrimaryKey() call throwing a

FinderException

), the method continues.

An alternative implementation would have been to place a unique index on the

Job table within the RDBMS and then to catch the

SQLException that might be thrown if a duplicate is attempted to be inserted.

Caution There is a race condition here. It’s possible that another user could insert a record between the check for duplicates and the actual SQL INSERT. The ejbCreate() method is called within a transaction; changing the RDBMS isolation level (in a manner specified by the EJB container) would eliminate this risk, although deadlocks could then occur.

Note that the skills field is set to an empty

ArrayList

. This holds a list of

SkillLocal references, this being the local interface to the

Skill bean. Of course, for a newly created

Job bean, this list is empty. The decision for the skills field to hold references to

SkillLocal objects rather than, say, just

String s holding the skill names, was taken advisedly. If the skill name is used (that is, the primary key of a skill), finding information about the skill would require extra steps. Perhaps more compellingly, this is also the approach taken for CMP beans and container-managed relationships, discussed in detail tomorrow.

Also noteworthy is the customerObj field. The

Job

, when created, is passed just a

String containing the customer’s name. In other words, this is a primary key to a customer. The customerObj field contains a reference to the parent customer bean itself by way of its

CustomerLocal reference.

Both the skills and the customerObj fields illustrate (for want of a better phrase) beanmanaged relationships. For the skills field, this is a many-to-many relationship, from

Job to

Skill

. For the customerObj field, this is a many-to-one relationship from

Job to

Customer

.

6

08 0672323842 CH06 3/20/02 9:31 AM Page 238

238 Day 6

As for the stateful Session beans that you learned about yesterday, the ejbCreate() and ejbPostCreate() methods both correspond to a single method called create() in the bean’s local-home interface. The list of arguments must correspond. Again, as for

Session beans, it is possible for there to be more than one create method with different sets of arguments, or indeed the createXXX() method naming convention can be used instead of overloading the method name of create()

.

The ejbRemove() method is the opposite of the ejbCreate() method; it removes a bean’s data from the persistent data store. Its implementation for

JobBean is shown in

Listing 6.8.

L

ISTING

6.8

JobBean

’s ejbRemove()

Method

1: package data;

2:

3: import javax.ejb.*;

4: import javax.naming.*;

5: // imports omitted

6:

7: public class JobBean implements EntityBean

8: {

9: public void ejbRemove(){

10: JobPK key = (JobPK)ctx.getPrimaryKey();

11: Connection con = null;

12: PreparedStatement stmt1 = null;

13: PreparedStatement stmt2 = null;

14: try {

15: con = dataSource.getConnection();

16: stmt1 = con.prepareStatement(

➥”DELETE FROM JobSkill

➥ WHERE job = ? and customer = ?”);

17: stmt1.setString(1, ref);

18: stmt1.setString(2, customerObj.getLogin());

19: stmt2 = con.prepareStatement(

➥”DELETE FROM Job

➥ WHERE ref = ? and customer = ?”);

20: stmt2.setString(1, ref);

21: stmt2.setString(2, customerObj.getLogin());

22: stmt1.executeUpdate();

23: stmt2.executeUpdate();

24: }

25: catch (SQLException e) {

26: error(“Error removing job “ + key, e);

27: }

28: finally {

29: closeConnection(con, stmt1, null);

30: closeConnection(con, stmt2, null);

31: }

32: ref = null;

08 0672323842 CH06 3/20/02 9:31 AM Page 239

Entity EJBs 239

L

ISTING

6.8

Continued

33: customer = null;

34: customerObj = null;

35: description = null;

36: location = null;

37: }

38: // code omitted

39: }

Each of the finder methods of the local-home interface must have a corresponding method in the bean. By way of example, Listing 6.9 shows two of the (three) finder methods for the

JobBean

.

L

ISTING

6.9

JobBean

’s Finder Methods

1: package data;

2:

3: import javax.ejb.*;

4: import java.sql.*;

5: import java.util.*;

6: // imports omitted

7:

8: public class JobBean implements EntityBean

9: {

10: public JobPK ejbFindByPrimaryKey(JobPK key) throws FinderException {

11: Connection con = null;

12: PreparedStatement stmt = null;

13: ResultSet rs = null;

14: try {

15: con = dataSource.getConnection();

16: stmt = con.prepareStatement(

➥”SELECT ref

➥ FROM Job

➥ WHERE ref = ? AND customer = ?”);

17: stmt.setString(1, key.getRef());

18: stmt.setString(2, key.getCustomer());

19: rs = stmt.executeQuery();

20: if (!rs.next()) {

21: throw new FinderException(“Unknown job: “ + key);

22: }

23: return key;

24: }

25: catch (SQLException e) {

26: error(“Error in findByPrimaryKey for “ + key, e);

27: }

28: finally {

29: closeConnection(con, stmt, rs);

30: }

31: return null;

6

08 0672323842 CH06 3/20/02 9:31 AM Page 240

240 Day 6

L

ISTING

6.9

Continued

32: }

33:

34: public Collection ejbFindByCustomer(String customer)

➥throws FinderException {

35: Connection con = null;

36: PreparedStatement stmt = null;

37: ResultSet rs = null;

38: try {

39: con = dataSource.getConnection();

40: stmt = con.prepareStatement(

➥”SELECT ref, customer

➥ FROM Job

➥ WHERE customer = ?

➥ ORDER BY ref”);

41: stmt.setString(1, customer);

42: rs = stmt.executeQuery();

43: Collection col = new ArrayList();

44: while (rs.next()) {

45: String nextRef = rs.getString(1);

46: String nextCustomer = rs.getString(2);

47: // validate customer exists

48: CustomerLocal nextCustomerObj =

➥customerHome.findByPrimaryKey(nextCustomer);

49: col.add(new JobPK(nextRef, nextCustomerObj.getLogin()));

50: }

51: return col;

52: }

53: catch (SQLException e) {

54: error(“Error in findByCustomer: “ + customer, e);

55: }

56: catch (FinderException e) {

57: error(“Error in findByCustomer, invalid customer: “ +

➥customer, e);

58: }

59: finally {

60: closeConnection(con, stmt, rs);

61: }

62: return null;

63: }

64:

65: // code omitted

66: }

The implementation of the ejbFindByPrimaryKey() method might seem somewhat unusual; it receives a primary key, and then returns it. Of course, what it has done as well is to have validated that an entity exists for the given primary key; if there were none, a javax.ejb.ObjectNotFoundException

would be thrown. The implementation of ejbFindByCustomer() is straightforward enough.

08 0672323842 CH06 3/20/02 9:31 AM Page 241

Entity EJBs 241

The

Job bean defines a home method, namely deleteByCustomer()

, and the corresponding method in the

JobBean class is ejbHomeDeleteByCustomer()

, as shown in Listing 6.10.

L

ISTING

6.10

JobBean.ejbHomeDeleteByCustomer()

Home Method

1: package data;

2:

3: import javax.ejb.*;

4: import java.sql.*;

5: import java.util.*;

6: // imports omitted

7:

8: public class JobBean implements EntityBean

9: {

10: public void ejbHomeDeleteByCustomer(String customer) {

11: Connection con = null;

12: PreparedStatement stmt2 = null;

13: PreparedStatement stmt1 = null;

14: try {

15: con = dataSource.getConnection();

16: stmt1 = con.prepareStatement(

➥”DELETE FROM JobSkill

➥ WHERE customer = ?”);

17: stmt2 = con.prepareStatement(

➥”DELETE FROM Job

➥ WHERE customer = ?”);

18: stmt1.setString(1, customer);

19: stmt2.setString(1, customer);

20: stmt1.executeUpdate();

21: stmt2.executeUpdate();

22: }

23: catch (SQLException e) {

24: error(“Error removing all jobs for “ + customer, e);

25: }

26: finally {

27: closeConnection(con, stmt1, null);

28: closeConnection(con, stmt2, null);

29: }

30: }

31: // code omitted

32: }

Implementing the Local Interface Methods

Each of the methods in the local interface has a corresponding method in the bean itself.

The corresponding methods for

JobBean are shown in Listing 6.11.

6

08 0672323842 CH06 3/20/02 9:31 AM Page 242

242 Day 6

L

ISTING

6.11

Business Methods of

JobBean

Correspond to the Methods of the Local

Interface

1: package data;

2:

3: import java.rmi.*;

4: import javax.ejb.*;

5: // imports omitted

6:

7: public class JobBean implements EntityBean

8: {

9: public String getRef() {

10: return ref;

11: }

12: public String getCustomer() {

13: return customer;

14: }

15: public CustomerLocal getCustomerObj() {

16: return customerObj;

17: }

18: public String getDescription() {

19: return description;

20: }

21: public void setDescription(String description) {

22: this.description = description;

23: }

24: public LocationLocal getLocation() {

25: return location;

26: }

27: public void setLocation(LocationLocal location) {

28: this.location = location;

29: }

30: /** returns (copy of) skills */

31: public Collection getSkills() {

32: return new ArrayList(skills);

33: }

34: public void setSkills(Collection skills) {

35: // just validate that the collection holds references to

➥SkillLocal’s

36: for (Iterator iter = getSkills().iterator(); iter.hasNext(); ) {

37: SkillLocal skill = (SkillLocal)iter.next();

38: }

39: // replace the list of skills with that defined.

40: this.skills = new ArrayList(skills);

41: }

42: // code omitted

43: }

The getSkills() and setSkills() methods bear closer inspection. The getSkills() method returns a copy of the local skills field because, otherwise, the client could

08 0672323842 CH06 3/20/02 9:31 AM Page 243

Entity EJBs 243 change the contents of the skills field without the

JobBean knowing. This isn’t an issue that would have arisen if the interface to

JobBean was remote, because a copy would automatically have been created. Turning to the setSkills() method, this checks to make sure that the new collection of skills supplied is a list of

SkillLocal references.

This is analogous to the setLocation() method that was discussed; the

Job

Entity bean is enforcing referential integrity with the

Skill

Entity bean.

Generating IDs

Sometimes an Entity bean already has a field (or fields) that represent the primary key, but at other times, the set of fields required may just be too large. Alternatively, the obvious primary key may not be stable in the sense that its value could change over the lifetime of an entity—something prohibited by the EJB specification. For example, choosing a

(lastname

,

firstname) as a means of identifying an employee may fail if a female employee gets married and chooses to adopt her husband’s surname.

In these cases, it is common to introduce an artificial key, sometimes known as a surro-

gate key. You will be familiar with these if you have ever been allocated a customer number when shopping online. Your social security number, library card number, driver’s license, and so on may well be just a pretty meaningless jumble of numbers and letters, but they are guaranteed to be unique and stable. These are all surrogate keys.

With BMP Entity beans, the responsibility for generating these ID values is yours, the bean provider. Whether numbers and letters or just numbers are used is up to you, although just numbers are often used in an ascending sequence (that is, 1, 2, 3, and so on). If you adopt this strategy, you could calculate the values by calling a stateless

Session bean—a number fountain, if you will. A home method could perhaps encapsulate the lookup of this

NumberFountainBean

.

The implementation of such a

NumberFountainBean can take many forms. There will need to be some persistent record of the maximum allocated number in the series, so a method such as getNextNumber(“MyBean”) could return a value by performing an appropriate piece of SQL against a table held in an RDBMS: begin tran update number_fountain set max_value = max_value + 1 where bean_name = “MyBean” select max_value from number_fountain where bean_name = “MyBean” commit

6

08 0672323842 CH06 3/20/02 9:31 AM Page 244

244 Day 6

One disadvantage with this approach is that the

NumberFountainBean

—or rather, the underlying database table—can become a bottleneck. A number of strategies have been developed to reduce this. One is to make the getNextNumber() method occur in a different transaction from the rest of the work. You will learn more about transactions on Day

8; for now, it is just necessary to know that while this will increase throughput, there is the risk of gaps occurring in the sequence.

If non-contiguous sequences are acceptable, even better throughput can be achieved by implementing a stateless Session bean that caches values in memory. Thus, rather than incrementing the maximum value by 1, it can increment by a larger number, perhaps by

100. Only every 100th call actually performs an SQL update, and the other 99 times, the number is allocated from memory. Of course, if the system crashes or power fails, there could be quite a large gap.

A final enhancement that improves scalability further and also improves resilience is to arrange for there to be a number of beans, each with a range of values. For example, these might be allocated using a high-order byte/low-order bytes arrangement.

Note Countries that allocate car license plates by state or by district are effectively using this approach.

One advantage of implementing a Session bean, such as

NumberFountainBean

, is that it isolates the dependency on the persistent data store that is holding the maximum value.

Also, the SQL to determine the next available number is easily ported across RDBMS.

On the other hand, many organizations use only a single RDBMS, so such portability is not needed. In these cases, the RDBMS may have built-in support for allocating monotonically increasing numeric values, and this can be used directly. For example,

SEQUENCE s can be used in Oracle, while both Microsoft SQL Server and Sybase provide so-called identity columns and the

@@identity global variable. So, for BMP Entity beans, another way to obtain the next value is to perform the SQL

INSERT

, obtaining the value from the back-end RDBMS. Note that most of these tools have the same scalability issues as the home-grown

NumberFountainBean

, and most also provide optimizations that can result in gaps in the series.

There is an alternative to using numbers for ID values, namely to generate a value that must be unique. On Windows PCs, you may well have seen strings in the format

{32F8CA14-087C-4908-B7C4-6757FE7E90AB}

In case you are wondering, this was found by delving into the Windows Registry and

(apparently) represents the

FormatGUID for .AVI files (whatever that means!). The point

08 0672323842 CH06 3/20/02 9:31 AM Page 245

Entity EJBs 245 is that it is—to all intents and purposes—guaranteed to be unique. In the case of GUIDs, it is unique because it is based on the MAC address of the ethernet card of the PC, plus the time.

Clearly, other algorithms can be created, and a quick search on the Web should throw up some commercial products and free software from which to select. For example, one algorithm generates values unique to the millisecond, the machine, the object creating the

ID, and the top-most method of the call stack.

Granularity Revisited

A recurrent theme when developing Entity beans is in selecting an appropriate granularity for the bean. Prior to EJB 2.0, Entity beans could only provide a remote interface, which meant that a relatively coarse grained interface was required to minimize the client-to-bean network traffic. Indeed, this is still the recommendation made for Session beans in EJB 2.0 that have a remote interface.

With EJB 2.0, Entity beans can have a local interface, meaning that the cost of interaction with the client becomes minimal. If the cost of interaction of the Entity bean to the persistent data store is not too high, fine-grained Entity beans are quite possible. This may be true, either because the EJB container can optimize the database access in some way (true only for CMP Entity beans) or if the data store resides on the same computer as the EJB container and, ideally, within the same JVM process space.

Note Running a persistent data store in the same process space as the EJB container is quite possible; a number of pure Java RDBMS—including Cloudscape, the database bundled with the J2EE RI—provide an “embedded mode.”

Under BMP however, the advice is generally not to use fine-grained Entity beans, principally because the EJB container will be unable to perform any database access optimization. Choosing the granularity is then best determined by focusing on the identity and lifecycle on the candidate Entity beans. Hence, order and order-detail should be a single

Order bean, but customer and order

, while related, should be kept separate.

In the case study, you will find that the

Job bean writes to both the

Job table and also the

JobSkill table (to record the skill(s) needed to perform the job).

Beware Those Finder Methods!

As you now have learned, Entity beans can be created, removed, and found through their home interface. While these all seem straightforward enough operations, there’s danger

6

08 0672323842 CH06 3/20/02 9:31 AM Page 246

246 Day 6 lurking in the last of these; finder methods can cripple the performance of your application if used incorrectly.

This probably doesn’t seem obvious, but if you consider the interplay between the EJB container (implementing the local-home interface) and the bean itself, it becomes easier to see:

• The local-home interface’s findManyByXxx() method is called. For the purposes of this discussion, this finder method returns a

Collection

.

• The local-home interface delegates to a pooled bean, calling its ejbFindManyByXxx() method. This performs an SQL

SELECT statement (or the equivalent), and returns back a

Collection of primary keys to the local-home interface.

• The local-home interface instantiates a

Collection the same size as was obtained from the bean and populates it with local proxies. Each local proxy is assigned a primary key.

So far so good, the client receives a

Collection of proxies. Suppose now that the client iterates over this collection, calling some getter method getXxx()

.

• The client calls getXxx() on the first proxy in its

Collection

. The proxy holds a primary key, but there is no corresponding bean actually associated with the proxy.

Therefore, the EJB container activates a bean from the pool, calls its ejbLoad() lifecycle method, and then finally delegates the getXxx() business method. After that method has completed, the ejbStore() method is called.

• This process continues for all of the proxies in the collection.

You can probably see the problem; the persistent data store will be hit numerous times.

First, it will be hit as a result of the ejbFindManyByXxx() method; this will return a thin column of primary key values. Then, because ejbLoad() is called for each bean, the rest of the data for that row is returned. This is shown in Figure 6.8.

Consequently, if 20 primary keys were returned by the bean following the initial ejbFindManyByXxx()

, the network would be transversed 21 times, and the database will be hit in all 41 times—once for the initial

SELECT and two times each for each of the beans.

There are a number of techniques that can eliminate this overhead, each with pros and cons:

• The most obvious solution is to not use finder methods that return

Collection s of many beans. Instead, use stateless Session beans that perform a direct SQL

SELECT query against the database, iterate over the

ResultSet

, and return back a

Collection of serializable value objects that mirror the data contained in the actual entity. This technique is called the Fast-lane Reader.

08 0672323842 CH06 3/20/02 9:31 AM Page 247

Entity EJBs 247

F

IGURE

6.8

Finder methods can result in poor performance under BMP.

local client

1. findManyBy

4. getXXX

[for each local proxy]

PK

1 local

1

.

.

.

PK n local n

5. ejbLoad

7. getXXX

8. ejbStore local home

2. ejbFindManyBy bean

3. select where…

PK

1

.

.

.

PK n bean

6. select…

9. update…

• Another technique that can be used is to alter the definition of the primary key class. As well as holding the key information that identifies the bean in the database, it also holds the rest of the bean’s data as well. When the original finder bean returns the

Collection of primary keys, the primary keys are held by the local proxies. When the beans are activated, they can obtain their state from the proxy by using entityContext.getLocalObject().getPrimaryKey()

. This technique has been dubbed the fat key pattern, for obvious reasons.

• Last, you may be able to remove the performance hit by porting the bean to use container managed persistence. Because under CMP the EJB container is responsible for all access to the persistent data store, many will obtain all the required information from the data store during the first findManyByXxx method call, and then eagerly instantiate beans using this information. You will be learning more about CMP tomorrow.

Incidentally, Figure 6.8 shows a graphic illustration of why Entity beans should, in general, define only a local interface—not a remote interface. If the client were remote rather than local, the total network calls for a finder method returning references to 20 beans would be double the original figure, namely 42! Moreover, every subsequent business method invocation (call to getYYY() for example) would inflict a further 20 network calls.

EJB Container Performance Tuning

Many organizations are wary of using Entity beans because of the performance costs that are associated with it. You have already seen the performance issues arising from using finder methods, but even ignoring this, any business method to an Entity bean will require two database calls—one resulting from the ejbLoad() that precedes the business method and one from the ejbStore() to save the bean’s new state back to the data store.

6

08 0672323842 CH06 3/20/02 9:31 AM Page 248

248 Day 6

Of course, these database calls may be unnecessary. If a bean hasn’t been passivated since the last business call, the ejbLoad() need not do anything, provided that nothing has updated the data store through non-EJB mechanisms. Also, if the business method called did not change the state of the bean the ejbStore() has nothing to do also.

Another scenario is where a bean is interacted with several times as part of a transaction.

You will be learning more about transactions on Day 8, so for now, just appreciate that when a bean is modified through the course of a transaction, either all of its changes in state or none of them need to be persisted. In other words, there is only really the requirement to call ejbStore() just once at the end of the transaction.

Taking these points together, the amount of network traffic from the EJB container to the persistent data store can be substantially reduced, down to the levels that might be expected in a hand-written J2SE client/server application. Although not part of the EJB specification, many EJB containers provide proprietary mechanisms to prevent unnecessary ejbLoad() or ejbStore() calls. Of course, the use of these mechanisms will make your bean harder to port to another EJB container, but you may well put up with the inconvenience for the performance gains realized. Indeed, if you are in the process of evaluating EJB containers, as many companies are, you may even have placed these features on your requirements list.

Configuring and Deploying a BMP

Entity Bean

Yesterday, you learned how to deploy Session beans by creating ejb-jar files with their own deployment descriptor and including the ejb-jar into an enterprise application.

Deploying Entity beans is done in precisely the same way, by creating ejb-jar files that contain the Entity bean classes, with appropriate entries in the deployment descriptor.

This deployment descriptor is the same deployment descriptor as for Session beans. As you saw yesterday, the root element of this deployment descriptor is the ejb-jar element:

<!ELEMENT ejb-jar (description?, display-name?, small-icon?, large-icon?, enterprise-beans, relationships?, assembly-descriptor?, ejb-client-jar?)>

Looking at the enterprise-beans element, this is defined as follows:

<!ELEMENT enterprise-beans (session | entity | message-driven)+>

The deployment descriptor can contain Session, Entity, and/or Message-driven beans. Or put another way, Session beans and Entity beans can be placed in the same ejb-jar if required.

08 0672323842 CH06 3/20/02 9:31 AM Page 249

Entity EJBs 249

Entity Element

The entity element of the DTD is defined as follows:

<!ELEMENT entity (description?, display-name?, small-icon?, large-icon?, ejb-name, home?, remote?, local-home?, local?, ejb-class, persistence-type, prim-key-class, reentrant, cmp-version?, abstract-schema-name?, cmp-field*, primkey-field?, env-entry*, ejb-ref*, ejb-local-ref*, security-role-ref*, security-identity?, resource-ref*, resource-env-ref*, query*)>

Many of the elements referred by the entity element are also used by the session element, and were discussed yesterday:

Presentational elements— description

, display-name

, small-icon

, and largeicon

.

Elements describing the components of the bean— ejb-name

, home

, remote

, localhome

, local

, and ejb-class elements. The local-home and local elements just name the interfaces that extend javax.ejb.EJBLocalHome

and javax.ejb.EJBLocal

, respectively.

Elements referring to the environment and other resources— env-entry

, ejb-ref

, ejb-local-ref

, resource-ref

, and resource-env-ref

.

The remaining elements are specific to Entity beans:

• persistence-type

Set to

Bean for bean-managed persistence or

Container for container-managed persistence. For the purposes of today, this will be set to

Bean

.

• prim-key-class

This mandatory element indicates the name of the primary key class. This will be a custom developed class for beans that have primary keys

(

JobPK for

Job bean) but may be a regular class ( java.lang.String

) for others

(for example, the

Location bean).

• cmp-version

, abstract-schema-name

, cmp-field

, prim-key-field

, query

These are used only for CMP beans and are covered tomorrow.

• reentrant

Set to true for reentrant Entity beans, or false for non reentrant beans. It’s safer to mark Entity beans as non reentrant.

• security-role-ref

, security-identity

You will learn more about security on

Day 15.

6

08 0672323842 CH06 3/20/02 9:31 AM Page 250

250 Day 6

Listing 6.12 shows the deployment descriptor for the

Job bean.

L

ISTING

6.12

entity

Element Descriptor for the

Job

Bean

1: <entity>

2: <display-name>JobBean</display-name>

3: <ejb-name>JobBean</ejb-name>

4: <local-home>data.JobLocalHome</local-home>

5: <local>data.JobLocal</local>

6: <ejb-class>data.JobBean</ejb-class>

7: <persistence-type>Bean</persistence-type>

8: <prim-key-class>data.JobPK</prim-key-class>

9: <reentrant>False</reentrant>

10: <ejb-local-ref>

11: <ejb-ref-name>ejb/SkillLocal</ejb-ref-name>

12: <ejb-ref-type>Entity</ejb-ref-type>

13: <local-home>data.SkillLocalHome</local-home>

14: <local>data.SkillLocal</local>

15: <ejb-link>data_entity_ejbs.jar#SkillBean</ejb-link>

16: </ejb-local-ref>

17: <ejb-local-ref>

18: <ejb-ref-name>ejb/LocationLocal</ejb-ref-name>

19: <ejb-ref-type>Entity</ejb-ref-type>

20: <local-home>data.LocationLocalHome</local-home>

21: <local>data.LocationLocal</local>

22: <ejb-link>data_entity_ejbs.jar#LocationBean</ejb-link>

23: </ejb-local-ref>

24: <ejb-local-ref>

25: <ejb-ref-name>ejb/CustomerLocal</ejb-ref-name>

26: <ejb-ref-type>Entity</ejb-ref-type>

27: <local-home>data.CustomerLocalHome</local-home>

28: <local>data.CustomerLocal</local>

29: <ejb-link>data_entity_ejbs.jar#CustomerBean</ejb-link>

30: </ejb-local-ref>

31: <security-identity>

32: <description></description>

33: <use-caller-identity></use-caller-identity>

34: </security-identity>

35: <resource-ref>

36: <res-ref-name>jdbc/Agency</res-ref-name>

37: <res-type>javax.sql.DataSource</res-type>

38: <res-auth>Container</res-auth>

39: <res-sharing-scope>Shareable</res-sharing-scope>

40: </resource-ref>

41: </entity>

Note the resource-ref dependency on jdbc/Agency

, just as you saw for Session beans that make JDBC calls. The res-ref-name is the coded name jdbc/Agency that appears in the setEntityContext() method of

LocationBean

. This logical reference is mapped

08 0672323842 CH06 3/20/02 9:31 AM Page 251

Entity EJBs to the physical resource through the auxiliary deployment descriptor agency_ea-sunj2ee-ri.xml

. There are also ejb-ref dependencies on various other beans; these are ultimately used to manage the relationships with the

Location

,

Skill

, and

Customer beans.

All of this information can be seen through the J2EE RI deploytool

GUI, as shown in

Figure 6.9.

F

IGURE

6.9

The deploytool

GUI displays deployment descriptor information graphically.

251

Good though the deploytool

GUI is, sometimes a command-line interface is required.

For example, you might want to automatically compile and then re-deploy your enterprise application in a project as part of a nightly build. Luckily, the deploytool also provides a command-line interface, so today you will deploy the Entity beans using this mechanism.

Under day06\agency

, the directory structure has been organized into a number of subdirectories, as shown in Table 6.2.

T

ABLE

6.2

Directory Structure under day06\agency

Subdirectory Contents

build a) buildAll

, which calls most of the other scripts in this directory, except b) deploy

, which deploys the enterprise application.

src

Java source for the client

, agency

, and data packages.

6

08 0672323842 CH06 3/20/02 9:31 AM Page 252

252 Day 6

T

ABLE

6.2

Continued

Subdirectory Contents

dd classes jar run

Standard XML deployment descriptors for the EJBs, for the enterprise application, and for the application clients. This directory also contains auxiliary deployment descriptors used by the J2EE RI to map the logical references in the standard deployment descriptors to the physical runtime environment managed by the J2EE RI container.

Compiled Java classes. The various compile* scripts in the build directory compile into this directory.

ejb-jar and ear

(Enterprise Application) archives. Generated by the various build* scripts in the build directory.

The scripts to run the various clients.

To deploy the enterprise application, start Cloudscape and the J2EE RI, and then type

> cd day06\agency\build

> buildAll

> deploy

It’s as simple as that!

One of the benefits of this approach is that bean developers, application assemblers and deployers who are familiar with the standard EJB deployment descriptors can modify the bean’s deployment configuration by simply editing the XML deployment descriptors directly—without having to get to grips with the J2EE RI GUI. Moreover, some of the names automatically assigned by the deploytool

GUI, such as the names of the ejb-jar files themselves, can be given more descriptive names. Consequently, the ejb-jar that contains the Entity beans is called data_entity_ejbs.jar

and its deployment descriptor is called data_entity_ejbs_ejb-jar.xml

.

If you look in the dd directory (or in the deploytool

GUI as shown in Figure 6.9), you can see that the case study separates out the Session beans and the Entity beans into two different ejb-jar s. The

Agency ejb-jar contains the same set of Session beans that you saw yesterday (although their implementation is different as you shall see shortly, they now delegate to the Entity beans), while the

Data ejb-jar has the new BMP Entity beans. How you choose to organize your enterprise application is up to you.

Client’s View

You’ve now learned how to specify, implement, and deploy BMP Entity beans, but how are they used? As you can probably imagine, the steps to obtain a reference to an Entity bean are similar to that for Session beans:

08 0672323842 CH06 3/20/02 9:31 AM Page 253

Entity EJBs 253

1. Look up the home interface for the Entity bean from JNDI.

2. To create a new entity instance, use the relevant home.create() method.

3. To locate an existing entity instance, use home.findByPrimaryKey() if the primary key is known, or some other home.findXxx() finder method to obtain a

Collection of matching entities.

4. For the returned local proxy to the Entity bean, invoke the business methods defined in the local interface of the bean.

The javax.ejb.EJBLocalObject

interface also defines a number of other methods that can be called by the client, and it is worth discussing the semantics of these briefly:

• The getPrimaryKey() method of

EJBLocalObject returns the primary key that identifies the bean.

Caution Note that because EJBLocalObject is also the super-interface for Session bean interfaces, this method can also be called when the client has a reference to the local proxy of a Session bean. However, because primary keys do not make sense for Session beans, an EJBException will always be thrown.

• If an Entity bean has both a local and a remote interface, and then

EJBObject.getPrimaryKey()

(from the remote proxy) and

EJBLocalObject.getPrimaryKey()

(from the local proxy) will both return objects that are equal (according to the definition the primary key’s definition of equals()

).

• The isIdentical() method can be used instead of comparing primary key classes to determine if two bean references refer to the same Entity bean. In other words, bean1.isIdentical(bean2) returns true if and only if bean1.getPrimaryKey().equals(bean2.getPrimaryKey())

.

One scenario that can occur is that a client can have a reference to an Entity bean, and then the bean could be deleted by some other client. This could occur either by an EJB application client that invokes remove() on the same Entity bean, or it could be a non-

EJB client that deletes the data directly from the underlying persistent data store. Either way, the original client will not be notified of this, and won’t detect this situation until it next invokes a method on the Entity bean. In this case, the client will receive a javax.ejb.NoSuchObjectLocalException

(a subclass of javax.ejb.EJBException

, in turn a subclass of java.lang.RuntimeException

) if accessing the Entity bean through its local interface.

6

08 0672323842 CH06 3/20/02 9:31 AM Page 254

254 Day 6

The javax.ejb.NoSuchObjectLocalException

caught by local clients is analogous to the java.rmi.NoSuchObjectException

that would be caught if accessing the Entity bean through its remote interface. Table 6.3 shows the table from yesterday detailing various other exceptions, supplemented with the exception classes received by local clients.

T

ABLE

6.3

System Exceptions Are Thrown in a Variety of Situations

What

Any bean

Event

Local Client

Receives

Throws javax.ejb.

javax.ejb

.

EJBException

(or any subclass)

EJBException

(or subclass)

BMP Entity Throws

NoSuchEntityException javax.ejb

.

bean

NoSuchEntity

Exception

Container

Remote Client

Receives

java.rmi

.

RemoteException java.rmi

.

NoSuchObject

Exception

When client invokes method on javax.ejb.

a reference to a bean that no

NoSuchObject longer exists

LocalException

When client calls a method without a transaction context javax.ejb.

TransactionRequired

LocalException

When client has insufficient security access javax.ejb.

AccessLocal

Exception

When transaction needs to be rolled back java.rmi.

NoSuchObject

Exception javax.transaction.

TransactionRequired

Exception java.rmi.

AccessException javax.ejb.

TransactionRolled javax.transaction.

TransactionRolled

Back LocalException Back Exception

As you can see, the EJB Specification makes some attempt at a naming standard so that the models are as similar as possible for local and remote clients.

Session Beans Revisited

The case study for today has the same set of Session beans as yesterday, and their interfaces are the same. However, their implementation is quite different, because they delegate all database interactions to the Entity bean layer.

08 0672323842 CH06 3/20/02 9:31 AM Page 255

Entity EJBs 255

As an example, Listing 6.13 shows the original updateDetails() method in the stateful

AdvertiseJob bean. The

AdvertiseJob bean provides services for managing jobs.

L

ISTING

6.13

AdvertiseJobBean.updateDetails()

Without an Entity Bean Layer

1: package agency;

2:

3: import java.util.*;

4: import javax.ejb.*;

5: import java.sql.*;

6: // imports omitted

7:

8: public class AdvertiseJobBean extends SessionBean

9: {

10: public void updateDetails(String description,

➥String location, String[] skills) {

11: if (skills == null) {

12: skills = new String[0];

13: }

14: Connection con = null;

15: PreparedStatement stmt = null;

16: try {

17: con = dataSource.getConnection();

18: stmt = con.prepareStatement(

➥”UPDATE JOB

➥ SET description = ?, location = ?

➥ WHERE ref = ? AND customer = ?”);

19: stmt.setString(1, description);

20: stmt.setString(2, location);

21: stmt.setString(3, ref);

22: stmt.setString(4, customer);

23: stmt.executeUpdate();

24: stmt = con.prepareStatement(

➥”DELETE FROM JobSkill

➥ WHERE job = ? AND customer = ?”);

25: stmt.setString(1, ref);

26: stmt.setString(2, customer);

27: stmt.executeUpdate();

28: stmt = con.prepareStatement(

➥”INSERT INTO JobSkill (job, customer, skill)

➥ VALUES (?, ?, ?)”);

29: for (int i = 0; i < skills.length; i++) {

30: stmt.setString(1, ref);

31: stmt.setString(2, customer);

32: stmt.setString(3, skills[i]);

33: stmt.executeUpdate();

34: }

35: this.description = description;

36: this.location = location;

6

08 0672323842 CH06 3/20/02 9:31 AM Page 256

256 Day 6

L

ISTING

6.13

Continued

37: this.skills.clear();

38: for (int i = 0; i < skills.length; i++)

39: this.skills.add(skills[i]);

40: }

41: catch (SQLException e) {

42: error(“Error updating job “ + ref + “ for “ + customer, e);

43: }

44: finally {

45: closeConnection(con, stmt, null);

46: }

47: }

48: }

Listing 6.14 shows the updated version, delegating the hard work to the

Job bean:

L

ISTING

6.14

AdvertiseJobBean.updateDetails() with an Entity Bean Layer

1: package agency;

2:

3: import java.util.*;

4: import javax.ejb.*;

5: import data.*;

6: // imports omitted

7:

8: public class AdvertiseJobBean extends SessionBean

9: {

10: private JobLocal job;

11: public void updateDetails(String description,

➥String locationName, String[] skillNames) {

12: if (skillNames == null) {

13: skillNames = new String[0];

14: }

15: List skillList;

16: try {

17: skillList = skillHome.lookup(Arrays.asList(skillNames));

18: } catch (FinderException ex) {

19: error(“Invalid skill”, ex); // throws an exception

20: return;

21: }

22: LocationLocal location = null;

23: if (locationName != null) {

24: try {

25: location = locationHome.findByPrimaryKey(locationName);

26: } catch (FinderException ex) {

27: error(“Invalid location”, ex); // throws an exception

28: return;

29: }

30: }

08 0672323842 CH06 3/20/02 9:31 AM Page 257

Entity EJBs 257

L

ISTING

6.14

Continued

31: job.setDescription(description);

32: job.setLocation(location);

33: job.setSkills(skillList);

34: }

35: // code omitted

36: }

The updated version is much more object-oriented; the knowledge of the database schema has been encapsulated where it rightfully belongs—in the Entity bean layer.

All this means that the

AdvertiseJob bean no longer has any dependencies on the jdbc/Agency DataSource

. On the other hand, it does now have dependencies on several of the Entity beans. These are defined using ejb-local-ref elements in the deployment descriptor. The relevant portion of the

AdvertiseJob deployment descriptor

( agency_session_ejbs_ejb-jar.xml

file in the dd directory) is shown in Listing 6.15:

L

ISTING

6.15

AdvertiseJob

Bean’s Reference to the Entity Beans

1: <ejb-local-ref>

2: <ejb-ref-name>ejb/SkillLocal</ejb-ref-name>

3: <ejb-ref-type>Entity</ejb-ref-type>

4: <local-home>data.SkillLocalHome</local-home>

5: <local>data.SkillLocal</local>

6: <ejb-link>data_entity_ejbs.jar#SkillBean</ejb-link>

7: </ejb-local-ref>

8: <ejb-local-ref>

9: <ejb-ref-name>ejb/LocationLocal</ejb-ref-name>

10: <ejb-ref-type>Entity</ejb-ref-type>

11: <local-home>data.LocationLocalHome</local-home>

12: <local>data.LocationLocal</local>

13: <ejb-link>data_entity_ejbs.jar#LocationBean</ejb-link>

14: </ejb-local-ref>

15: <ejb-local-ref>

16: <ejb-ref-name>ejb/JobLocal</ejb-ref-name>

17: <ejb-ref-type>Entity</ejb-ref-type>

18: <local-home>data.JobLocalHome</local-home>

19: <local>data.JobLocal</local>

20: <ejb-link>data_entity_ejbs.jar#JobBean</ejb-link>

21: </ejb-local-ref>

22: <ejb-local-ref>

23: <ejb-ref-name>ejb/CustomerLocal</ejb-ref-name>

24: <ejb-ref-type>Entity</ejb-ref-type>

25: <local-home>data.CustomerLocalHome</local-home>

26: <local>data.JobLocal</local>

27: <ejb-link>data_entity_ejbs.jar#CustomerBean</ejb-link>

28: </ejb-local-ref>

6

08 0672323842 CH06 3/20/02 9:31 AM Page 258

258 Day 6

Note the ejb-link reference, which names the bean that implements the required interfaces. This notation is used rather than a JNDI name because JNDI can (potentially) refer to remote EJBs, whereas local EJBs must—by definition—be deployed on the same server.

Patterns and Idioms

Yesterday, you saw some patterns and idioms that apply to writing Session beans. In this section, you will see some more that relate to BMP Entity beans and local interfaces. If you are impatient to get onto the exercise, feel free to skip this section and revisit later.

Interfaces, Façades, and State

This is a pattern that relates mostly to Session beans, but is discussed today rather than yesterday because it uses local interfaces.

While Session beans can provide both a remote and a local interface, you’ll only want to provide one or the other more often than not. Generally, you should aim to have a small number of Session beans that offer a remote interface, and the remainder will be local

“helper” beans. In design pattern terms, the remote Session beans create a Façade. This pattern is discussed more fully on Day 18, “Patterns.”

The Façade beans will likely be stateful. Certainly, the helper beans should not be stateful, because chaining stateful objects is poor design, as noted yesterday.

Use Local Interfaces for Entity Beans

Entity beans should only ever be accessed through a local interface, and there are some very good reasons for this.

First, accessing Entity beans through a remote interface implies network traffic and the cost of cloning serializable objects. When the client runs in the same JVM as the Entity bean, a local interface eliminates cost.

This ties in with the previous discussion on Session bean interfaces and façades. Session beans provide a remote interface to the enterprise application, so Session beans should act as a front-end to Entity beans. Some people think of this as the verb/noun paradigm—the Session beans are the verbs (the doing end of the application) and the

Entity beans are the nouns (the reusable business objects within some domain). Defining only local interfaces to Entity beans effectively enforces this pattern.

Second, finder methods of Entity beans—already expensive to use—become even more so when the client is remote. This was remarked on earlier today.

08 0672323842 CH06 3/20/02 9:31 AM Page 259

Entity EJBs 259

Lastly, and perhaps most significantly, local interfaces are the cornerstone of containermanaged relationships (CMR)—part and parcel of container-managed persistence. You’ll be learning all about this tomorrow. If you build your BMP beans using local interfaces, you provide a migration path to implementing those beans using CMP in the future.

Dependent Value Classes

Under BMP, the Entity bean’s state can be represented in any way that is appropriate.

Moreover, the bean can persist this state, in any way it wants.

Sometimes, a bean’s state will be simple enough (that is, just a set of scalar fields) that it will correspond to a row in an RDBMS table. More often though, some of those fields will be vector, and they might even include some complex data, such as a map or photo and so on.

One simple solution to persisting such objects is to ensure that the fields of the bean are either scalar primitives or are a reference to a serializable object, an instance of what are sometimes called dependent value classes. The structure of that object can be as complex as needed, so long as it is serializable. The scalar fields are stored in regular columns in the RDBMS table, and the serializable object is stored as a binary large object (BLOB).

For example, the

Job bean is mapped to both the

Job and

JobSkill table in the case study, accessing both in its ejbLoad() and ejbStore() methods. The

JobBean

.

skills field is a

List of

SkillLocal references.

An alternative design would be to store the skills list as a BLOB in the database. The

Job table would be redefined to be something like the following: create table Job

(ref varchar(16), customer varchar(16), description varchar(512),

) location varchar(16), skills long varbinary -- store a BLOB in Cloudscape

There is then a one-to-one mapping between an instance of the

Job

Entity bean and a row in the

Job table. One slight complication is that because the

JobBean.skills

field contains references to

SkillLocal references, which are not necessarily serializable, the skills variable would not be serializable either. So, in the ejbStore() method, a

List of skill names (that is, primary key to each

SkillLocal

) would be created and saved to the database. This

List of skill names is the “dependent value class.” stmt = con.prepareStatement(

➥”UPDATE Job

➥ SET description = ?, location = ?, skills = ?

➥ WHERE ref = ? AND customer = ?”);

6

08 0672323842 CH06 3/20/02 9:31 AM Page 260

260 Day 6 stmt.setString(1, description); if (location != null) { stmt.setString(2, location.getName());

} else { stmt.setNull(2, java.sql.Types.VARCHAR);

}

List skillNameList = new ArrayList(); for(Iterator iter = this.skills.iterator(); ) { skillNameList.add( ( (SkillLocal)iter.next() ).getName() );

} stmt.setBlob(3, skillNameList); stmt.setString(4, ref); stmt.setString(5, customerObj.getLogin()); stmt.executeUpdate();

Conversely, in the ejbLoad()

, the

List of skill names would be converted back to a

List of

SkillLocal references. The

SELECT statement would be as follows: stmt = con.prepareStatement(

“SELECT description,location,skills FROM Job WHERE ref = ? AND customer = ?”); and the processing of the result set would be this.description = rs.getString(1);

String locationName = rs.getString(2); this.location = (locationName != null)?

➥locationHome.findByPrimaryKey(locationName):null;

List skillNameList = (List)rs.getBlob(3); this.skills = skillHome.lookup(skillNameList);

The skillHome.lookup() home method of the

Skill bean does the actual conversion from name to

SkillLocal

. (In fact, this method is actually used in

JobBean

’s ejbLoad() method, so you can check out the code yourself).

This approach can substantially reduce the coding effort, although you should also be aware of some of the downsides:

• First, the BLOB field is effectively atomic; even if just a small piece of information is changed (for example, a new skill is added), and then entire BLOB must be replaced.

• Furthermore, information previously easily accessible can now only be accessed through a single route. In the previous example, the data that was previously in the

JobSkill table is now buried within the

Job.skills

field. It is no longer possible to perform an efficient SQL

SELECT to find out which jobs require a certain skill.

Instead, such a query will involve instantiating and then querying every

Job bean instance.

08 0672323842 CH06 3/20/02 9:31 AM Page 261

Entity EJBs 261

• Last, the data in the persistent data store is stored in Java’s own serializable format.

While this is a well-defined structure, it nevertheless makes it non-trivial for non-

Java clients to access this data.

Note It is possible to serialize Java classes in a custom-defined manner (for example, as an XML string) by either providing a readObject() and writeObject() method or by implementing java.io.Externalizable

and implementing readExternal() and writeExternal() methods. However, such an approach would probably involve more development effort than had been saved by using a BLOB to store the dependent value class.

Because dependent value classes are serializable, it is also possible to use them as the return types of accessor methods (getters and setters) of the interface. Indeed, prior to the introduction of local interfaces in EJB 2.0, this was a recommended pattern to minimize network traffic across the Entity bean’s remote interface. However, provided that only local interfaces are provided (and especially if using CMP), there is nothing wrong with providing fine-grained access to the values of the Entity bean. In effect, this design pattern has been deprecated with EJB 2.0.

Self-Encapsulate Fields

In the case study BMP beans, the private fields that represent state are accessed directly within methods. For example, the following is a fragment of the

JobBean.ejbCreate() method: public JobPK ejbCreate (String ref, String customer) throws CreateException {

// database access code omitted this.ref = ref; this.customer = customer; this.description = description; this.location = null; this.skills = new ArrayList();

// further code omitted

}

Some OO proponents argue that all access to fields should be through getter and setter accessor methods, even for other methods of the class. In other words, the principle of encapsulation should be applied everywhere. Using such an approach, the ejbCreate() method would be as follows:

6

08 0672323842 CH06 3/20/02 9:31 AM Page 262

262 Day 6 public JobPK ejbCreate (String ref, String customer) throws CreateException {

// database access code omitted setRef(ref); setCustomer(customer); setDescription(description); setLocation(null); setSkills(new ArrayList());

// further code omitted

}

Some people find this overly dogmatic, and, indeed, the code in the case study takes the more direct approach. However, you may want to consider self-encapsulation because it makes BMP beans easier to convert to CMP. As you will see tomorrow, all accessing to the Entity bean’s state must be through accessor methods.

Don’t Use

Enumeration for Finders

The EJB 1.0 specification was introduced before J2SE 1.2, so the specification allowed finder methods that returned many instances to return java.util.Enumeration

instances. For backward compatibility, the EJB 2.0 specification still supports this, but you should always use java.util.Collection

as the return type for finder methods returning more than one Entity bean instance.

Acquire Late, Release Early

In conventional J2SE programs, the idiom usually is to connect to the database at the start of the program when the user logs in, and only disconnect when the user logs out.

Holding onto the open database connection while the user logs in substantially improves performance; database connections are relatively expensive to obtain. So, for J2SE programs, the mantra is “Acquire early, release late.”

With J2EE programs, things are inverted. The database connection should be obtained just before it is required, and closed immediately after it has been used. In other words,

“Acquire late, release early.” This is shown in the

Job bean, as shown in Listing 6.16.

L

ISTING

6.16

Acquire Late, Release Early, as Shown in

JobBean

1: package data;

2:

3: import javax.ejb.*;

4: import java.sql.*;

5: // imports omitted

6:

08 0672323842 CH06 3/20/02 9:31 AM Page 263

Entity EJBs 263

L

ISTING

6.16

Continued

7: public class JobBean implements EntityBean

8:

9: public void ejbLoad() {

10: JobPK key = (JobPK)ctx.getPrimaryKey();

11: Connection con = null;

12: PreparedStatement stmt = null;

13: ResultSet rs = null;

14: try {

15: con = dataSource.getConnection();

16: stmt = con.prepareStatement( … );

17:

18: // SQL code omitted

19:

20: }

21: catch (SQLException e) {

22: error(“Error in ejbLoad for “ + key, e);

23: }

24: catch (FinderException e) {

25: error(“Error in ejbLoad (invalid customer or location) for “

➥ + key, e);

26: }

27: finally {

28:

closeConnection(con, stmt, rs);

29: }

30: }

31: // code omitted

32: }

The reason that this works is because the database connection is obtained from a javax.sql.DataSource

(line 15) in a J2EE environment, rather than using the java.sql.DriverManager.getConnection() method. Obtaining connections from

DataSource s is not expensive in performance terms because they are logical connections, not physical connections. When such a connection is obtained, it is merely obtained from a connection pool, and when it is “closed,” it is simply returned back to the connection pool.

Indeed, using the J2SE idiom of acquire early, release late (for example, by obtaining a connection in setEntityContext() and releasing it in unsetEntityContext()

) can adversely affect performance, because every instantiated bean would have its own database connection. This may well reduce application throughput because the memory resources of both the EJB container and the database server would be increased to handle many open database connections. In comparison, the J2EE idiom means that the number of database connections open is no more than the number of methods concurrently executing.

6

08 0672323842 CH06 3/20/02 9:31 AM Page 264

264 Day 6

Business Interface Revisited

Yesterday, you learned about the business interface idiom, whereby the business methods are pulled out into a separate interface such that the bean itself can implement this interface. This principle can equally be applied to Entity beans using local interfaces, as shown in Figure 6.10.

F

IGURE

6.10

Business interfaces can be applied to Entity beans with local interfaces.

interface

EJBLocalHome

remove interface

EJBLocalObject

getEJBLocalHome getPrimaryKey remove isIdentical interface

data.JobBus

getRef getCustomer getCustomerObj setDescription getDescription setLocation getLocation getSkills setSkills interface

data.JobLocalHome

create findByPrimaryKey findByCustomer findByLocation deleteByCustomer interface

data.JobLocal

attributes, EJB lifecycle methods and private helper methods have been hidden.

data.JobBean

getRef getCustomer getCustomerObj getDescription setDescription getLocation setLocation getSkills setSkills

There is one difference when applying this technique to beans that only have local interfaces; there is no longer any need for the methods of the business interface to throw

RemoteException

, because the local interface of the bean (

JobLocal in Figure 6.10) is not itself remote. Even so, the bean still does not implement its local interface because the methods of the

EJBLocalObject interface are there to be implemented by the local proxy object, not by the bean.

Gotchas

The following is a quick checklist of “gotchas” to help you with your implementation:

• Primary keys must be immutable. In other words, it is not possible to change the value of a primary key for an entity once assigned (see EJB specification, section

10.3.5).

Of course, there is nothing to prevent you from directly changing the data in the underlying persistent data store (for example, with an SQL

UPDATE statement). But you will need to do this with the EJB container offline or otherwise at rest.

08 0672323842 CH06 3/20/02 9:31 AM Page 265

Entity EJBs 265

• Sometimes, Entity beans interact with non-data store resources. An example might be a client java.net.Socket

or perhaps a subscription to a JMS topic (covered more on Days 9, “Java Messaging Service,” and Day 10, “Message-Driven

Beans”). These resources will need to be acquired in both ejbActivate()

(for an existing bean) and also for ejbCreate()

(if the bean has just been created).

Similarly, resources should be released in both ejbPassivate() and ejbRemove()

.

This is because a bean being deleted will not be passivated first.

• Finder methods can return

Collection s, but they can’t return

List s,

Set s, or

Map s.

However, this capability is planned for future versions of the EJB specification.

• If you have two bean references, note that the value of bean1.equals(bean2) is unspecified, and that bean1 == bean2 is also unspecified. Moreover, hashCode() may differ for two references to the same underlying EJB. (All of these points are made in the EJB specification, section 9.8.)

The correct way to compare bean identity is to use bean.isIdentical() or to use the equals() method on the primary key classes.

• Beware of a reliance on pass-by-reference side-effects when using local interfaces.

Such a reliance would compromise portability.

Summary

Another long day, but you now have lots of good new material under your belt. You’ve learned that Entity beans represent persistent domain data with corresponding domain

(not application) logic. You’ve seen that the constituent parts of Entity beans are pretty much the same as Session beans, though Entity beans also require a primary key class that must be custom-developed if the key is composite.

You’ve also learned that there are two different ways to implement Entity beans, either using bean-managed persistence, whereby the persistence code (JDBC, for example) resides within the bean code, or using container-managed persistence. You now know the lifecycle for BMP beans and how to implement such beans.

You saw that the EJB specification allows local interfaces to be defined for EJBs, as well as or instead of remote interfaces, and saw several good reasons why Entity beans should always use local interfaces.

Onto deployment, you now know that the J2EE RI allows EJBs can be deployed using a command line interface. A deeper understanding of the XML deployment descriptor is needed, but the process for deployment is (arguably) more portable and faster.

Finally, you’ve learned numerous design techniques, patterns, and idioms that should set you up for designing and implementing Entity beans effectively.

6

08 0672323842 CH06 3/20/02 9:31 AM Page 266

266 Day 6

Q&A

Q What do Entity beans represent?

A Entity beans represent persistent data that can be accessed and shared by many clients over time.

Q What are the two types of Entity beans?

A The two types of Entity beans are BMP and CMP.

Q Why are local interfaces preferable to remote interfaces for Entity beans?

A Local interfaces perform better because there is no network traffic when calling a bean through its local interface, and there is also no need to clone serializable objects. They are also the basis for CMP.

Q How does a BMP Entity bean know what its primary key is?

A It can be passed as an argument of ejbCreate()

, it could be generated by the

RDBMS, it could be generated by some other bean, or it might be generated as a pseudo-random value using an algorithm that guarantees uniqueness.

Q Which two methods should the primary key class implement?

A The primary key class should implement the hashCode() and equals() methods.

Exercises

The exercise starts with a version of today’s case study that has a complete set of Session beans, but an incomplete set of Entity beans. Where there is no Entity bean, the Session bean performs direct SQL. The state of affairs is shown in Table 6.4.

T

ABLE

6.4

Case Study Session and Entity Beans

Session

Bean

Agency

Advertise

Functional

Area

Applicants

Customers

Locations

Skills

Job

Functions

create, delete, find all

Implementation/

Delegation

Direct SQL create, delete, find all

Customer bean add, get details, get plural, remove

Location bean add, get details, get plural, remove

Skill bean create, delete, get plural

Job bean

AdvertiseJob Job

Register

Customer

Applicant get details, update get details, update get details, update

Customer bean

Skill bean,

Location bean

Direct SQL

08 0672323842 CH06 3/20/02 9:31 AM Page 267

Entity EJBs 267

The exercise is to implement an

Applicant

Entity bean and to update the

Agency and

Register

Session beans to use this new Entity bean.

The

Applicant bean should map itself to the

Applicant and

ApplicantSkill tables and define the following fields:

• login

This is the primary key for the

Applicant

Entity bean.

• name

Simple scalar field.

• email

Simple scalar field.

• summary

Simple scalar field.

• location

Should be a reference to a

LocationLocal to ensure referential integrity.

• skills

Should be a collection of

SkillLocal references to ensure referential integrity.

You should find that the structure of your new bean shares many similarities with the

Job

Entity bean. One difference will be the primary key. The

Job bean required a

JobPK because it had a composite primary key. For your

Applicant bean, you should not need to develop a custom primary key class because applicants will be identified simply by their login

—a simple

String

.

The

ApplicantLocalHome and

ApplicantLocal interfaces have already been provided; note their similarity to

JobLocalHome and

JobLocal

.

The directory structure of day06\exercise is the same as yesterday:

• src

The source code for the EJBs and clients.

• classes

Directory to hold the compiled classes; empty.

• dd

Holds XML deployment descriptors.

• build

Batch scripts (for Windows and UNIX) to compile the source and to build the EAR files into the jar directory.

• jar

Holds agency.ear

: the agency enterprise application. Also holds agencyClient.jar

, the client-side JAR file optionally generated when deploy

EAR. This directory also holds some intermediary JAR files that are used only to create the previous two jar files.

• run

Batch scripts (for Windows and UNIX) to run the JARs. Use the files in the jar directory.

In the detailed steps that follow, note one difference from yesterday is that today you will be defining and configuring the EJB as part of the enterprise application by directly editing the XML deployment descriptors in the dd directory. If you feel uneasy about doing this, there is nothing to prevent you from making the changes through the GUI.

6

08 0672323842 CH06 3/20/02 9:31 AM Page 268

268 Day 6

Do note, however, that the build scripts that create the agency.ear

file do require that the

ApplicantBean.java

source exists (even if its implementation is incomplete).

The steps you should follow are:

1. Locate the

ApplicantBean.java

file within day06\exercise\src\data

. This should have an empty implementation.

2. Implement

ApplicantBean to support the

Applicant and

ApplicantLocalHome interfaces supplied. Base your implementation on

JobBean

, if you want.

3. Next, modify the

AgencyBean

Session bean. The findAllApplicants()

, createApplicant()

, and deleteApplicant() methods should instead delegate to

ApplicantHome

.

4. Now update the

RegisterBean

Session bean. In its ejbCreate() method, it should obtain a reference to an underlying

Applicant

Entity bean. Each of the business methods should then delegate to this applicant. If you want something to work from, look at the approach adopted by the

AdvertiseJob

Session bean, delegating to an instance of

Job

Entity bean.

5. Update the data_entity_ejbs_ejb-jar.xml

deployment descriptor in the dd directory; again, cloning and adapting the

Job bean entries will be a good start.

6. Update the agency_session_ejbs_ejb-jar.xml

deployment descriptor to indicate the new dependencies of the

Agency and

Register

Session beans. Both will depend on

ApplicantLocal

; you should also find that

Register depends on

SkillLocal and

LocationLocal

(to call the business methods of

Applicant

).

7. The buildDataEntityEjbs script already references

ApplicantBean

, so there is no need to change it. This causes your classes to be added to the resultant data_entity_ejbs.jar ejb-jar file.

8. Now, build the jar\agency.ear

enterprise application by using build\buildAll

.

Load the resultant EAR file into deploytool

, and check that the EJB is correctly defined. If it is not, either make the appropriate changes and run buildAll or make the changes through the deploytool

GUI itself. Then, save the deployment descriptors into the dd directory.

9. Your agency.ear

file is not quite ready to deploy, because the vendor-specific mapping information has not yet been specified. This is most easily generated by deploying the enterprise application from deploytool

. The wizard that then appears will ensure that you have the opportunity to indicate any missing information. Then, test by using the

AllClients client, invoked using the run\runAll script.

08 0672323842 CH06 3/20/02 9:31 AM Page 269

Entity EJBs

10. Optionally, you may want to save the auxiliary deployment descriptor to dd\agency_ea-sun-j2ee-ri.xml

. If you do this, you will be able to build and deploy the application directly from the command line using build\buildAll and build\deploy

, respectively. However, to obtain the auxiliary deployment descriptor, you will need to manually load the agency.ear

file (from the previous step) into WinZip or equivalent and extract the auxiliary deployment descriptor; the deploytool

GUI does not provide any direct mechanism.

Good luck. A working example can be found in day06\agency

(with a correct auxiliary deployment descriptor).

269

6

08 0672323842 CH06 3/20/02 9:31 AM Page 270

09 0672323842 CH07 3/20/02 12:20 PM Page 271

W

EEK

1

D

AY

7

CMP and EJB QL

Yesterday, you learned how to specify, implement, and deploy bean-managed persistence (BMP) Entity beans. Today, you will learn

• How to specify, implement, configure and deploy CMP Entity beans

• How to use EJB Query Language (EJB QL)

• How to define relationships between CMP Entity beans

Overview of Container-Managed

Persistence

The EJB specification provides for two different ways of implementing Entity beans. The first approach, covered yesterday, is for the bean provider to embed the persistence logic within the bean itself—hence the name bean-managed persistence or BMP. The second is for the container vendor to provide that logic, either generated by the EJB container vendor’s deployment tools or as part of the EJB container itself. Entity beans built this way are called CMP Entity beans.

09 0672323842 CH07 3/20/02 12:20 PM Page 272

272 Day 7

Note CMP Entity beans have always been part of the EJB specification, first in EJB

1.0 and then with some minor refinements in EJB 1.1. The changes to CMP

Entity beans in EJB 2.0 are substantial—so substantial, in fact, that CMP 1.1

Entity beans are not forward compatible with EJB 2.0.

To deal with this, the EJB specification actually provides two different ways to write CMP Entity beans. The first is the legacy 1.1 approach; beans that are written this way indicate it using an entry in their deployment descriptor. The second is using the new and far more powerful approach introduced in EJB 2.0.

Today, you will be learning only about the new EJB 2.0 approach.

The “anatomy” of CMP Entity beans is very much the same as BMP Entity beans:

• They have a local-home (or remote home) interface that defines the create methods, the finder methods, optional home methods, and a remove method.

• They have a local (or remote) interface that defines the business methods of the bean.

• Obviously, they have the bean class itself that implements methods corresponding to the previously mentioned interfaces, and implements the lifecycle methods inherited from javax.ejb.EntityBean

.

• Finally, they may have a primary key class (and must have one if the primary key is composite).

However, there are some differences. The responsibilities of the bean in the lifecycle methods are different, because there is no longer any requirement to persist the bean’s state. This raises the question as to when the state is persisted by the container, because it could be done either before the lifecycle method is called or after. There are changes in the interactions between the container and the bean, as you will see.

Another significant difference is the finder methods. Under BMP, the bean provider writes the appropriate finder methods that interact with the persistent data store. Under

CMP, the container will do this work, so there is no longer any need to implement the finder methods in the bean. However, the bean provider must still specify the nature of the query to be performed to obtain the correct data from the data store. This is done using EJB Query Language (EJB QL), appearing in the bean’s deployment descriptor.

EJB QL shares many similarities with ANSI SQL 92, so you should not have too many difficulties picking it up.

09 0672323842 CH07 3/20/02 12:20 PM Page 273

CMP and EJB QL 273

Just as tables in relational databases have relationships, so too do Entity beans. You saw this yesterday with the relationship between the

Job bean, which had relationships with the

Skill

,

Location

, and

Customer beans. Under BMP, the bean provider must write the code that maintains all of these relationships explicitly. If CMP is used, these relationships can be declaratively defined using container-managed relationships, or CMR.

Again, the declarations of these associations are in the bean’s deployment descriptor.

Relationships between Entity beans are intrinsically fine-grained. For example, a manyto-many relationship between

Job and

Skill

(indicating which skills are needed for such-and-such a job) would involve dealing with many ( job,skill

) tuples in the case study stored in the

JobSkill table. You know that Entity beans can have either a local or a remote interface, and that it’s good practice to only ever interact with an Entity bean through its local interface because this reduces network traffic. Because the performance cost of maintaining a fine-grained relationship across the network would be too severe, the EJB specification requires that container-manager relationships between Entity beans are defined only through local interfaces. Indeed, one of the primary reasons for the introduction of local interfaces in the EJB specification was to make CMR feasible.

N-tier Architecture (Revisited Again) and CMP Fields

CMP has an impact on the n-tier architecture that you seen have on several previous days. Figure 7.1 shows an update of a figure that you saw yesterday.

F

IGURE

7.1

CMP Entity beans are split into two components.

«swing» user interface

«session EJB» application logic

«entity EJB» domain logic

«bean provider»

CMP bean

«database» persistence layer

«servlet» user interface

«EJB container»

CMP implementation

«servlet» user interface

«session EJB» application logic

There are still four tiers to the architecture—namely, the interface, application, domain, and persistence layers. However, with CMP, the Entity beans split into two components.

The first component is provided by you, the bean provider. This defines the bean’s local-home and local interfaces, but the implementation of the bean itself is incomplete.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 274

274 Day 7

It provides a full implementation of the business methods, but there is no implementation of the accessor methods for the bean’s state. Indeed, you will see that the methods are marked as abstract

. The concrete implementation of the CMP bean is completed by the EJB container provider. This component has dependencies on both the bean provider’s bean, and—of course—on the persistence layer. The first dependency is because the concrete implementation uses the bean provider’s abstract bean class as its superclass; in other words, it extends from the CMP bean. The second dependency is because the implementation of the bean performs appropriate data store calls.

You may recognize this design as an instance of the Template design pattern. The abstract CMP bean provided by the bean provider is a template, defining certain mandatory “hook” methods—namely, the accessor methods. The implementation of these hooks is provided by the EJB container in terms of the concrete CMP implementation.

Listing 7.1 shows this for the

Job

Entity bean. This bean defines a pair of accessor methods (the getter and setter) for each of its fields— ref

, customer

, description

, location

, and skills

.

L

ISTING

7.1

The

JobBean

’s Fields Are Implied by the Presence of These Abstract Accessor

Methods

1: package data;

2:

3: import javax.ejb.*;

4: // imports omitted

5:

6: public abstract class JobBean implements EntityBean {

7:

8: public abstract void setRef(String ref);

9: public abstract String getRef();

10:

11: public abstract void setCustomer(String customer);

12: public abstract String getCustomer();

13:

14: public abstract String getDescription();

15: public abstract void setDescription(String description);

16:

17: public abstract LocationLocal getLocation();

18: public abstract void setLocation(LocationLocal location);

19:

20: public abstract Collection getSkills();

09 0672323842 CH07 3/20/02 12:20 PM Page 275

CMP and EJB QL 275

L

ISTING

7.1

Continued

21: public abstract void setSkills(Collection skills);

22:

23: // code omitted

24: }

Each of these accessors is implemented by the concrete CMP implementation. The actual instance variables are effectively part of the subclass’ implementation, ultimately populated from the persistent data store.

Caution This naming scheme throws up a very curious restriction, specifically that cmp-fields must not start with a capital letter. Thus, customer and even cUSTOMER are valid names, but CUSTOMER would not be. This is because the methods capitalize the cmp-fields, and one cannot capitalize a capital!

Another way of thinking about this design is in terms of vertical delegation. The Session beans can call the accessor methods on methods defined in the superclass, but the actual method that is invoked is the implementation defined in the subclass. The superclass

CMP bean “delegates” vertically down to its subclass CMP implementation.

This design gives several advantages. One immediate advantage of this pattern is that it gives the EJB container (through the concrete bean implementation) much more control over populating the bean’s state, without compromising good OO principles. For example, it is up to the EJB container whether it chooses to obtain all of the bean’s state when the bean is activated or created (eager loading), or whether it chooses to fetch the bean’s state from the persistent data store as and when needed (lazy loading). Indeed, the concrete implementation may adopt some half-way house, eagerly loading all the scalar data pertaining to the bean but lazily loading data corresponding to bean relationships.

Indeed, this advantage is pointed out in the EJB specification (section 10.4.2.1).

Another advantage is that the EJB container only need persist the bean’s state to the data store when the bean’s state has changed. The concrete implementation can keep track of the before-and-after versions of the bean’s state and compare them to see if any have changed as the result of a business method invocation. If a read-only accessor method (a

“getter” method) is called, there would be no change in state and so the concrete implementation need not perform an unnecessary update to the data store. Taking this on one further stage, when the bean’s state is changed, the EJB container need only update those fields that have changed and can ignore fields that have not changed. This reduces network traffic between the EJB container and the persistent data store.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 276

276 Day 7

One final advantage worth mentioning is that some value-add services, such as optimistic locking, can be implemented by EJB container vendors more straight-forwardly.

A Quick Word about the Case Study Database

As you go through the remaining topics for today, you may well want to load up and run the case study code. Before you can do this, the

Agency database needs to be modified to support CMP. This is because the J2EE RI container generates its own SQL schema to store the Entity beans’ data.

Figure 7.2 shows the revised schema for the case study, as generated by the J2EE RI container.

F

IGURE

7.2

The case study database schema changes under CMP.

CustomerBeanTable

login address1 address2 email name advertises

JobBeanTable

ref customer CustomerBeanTable.login(FK) description requires at location for

LocationBeanTable

name description

Z

JobBean_location_LocationBean_Table

_JobBean_ref JobBeanTable.ref(FK)

_JobBean_customer JobBeanTable.customer(FK)

_LocationBean_name LocationBeanTable.name(FK)

ApplicantBeanTable

login email name summary workplace for works at

Z

ApplicantBean_location_LocationBean_Table

_ApplicantBean_login ApplicantBeanTable.login(FK)

_LocationBean_name LocationBeanTable.name(FK) has

JobBean_skills_SkillBean_Table

_JobBean_ref JobBeanTable.ref(FK)

_JobBean_customer JobBeanTable.customer(FK)

_SkillBean_name SkillBeanTable.name(FK)

SkillBeanTable

name description needed for capabilities of

ApplicantBean_skills_SkillBean_Table

_SkillBean_name SkillBeanTable.name(FK)

_ApplicantBean_login ApplicantBeanTable.login(FK)

The J2EE RI container can automatically create this schema when the Entity beans are deployed. However, because there is example data in the case study database, it is easier to run the provided utility to “upgrade” the database to support CMP. The

Agency

Session bean queries the Cloudscape RDBMS tables directly. To ensure that this continues to work, the utility also creates SQL views with the names of the old tables

(

JobSkill and so on) against the new tables.

09 0672323842 CH07 3/20/02 12:20 PM Page 277

CMP and EJB QL 277

The steps for converting the database to support CMP are as follows:

1. Shut down Cloudscape if it is running.

2. Back up the current (pre day 7) version of the Agency database, under

%J2EE_HOME%\cloudscape

.

Under Windows, you can do this using copy and paste, or from the command line type:

> cd %J2EE_HOME%\cloudscape

> xcopy Agency bmpAgency /I /E

Under Unix, you can do this using the following:

$ cd $J2EE_HOME/cloudscape

$ cp –r Agency bmpAgency

3. Restart Cloudscape.

4. Under the case study day07\Database directory, run the batch

CreateCMPAgency.bat

(Windows) or

CreateCMPAgency.sh

(Unix). This calls

CreateCMPAgency.java

(already compiled for you) which, in turn, creates the previous tables, populates the tables with the same sample data, and creates the views for backward compatibility.

5. When you have done this, you may want to shut down Cloudscape and then backup the CMP version of the database to a directory called cmpAgency

, using similar commands to those in step 2. That way, you can easily switch between the two different schemas. To reinstate either version, just delete the

Agency directory and then copy back either the bmpAgency or cmpAgency using xcopy

(Windows) or cp -r

(Unix).

CMP Entity Bean Lifecycle

The lifecycle for CMP Entity beans is substantially the same as BMP Entity beans, reflected in an almost identical diagram (see Figure 7.3).

This diagram differs from the lifecycle you saw yesterday in that there are no ejbFindXxx() methods for pooled beans. This is not to say that pooled CMP Entity beans do not perform finder methods; they do. However, the EJB container generates the actual code that performs this. There will be a finder method in the bean’s local-home interface, but not necessarily an equivalent ejbFindXxx() method in the bean itself (and certainly not in the code written by the bean provider).

7

09 0672323842 CH07 3/20/02 12:20 PM Page 278

278 Day 7

F

IGURE

7.3

The javax.ejb.EntityBean

lifecycle for CMP

Entity beans.

[pool too small]

/setEntityContext

[pool too large]

/unsetEntityContext

Pooled

/ejbActivate

/ejbCreate

Creating exit/^ejbLocalObject.new()

/ejbPostCreate

/ejbRemove

/ejbPassivate

Cached ejbLoad

/“business method” ejbStore

Note Where the implementation of the finder methods is will depend on the EJB container. It does seem likely that many vendors will choose to place the finder logic in the bean’s concrete implementation. An advantage of this approach (for the EJB container vendor) is that the rest of the EJB container need not differentiate between BMP and CMP Entity beans.

Yesterday, you saw the responsibilities of the bean provider for each of these lifecycle methods for the

Job bean when implemented using BMP. The implementation is somewhat simpler when using CMP, as shown next.

As for BMP Entity beans, the setEntityContext() and unsetEntityContext() methods must be implemented to look up any required resources (although the resources are likely to be different than those needed for BMP; in particular, the JDBC DataSource should not be needed).

Under BMP, the ejbCreate() method was responsible for persisting the newly created bean’s state to the persistent data store. Under CMP, the ejbCreate() method does not need to do this, but does still need to set the bean’s fields to the parameters passed in.

This can include generating a unique primary key value.

09 0672323842 CH07 3/20/02 12:20 PM Page 279

CMP and EJB QL 279

The BMP version of ejbRemove() was responsible for physically removing the bean’s state from the data store. The CMP version does not need to do this (and later on, you’ll see that this method actually has a null implementation).

Nevertheless, sometimes there may be occasions when work needs to be done in the ejbRemove() method. For example, you could imagine an Entity bean that has some subscribers interested in observing its changing state. One such Entity bean might be a headline news item publicized from Reuters. When this Entity bean is finally removed, it would notify those subscribers of the event.

On a more mundane level, the bean might want to prevent the delete from occurring if, for example, some referential integrity constraint would be violated by the bean being removed. In such cases, you need to know that the ejbRemove() is called before the container actually removes the data.

Next are the ejbLoad() and ejbStore() methods. Under BMP, these methods for the

Job bean were substantial, because they had to read/write the bean’s state to both the

Job and

JobSkill tables. Under CMP, unless there is any derived data to be maintained, these methods could well have an empty implementation.

The ejbActivate() and ejbPassivate() methods for a CMP Entity bean are pretty much identical to their implementations for a BMP Entity bean; after all, these methods have nothing to do with the persistent data store.

If you’ve been mentally (or manually) comparing this section with the previous section yesterday, you’ll note that yesterday there was discussion on the finder methods. There is no such discussion here because the finder methods will be generated automatically.

However, as the bean provider, you will need to indicate to the container the semantics of the finder queries. To do that, you must be familiar with EJB QL. But to understand EJB

QL queries of any complexity, you need first to understand container-managed relationships. CMR is discussed in the next section, followed then by EJB QL.

Container-Managed Relationships

Container-managed relationships (CMR) might possibly sound pretty daunting, and certainly from the EJB container vendor’s perspective, there could be some fairly complex activity happening behind the scenes. However, from the bean provider’s perspective

(that is, you), they are fairly straightforward and easy to use.

CMRs are defined declaratively through the deployment descriptor, underneath the relationships element. Therefore, container-managed relationships can only be defined between Entity beans that reside within the same local relationship scope (EJB specification,

7

09 0672323842 CH07 3/20/02 12:20 PM Page 280

280 Day 7 section 10.3.2). What this means in practice is that beans that have relationships must be deployed in the same ejb-jar file. You will be learning more about actually declaring

CMRs later today, in “Configuring a CMP Entity Bean.”

Note

The restriction that CMR can only be defined between EJBs deployed in the same ejb-jar file could possibly create problems. Some organizations maintain static reference data globally and replicate that data locally. This works because such data is often updated relatively infrequently.

By its nature, reference data is referenced (!), so one would expect relationships from domain-specific Entity beans up to cross-organizational reference beans. However, if the reference Entity beans are deployed separately from the domain-specific Entity beans (as would be likely), no such relationships can be assigned.

Relationship Types

CMR allows three different types of relationships to be defined between Entity beans:

• One-to-one

• One-to-many

• Many-to-many

The first two relationship types are to be expected, but the last is perhaps more unexpected if you are used to using RDBMS. In relational theory, it is not possible to create a many-to-many relationship directly; instead, a link (or association) table is required.

Indeed, such a table can be seen directly in the BMP version of the case study database; the many-to-many link between jobs and skills is captured in the

JobSkill table.

However, the EJB specification allows many-to-many links to be defined directly for

Entity beans, an immediate simplification over the RDBMS approach.

Note

Of course, most EJB containers—the J2EE RI included—will persist to RDBMS, so will require a link table in the physical schema. Indeed, the J2EE RI uses a link table even for one-to-many associations. You can see this if you look back to Figure 7.2. For example, the one-to-many link from

Job to

Location is captured through the snappily named

JobBean_location_LocationBean_Table table.

09 0672323842 CH07 3/20/02 12:20 PM Page 281

CMP and EJB QL 281

These relationship types actually refer to the maximum cardinality (also sometimes called multiplicity) of the related beans in the relationship. That is, saying that there is “a one-to-many relationship between

Location and

Job

” is shorthand for “the maximum number of

Location s that a

Job can be related to is one, and the maximum number of

Job s that a

Location can be related to is many.” There is also the question of minimum cardinality. In other words, is it necessary for a

Job to be related to any

Location

(or can it be related to none)? Equally, must a

Location have any

Job related to it?

The EJB specification answers this question implicitly by always allowing a minimum cardinality of zero. Hence, “one-to-many” also allows for none-to-many, one-to-none, one-to-many, and (trivially) none-to-none.

There are sometimes situations when a minimum cardinality of none is not acceptable.

For example, it might be the case that a

Job must always relate to a

Location

. (Actually, for the case study, this is not enforced except in principle). In these cases, it is up to the bean to do the appropriate validation. In other words, the

Job bean would only define a create() method that accepted a

Location bean reference, and if it provided a setLocation() accessor method in its interface, it would ensure that the supplied

Location reference was not null

.

A related question is, “What happens if a bean is removed?” Suppose that the

Job bean relates to a

Location

, and the

Location bean is deleted. The

Job bean will be left with a null reference. In relational terms, this is sometimes called a cascade null.

Suppose (again) that every

Job must always relate to a nonnull Location

. There are a number of options:

• The first, somewhat radical, option is to remove the related

Job beans—in other words, perform a cascade delete. CMR supports this directly (it is specified through the deployment descriptor) and will remove each

Job bean in turn.

• Second, the application can prevent the removal of the foreign key

Location bean from occurring. This would be done by implementing an appropriate check in the ejbRemove() lifecycle method.

• Another alternative would be to reassign every impacted

Job bean to some new

Location

. Again, the ejbRemove() method would need to do this work.

The second option is probably the most likely, so you should take care to do this type of minimum cardinality analysis to make sure that you do not unwittingly end up with beans that have null relationships when the semantics of the problem domain prohibit this from occurring.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 282

282 Day 7

Navigability

In addition to specifying multiplicity of the relationships, CMR also allows the navigability of the relationship to be defined. The navigability is either unidirectional or bidirectional.

Navigability is defined by indicating the name of the field that references the related bean. For example, in a many-to-one relationship between the

Job and

Location beans, indicating a field of location for the

Job bean means that there is navigability from

Job to

Location

. There may not necessarily be navigability in the opposite direction; that would depend on whether the

Location bean defines a field called jobs

.

Caution While the case study does not require bi-directional navigability either from

Location to

Job or from

Skill to

Job

, it does define navigability nevertheless. Otherwise, the code generated by the J2EE RI 1.3 deployment tools

(somewhat unfortunately) does not compile—not something to inspire confidence!

The term “field” (or more properly, cmr-field

) used here indicates the accessor methods for the virtual fields defined in the CMP superclass and implemented by the EJB container. The next section looks at these methods in more detail.

cmr-field s

By way of example, the location cmr-field for the

Job bean has accessor methods of getLocation() and setLocation()

, and the skills cmr-field has the accessor methods getSkills() and setSkills()

. The return type of these methods depend on the multiplicity of the relationship.

The relationship from

Job to

Location is many-to-one, so the methods that correspond to the location cmr-field are as follows: public abstract LocationLocal getLocation(); public abstract void setLocation(LocationLocal location);

09 0672323842 CH07 3/20/02 12:20 PM Page 283

CMP and EJB QL

Tip The same restriction on naming that applies to cmp-fields also applies to cmr-fields: the name must not start with a capital letter.

For single-valued cmr-field s, the return type for the getter and the parameter to the setter is the local interface of the related bean (

LocationLocal in this case). Yesterday, it was noted that local interfaces are the cornerstone of container-managed relationships; this shows why. Remote interfaces cannot be used in CMR.

Note

This isn’t recommended, but there is nothing in principle to prevent an

Entity bean with only a remote interface from having relationships with other Entity beans. However, the target Entity beans must themselves have a local interface, and the relationship will be unidirectional. The absence of a local interface in the source Entity bean prevents the related Entity beans navigating back to the Entity bean.

The

Job bean also has a relationship with the

Skill bean, this time many-to-many. Thus, the skills cmr-field corresponds to the following methods: public abstract java.util.Collection getSkills(); public abstract void setSkills(java.util.Collection skills);

This is a multi-valued cmr-field because a collection of values is returned, not just a single value. The collection returned here is a java.util.Collection

of references to the local interface of the related bean (

SkillLocal in this case). The EJB specification also allows for java.util.Set

s to be returned. The EJB specification does not currently allow

List s or

Map s to be returned from multi-valued cmr-field s, but does hint that they may be added in the future.

Note that the fields of a bean are either regular cmp-field s or they are cmr-field s (or they are just regular instance variables, not managed by the container at all). Put another way, cmp-field s cannot be defined that have references to other beans as their argument or return type; such fields must be defined as cmr-field s.

283

7

09 0672323842 CH07 3/20/02 12:20 PM Page 284

284 Day 7

Composite Primary Keys and Relationships

The

Job

Entity bean has a relationship with both the

Location bean and the

Customer bean. The getCustomer() method returns the name of a customer as a

String

, whereas the getLocation() method returns a reference to a

LocationLocal

. In other words, the former returns the name (the primary key) to a bean, and the latter returns the bean itself. So why the difference?

The reason is that the customer field is part of the primary key for the

Job bean, and appears in

JobPK

. Every public field in

JobPK must have a corresponding field in the bean itself.

If the

JobPK class defined its customer field to be a reference to a

CustomerLocal

, the

JobPK class could not be guaranteed to be serializable.

This shows up a very subtle area, not highlighted at all in the EJB specification. In the case study, there is a relationship between

Customer and

Job

, in that

Job s are identified by

Customer

. In other words, the primary key of

Job contains the primary key of the

Customer that “owns” that

Job

.

The case study does not define the one-to-many relationship between

Customer and

Job

.

If this had been done, a virtual field and corresponding accessor methods

(

{get/set}CustomerObj() methods) would need to have been defined. The problem that would then have arisen, however, is that potentially the name of the customer returned by getCustomer() may not correspond to the actual customer returned by getCustomerObj()

.

In commercial EJB containers, this problem can be resolved by mapping both the getCustomer() and getCustomerObj() methods to the same physical data in the persistent data store (the customer column in the

Job table). This prevents them from getting out of step (although even here, the EJB container would need to make the customerObj field read-only because allowing it to be changed would implicitly change the primary key of the

Job bean).

However, the J2EE RI container does not make the mapping of the Entity beans data to the physical schema explicit. While it might be possible to modify the implied mapping, it would be unclear (from an education standpoint) what was being done. For this reason, the case study does not define the

Customer

/

Job relationship. This is why, for example, the customerObj field is derived from the customer field, and is looked up in the

JobBean

’s ejbLoad() method.

Not only do the methods corresponding to the cmr-field return and accept only local and not remote interfaces to beans, they also cannot appear in the remote interface of the bean. This is not really surprising. After all, the return types and arguments to the methods corresponding to the cmr-field take only local interfaces of remote beans, so the client of the bean invoking the cmr-field methods must be local. You saw yesterday that there are very good reasons why remote interfaces are bad news for Entity beans; this is another reason not to provide a remote interface.

09 0672323842 CH07 3/20/02 12:20 PM Page 285

CMP and EJB QL 285

Note The other reason that cmr-field methods cannot appear in the remote interface is to prevent untoward network traffic. The performance cost of transporting large collections of references across the wire (even assuming those references were serializable) would be overwhelming.

Table 7.1 compares the use of cmp-field s and cmr-field s in interfaces.

T

ABLE

7.1

cmp-field s and cmr-field s and Interfaces

Feature cmp-field

Can appear in local interface

Yes

Can appear in remote interface

Can accept as parameters and return local references to beans

Yes

Not recommended though;

Entity beans should be accessed via remote clients.

No cmp-field s deal only with primitives (or serializable objects).

Can accept as parameters No and return remote references cmp-fields deal only with to beans primitives (or serializable objects).

cmr-field

Yes

No

Yes

No

Container managed relationships are defined only through local interfaces of beans.

The EJB specification also requires that the

Collection returned by a cmr-field method is only used in the same transaction context. You will be learning more about transactions tomorrow, but for now, just consider the following scenario. A Session bean could invoke a multi-valued cmr-field

’s getter method and receive back a

Collection

. It could then hold onto this

Collection for a few seconds, minutes, days, or months. It might also remove and add elements to this

Collection

. If the client then calls the setter method for the bean, there are no guarantees that either the

Collection hasn’t been changed by some other client of the entity bean (the so-called lost update problem) or that the elements in the originally returned collection are still valid (the repeatable read problem).

The EJB specification’s insistence that collections are only manipulated within a transaction solves these problems, primarily because transactions both prevent the items in the

Collection from being removed, and ensure that new items will remain valid.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 286

286 Day 7

Manipulating Relationships

In the context of an RDBMS, relationships between tables can be manipulated in several ways. Consider, for example, the case study (with the schema used in Day 6, “Entity

EJBs”). The

Job and

Skill tables are in a many-to-many relationship, resolved through the

JobSkill link table, and the

Job and

Location table have a many-to-one relationship, implemented through the location column acting as a foreign key in the

Job table.

To change a many-to-many relationship means adding or removing entries from the appropriate link table. In the example, this means adding or removing

(job, skill) tuples from the

JobSkill table.

To change a many-to-one relationship means changing the value of the foreign key column. In the example, this means changing the value of the location column in the

Job table.

Manipulating relationships between Entity beans is somewhat different. As you have seen, the setter method for a multi-valued cmr-field

(such as setSkills()

) takes an entire

Collection of items. And for many-to-one relationships (with the appropriate navigability), the relationship can be modified from the “parent” end, just as much as from the “child” end.

Moreover, for multi-valued cmr-field s, the collection of beans referenced by the relationship can be modified just by using the usual add() or remove() methods of the java.util.Collection

interface.

The EJB specification lays out in some detail the semantics of various actions that impact relationships between Entity beans. Many of these are straightforward and act as expected, but a few deserve special comment.

Figure 7.4 shows an example configuration of

Location and

Job beans, and indicates the relationships before and after executing: loc1.getJobs().add(job21)

The relationships indicated

{new} are created as a result of this action, while the relationships indicated

{destroyed} disappear. (This rather elegant notation is an enhancement to UML, described by deSouza and Wills’ Catalysis Method.)

Initially, loc1 is associated with job11 through job1n

, and similarly, loc2 is associated with job21 through job2n

. You can see that as a result of the action, the job21 bean is added to the collection of jobs associated with the loc1 location. However, perhaps less obviously, the job21 bean is also removed from the collection of jobs associated with the loc2 location.

This is because there is a one-to-many relationship between

Location and

Job

, so the job21 bean cannot have a relationship with both loc1 and loc2 at the same time.

09 0672323842 CH07 3/20/02 12:20 PM Page 287

F

IGURE

7.4

Before and after object diagram for loc1.getJobs().add

(job21)

.

loc1

CMP and EJB QL

(new) job1.1

job1.2

job1.n

job2.1

job2.2

{destroyed} loc2 job2.n

287

Note that the collection of jobs associated with loc1 changes, even though the setJobs() method of

Location is not called! At least, these are the semantics laid out by the EJB specification, but it would be wise to check that your own EJB container correctly implements this. (The J2EE RI server does implement this correctly.)

The java.util.Collection

interface defines the addAll() method as well as the add() method, and this is also supported for CMR collections. This takes a collection of elements rather than a single element. For one-to-many associations, the addAll() has the same semantics as add() in that any child elements (the

Job bean in the example) that are added to some collection will also be removed from the collection where they resided.

The diagram in Figure 7.4 holds good for either unidirectional relationships with navigability from

Location to

Job

, and for bi-directional relationships.

Figure 7.5 shows the “opposite” case of removing a bean from a collection, having executed loc1.getJobs().remove(job1n)

F

IGURE

7.5

Before and after object diagram for loc1.getJobs().

remove(job1n)

.

loc1 job1.1

job1.2

job2.1

job2.2

loc2 job1.n

job2.n

{destroyed}

7

09 0672323842 CH07 3/20/02 12:20 PM Page 288

288 Day 7

Initially, loc1 is associated with job11 through job1n

, and similarly, loc2 is associated with job21 through job2n

. As you can see, after executing this action, the job1n bean is no longer associated with any

Location bean. Earlier today, it was noted that the EJB specification always allows a minimum multiplicity of zero, and this is what has occurred here. Suppose though that every

Job should always be related to a

Location

— that is, a minimum multiplicity of one. Given that the job’s location is being set to null indirectly (by calling remove() on the returned

Collection from the getJobs() method), there is no easy way to enforce this business rule.

The only available solution is to not make the getJobs() method available in the interface of the bean. You could provide another method, perhaps named getJobsCopy()

, that returns a copy of the

Collection and make it clear that adding or removing beans to this

Collection will not influence the relationship itself. Even better, the

Collection could be made immutable. The following is a possible implementation for this method (assuming java.util.* is import ed): public Collection getJobsCopy() {

List jobs = new ArrayList(); for(Iterator iter = getJobs().iterator(); iter.hasNext(); ) { jobs.add(iter.next());

} return Collections.unmodifiableList(jobs);

}

Incidentally, another way of removing elements from the collection returned by a getter method is to do so using an

Iterator

. Indeed, changing the contents of a

Collection

, either directly by using remove() or indirectly, such as by the use of add() as described in figure 7.4, will invalidate any

Iterator s instantiated from that

Collection

. In any case, the following would disassociate the loc1 location from all of its jobs: for(Iterator iter = loc1.getJobs().iterator(); iter.hasNext(); ) { iter.next(); iter.remove();

}

The diagram in Figure 7.5 again holds good for either unidirectional relationships with navigability from

Location to

Job and for bi-directional relationships.

The case study does not define any one-to-one relationships between beans, so imagine that there is an

Employee bean that has a one-to-one relationship with

Job

. This could perhaps represent the Employee of the

Advertiser who originally placed the

Job advert.

In any case, Figure 7.6 shows the object diagram having executed job1.setEmployee(job2.getEmployee())

09 0672323842 CH07 3/20/02 12:20 PM Page 289

CMP and EJB QL 289

F

IGURE

7.6

Before and after object diagram for job1.setEmployee( job2.getEmployee() )

.

job1

{destroyed} emp1

{new} emp2

{destroyed} job2

Initially, job1 has a relationship with emp1

, and job2 has a relationship with emp2

. After the action, emp1 has no relationship with any

Job

, job2 has no relationship with any

Employee

, and job1 has a relationship with emp2

.

This seems quite straightforward, but again, a minimum multiplicity of zero is needed. If either every

Employee must always relate to a

Job

, or if every

Job must always relate to an

Employee

, there is no method where this application validation can be performed. The

Job bean’s setEmployee() method might look like a good candidate, until you realize that this method is abstract and is generated entirely by the beans’ subclass.

The solution, again, must be to not expose the setter method in the interface of the bean.

Instead, a method, such as setEmployeeField()

, could be provided. Its implementation, for the case where every

Job must relate to an

Employee

(though not every

Employee need have a relationship with a

Job

), might be as follows: public void setEmployeeField(EmployeeLocal newEmployee) { if (newEmployee.getJob() != null) { throw new IllegalArgumentException(

➥”New employee must not already be related to a job”);

} setEmployee( newEmployee );

}

As you can see, this implementation requires that the supplied employee is not related to a job. (If it were, that job would, in turn, need to be assigned a new employee not related to any job, and so on.)

Note Clearly, this technique is good wherever some application-level validation is required. For example, if a bean had a cmp-field called phoneNumber

, the format of the supplied number could be checked.

The diagram in Figure 7.6 holds good for unidirectional relationships with navigability from

Job to

Employee and for bi-directional relationships.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 290

290 Day 7

Figure 7.7 returns to the relationship between the

Location and

Job beans, this time showing the object diagrams having executed loc1.setJobs(loc2.getJobs())

F

IGURE

7.7

Before and after object diagram for loc1.setJobs(loc2.

getJobs())

.

loc1

{new}

{destroyed} job1.1

{destroyed} job1.2

job2.1

{destroyed} job2.2

{destroyed} loc2

{destroyed} job1.n

job2.n

{destroyed}

{new}

{new}

Again, initially loc1 is associated with job11 through job1n

, and similarly, loc2 is associated with job21 through job2n

. After the action has completed, the

Collection of jobs previously associated to loc2 are associated to loc1

. The loc2 bean itself, and the

Collection of jobs previously associated with loc1

, no longer have any associations.

The discussion for the diagram in Figure 7.7 follows similar lines to that for the previous diagram in Figure 7.6. Specifically, handling minimum cardinality of one for either

Job or

Location will require the setJobs() method to be removed from the interface of the bean. Instead, a method, such as setJobsField()

, should be exposed that will do the validation and then delegate to setJobs()

.

It’s also worth remarking that behind the scenes, the EJB container is having to perform some significant updates to the persistent data store. Assuming an RDBMS physical schema of

Location and

Job tables with a foreign key in the

Job table, the foreign key for job11 through job1n would need to be set to null

, and the foreign key for job21 through job2n updated to be loc1

.

Note

You saw earlier today that the actual physical schema used by J2EE RI actually has a separate link table called

JobBean_location_LocationBean_Table that holds the foreign key. For the J2EE RI to implement this action, it would need to delete all the rows in this table for loc1 and loc2

, and then reinsert new rows corresponding to the

(loc1, job21)

… tuples.

(loc1, job2n)

09 0672323842 CH07 3/20/02 12:20 PM Page 291

CMP and EJB QL 291

If you were to design a bean with multi-valued relationships without using CMR, you might well have provided methods such as addSkill() and removeSkill()

. As you now appreciate, CMR itself does not require or provide such methods, relying instead on a single setter method for the cmr-field

.

Of course, there is nothing to prevent providing methods, such as addSkill() and removeSkill()

, in the bean itself. Indeed, as you have seen, there are good reasons why the setSkills() method might not appear in the local interface to the bean; providing just addSkill() and removeSkill() in the interface would provide an (arguably) more natural interface to the bean while also allowing the bean to perform any applicationspecific validation.

Finally in this section, note that it is illegal to call the setter method of a multi-valued cmr-field with null

; a java.lang.IllegalArgumentException

will be thrown by the

EJB container if this is attempted. In the example, to indicate that a job has no skills associated with it, an empty collection (for example, java.util.Collection.EMPTY_LIST

) must be passed as the argument to setSkills()

.

EJB QL

By now, you should have a good idea as to how a bean’s CMP fields and CMR fields are managed and persisted by the EJB container. However, there is still one area that you covered yesterday when you were learning about BMP Entity beans that has not yet been touched on today, and that is how to specify finder methods. For this, you need to know something about the EJB Query Language, or EJB QL.

EJB QL was introduced in the EJB 2.0 specification to make beans more portable across

EJB containers. Previously, each EJB container vendor created their own proprietary mechanism for specifying the semantics of finder methods. This is no longer the case.

As a language, EJB QL is based on ANSI SQL, which you have seen embedded within the case study code (for example, on Day 5, “Session EJBs” and Day 6, “Entity EJBs”).

EJB QL also bears some similarity with the Object Constraint Language (OCL), part of

UML. Familiarity with ANSI SQL (and ideally OCL too) will see you well on the way to picking up EJB QL.

Select Methods

The EJB 2.0 specification also introduces another concept that also uses EJB QL queries, namely that of select methods. These are like finder methods in that their purpose is to return data from the persistent data store, but there are also some important differences.

Table 7.2 is an expanded version of the comparison that appears in the EJB specification.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 292

292 Day 7

T

ABLE

7.2

Finder and Select Methods Compared

Feature

Can be defined for CMP

Entity beans

Can be defined for BMP

Entity beans

Finder Method

Yes

Yes

Select Method

Yes

No

Semantics specified using

EJB QL query, implementation generated by container

Yes

Appears in local-home (home) Yes interfaces This is the way in which formal arguments are specified.

Abstract method signature in bean class

Returns local (remote) references to bean

Yes

No

No Yes

This is the way in which the formal arguments are specified.

Yes

Local references are returned if

Yes

By default, local references specified on local-home interface, are returned, but remote remote references if specified on references can be returned if home interface.

indicated in the deployment descriptor (the result-typemapping element).

Returns any cmr-field cmp-field or No Yes

A different syntax of EJB QL query is used.

Yes Returns java.util.Collections

Yes of references

Returns java.util.Sets

of No references Duplicates can be eliminated using the distinct keyword in the EJB QL query.

Returns java.util.

Enumerations of references

Yes

This is to support legacy EJB 1.x beans; do not use.

Called on pooled beans

Yes

No

Yes

The EJB container selects an

Yes

If being called from a home unused bean to perform the query. method.

09 0672323842 CH07 3/20/02 12:20 PM Page 293

CMP and EJB QL 293

T

ABLE

7.2

Continued

Feature

Called on activated beans

Finder Method

No

Own transaction context can be specified

Yes

Select Method

Yes

If being called from a business method, this allows the underlying query to be logically scoped (for example, based on the primary key), if needed.

No

Because the select method does not appear in the interfaces, no transaction context can be defined. Instead, the context of the calling method is used.

You’ll be learning how to write select methods later, as part of specifying and implementing CMP beans.

Syntax and Examples

The EJB Specification formally defines the syntax of EJB QL queries using Bacchus

Normal Form (BNF). This can be somewhat heavy going to wade through, but is comprehensive. This section introduces EJB QL using BNF, with annotations and examples along the way.

An EJB QL query is defined as follows:

EJB QL ::= select_clause from_clause [where_clause]

This says that a query consists of a select_clause and a from_clause

, and optionally a where_clause

. Immediately, you can see the similarity with ANSI SQL.

In ANSI SQL, you may recall that the columns listed in the

SELECT clause relate to the tables identified in the

FROM clause. So in exploring EJB QL, it makes sense to look at the from_clause before the select_clause

; after all, both are needed for any wellformed EJB QL query.

from_clause

The from_clause is defined as follows: from_clause ::=

FROM

identification_variable_declaration

➥[, identification_variable_declaration]*

7

09 0672323842 CH07 3/20/02 12:20 PM Page 294

294 Day 7

This says that in an EJB QL query, the from_clause consists of one or more identification_variable_declaration clauses. This corresponds to ANSI SQL where there are one or more tables/views listed in a SQL

FROM clause. The identification_variable_declaration clause is then defined as follows: identification_variable_declaration ::=

➥{range_variable_declaration | collection_member_declaration}

➥[

AS

] identification_variable where range_variable_declaration ::= abstract_schema_name and collection_member_declaration ::=

IN

(collection_valued_path_expression) collection_valued_path_expression ::=

➥identification_variable.

➥[single_valued_cmr_field.]*

➥collection_valued_cmr_field

In other words, each item in the from_clause either is just an abstract_schema_name

, or is an expression of the form

IN (o.abc.def.xyz)

. The abstract_schema_name is probably the easier expression to understand. Each CMP Entity bean has a corresponding abstract_schema_name

, ultimately declared in the deployment descriptor. So in ANSI

SQL terms, this is very similar to just listing the “table” that corresponds to the bean.

The

IN (o.abc.def.xyz) expression is similar to an OCL expression. Here o refers is the identication_variable assigned by the previous

AS phrase. In other words, it corresponds to some abstract_schema_name

(that is, bean) also in the from_clause

. The abc and def are single_value_cmr_field s. In other words, these are fields of the o bean that return a single element. The abc is a field of o

, returning a reference to a bean, and def is a field of this reference that has a field xyz

. This xyz field, in turn, returns a

Collection or

List of some other data (a bean or otherwise).

That’s pretty heavy, so some examples from the case study may clarify things. Assuming that the

Job bean has an abstract schema name called

Job

, that the

Customer bean has an abstract schema name called

Customer

, and so on.

FROM Job AS j sets up an identification_variable called j that refers to the

Job schema. The comparison with ANSI SQL is obvious; the syntax is the same.

FROM Job AS j, IN (j.skills) AS s sets up the j identification_variable as before and also an identification_variable called s that refers to each of the skills related to the

Job bean in turn.

09 0672323842 CH07 3/20/02 12:20 PM Page 295

CMP and EJB QL 295

Comparing this to ANSI SQL, this most likely would correspond to

FROM Job AS j

INNER JOIN JobSkill AS s

ON s.customer = j.customer

AND s.ref = j.ref

or if you prefer the old-fashioned way:

FROM Job AS j, JobSkill AS s

WHERE s.customer = j.customer

AND s.ref = j.ref

The following is another example:

FROM Job AS j, IN (j.location.jobs) AS k sets up the j identification_variable as before and also an identification_variable called k that refers to all of the jobs that have the same location as the original job.

To see this, note that j.location

returns a reference to the

Location bean for the

Job

, and then that j.location.jobs

returns the

Collection of

Jobs for that

Location

. Of course, this

Collection will include the original job, but it will include others as well.

Comparing this to ANSI SQL, you can see that this is a self-join:

FROM Job AS j

INNER JOIN Job AS k

ON j.location = k.location

or in the old money:

FROM Job AS j, Job AS k

WHERE j.location = k.location

select_clause

The select_clause is defined as follows: select_clause ::=

SELECT

[

DISTINCT

] {single_valued_path_expression |

OBJECT

(identification_variable)}

The easy case is the

SELECT OBJECT(o) style of the select_clause

, where o is an identication_variable defined by the from_clause

. (You see now why the from_clause was presented first.) This returns all the data from the data store required to instantiate a bean. In ANSI SQL terms, you might think of it as an intelligent

SELECT *

FROM ...

.

All finder methods must use the

SELECT OBJECT(o) style, where the objects returned are of the schema associated with the bean for which the finder is being specified. The other style of the select_clause

, using a single_valued_path_expression is for use only by select methods that you were introduced to briefly earlier. (More on these to follow.)

7

09 0672323842 CH07 3/20/02 12:20 PM Page 296

296 Day 7

The following are some examples from the case study.

SELECT OBJECT(j)

FROM Job as j will return all jobs. If the

JobLocalHome interface defined a finder method called findAll()

, this would be the corresponding EJB QL query.

The next example

SELECT DISTINCT OBJECT(s)

FROM Job as j, IN (j.skills) as s will return back all skills used by any job. Because some skills will be required by more than one job, the

DISTINCT keyword is used to eliminate duplicates. This EJB QL query might perhaps be associated with a finder method called findAllRequiredSkills() on the

SkillLocalHome interface.

The other style of the select_clause uses a single_valued_path_expression

. This is defined as follows: single_valued_path_expression ::=

➥{single_valued_navigation | identification_variable}

➥.cmp_field | single_valued_navigation where a single_valued_navigation is single_valued_navigation ::= identification_variable.[single_valued_cmr_field.]*

➥single_valued_cmr_field

Taking these definitions together, a single_valued_path_expression is effectively just a chain of (none or many) single_valued_cmr_fields

( cmr-field s returning a reference to a single bean, and not a collection), eventually finishing with a cmp-field

.

In the following examples

SELECT DISTINCT j.location.name

FROM Job as j location is the cmr-field of the

Job schema (identified by j

), and name is the cmpfield of the bean referenced by the cmr-field

(a

Location bean, obviously).

This returns the names of the locations where there are jobs. Comparing this to ANSI

SQL, you can see that EJB QL is actually simpler (because of its use of the OCL-like path expressions to navigate between beans):

SELECT DISTINCT l.name

FROM Job as j INNER JOIN Location as l ON j.location = l.location

However, the following is not allowed:

SELECT DISTINCT j.skills.name

FROM Job as j

09 0672323842 CH07 3/20/02 12:20 PM Page 297

CMP and EJB QL 297

This is because the skills cmr-field of

Job returns a collection of skills, not a single skill. The correct way to phrase this query is as follows:

SELECT DISTINCT s.name

FROM Job as j, IN (j.skills) AS s

You might like to think of the s identification variable as an iterator over the collection of skills returned by the skills cmr-field

.

Note that the select_clause in EJB QL can only ever return a single item of information, so the following also is not allowed:

SELECT DISTINCT j.location.name, j.location.description

FROM Job as j

where_clause

The where_clause is optional in an EJB QL query, but will be present in the majority of cases. It is defined in BNF as follows: where_clause ::=

WHERE

conditional_expression conditional_expression ::=

➥conditional_term | conditional_expression conditional_term ::=

OR

conditional_term

➥conditional_factor | conditional_term

AND

conditional_factor conditional_factor ::=

➥[

NOT

] conditional_test

This just says that clauses can be combined using the usual

AND

,

OR

, and

NOT

. Much of the rest of the formal BNF definitions for the where_clause also makes somewhat heavy work of some fairly straightforward concepts, so to paraphrase,

• conditional_test s can involve

=

,

>

,

<

,

>=

,

<=

, and

<> operators. These apply variously to numbers and datetimes (all operators), and strings, Booleans, and Entity beans (the

= and

<> operators). You will recall that Entity beans are considered identical if their primary keys are equal.

• Comparisons can involve input parameters, where these correspond to the arguments of the finder or select method. More on this topic shortly.

• Arithmetic expressions can use the

BETWEEN...AND

operator, just as in ANSI SQL.

• String expressions can be compared against lists using the

IN operator and against patterns using the

LIKE operator. These also exist within ANSI SQL. Unlike Java, string literals should appear in single quotes.

• The

IS NOT NULL operator exists to determine if an object is null

. Again, this syntax is borrowed from ANSI SQL.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 298

298 Day 7

Note ANSI SQL supports the concept of nullable primitives ( int s and so on), whereas Java and EJB QL do not. However, nullable primitives can be simulated by using wrapper classes as cmp-field s and using these in EJB QL expressions.

There are some more operators to EJB QL and some built-in functions, but first, some examples using these operators are in order.

SELECT OBJECT(c)

FROM Customer AS c

WHERE c.name LIKE “J%”

This will find all customers whose name begins with the letter J. Note that the ANSI

SQL wildcards (

% to match none or many characters,

_ to match precisely one character) are used.

The following

SELECT OBJECT(j)

FROM Jobs AS j

WHERE j.location IS NULL will find all jobs where the location has not been specified.

SELECT l.description

FROM Location AS l

WHERE l.name IN (“London”, “Washington”) returns the descriptions of the locations named

London and

Washington

.

The where_clause can also include input parameters. These parameters correspond to the arguments of the finder or the select method, as defined in the local-home interface or bean, respectively.

For example, the

Job bean declares the following finder method in the

JobLocalHome interface:

Collection findByCustomer(String customer);

The EJB QL query for this finder method is as follows:

SELECT OBJECT(j)

FROM Job AS j

WHERE j.customer = ?1

?1

acts as a placeholder, with the

1 indicating that the first argument of the finder method (the customer string) be implicitly bound to this input parameter. Unlike

JDBC SQL strings, the number is required because the binding is implicit, not explicit.

09 0672323842 CH07 3/20/02 12:20 PM Page 299

CMP and EJB QL 299

It is also needed in the cases where a single argument is used more than once in the query. For example, consider the following finder method:

Collection findLocationsNamedOrNamedShorterThan(String name);

This might have an EJB QL query of

SELECT OBJECT(l)

FROM Location AS l

WHERE l.name = ?1

OR LENGTH(l.name) < LENGTH(?1)

This example uses the built-in function

LENGTH that returns the length of a

String

. In any case, this rather peculiar finder method will find those locations that have the exact name, and will also return any name whose length is strictly shorter than the supplied name

. You can see that the

?1

placeholder appears more than once because the name argument needs to be bound to the query in two places.

EJB QL defines just a few more built-in functions. The functions that return a string are

CONCAT and

SUBSTRING

. The functions that return a number are

LENGTH

,

ABS

,

SQRT

, and

LOCATE

. This last is effectively the same as

String.indexOf(String str, int fromIndex)

.

EJB QL defines two final operators—

IS [NOT] EMPTY and

[NOT] MEMBER OF

. Neither of these have any direct equivalents in ANSI SQL, but both do have equivalents in OCL.

The

IS [NOT] EMPTY operator can is similar to the isEmpty operator of OCL. It can be used to determine whether a collection returned by a cmr-field is empty. For example,

SELECT OBJECT(s)

FROM Skill AS s

WHERE s.jobs IS EMPTY will return all those skills that are not marked as required by any job. This might be the query for a finder method on the

SkillLocalHome interface, called something like findNotNeededSkills()

.

In fact, this type of query can be expressed in ANSI SQL, though it does require a subquery:

SELECT s.*

FROM Skill AS s

WHERE NOT EXISTS

( SELECT *

FROM JobSkill AS j

WHERE s.skill = j.skill )

7

09 0672323842 CH07 3/20/02 12:20 PM Page 300

300 Day 7

The

[NOT] MEMBER OF operator is similar to the include operator of OCL. Consider the following finder method on the

JobLocalHome interface:

Collection findJobsRequiringSkill(SkillLocal skill);

The EJB QL query for this would be

SELECT OBJECT(j)

FROM Job AS j

WHERE ?1 MEMBER OF j.skills

Again, this can be expressed in ANSI SQL, but only using a subquery:

SELECT j.*

FROM Job AS j

WHERE EXISTS

( SELECT *

FROM JobSkill AS s

WHERE j.customer = s.customer

AND j.ref = s.ref

AND s.skill = ?1 )

Further Notes

You may have heard of the “OO/relational impedance mismatch.” This is that to deal with objects, each must be instantiated and then a message sent to it. On the other hand, relational theory deals with sets of elements sharing some common attribute; to identify these elements without instantiating them effectively breaks encapsulation.

EJB QL does a pretty reasonable job of reconciling these concerns. By allowing queries to be expressed in a set-oriented syntax, the EJB container can easily map these to ANSI

SQL when the persistent data store is an RDBMS. On the other hand, the generated implementations of finder methods return only objects or

Collection s of objects.

Nevertheless, EJB QL does have some limitations. For example, when constructing an

EJB QL query, only declared relationships between beans can be followed. It is not possible to join arbitrary fields together (as it is in ANSI SQL). For example, those

Customer s who are also

Applicant s could not be identified using a condition such as

Applicant.name = Customer.name

.

There are a number of other cases where EJB QL is not (yet) as powerful as ANSI SQL.

For example, EJB QL does not support grouping and aggregating, ordering, subqueries, and unions. Expect these features to be added as EJB QL matures. Also, even though

EJB QL does not directly support subqueries, one might not be needed anyway thanks to the

IS [NOT] EMPTY and

[NOT] MEMBER OF operators.

09 0672323842 CH07 3/20/02 12:20 PM Page 301

CMP and EJB QL 301

Specifying a CMP Entity Bean

Specifying a CMP Entity bean is identical to specifying a BMP Entity bean; it consists of defining the local-home interface and the local interface. This makes sense; after all, to the user of the bean, it should not matter whether the bean is implemented internally using CMP or BMP.

The Local-Home Interface

For completeness, Listing 7.2 is the local-home interface of the

Job bean.

L

ISTING

7.2

JobLocalHome

Interface

1: package data;

2:

3: import java.rmi.*;

4: import java.util.*;

5: import javax.ejb.*;

6:

7: public interface JobLocalHome extends EJBLocalHome

8: {

9: JobLocal create (String ref, String customer) throws CreateException;

10: JobLocal findByPrimaryKey(JobPK key) throws FinderException;

11: Collection findByCustomer(String customer) throws FinderException;

12: Collection findByLocation(String location) throws FinderException;

13: void deleteByCustomer(String customer);

14: }

The Local Interface

Listing 7.3 shows the local interface for the

Job bean; it, too, is unchanged from the

BMP version.

L

ISTING

7.3

JobLocal

Interface

1: package data;

2:

3: import java.rmi.*;

4: import javax.ejb.*;

5: import java.util.*;

6:

7: public interface JobLocal extends EJBLocalObject

8: {

9: String getRef();

10: String getCustomer();

11: CustomerLocal getCustomerObj(); // derived

12:

13: void setDescription(String description);

7

09 0672323842 CH07 3/20/02 12:20 PM Page 302

302 Day 7

L

ISTING

7.3

Continued

14: String getDescription();

15:

16: void setLocation(LocationLocal location);

17: LocationLocal getLocation();

18:

19: Collection getSkills();

20: void setSkills(Collection skills);

21: }

Implementing a CMP Entity Bean

Just as for BMP Entity beans, implementing a CMP Entity bean involves providing an implementation for the methods of the javax.ejb.EntityBean

, corresponding methods for each method in the home interface, and a method for each method in the remote interface.

Implementing javax.ejb.EntityBean

Under BMP, the setEntityContext() method was used to look up various bean home interfaces from JNDI, and the JDBC

DataSource called java:comp/env/jdbc/Agency was also obtained. Because most of these relationships are now managed by the container, only a couple of home interfaces now need to be obtained, and there is no requirement to look up the

DataSource

. Listing 7.4 shows this.

L

ISTING

7.4

The

JobBean

’s setEntityContext() and unsetEntityContext()

Methods

1: package data;

2:

3: import javax.ejb.*;

4: import javax.naming.*;

5: // imports omitted

6:

7: public abstract class JobBean implements EntityBean {

8:

9: private EntityContext ctx;

10:

11: public void setEntityContext(EntityContext ctx) {

12: this.ctx = ctx;

13: InitialContext ic = null;

14: try {

15: ic = new InitialContext();

16: customerHome = (CustomerLocalHome)

➥ic.lookup(“java:comp/env/ejb/CustomerLocal”);

17: jobHome = (JobLocalHome)

➥ic.lookup(“java:comp/env/ejb/JobLocal”);

09 0672323842 CH07 3/20/02 12:20 PM Page 303

CMP and EJB QL 303

L

ISTING

7.4

Continued

18: }

19: catch (NamingException ex) {

20: error(“Error looking up depended EJB or resource”, ex);

21: return;

22: }

23: }

24:

25: public void unsetEntityContext() {

26: this.ctx = null;

27: customerHome = null;

28: jobHome = null;

29: }

30:

31: // code omitted

32: }

The lookup of the

CustomerHome home interface is required because the relationship from

Customer to

Job is maintained by the bean.

The lookup of the

JobHome home interface is required only because the ctx.getLocalHome() method returns

NULL for the J2EE RI 1.3. This would appear to be a bug. The ejbHomeDeleteByCustomer() home method actually uses the bean’s own home interface.

As noted previously, under CMP, the ejbLoad() and ejbStore() methods often have empty implementations. If there is derived data to be maintained, it should be managed here. Indeed, the

JobBean class does need to do this, as shown in Listing 7.5.

L

ISTING

7.5

The

JobBean

’s ejbLoad() and ejbStore()

Methods

1: package data;

2:

3: import javax.ejb.*;

4: import javax.naming.*;

5: // imports omitted

6:

7: public abstract class JobBean implements EntityBean {

8:

9: public void ejbLoad() {

10: JobPK key = (JobPK)ctx.getPrimaryKey();

11:

12: try {

13: this.customerObj =

➥customerHome.findByPrimaryKey(getCustomer());

14: }

15: catch (FinderException e) {

16: error(“Error in ejbLoad (invalid customer) for “ + key, e);

7

09 0672323842 CH07 3/20/02 12:20 PM Page 304

304 Day 7

L

ISTING

7.5

Continued

17: }

18: }

19:

20: public void ejbStore() { }

21:

22: // code omitted

23: }

The ejbLoad() method is called after the bean’s state has been populated, so the bean’s state can be read through the accessor methods, if needed.

The findByPrimaryKey() method call on line 13 populates the customerObj field, using the value of getCustomer() accessor method. It’s worth appreciating that getCustomer() returns just a

String

. In other words, this is the name (actually, the primary key) of a customer. To save business methods having to continually look up the actual customer that corresponds to this customer name, the ejbLoad() method does it once.

You can see that the ejbStore() method is trivial—there is nothing to do.

The ejbActivate() and ejbPassivate() methods have nothing to do with data stores, so their implementation is unchanged from the BMP version. This is shown in Listing 7.6.

L

ISTING

7.6

The

JobBean

’s ejbActivate() and ejbPassivate()

Methods

1: package data;

2:

3: import javax.ejb.*;

4: // imports omitted

5:

6: public abstract class JobBean implements EntityBean {

7:

8: public void ejbPassivate() {

9: setRef(null);

10: setCustomer(null);

11: customerObj = null;

09 0672323842 CH07 3/20/02 12:20 PM Page 305

CMP and EJB QL 305

L

ISTING

7.6

Continued

12: setDescription(null);

13: setLocation(null);

14: }

15:

16: public void ejbActivate() { }

17:

18: // code omitted

19: }

Implementing the Local-Home Interface Methods

The methods of the local-home interface are implemented partly in code and partly through the provision of an appropriate EJB QL query. This section shows the queries that correspond to the finder methods in the home interface; the next section (configuring a CMP Entity bean) shows how to take those EJB QL strings and put them into the correct part of the deployment descriptor.

Create and Remove Methods

Listing 7.7 shows the ejbCreate() method for

Job bean under CMP.

L

ISTING

7.7

The

JobBean

’s ejbCreate()

Method

1: package data;

2:

3: import javax.ejb.*;

4: // imports omitted

5:

6: public abstract class JobBean implements EntityBean {

7:

8: public JobPK ejbCreate(String ref, String customer)

➥throws CreateException {

9: // validate customer login is valid.

10: try {

11: customerObj = customerHome.findByPrimaryKey(customer);

12: } catch (FinderException ex) {

13: error(“Invalid customer.”, ex);

14: }

15:

16: JobPK key = new JobPK(ref,customerObj.getLogin());

17: // for BMP, there was a workaround here,

➥namely to call ejbFindByPrimaryKey

18: // under CMP, cannot call since doesn’t exist.

19: // instead, use jobHome interface ...

20: try {

7

09 0672323842 CH07 3/20/02 12:20 PM Page 306

306 Day 7

L

ISTING

7.7

Continued

21: jobHome.findByPrimaryKey(key);

22: throw new CreateException(“Duplicate job name: “+key);

23: }

24: catch (FinderException ex) {}

25:

26: setRef(ref);

27: setCustomer(customer);

28: setDescription(null);

29: setLocation(null);

30: return null;

31: }

32:

33: // code omitted

34: }

Note the use of accessor methods (the “setter” methods) to save the bean’s state. This contrasts with the BMP equivalent where the fields were written to directly (for example, this.ref = ref

).

The implementation of this method would have been even shorter if the findByPrimaryKey() call checking for duplicates had been omitted. Indeed, if an

RDBMS is being used as the persistent data store (as it is for the case study), there is likely to be a unique index on the primary key on the appropriate table, meaning that any attempt to

INSERT a duplicate would be detected.

Caution Strictly, the appropriate exception to throw here is a

DuplicateKeyException, not a CreateException. However, the EJB specification does not mandate this. Moreover, the specification even allows that the EJB container can defer any interaction with the persistent data store until the end of the transaction, and is not clear in this circumstance what type of exception should be raised.

As the EJB specification continues to mature, it will more fully define expected exceptions in different situations. Until then, beware that—regardless of the hype—moving from one EJB container to another may well throw up differences that will necessitate changes in your application.

The ejbCreate() method is called first, and then the container’s concrete implementation persists the bean’s state, and then the bean’s ejbPostCreate() method is called.

09 0672323842 CH07 3/20/02 12:20 PM Page 307

CMP and EJB QL 307

Under CMP, the bean should return null from the ejbCreate() method. This compares to returning the actual primary key under BMP. Put another way, it doesn’t matter what your method returns, it will be ignored. The reason for this is that the EJB container can access the information that constitutes the primary key anyway, by virtue of the cmpfield s.

Note The technical reason that the EJB container requires that CMP Entity beans return null is so that EJB container vendors can implement CMP by effectively subclassing the CMP bean and creating a BMP Entity bean (so far as the rest of the EJB container is concerned). Indeed, if you look at the generated code, this is precisely what the J2EE RI container does: package data; public final class JobBean_PM extends JobBean implements com.sun.ejb.PersistentInstance { public data.JobPK ejbCreate(java.lang.String param0, java.lang.String param1) throws javax.ejb.CreateException { com.sun.ejb.Partition partition = com.sun.ejb.PersistenceUtils.getPartition(this); partition.beforeEjbCreate(this); super.ejbCreate(param0, param1); return (data.JobPK) partition.afterEjbCreate(this);

}

// code omitted

}

You can see the call to super.ejbCreate()

. The return type is ignored, but the subclass’ ejbCreate() does return a primary key to the rest of the EJB container.

The ejbRemove() method for the

Job bean is shown in Listing 7.8.

L

ISTING

7.8

The

JobBean’s ejbRemove()

Methods

1: package data;

2:

3: import javax.ejb.*;

4: // imports omitted

5:

6: public abstract class JobBean implements EntityBean {

7:

8: public void ejbRemove() { }

7

09 0672323842 CH07 3/20/02 12:20 PM Page 308

308 Day 7

L

ISTING

7.8

Continued

9:

10: // code omitted

11: }

As you can see, the implementation of ejbRemove() is trivial—there is nothing to do.

Nevertheless, an implementation is required.

Caution

The BMP version of ejbRemove() for Job bean reset all the fields to null.

Strictly speaking, there was no direct requirement for doing this, because when the bean instance is next used from the pool, its ejbLoad() will

(should) populate all fields.

When implementing CMP Entity beans, you absolutely must not reset the fields to null. Doing so will cause the EJB container to throw an exception, because the bean’s state is required so that the container can remove the correct data from the persistent data store.

Finder Methods

The implementation of the finder methods is by formulating appropriate EJB QL queries.

The

JobLocalHome interface defines three finder methods— findByPrimaryKey()

, findByCustomer()

, and findByLocation()

.

The first bit of good news is that there is no need to define an EJB QL query for the findByPrimaryKey() method at all. You will recall that the primkey-class element is used in the deployment descriptor to indicate to the EJB container the class (either custom or pre-existing) that represents the primary key of the Entity bean. When there is a custom primary key class, the EJB container can use the structure of that class to figure out how to implement this method.

If there is no custom primary key class, an additional piece of information is required in the deployment descriptor—namely, the primkey-field element. This nominates the

(single) cmp-field that represents the primary key for the bean.

If you are using a custom primary key class (such as

JobPK

), you do need to ensure that its public fields correspond exactly in name and type to a subset of the cmp-field s of the bean. The

JobPK class is shown in Listing 7.9.

09 0672323842 CH07 3/20/02 12:20 PM Page 309

CMP and EJB QL 309

L

ISTING

7.9

JobPK

Class

1: package data;

2:

3: import java.io.*;

4: import javax.ejb.*;

5:

6: public class JobPK implements Serializable

7: {

8: public String ref;

9: public String customer;

10:

11: public JobPK() {

12: }

13: public JobPK(String ref, String customer) {

14: this.ref = ref;

15: this.customer = customer;

16: }

17:

18: public String getRef() {

19: return ref;

20: }

21: public String getCustomer() {

22: return customer;

23: }

24:

25: // code omitted

26: }

The EJB container will match the ref and customer fields with getRef()

/ setRef() and getCustomer()

/ setCustomer() accessor methods for the cmp-field s. You can see here that the fields in the primary key class must be declared to have public visibility.

Moving onto the other finder methods, the findByCustomer() method has the following signature in the local-home interface:

Collection findByCustomer(String customer) throws FinderException;

The EJB QL query for this is as follows:

SELECT OBJECT(j)

FROM Job AS j

WHERE j.customer = ?1

The other finder method is findByLocation(), whose signature is as follows:

Collection findByLocation(String location) throws FinderException;

7

09 0672323842 CH07 3/20/02 12:20 PM Page 310

310 Day 7

The EJB QL query for this is as follows:

SELECT OBJECT(;)

FROM Job AS ;

WHERE ;.location.name = ?1

Home Methods

The last method in the local-home interface is the home method deleteByCustomer()

.

This is used by clients when removing a customer; all of its jobs must also be removed.

You’ve already seen the implementation of this home method under BMP, using SQL to delete from the

Job and

JobSkill tables. Under CMP, the implementation is somewhat more object-oriented, as shown in Listing 7.10.

L

ISTING

7.10

JobBean

’s ejbHomeDeleteByCustomer

() Method

1: package data;

2:

3: import java.rmi.*;

4: import java.util.*;

5: import javax.ejb.*;

6: // imports omitted

7:

8: public abstract class JobBean implements EntityBean {

9:

10: public void ejbHomeDeleteByCustomer(String customer) {

11: try {

12: Collection col = this.jobHome.findByCustomer(customer);

13: for (Iterator iter = col.iterator(); iter.hasNext(); ) {

14: JobLocal job = (JobLocal)iter.next();

15: // remove job from collection

16: iter.remove();

17: // remove job itself

18: job.remove();

19: }

20: }

21: catch (FinderException e) {

22: error(“Error removing all jobs for “ + customer, e);

23: }

24: // needed because of the explicit job.remove()

25: catch (RemoveException e) {

26: error(“Error explicitly removing job for “ + customer, e);

27: }

28:

29: }

30: // code omitted

31:

32: }

09 0672323842 CH07 3/20/02 12:20 PM Page 311

CMP and EJB QL 311

The method calls the bean’s own findByCustomer() method, which returns a collection of jobs for the customer. It then iterates over these jobs and removes them one-by-one.

Caution

The jobHome field holds a reference to the bean’s own home interface. In principle, the entityContext.getEJBHome() method could have been called, but the J2EE RI 1.3 container seems always to return null

.

If there had been no suitable finder method, this would have been a prime case for using a select method. The select method would be declared in the

JobBean class as follows: package data; import java.rmi.*; import java.util.*; import javax.ejb.*;

// imports omitted public abstract class JobBean implements EntityBean { public abstract Collection ejbSelectByCustomer(String customer);

// code omitted

}

The EJB QL query would be identical to that of the finder method. The only other difference would be in how the method was called. Rather than invoking the finder method through the home interface, the ejbSelect method could be called directly. So, in the ejbHomeDeleteByCustomer() method, the line

Collection col = this.jobHome.findByCustomer(customer); would be replaced with

Collection col = this.ejbSelectByCustomer(customer);

In fact, the BMP and CMP implementations are not quite comparable; the CMP implementation is a little more robust. As was mentioned earlier, the BMP version of this method just deleted the appropriate rows from the

Job and

JobSkill table (turn back to yesterday’s chapter to see this, if needed). This means that the

Job bean’s ejbRemove() method is never called; there is no opportunity for the bean to perform clean-up processing.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 312

312 Day 7

Note An alternative design again might have been to define a cascade delete relationship from

Customer through to

Job

. The EJB Specification does require that ejbRemove() is called on every bean being deleted as a result of the cascade, so the net result is similar to the CMP implementation.

However, as has been remarked earlier, setting up a (cascade delete) relationship for

Customer and

Job is not easy when using the J2EE RI, because it does not easily support relationships between beans when one bean is identified by another (part of the primary key is also a foreign key).

Note A cascade delete relationship might be preferable to the CMP implementation. A naïve EJB container implementation would work the same way as the hand-coded CMP implementation, explicitly deleting each and every child bean one-by-one. The performance hit could be substantial.

A more sophisticated EJB container implementation ought to be able to call ejbRemove() for each bean, but then delete all of the child beans using a single call to the persistent data store; in other words, combining the best of the BMP and CMP approaches.

A related issue is that if a client happens to have a reference to a

Job for the

Customer being deleted, they won’t find out that the

Job has been removed until they attempt to access that

Job again. At that point, the client will receive a java.rmi.NoSuchObjectException

. But note that this isn’t a problem just with BMP; the same behavior will occur for CMP also.

Implementing the Local Interface Methods

Looking back at the

JobLocal interface back in Listing 7.3, you can see that many of the methods in the local interface simply expose the bean’s cmp-field s or cmr-field s to its clients. Of course, there is no implementation for these methods because they are implemented by the EJB container’s deployment tools. Therefore, all that is required is to copy the methods over from the local interface and mark them abstract. If you cast your eyes back all the way to Listing 7.1, you’ll see that this is precisely what was done.

The only method in the

JobLocal interface that does not correspond to an accessor method for a cmp-field or cmr-field is the getCustomerObj() method. Its implementation is shown in Listing 7.11.

09 0672323842 CH07 3/20/02 12:20 PM Page 313

CMP and EJB QL 313

L

ISTING

7.11

JobBean

’s getCustomerObj()

Method

1: package data;

2:

3: import javax.ejb.*;

4: // imports omitted

5:

6: public abstract class JobBean implements EntityBean {

7: private CustomerLocal customerObj; // derived

8: public CustomerLocal getCustomerObj() {

9: return customerObj;

10: }

11: // code omitted

12: }

Pretty straightforward, though of course the hard work is done in ejbLoad()

(Listing 7.5) and ejbCreate()

(Listing 7.7). Recall that the customerObj field holds the actual reference to the “parent”

Customer for the

Job and is derived from the customer cmp-field that holds merely the name of the

Customer

. Because the customer cmp-field makes up part of the primary key, it is immutable, and so is the customerObj field—hence, no setCustomerObj() method.

Configuring a CMP Entity Bean

As you now appreciate, a substantial part of the implementation effort for CMP Entity beans is simply completing the deployment descriptor correctly.

This will always involve completing the entity element of the deployment descriptor, either manually or using a GUI tool such as deploytool

. If the CMP Entity has container-managed relationships, these too must be specified under the relationships element of the deployment descriptor.

The entity

Element

To remind you, the structure of the deployment descriptor is as follows:

<!ELEMENT ejb-jar (description?, display-name?, small-icon?, large-icon?, enterprise-beans, relationships?, assembly-descriptor?, ejb-client-jar?)>

Here you can see the relationships element, with

<!ELEMENT enterprise-beans (session | entity | message-driven)+>

7

09 0672323842 CH07 3/20/02 12:20 PM Page 314

314 Day 7 and the entity element defined as

<!ELEMENT entity (description?, display-name?, small-icon?, large-icon?, ejb-name, home?, remote?, local-home?, local?, ejb-class, persistence-type, prim-key-class, reentrant, cmp-version?, abstract-schema-name?, cmp-field*, primkey-field?, env-entry*, ejb-ref*, ejb-local-ref*, security-role-ref*, security-identity?, resource-ref*, resource-env-ref*, query*)>

Looking first at the entity element, much of it will be unchanged. What needs to be specified for a CMP entity are

• cmp-version

Always set to

2.0

. The value

1.1

is supported only for legacy CMP

Entity beans written to the EJB 1.1 specification.

• abstract-schema-name

Any unique name, this defines the name used to identify the bean in EJB QL queries. It makes sense to base this on the name of the bean.

In the case study, the

JobBean bean has a schema with the name of

Job

.

• cmp-field

One for each cmp-field

(but not cmr-field s). In the

Job bean, the cmp-field s are ref

, customer

, and description

. The location and skills fields are cmr-field s representing relations to the

Location and

Skill beans respectively, and so do not appear.

• primkey-field

This optional field is used when the primkey-class element does not identify a custom primary key class. It is not specified for the

Job bean, but for the

Location bean, for example, it is specified and is set to name

.

• query

Defines an EJB QL query, associating it with a finder or select method.

Listing 7.12 shows the entity element for the

Job bean.

L

ISTING

7.12

Job

Bean’s entity

Element

1: <entity>

2: <display-name>JobBean</display-name>

3: <ejb-name>JobBean</ejb-name>

4: <local-home>data.JobLocalHome</local-home>

5: <local>data.JobLocal</local>

6: <ejb-class>data.JobBean</ejb-class>

7: <persistence-type>Container</persistence-type>

09 0672323842 CH07 3/20/02 12:20 PM Page 315

CMP and EJB QL

L

ISTING

7.12

Continued

8: <prim-key-class>data.JobPK</prim-key-class>

9: <reentrant>False</reentrant>

10: <cmp-version>2.x</cmp-version>

11: <abstract-schema-name>Job</abstract-schema-name>

12: <cmp-field>

13: <description>no description</description>

14: <field-name>ref</field-name>

15: </cmp-field>

16: <cmp-field>

17: <description>no description</description>

18: <field-name>description</field-name>

19: </cmp-field>

20: <cmp-field>

21: <description>no description</description>

22: <field-name>customer</field-name>

23: </cmp-field>

24: <ejb-local-ref>

25: <ejb-ref-name>ejb/CustomerLocal</ejb-ref-name>

26: <ejb-ref-type>Entity</ejb-ref-type>

27: <local-home>CustomerLocalHome</local-home>

28: <local>CustomerLocal</local>

29: <ejb-link>data_entity_ejbs.jar#CustomerBean</ejb-link>

30: </ejb-local-ref>

31: <!-- shouldn’t be needed, but

➥ctx.getEJBHome() returns null in J2EE RI -->

32: <ejb-local-ref>

33: <ejb-ref-name>ejb/JobLocal</ejb-ref-name>

34: <ejb-ref-type>Entity</ejb-ref-type>

35: <local-home>JobLocalHome</local-home>

36: <local>JobLocal</local>

37: <ejb-link>data_entity_ejbs.jar#JobBean</ejb-link>

38: </ejb-local-ref>

39: <security-identity>

40: <description></description>

41: <use-caller-identity></use-caller-identity>

42: </security-identity>

43: <query>

44: <description></description>

45: <query-method>

46: <method-name>findByCustomer</method-name>

47: <method-params>

48: <method-param>java.lang.String</method-param>

49: </method-params>

50: </query-method>

51: <ejb-ql>SELECT OBJECT(j)

52: FROM Job AS j

53: WHERE j.customer = ?1</ejb-ql>

54: </query>

315

7

09 0672323842 CH07 3/20/02 12:20 PM Page 316

316 Day 7

L

ISTING

7.12

Continued

55: <query>

56: <description></description>

57: <query-method>

58: <method-name>findByLocation</method-name>

59: <method-params>

60: <method-param>java.lang.String</method-param>

61: </method-params>

62: </query-method>

63: <ejb-ql>SELECT OBJECT(o)

64: FROM Job o

65: WHERE o.location.name = ?1</ejb-ql>

66: </query>

67: </entity>

The definition of the query element is as follows:

<!ELEMENT query (description?, query-method, result-type-mapping?, ejb-ql)>

You can see from the listing that the query-method element just identifies the name of the finder or select method. Note that if a finder method is being identified, it is the name appearing in the local-home (or home) interface; that is, without the ejb prefix. On the other hand, if a select method is being identified, there will be an ejb prefix, because select methods never appear in the interfaces of the bean.

The result-type-mapping element applies only if the method identified by querymethod has identified a select method, and only then if the EJB QL string returns Entity bean references (that is, if the

SELECT [DISTINCT] OBJECT(x) style of select_clause has been used). The allowable values are

Local and

Remote

, indicating whether the clause should return references through the local or remote interfaces. Obviously, if specified, the bean must provide an interface of the appropriate type; if not specified, the bean must provide a local interface, because this is the implied value for the resulttype-mapping element.

Of course, all of this deployment information can be entered graphically using the deploytool

. Figure 7.8 shows some of the equivalent information for Listing 7.12.

F

IGURE

7.8

deploytool lets CMP deployment information be defined through a GUI.

09 0672323842 CH07 3/20/02 12:20 PM Page 317

CMP and EJB QL 317

The relationships

Element

The relationships element is defined in the EJB 2.0 DTD as follows:

<!ELEMENT relationships (description?, ejb-relation+)>

That is, it consists of one or more ejb-relation elements. These in turn are defined as

<!ELEMENT ejb-relation (description?, ejb-relation-name?, ejb-relationship-role, ejb-relationship-role)> which is a somewhat curious definition: an optional description, an optional name, and then precisely two ejb-relationship-role elements. Each of these identifies the role that some bean is playing with respect to the relationship.

Note

It is possible that the same bean appears in both roles to model recursive relationships.

The ejb-relationship-role element is defined as follows:

<!ELEMENT ejb-relationship-role (description?, ejb-relationship-role-name?, multiplicity, cascade-delete?, relationship-role-source, cmr-field?)> with

<!ELEMENT relationship-role-source (description?, ejb-name)>

You can see that the relationship-role-source element merely identifies an Entity bean by name. This element’s name is perhaps somewhat misleading, because it is neither a “source” nor (for that matter) a target within the relationship. The navigability of the relationship comes from the presence (or not) of the cmr-field element. Of course, at least one of the ejb-relationship-role elements must have a cmr-field specified, and if both do, the relationship is bi-directional.

The choices for the multiplicity element are either

One or

Many

. There will be two such elements in the complete ejb-relation element, so this is what gives one-to-one, oneto-many, many-to-one, or many-to-many.

There is also an optional cascade-delete element. Perhaps non-intuitively, this is placed on the “child” (multiplicity = many) side of a relationship.

Finally, the cmr-field is defined as follows:

<!ELEMENT cmr-field (description?, cmr-field-name, cmr-field-type?)>

7

09 0672323842 CH07 3/20/02 12:20 PM Page 318

318 Day 7

The cmr-field-name element just names the cmr-field

(for example, skills or location for the

Job bean). The cmr-field-type element is needed only for multi-valued cmr-field s (for example, skills for the

Job bean) and indicates whether the return type is a java.util.Collection

or java.util.Set

.

Caution Do not confuse this return type with the allowable return types for select methods. These are unrelated areas that just happen to have the same allowable return types.

Okay! Time for an example or two from the case study to see if all that theory makes sense.

Listing 7.13 shows the ejb-relation element defining the one-to-many relationship between the

Location bean and the

Job bean.

L

ISTING

7.13

The

Location/Job

Relationship

1: <ejb-relation>

2: <ejb-relation-name></ejb-relation-name>

3: <ejb-relationship-role>

4: <ejb-relationship-role-name>JobBean</ejb-relationship-role-name>

5: <multiplicity>Many</multiplicity>

6: <relationship-role-source>

7: <ejb-name>JobBean</ejb-name>

8: </relationship-role-source>

9: <cmr-field>

10: <cmr-field-name>location</cmr-field-name>

11: </cmr-field>

12: </ejb-relationship-role>

13: <ejb-relationship-role>

14: <ejb-relationship-role-name>

LocationBean

➥</ejb-relationship-role-name>

15: <multiplicity>One</multiplicity>

16: <relationship-role-source>

17: <ejb-name>LocationBean</ejb-name>

18: </relationship-role-source>

19: <cmr-field>

20: <cmr-field-name>jobs</cmr-field-name>

21: <cmr-field-type>java.util.Collection</cmr-field-type>

22: </cmr-field>

23: </ejb-relationship-role>

24: </ejb-relation>

Figure 7.9 shows this relationship overlaid onto the implied abstract schema.

09 0672323842 CH07 3/20/02 12:20 PM Page 319

CMP and EJB QL 319

F

IGURE

7.9

The ejb-relation element describes the characteristics of a one-to-many relationship in the abstract schema.

Location

name description location location for

<ejb-relation-role>

<ejb-relationsip-role-name>

<multiplicity>

<relationship-role-source>/<ejb-name>

<cmr-field>/<cmr-field-name>

</ejb-relation-role> jobs

Job

customer ref description location Location.name(FK) jobs requires

Skill

name skills description

<ejb-relation-role>

<ejb-relationsip-role-name>

<multiplicity>

<relationship-role-source>/<ejb-name>

<cmr-field>/<cmr-field-name>

</ejb-relation-role>

Another example; Listing 7.14 shows the ejb-relation element defining the many-tomany relationship between the

Job bean and the

Skill bean.

L

ISTING

7.14

The

Job/Skill

Relationship

1: <ejb-relation>

2: <ejb-relation-name></ejb-relation-name>

3: <ejb-relationship-role>

4: <ejb-relationship-role-name>JobBean</ejb-relationship-role-name>

5: <multiplicity>Many</multiplicity>

6: <relationship-role-source>

7: <ejb-name>JobBean</ejb-name>

8: </relationship-role-source>

9: <cmr-field>

10: <cmr-field-name>skills</cmr-field-name>

11: <cmr-field-type>java.util.Collection</cmr-field-type>

12: </cmr-field>

13: </ejb-relationship-role>

14: <ejb-relationship-role>

15: <ejb-relationship-role-name>

SkillBean

➥</ejb-relationship-role-name>

16: <multiplicity>Many</multiplicity>

7

09 0672323842 CH07 3/20/02 12:20 PM Page 320

320 Day 7

L

ISTING

7.14

Continued

17: <relationship-role-source>

18: <ejb-name>SkillBean</ejb-name>

19: </relationship-role-source>

20: <cmr-field>

21: <cmr-field-name>jobs</cmr-field-name>

22: <cmr-field-type>java.util.Collection</cmr-field-type>

23: </cmr-field>

24: </ejb-relationship-role>

25: </ejb-relation>

Figure 7.10 shows these elements overlaid onto the abstract schema.

F

IGURE

7.10

The ejb-relation element describes the characteristics of a many-to-many relationship in the abstract schema.

Location

name description location location for

<ejb-relation-role>

<ejb-relationsip-role-name>

<multiplicity>

<relationship-role-source>/<ejb-name>

<cmr-field>/<cmr-field-name>

</ejb-relation-role> jobs

Job

customer ref description location Location.name(FK) jobs requires

Skill

name skills description

<ejb-relation-role>

<ejb-relationsip-role-name>

<multiplicity>

<relationship-role-source>/<ejb-name>

<cmr-field>/<cmr-field-name>

</ejb-relation-role>

As always, configuring these deployment descriptor elements can be done either by editing the deployment descriptors directly or by using the deploytool

. Figure 7.11 shows the deploytool for the

Job

/

Skill relationship.

To actually deploy the enterprise application, use the buildAll and deploy batch scripts in the day07\build directory, or use buildAll to assemble the enterprise application and deploy from deploytool

.

09 0672323842 CH07 3/20/02 12:20 PM Page 321

CMP and EJB QL 321

F

IGURE

7.11

deploytool can be used to configure relationships through its

GUI.

Over the last three days, you have seen Session beans, BMP Entity beans and CMP

Entity beans deployed using both the graphical deploytool and then using batch scripts.

Clearly, the information held in those deployment descriptors is valuable and should be under source code control. Moreover, the mechanism for building and deploying enterprise applications should be able to use that information rather than recreate it from scratch. This suggests that, for the production environment, the graphical deploytool should be used only for deploying enterprise applications and for configuring J2EE RI servers.

On the other hand, in the development environment, it can be an error-prone task to attempt to write XML deployment descriptors from scratch. If a valid deployment descriptor already exists, modifying it (to add a new ejb-ref element or something similar) can often be accomplished, but larger changes (such as adding a completely new bean) will be more difficult without much practice. Here, deploytool comes into its own to modify the enterprise application as required (or indeed, to create an enterprise application from scratch).

When you are happy that the beans and clients in your enterprise application are correctly configured, deploytool allows the XML deployment descriptor to be saved, for checking into source code control. This is shown in Figure 7.12.

F

IGURE

7.12

deploytool allows deployment descriptors to be saved as XML files.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 322

322 Day 7

The Tools, Descriptor Viewer menu option brings up a dialog box displaying the XML deployment descriptor; from there, the data can be saved as a file. This menu option is context sensitive, so what it shows will depend on the node selected in the explorer on the left pane in the GUI. The descriptor viewer dialog should be brought up for each node under the enterprise application node, and for the enterprise application node itself.

In the case study, this means for each of the clients, for the data

Entity EJBs, for the agency

Session EJBs, and for the agency application node.

Deploying a CMP Entity Bean

The enterprise application can be deployed either from the command line ( deploy script in the build directory) or using deploytool itself.

However, before an enterprise application containing CMP Entity beans can be deployed, the default SQL must be generated by using the

Deployment Settings dialog box. This is performed once for each CMP Entity bean, as shown in Figure 7.13.

F

IGURE

7.13

deploytool allows

SQL to create the database schema to be generated.

You can see from the figure that J2EE RI generates default SQL. It allows the SQL query for the finder and select methods to be tuned, and also (the container methods radio button) allows the actual SQL to create the tables, insert rows, and so on to be modified also. The case study does not change any of this default SQL.

Note Right at the beginning of today’s chapter, it was noted that the schema of the database for the case study had changed from that of Day 6. If one wanted to use the exact schema from Day 6, it could have been entered here.

09 0672323842 CH07 3/20/02 12:20 PM Page 323

CMP and EJB QL 323

Also, the dialog allows the underlying tables to be created and deleted on deploy/undeploy. This obviously isn’t appropriate for a production environment, because it would delete any data already there. It also is not appropriate for the case study, because there is example data.

Note The

CreateCMPAgency script (provided in the day07\Database directory) creates exactly the same schema as that generated by default by J2EE RI. It also populates that schema with the same data as in Day 6 and creates views for backwards compatibility.

You will recall that the auxiliary deployment descriptor agency_ea-sun-j2ee-ri.xml

contains all the mappings of the logical dependencies of the EJBs to the physical runtime environment. This includes all of the SQL specified in Figure 7.13.

It was noted earlier that when creating a new CMP Entity bean, it is often easiest to load the enterprise application into deploytool and then save the XML deployment descriptor using the Tools, Descriptor Viewer menu option/dialog. Unfortunately, deploytool does not provide any easy way to write out the auxiliary deployment descriptor, and it is required for the command line approach. The buildAll script calls the addJ2eeRiToEar script that does precisely this.

The only real option is to save the agency.ear

file once modified, and then use a tool, such as WinZip, to load up the EAR file. The auxiliary deployment descriptor can be extracted from that.

Patterns and Idioms

This section presents some patterns and idioms that relate to CMP Entity beans. You’ll recognize some of the points made here; they were made earlier in the “Container

Managed Relationships” section.

Normalize/Denormalize Data in ejbLoad()

/ ejbStore()

Under CMP, the ejbLoad() and ejbStore() methods don’t have very much (or indeed anything) to do; the interactions with the persistent data store are done by the EJB container.

However, it may be that the physical schema of the persistent data store (especially if that persistent data store is an RDBMS) does not correspond exactly with the logical schema of the Entity bean.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 324

324 Day 7

For example, the

Applicant table defines two columns— address1 and address2

.

However, at least conceptually, the

Applicant

Entity bean has a vector field of address

, of type

String[]

; there could be many lines in the address

(and it’s just that the physical schema of the persistent data store constrains the size of this vector to 2):

Santa Claus

No. 1 Grotto Square (line 1)

Christmas Town (line 2)

North Pole (line 3)

The World (line 4)

Earth (line 5)

The Solar System (line 6, and so on …)

Because the ejbLoad() method is called after the EJB container has loaded the data, it may renormalize the data. In other words, the data in the two cmp-field s of address1 and address2 can be converted into the

String[] address field. The bean’s clients’ view

(as presented by the methods of the local interface) is that the address field is a vector.

Conversely, the ejbStore() method, called just before the EJB container updates the data, can denormalize the data. In other words, the data in the address vector field can be “posted” back to the address1 and address2 cmp-field s.

Don’t Expose cmp-field s

Although the EJB specification allows cmp-field s to be exposed in the local (or remote) interface of a CMP Entity bean, there are problems with doing so. Because the setter method that corresponds to the field is generated by the EJB container, it is not possible to perform any application-level validation.

Instead, it is better to create a shadow setter method, have it do any validation, and then delegate to the actual cmp-field setter method.

You may also want to create a shadow getter method. This would allow you symmetry in the names of the methods, and you could also perhaps do some caching of values or other application-level logic.

As an example, instead of exposing the getter and setter methods for the description cmp-field of the

Job bean, you might have a local interface of package data; import javax.ejb.*;

// imports omitted public interface JobLocal extends EJBLocalObject {

String getDescriptionField(); void setDescriptionField(String description);

09 0672323842 CH07 3/20/02 12:20 PM Page 325

CMP and EJB QL 325

// code omitted

} with a corresponding implementation of package data; import javax.ejb.*;

// imports omitted public abstract class JobBean implements EntityBean { public String getDescriptionField() {

// any application logic here return getDescription();

} public void setDescriptionField(String description) {

// any application logic and validation here setDescription(description);

} public abstract String getDescription(); public abstract void setDescription(String description);

}

Don’t Expose cmr-field s

Although the EJB specification allows cmr-field s to be exposed in the local interface of a CMP Entity bean, it may be best not to. There are two reasons why exposing the cmrfield causes problems, both related to the returned collection from the getter method of a cmr-field

:

• The first is that this returned collection is mutable. A client can change of the

Entity bean’s relationships with other beans by manipulating this collection. In other words, the bean’s state is changed without it being aware.

• The second is that the returned collection becomes invalid when the transaction context changes. This is actually good that this is so, but it is a subtle point, and some developers might not appreciate it if less than familiar with EJB transactions

(making debugging their application somewhat tricky).

An alternative to exposing the setter method of a cmr-field would be for the bean to offer alternative methods, such as addXxx() and removeXxx()

, on the bean itself and have these call the setter method. An alternative to exposing the getter method would be to expose a shadow method called something like getXxxCopy()

. This would create a copy of the collection. The method name suggests to the client that they will not be able to change the relationships of the bean. Indeed, the returned collection could even be made immutable.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 326

326 Day 7

As an example, instead of exposing the getter and setter methods for the skills cmp-field of the

Job bean, you might have a local interface of package data; import javax.ejb.*; import java.util.*;

// imports omitted public interface JobLocal extends EJBLocalObject {

Collection getSkillsCopy(); void addSkill(SkillLocal skill); void removeSkill(SkillLocal skill);

// code omitted

} with a corresponding implementation of package data; import javax.ejb.*; import java.util.*;

// imports omitted public abstract class JobBean implements EntityBean { public Collection getSkillsCopy() {

List skills = new ArrayList(); for(Iterator iter = getSkills().iterator(); iter.hasNext(); ) { skills.add(iter.next());

} return Collections.unmodifiableList(skills);

} public void addSkill(SkillLocal skill) { getSkills().add(skill);

} public void removeSkill(SkillLocal skill) { getSkills().remove(skill);

} public abstract Collection getSkills(); public abstract void setSkills(Collection skills);

// code omitted

}

Enforce Referential Integrity Through the Bean’s

Interface

Entity beans represent the domain layer in the n-tier architecture, and Entity beans have relationships among themselves. If a method in a bean’s interface has an argument of some bean

(usually for a relationship), this bean should be defined via the local reference rather than by its primary key. In other words, referential integrity is effectively enforced; the client guarantees that the bean exists, because it passes through an actual reference to that bean.

09 0672323842 CH07 3/20/02 12:20 PM Page 327

CMP and EJB QL 327

This idiom is honored implicitly for CMP Entity beans that expose their cmr-field s. For

CMP Entity beans that provide shadow methods (as discussed earlier), these shadow methods should still deal with local references to the related beans, rather than identifying the related bean by its primary key.

Note

This idiom applies equally to BMP Entity beans. Indeed, if BMP Entity beans are written to follow this idiom, it becomes that much easier to convert them to CMP.

Use Select Methods to Implement Home Methods

Select methods can only be called by a bean itself, so they act as helper methods. A common place where they are often used is in implementing home methods.

For example, the

Job bean could have provided a home interface to count the number of jobs advertised. This could have been implemented as follows: package data; import javax.ejb.*;

// imports omitted public interface JobLocal extends EJBLocalObject { int countJobs();

// code omitted

} with a corresponding implementation of package data; import javax.ejb.*; import java.util.*;

// imports omitted public abstract class JobBean implements EntityBean { public int ejbHomeCountJobs() { int count = 0; for (Iterator iter = ejbSelectAllJobs().iterator(); iter.hasNext(); ) { iter.next(); count++;

} return count;

} public abstract Collection ejbSelectAllJobs();

// code omitted

}

7

09 0672323842 CH07 3/20/02 12:20 PM Page 328

328 Day 7

The ejbSelectAllJobs()

EJB QL query string would be simply

SELECT OBJECT(j)

FROM Jobs AS j

Note

EJB QL does not (yet) support a

SELECT COUNT(*) syntax, so this is the only way of performing counts (other than resorting to direct access to the data store).

Gotchas

The following is a quick checklist of “gotchas” to help you with your implementation:

• If the primary key is composite, a custom primary key class must be defined. The fields of this class must be public and must correspond in name and type to cmpfield s of the bean class.

• cmp-field and cmr-field fields must begin with a lowercase letter, (so that it can be capitalized in the corresponding getter and setter method names).

• If a collection is returned from a cmr-field

’s getter method, it must not be modified other than through

Iterator.remove()

(see EJB specification, section

10.3.6.1).

• Collections returned by getter methods cannot be used outside of the transaction context in which they were materialized.

• If a bean has no relationships, the

Collection returned by the cmr-field

’s getter method will be empty, it will not be null

. Conversely, if calling a cmr-field

’s setter method, null cannot be used; instead an empty collection (such as

Collections.EMPTY_LIST

) must be passed in.

• EJB QL strings are in single quotes!!!

• EJB QL strings define placeholders as

?1

,

?2

, and so on (rather than the JDBC syntax of just

?

). This allows the arguments of the corresponding finder or select method to be bound in more than once.

• When comparing strings in EJB QL, the strings must be identical to be equal. (This is different from SQL where trailing whitespace is usually ignored.)

• An EJB QL empty string is

‘’

(0 characters). Some RDBMS treat this as a null string, so beware! (See EJB Specification, section 11.2.9.)

09 0672323842 CH07 3/20/02 12:20 PM Page 329

CMP and EJB QL 329

Summary

Well done! In three days (Days 5, 6, and 7), you’ve covered pretty much everything you need to know about writing EJBs. There’s a little mopping up of relatively minor issues tomorrow, but you now well and truly have the essentials under your belt.

Today you saw how the n-tier architecture is subtly revised again, with the responsibility for persistence delegated downwards (into subclasses) to the EJB container-generated subclasses of the CMP Entity bean. You also learned that the lifecycle for CMP Entity beans is substantially the same as BMP Entity beans and, in general, there is less coding to be done in the lifecycle methods ( ejbCreate()

, ejbLoad()

, ejbStore()

, and ejbRemove()

).

You now know that cmp-field s are defined in the deployment descriptor and correspond to primitive and to

Serializable objects. They are represented in the CMP bean class as abstract getter and setter methods. You also know that cmr-field s work very much the same way but deal with references to the local interfaces of other Entity beans (or collections thereof). This is what makes local interfaces the basis of container-managed relationships.

Finally, you saw how to construct EJB QL queries and how to relate them to both finder methods and to the helper select methods.

Q&A

Q What does CMP stand for?

A CMP stands for Container Managed Persistence.

Q Can the getter and setter methods of cmp-field s and cmr-field s appear in the bean’s interfaces?

A

cmp-field methods can appear in any interface; cmr-field methods can appear only in local interfaces.

Q What keywords or features of ANSI SQL does EJB QL not (yet) support?

A The keywords and features EJB QL doesn’t support include count(*)

, union, group by, order by (among others).

Q How are the parameters of a select method defined?

A The select method is written as a public method in the form ejbSelect() in the class itself.

7

09 0672323842 CH07 3/20/02 12:20 PM Page 330

330 Day 7

Q How is navigability defined in the deployment descriptor?

A The presence of the cmr-field element indicates navigability.

Exercises

The job agency case study defines a complete set of Session beans, and Entity beans. All of the Entity beans except the

Applicant bean are implemented using CMP; the

Applicant bean has been implemented using BMP. The exercise is to implement an

Applicant

Entity bean to use CMP. The source code and deployment descriptors can be found under day07\exercise

, and the directory structure is the same as yesterday.

In more detail, the fields of the

Applicant bean need to be converted into either cmpfield s or cmr-field s:

• login

This is the primary key for the

Applicant

Entity bean and will be a cmpfield

.

• name cmp-field

• email cmp-field

• summary cmp-field

• location cmr-field to the

Location bean. There will be a one-to-many relationship between

Location to

Applicant

.

• skills cmr-field to the

Skill bean. There will be a many-to-many relationship between the

Applicant and

Skill beans.

You should find that the

Job

CMP Entity bean acts as a good model for your new version of the

Applicant bean. One difference is in the primary key. The

Job bean required a

JobPK because it had a composite primary key. For your

Applicant bean, there is no custom primary key, so you will need to nominate the login cmp-field as its primary key using the primkey-field element in the deployment descriptor. Another related difference is that the

Job bean has to deal with ensuring that its customer cmp-field is a valid identifier for a

Customer

. Your

Applicant bean will not have this complication.

There should be no need to change the

Agency and

Register

Session beans that call the

Applicant bean, because it is only the internal implementation that is changing, not the interfaces.

The steps you should perform are as follows:

1. If you didn’t do so earlier today, convert the

Agency database to its CMP version.

The steps to do this were described in the “A Quick Word about the Case Study

Database” section.

09 0672323842 CH07 3/20/02 12:20 PM Page 331

CMP and EJB QL

2. Re-acquaint yourself with the

ApplicantLocalHome and

ApplicantLocal interfaces. However, these will not need to be changed.

3. Update the

ApplicantBean class; base this on

JobBean

.

4. The CMR relationships that will be built will be bi-directional. This is needed because, otherwise, the code generated by the J2EE RI fails to compile. So, in each of

LocationBean and

SkillBean

, uncomment the getApplicants() and setApplicants() methods. Note that these have not been exposed in the respective interfaces.

5. Build the enterprise application ( agency.ear

in the jar directory) and then load it into deploytool

. Delete the

Applicant bean from the enterprise application, and then add it in again into the data_entity_ejbs ejb-jar

. As you add it, you can specify that it is a CMP bean.

6. Specify the appropriate cmp-field s for the new bean.

7. Add a many-to-many relationship from

Applicant to

Skill

. Make the relationship bi-directional.

8. Add a many-to-one relationship from

Applicant to

Location

. Make the relationship bi-directional.

9. Use the deploytool

’s Descriptor Viewer dialog to save the XML deployment descriptor.

10. Now deploy the enterprise application (from the deploytool

GUI), providing the necessary auxiliary deployment information in the wizard that is displayed. Test your program by using the

AllClients client, run with run\runAll

.

11. Optionally, use WinZip or equivalent to extract the auxiliary deployment descriptor and save as dd\agency_ea-sun-j2ee-ri.xml

from the agency.ear

file previously generated. Then re-build and deploy using build\buildAll and build\deploy

.

Good luck. A working example can be found in day07\agency

.

331

7

09 0672323842 CH07 3/20/02 12:20 PM Page 332

10 0672323842 Week2 3/20/02 9:27 AM Page 333

W

EEK

2

Developing J2EE

Applications

8 Transactions and Persistence

9 Java Messaging Service

10 Message-Driven Beans

11 JavaMail

12 Servlets

13 JavaServer Pages

14 JSP Tag Libraries

12

13

14

10

11

8

9

10 0672323842 Week2 3/20/02 9:27 AM Page 334

11 0672323842 CH08 3/20/02 9:29 AM Page 335

W

EEK

2

D

AY

8

Transactions and

Persistence

You have spent the last three days covering EJBs in detail. In particular, you learned how to specify, implement, configure and deploy container-managed persistence (CMP) Entity beans. Along with BMP Entity beans (Day 6, “Entity

EJBs”) and Session beans (Day 5, “Session EJBs”), you now have a good appreciation of the EJB technology.

EJBs have been called transactional middle-tier components. Until now, you haven’t had to worry too much about transactions in an EJB context because they are reasonably transparent. In fact, you have been using container-

managed transaction demarcation. However, for those cases where you require explicit control, EJB provides two solutions. You can write beans that manage their own transactions—bean managed transaction demarcation—or you can also extend the lifecycle of Session beans to give them visibility of the transaction demarcations. You will be learning about this today.

11 0672323842 CH08 3/20/02 9:29 AM Page 336

336 Day 8

You spent Day 6 and Day 7, “CMP and EJB QL,” comparing the two different persistence approaches offered by EJB in the guise of BMP and CMP Entity beans. The BMP

Entity beans were implemented using JDBC, but that is only one of a number of technologies offered by J2EE and Java in general. Today, you will learn

• How to manage transactions explicitly in EJBs

• How transactions are managed “behind the scenes” in an EJB environment

• Some other persistence technologies other than JDBC—specifically, SQLj and

JDO

Overview of Transactions

If you’ve used RDBMS before, or completed a Computer Studies course or read any other J2EE book, you’ve probably read a section like this one already. But read on anyway, if only to be acquainted with some of the J2EE terminology that is used in this arena.

A transaction is an atomic unit of work:

Atomic unit means indivisible—either every step within the transaction completes or none of them do.

Work here usually means some modification to data within a persistent data store.

In RDBMS terms, this means one or more

INSERT

,

UPDATE

, or

DELETE s. However, strictly speaking, it also applies to reading of data through

SELECT statements.

For a persistent data store to support transactions, it must pass the so-called “ACID” test:

Atomic—The transaction is indivisible; the data store must ensure this is true.

Consistent—The data store goes from one consistent point to another. Before the transaction, the data store is consistent; afterwards, it is still consistent.

The age-old example is of transferring money between bank accounts. This will involve two

UPDATE s, one decrementing the balance of account #1 and the other incrementing the balance of account #2. If only one

UPDATE occurs, the transaction is not atomic, and the data store is no longer in a consistent state.

Isolation—The data store gives the illusion that the transaction is being performed in isolation. Enterprise applications have many concurrent users who are all performing transactions at the same time, so behind the scenes, the data store uses techniques, such as locks, to serialize access to the contended data where necessary.

Durability—If a transaction completes, any changes made to the data as a result of that transaction must be durable. In other words, if the power were to fail a millisecond after the transaction has completed, the change must still be there when power is reconnected.

11 0672323842 CH08 3/20/02 9:29 AM Page 337

Transactions and Persistence 337

Note

Many data stores use transaction logs to address this requirement. The transaction log holds a journal of changes made to the actual data. When the transaction completes, the log is written to disk, although the actual data need not be.

If you are a Windows user, you may know that Windows NT, 2000 and XP support a filesystem type called NTFS. This replaces the FAT and FAT32 filesystem types used in Windows 95, 98, and ME.

Microsoft often say that NTFS is more reliable, although slightly slower than

FAT/FAT32. This is because NTFS has a built-in transaction log, whereas

FAT/FAT32 does not.

Many data stores allow transactions to be started explicitly using a syntax such as the following: begin transaction t1 where t1 is the (optional) name transaction. Transactions are completed using either commit

(make changes permanent) or rollback

(undo all changes made in the transaction, and revert all data back to the state before the transaction began). Many data stores will use commit transaction t1 and rollback transaction t1

8

Note

Some data stores support the concept of nested transactions, whereby (for a single user) one transaction can be started while another transaction is still in progress. In other words, two begin transaction s can be submitted without a commit or rollback between them.

However, the EJB specification and many others support only flat transac-

tions, whereby one transaction must be completed before another is begun

(see EJB specification, section 17.1.2). Consequently, nested transactions are not considered further.

To conclude this short introduction, consider the fragment of SQL shown in Listing 8.1.

It transfers $50 from account #20457 to account #19834.

11 0672323842 CH08 3/20/02 9:29 AM Page 338

338 Day 8

L

ISTING

8.1

Example Fragment of SQL to Transfer Money Between Accounts

1: begin transaction transfer_money

2:

3: update account

4: set balance = balance - 50

5: where account_id = 20457

6:

7: update account

8: set balance = balance + 50

9: where account_id = 19834

10:

11: commit transaction transfer_money

In effect, there are two different types of commands:

• Lines 1 and 11 demarcate the transaction.

• Lines 3–5 and 7–9 modify data.

A conventional RDBMS processes all of the SQL in Listing 8.1, but it is performing two different roles in doing so. To understand and process the transaction demarcation commands, it is acting as a transaction manager. To understand and process the remaining lines, it is acting as a resource manager.

In the EJB specification, these two responsibilities are split. In principle, you can think of the EJB container as acting as the transaction manager, and the persistent data store acting only as the resource manager. The term transaction coordinator is sometimes used instead of transaction manager because there could be more than one resource whose transactions are being coordinated.

Splitting the responsibilities of transaction management and resource management has two consequences. For the bean provider, it means that to start a transaction, the bean must interact both with the EJB container and with the persistent data store. The former interaction is largely implicit because it is configured through the deployment descriptor

(and as you know, the latter interaction is implicit if CMP Entity beans are used). The other consequence is that, for the persistent data store, it must defer all transaction control responsibility up to the EJB container. Behind the scenes, there is some quite sophisticated communication going on; you’ll learn a little about this activity later on today.

Container-Managed Transaction Demarcation

You’ve spent the last three days writing and deploying EJBs without really having to worry too much about transactions. This isn’t to say that there have been no transactions in use; far from it. Every interaction with the database performed in the case study has

11 0672323842 CH08 3/20/02 9:29 AM Page 339

Transactions and Persistence 339 involved transactions. However, the Session and Entity beans deployed have used con-

tainer manager transaction demarcation (here referred to as CMTD, though the abbreviation isn’t used in the EJB specification). Information in the deployment descriptor indicates when the EJB container should start and commit transactions.

Figure 8.1 shows a diagram that you saw first on Day 6.

F

IGURE

8.1

The EJB proxy objects implement transaction

(and security) control.

home stub remote client remote stub location transparency home local client remote security and transactions local home local bean

8

This shows how the EJB proxy objects (those implementing the javax.ejb.EJBObject

or javax.ejb.EJBLocalObject

interfaces) implement the transaction semantics. This is one of the reasons that a bean must never implement its own remote interface. To do so would mean that it could unwittingly return a reference to itself as a

Remote interface, subverting any security and transaction checks performed by its proxy.

Listing 8.2 shows a fragment of the deployment descriptor for the

AdvertiseJob

Session bean from Day 7.

L

ISTING

8.2

Deployment Descriptor for

AdvertiseJob

Session Bean

1: <ejb-jar>

2: <display-name>Agency</display-name>

3: <enterprise-beans>

4: <session>

5: <display-name>AdvertiseJobBean</display-name>

6: … lines omitted …

7: <transaction-type>Container</transaction-type>

8: … lines omitted …

9: </session>

10: </enterprise-beans>

11: <assembly-descriptor>

12: <container-transaction>

11 0672323842 CH08 3/20/02 9:29 AM Page 340

340 Day 8

L

ISTING

8.2

Continued

13: <method>

14: <ejb-name>AdvertiseJobBean</ejb-name>

15: <method-intf>Remote</method-intf>

16: <method-name>updateDetails</method-name>

17: <method-params>

18: <method-param>java.lang.String</method-param>

19: <method-param>java.lang.String</method-param>

20: <method-param>java.lang.String[]</method-param>

21: </method-params>

22: </method>

23: <trans-attribute>Required</trans-attribute>

24: </container-transaction>

25: … lines omitted …

26: </assembly-descriptor>

27: </ejb-jar>

As you have seen over the last three days, the enterprise-beans element consists of session or entity elements. The session element has a transaction-type element which, under CMTD, should have the value of Container. For entity elements, the transactiontype is not specified because entity beans must always be deployed using CMTD, so it is implicit.

Each method of the remote interface must be present in a container-transaction element. The same is true of the methods in the home interface. The relevant DTD definitions that govern the structure of the deployment descriptor are as follows:

<!ELEMENT ejb-jar (description?, display-name?, small-icon?, large-icon?, enterprise-beans, relationships?, assembly-descriptor?, ejb-client-jar?)>

The assembly-descriptor element is defined as follows:

<!ELEMENT assembly-descriptor (security-role*, method-permission*,

➥container-transaction*, exclude-list?)>

You can see here that assembly-descriptor element is all about providing the information used to create the EJB proxy objects. The security-role

, method-permission

, and exclude-list elements are security related, and the container-transaction element obviously defines transaction-related information.

The security-related elements of the assembly-descriptor element are not considered further here. However, the container-transaction element is relevant to the discussion.

It is defined in the DTD as follows:

<!ELEMENT container-transaction (description?, method+, trans-attribute)> and the method element is defined as

<!ELEMENT method (description?, ejb-name, method-intf?,

➥method-name, method-params?)>

11 0672323842 CH08 3/20/02 9:29 AM Page 341

Transactions and Persistence 341

So, a container-transaction element identifies one or more method s. The method element identifies a method in the home or remote interface; the method-intf element is only needed in those rare occasions when there happens to be a method of the same name in both the home and remote interfaces. The method-name must be specified, although the value of

* can be used as a convenient shortcut to indicate all methods. The method-params element is optional and is used to distinguish between overloaded versions of the same method name. If not specified, the method element identifies all overloaded versions of the method with the specified name.

Finally, the bit that really matters. The trans-attribute element indicates the transactional characteristics to be enforced when the specified method is invoked. A transaction may or may not be in progress; in the terminology of the EJB specification, there may or may not be a current valid transactional context. When a method is invoked, the EJB container needs to know what should occur. For example, if there is no transaction in progress, is one needed to execute the method? Should a transaction be started automatically if there isn’t one? Perhaps a transaction can be started even if another one is in progress? And so on.

There are six possible values; their semantics are shown in Table 8.1.

T

ABLE

8.1

Different CMTD Semantics Are Indicated by the trans-attribute

Element transattribute

NotSupported

Required

Supports

Meaning

Method accesses a resource manager that does not support an external transaction coordinator. Any current transaction context will be suspended for the duration of the method.

A transaction context is guaranteed. The current transaction context will be used if present; otherwise, one will be created.

Use valid transaction context if available (acts like

Required

). Otherwise, use unspecified transaction context (acts like

NotSupported

).

Notes

The EJB architecture does not specify the transactional semantics of the method.

Commonly used.

RequiresNew

A new transaction context will be created. Any existing valid transaction context will be suspended for the duration of the method.

Acts as either

Required or

NotSupported

. This makes the

Supports a highly dubious choice. The method must guarantee to work in the same way whether or not there is a transaction context available.

Can reduce contention (for example, for a bean that generates unique IDs or for a bean that writes to a log).

8

11 0672323842 CH08 3/20/02 9:29 AM Page 342

342 Day 8

T

ABLE

8.1

Continued transattribute

Mandatory

Meaning

A valid transaction context must exist; an exception will be thrown by the EJB container otherwise. The transaction context will be used.

Never

Notes

Useful for helper beans’ methods, designed to be called only from another bean.

Acts as

NotSupported

.

There must be no current transaction context.

An exception will be thrown by the EJB container otherwise. The method invokes with an unspecified transaction context.

For Entity beans, only the

Required

,

RequiresNew

, and

Mandatory trans-attribute values are recommended. The problem with

NotSupported

,

Never

, and (if invoked with no current transaction context)

Supports is that, in addition to performing the business method, the EJB container must also perform the ejbLoad() and ejbStore() methods.

These will be performed with the same transactional context as the business method, which is to say, with no transactional context. What might happen then is somewhat undefined, as the EJB specification is at pains to point out. Indeed, it goes so far as to list (in section 17.6.5) four or five different ways in which the EJB container might decide to act.

Caution Never use NotSupported, Never, or Supports as trans-attribute values with Entity beans.

As usual, the deploytool

GUI can also be used to configure the information within the deployment descriptor, as shown in Figure 8.2.

You are likely to find that the vast majority of your beans’ methods will use the

Required trans-attribute value. Indeed, this is the value that has been used in the case study over previous days.

Although CMTD means that the EJB container automatically starts, suspends, and completes transactions, this is not to say that the beans themselves cannot influence the transactions. After all, if an Entity bean hits an error condition that means that the transaction should be aborted, it needs some way to indicate this to the EJB container. As an example, consider the hackneyed example of withdrawing money from a bank account. If the balance would go into the red, (or perhaps more likely, beyond an overdraft limit), the

Entity bean that represents the account would want to indicate that the transaction should be aborted.

11 0672323842 CH08 3/20/02 9:29 AM Page 343

F

IGURE

8.2

deploytool lets CMT characteristics be defined on a permethod basis.

Transactions and Persistence 343

8

Note

It’s interesting to compare the XML deployment descriptor approach to using deploytool

. The deployment descriptor places the security- and transaction-related information in the assembly-descriptor element, away from the definition of the beans themselves (which reside under the enterprise-beans element). This is interesting in its own right, because it ties back to the different EJB roles you learned about back on Day 2, “The J2EE

Platform and Roles.” The intention is that the bean provider completes just the information in the enterprise-beans element, while the application assembler completes the information under the assembly-descriptor

. This allows an EJB component to be used in different applications, with different transaction and security requirements.

In contrast, the deploytool does not differentiate between the two roles; the transaction (and security) information are just two of the seven tabs on the right side of the GUI, providing information about the selected EJB; the other five tabs pertain to information found under the enterprise-beans element.

To do this, the bean can use two transaction-related methods provided by its

EJBContext

.

In the case of a Session bean, this will be the javax.ejb.SessionContext

passed in through the setSessionContext() method, and for an Entity bean, this will be the javax.ejb.EntityContext

passed in through setEntityContext()

. To remind you,

Figure 8.3 shows a UML class diagram illustrating the methods provided by these interfaces.

11 0672323842 CH08 3/20/02 9:29 AM Page 344

344 Day 8

F

IGURE

8.3

The

EJBContext provides access to the current transaction.

interface

EJBContext

+ getEJBHome():EJBHome

+ getEJBLocalHome():EJBLocalHome

+ getEnvironment():Properties

+ getCallerIdentity():Identity

+ getCallerPrincipal():Principal

+ IsCallerInRole(Identity:Identity):boolean

+ IsCallerInRole(s:String):boolean

+ getUserTransaction():UserTransaction

+ setRollbackOnly():void

+ getRollbackOnly():boolean interface

EntityContext

+ getEJBLocalObject():EJBLocalO

+ getEJBObject():EJBObject

+ getPrimaryKey():Object

+ setEJBLocalObject(pO:EJBObje interface

javax.transaction.UserTransaction

+ begin():void

+ commit():void

+ getStatus():int

+ rollback():void

+ setRollbackOnly():void

+ setTransactionTimeout(:int):void !

interface

SessionContext

+ getEJBLocalObject():EJBLocalO

+ getEJBObject():EJBObject interface

javax.transaction.Status

+STATUS_ACTIVE:int

+STATUS_MARKED_ROLLBACK:int

+STATUS_PREPARED:int

+STATUS_COMMITTED:int

+STATUS_ROLLEDBACK:int

+STATUS_UNKNOWN:int

+STATUS_NO_TRANSACTION:int

+STATUS_PREPARING:int

+STATUS_COMMITTING:int

+STATUS_ROLLING_BACK:int

!

To cause the transaction to be aborted, the CMTD bean can call setRollbackOnly()

.

This instructs the EJB container to prevent the transaction from being committed. The bean cannot rollback the transaction directly, because the transaction itself is “owned” by the EJB container, not the bean. The getRollbackOnly() method obviously just indicates whether the transaction has been marked for rollback.

There is one other transaction-related method in

EJBContext

, namely getUserTransaction()

. However, this cannot be called by a CMTD bean, and any attempt to do so will result in the EJB container throwing a java.lang.IllegalStateException.

Note

The remaining methods in

EJBContext provide access to the home interface(s) of the bean ( getEJBHome() and getEJBLocalHome()

) and to the security context ( getCallerPrincipal()

, isCallerInRole(String)

). The other methods have been deprecated.

One last point relating to CMTD beans—they must not make use of any resource manager-specific transaction management methods that would interfere with the EJB container’s own management of the transaction context. Consequently, a BMP Entity bean cannot call commit()

, setAutoCommit()

, and so on a java.sql.Connection

object.

If your bean does need more fine-grained control over transactions, the bean must be deployed using bean-managed transaction demarcation. This is discussed next.

11 0672323842 CH08 3/20/02 9:29 AM Page 345

Transactions and Persistence 345

Bean Managed Transaction Demarcation

If an EJB is deployed using bean managed transaction demarcation (here referred to as

BMTD, though this abbreviation isn’t used in the EJB specification itself), the EJB container allows the bean to obtain a reference to a javax.transaction.UserTransaction

object using the

EJBContext

. You can see this in Figure 8.3.

Motivation and Restrictions

An EJB might need to be deployed under BMTD if the conditions on which a transaction is started depend on some programmatic condition. It could be that one method starts the transaction and another method completes the transaction.

However, the cases where BMTD is needed are few and far between. Indeed, they are so rare that the EJB specification limits BMTD to Session beans. Entity beans can only be deployed with CMTD. This makes sense because Entity beans represent persistent transactional data; the transaction context should be as deterministic as possible.

Moreover, if a stateless Session bean starts a transaction, it must also commit that transaction before the method completes. After all, the stateless Session bean will have no memory of the client that just invoked its method after that method completes, so one cannot expect that the transaction context is somehow miraculously preserved. If you write a BMTD stateless Session bean that does not commit its own transaction, the EJB container will rollback the transaction and throw an exception back to the client

( java.rmi.RemoteException

for remote clients, or javax.ejb.EJBException

for local).

Indeed, even with stateful Session beans, there is a restriction. Any current transaction in progress when the BMTD Session bean is called will be suspended by the EJB container, not propagated to the BMTD bean. It is possible that, from the bean’s perspective, there is a current transaction, but that would refer to any transaction not committed when a method on that bean was last called.

Using the Java Transaction API

When a Session bean is deployed under BMTD, there is an implementation choice as to how it should manage its transactions. If interacting solely with an RDBMS, the Session bean can manage the transactions directly through the JDBC API. Alternatively, it can use the Java Transaction API, defined by the classes and interfaces in the javax.transaction and the javax.transaction.xa

packages. The latter is to be preferred, if only because transactional access to Java Messaging Service resources (you’ll be learning more about these tomorrow and the day after) can only be performed through the JTA

API. Equally, servlets can also use the JTA API.

8

11 0672323842 CH08 3/20/02 9:29 AM Page 346

346 Day 8

Note The Java 2 Platform Enterprise Edition Specification, the document that defines the interoperability of all the technologies that make up the J2EE platform, only discusses transaction interoperability in the context of the JTA API.

If nothing else, the semantics of intermixing JDBC and JTA calls are not exhaustively defined, so this should be avoided to minimize chances of portability problems if moving to a different vendor’s EJB container.

For a Session bean to start a transaction, it should first call the getUserTransaction() method of its

SessionContext

. You’ll recall that this was the method that throws an exception under CMTD, but it is the centerpiece of transaction control under BMTD.

Obtaining a

UserTransaction does not mean that a transaction has been started. Rather, it must be started using the begin() method. The transaction can then be completed using either the commit() or the rollback() method. The current status can also be obtained using getStatus()

. This returns an int whose meaning is defined by the constants in the javax.transaction.Status

interface. Some of the most common status values are shown in Table 8.2.

T

ABLE

8.2

Some of the Constants Defined in javax.transaction.Status

Constant Meaning Typical actions

STATUS_NO_TRANSACTION

No transaction is active.

tran.begin() to start new transaction.

STATUS_ACTIVE

A transaction is active and can be used.

Use resource manager.

tran.commit() to commit tran.rollback() to rollback

STATUS_MARKED_ROLLBACK

A transaction is active, but has been marked for rollback. Any attempt to commit it will result in a javax.transaction.

RollbackException being thrown.

tran.rollback()

Note There are more constants in the

Status interface than those listed in Table

8.2. Later today, (in the “Transactions: Behind the Scenes” section), you’ll be learning about some of the “under-the-covers” mechanics of transaction management; the full list is presented there.

Listing 8.3 shows a possible implementation for the updateDetails() method of

AdvertiseJob bean using BMTD.

11 0672323842 CH08 3/20/02 9:29 AM Page 347

Transactions and Persistence

L

ISTING

8.3

BMTD Implementation of

AdvertiseJobBean.updateDetails()

1: package agency;

2:

3: import javax.ejb.*;

4: import javax.transaction.*;

5: // imports omitted

6:

7: public class AdvertiseJobBean extends SessionBean {

8: public void updateDetails (String description,

➥String locationName, String[] skillNames) {

9:

10: int initialTranStatus = beginTransactionIfRequired();

11:

12: if (skillNames == null) {

13: skillNames = new String[0];

14: }

15: List skillList;

16: try {

17: skillList = skillHome.lookup(Arrays.asList(skillNames));

18: } catch(FinderException ex) {

19: error(“Invalid skill”, ex, initialTranStatus);

➥ // throws an exception

20: return;

21: }

22:

23: LocationLocal location=null;

24: if (locationName != null) {

25: try {

26: location = locationHome.findByPrimaryKey(locationName);

27: } catch(FinderException ex) {

28: error(“Invalid location”, ex, initialTranStatus);

➥ // throws an exception

29: return;

30: }

31: }

32:

33: job.setDescription(description);

34: job.setLocation(location);

35: job.setSkills(skillList);

36:

37: completeTransactionIfRequired(initialTranStatus);

38: }

39:

40: private int beginTransactionIfRequired() {

41:

42: UserTransaction tran = this.ctx.getUserTransaction();

43: // start a new transaction if needed, else just use existing.

44: // (simulates trans-attribute of REQUIRED)

45: int initialTranStatus;

347

8

11 0672323842 CH08 3/20/02 9:29 AM Page 348

348 Day 8

L

ISTING

8.3

Continued

46: try {

47: initialTranStatus = tran.getStatus();

48: switch(initialTranStatus) {

49: case Status.STATUS_ACTIVE:

50: // just use

51: break;

52: case Status.STATUS_NO_TRANSACTION:

53: // create

54: try {

55: tran.begin();

56: } catch(NotSupportedException ex) {

57: // shouldn’t happen (only thrown if asking for nested exception

58: // and is not supported by the resource manager; not attempting

59: // to do this here).

60: throw new EJBException(

➥ “Unable to begin transaction”, ex);

61: }

62: break;

63:

64: // code omitted; other Status’ covered later

65:

66: default:

67: throw new EJBException(

68: “Transaction status invalid, status = “ +

➥ statusAsString(initialTranStatus));

69: }

70: } catch(SystemException ex) {

71: throw new EJBException(“Unable to begin transaction”, ex);

72: }

73:

74: return initialTranStatus;

75: }

76:

77: /**

78: * expects initialTranStatus to be either

➥ STATUS_NO_TRANSACTION or STATUS_ACTIVE;

79: * semantics undefined otherwise

80: */

81: private void completeTransactionIfRequired(int initialTranStatus) {

82:

83: UserTransaction tran = this.ctx.getUserTransaction();

84:

85: // if transaction was started, then commit / rollback as needed.

86: // (simulates trans-attribute of REQUIRED)

87: if (initialTranStatus == Status.STATUS_NO_TRANSACTION) {

88: try {

89: if (tran.getStatus() == Status.STATUS_MARKED_ROLLBACK) {

90: tran.rollback();

11 0672323842 CH08 3/20/02 9:29 AM Page 349

Transactions and Persistence 349

L

ISTING

8.3

Continued

91: } else {

92: tran.commit();

93: }

94: } catch(Exception ex) {

95: throw new EJBException(

➥ “Unable to complete transaction”, ex);

96: }

97: }

98: }

99: }

The two helper methods, beginTransactionIfRequired() and completeTransactionIfRequired()

, isolate the actual transaction management code, so it can be reused across different methods.

Deploying a BMTD Bean

Of course, when deploying a bean under BMTD, the deployment descriptor should indicate a transaction-type element of

Bean

, and you will not need any containertransaction elements under the application-assembly element. Figure 8.4 shows deploytool for the

AdvertiseJob bean, indicating this fact.

F

IGURE

8.4

BMTD is indicated through the deployment descriptor, as shown in deploytool

.

8

11 0672323842 CH08 3/20/02 9:29 AM Page 350

350 Day 8

Incidentally, if a BMTD Session bean calls getRollbackOnly() or setRollbackOnly() on its

SessionContext

, the EJB container will throw a java.lang.IllegalStateException

. This is reasonable; if a BMTD has access to the

UserTransaction object, it has no need for these methods. Instead, it can call the getStatus() method of

UserTransaction

, and explicitly call rollback() if needed.

Client-Demarcated Transactions

As well as Session beans managing their own transactions, it is also possible for clients to initiate the transaction and have it propagate through to the EJBs. Here, “client” means either an application client written using the Swing GUI (such as you have seen in the case study), or it could equally refer to a Web-based client implemented using servlets and JSPs.

For either of these clients, the EJB architecture requires that a

UserTransaction context can be obtained via JNDI, bound under the name of java:comp/UserTransaction

. So the code fragment shown in Listing 8.4 will do the trick.

L

ISTING

8.4

Obtaining a

UserTransaction

Object from JNDI

1: // assuming:

2: // import javax.naming.*;

3: // import javax.transaction.*;

4: InitialContext ctx = new InitialContext();

5: UserTransaction tran = (UserTransaction)

➥ctx.lookup(“java:comp/UserTransaction”);

6: tran.begin();

7: // call session and entity beans

8: tran.commit();

That said, if you find yourself needing to use client-demarcated transactions, you should look at your application design and see if you are happy with it. After all, Session beans

(are meant to) represent the application logic of your application, and this should surely include defining the transactional boundaries of changes to persistent data. Application clients should only provide a presentational interface to your application.

If that philosophical argument does not appeal, perhaps this might. A rogue client could be coded such that it begins a transaction, interacts with (and therefore ties up) various resources, such as Entity beans, and then not commit. This could seriously impact the performance of your application.

Exceptions Revisited

On Days 5 and 6, you learned the appropriate exceptions for your EJB to throw. In summary

11 0672323842 CH08 3/20/02 9:29 AM Page 351

Transactions and Persistence 351

• To throw an application-level exception (indicating that a possibly recoverable condition has arisen), throw any checked exception (excluding java.rmi.RemoteException

).

• To throw a system-level exception (indicating that a non-recoverable severe condition has arisen), throw any java.lang.RuntimeException

(usually a subclass of javax.ejb.EJBException

).

If an application-level exception is thrown by a bean, it is up to that bean whether the current transaction is affected or not. If the bean takes no action other than raising its exception, the current transaction will be unaffected. The exception will simply propagate back to the calling client.

However, CMTD beans may decide to mark the current transaction for rollback, meaning that the “owner” of the transaction (the EJB container or some BMTD bean) will be unable to commit that transaction.

If a BMTD bean hits an error condition, it has a choice. Because it “owns” the transaction, it can simply do a rollback. Alternatively, it might elect to keep the transaction active.

If a system-level exception is thrown by a bean, this does have consequences for any current transaction. If any bean throws a system exception, the EJB container will mark the current transaction for rollback. If that bean happens to be a CMTD bean, and the EJB container started a transaction just before invoking the CMTD method (as a result of a

Required or

RequiresNew trans-attribute

), the EJB container will actually rollback that transaction.

To summarize,

• An application-level exception may or may not leave the current transaction active; use getStatus() or getRollbackOnly() to find out.

• A system-level exception will either mark the current transaction for rollback or even do the rollback.

One last thing on transactions and exceptions. Most of the exceptions in javax.ejb

(

CreateException

,

RemoveException

, and so on) are application exceptions. Some of these, especially with CMP Entity beans, are raised by the EJB container itself. Rather unhappily, the EJB specification does not mandate whether these application exceptions should mark any current transaction for rollback (see section 10.5.8). Instead, it just indicates that the getStatus() or getRollbackOnly() methods should be used to determine the status of the current transaction. In practical terms, what this means is that different

EJB containers could have different implementations, compromising portability.

8

11 0672323842 CH08 3/20/02 9:29 AM Page 352

352 Day 8

Extended Stateful Session Bean Lifecycle

Occasionally, there is a need for a stateful Session bean to have visibility as to the progress of the underlying transaction. Specifically, a bean might need to know

• Has the transaction started?

• Is the transaction about to complete?

• Did the transaction complete successfully or was it rolled back?

For example, consider the age-old example of a

ShoppingCart bean. In its purchase() method, it is likely to modify its internal state. Perhaps it holds its contents in a java.util.List

called currentContents

. On purchase()

, it might move the contents of currentContents to another java.util.List

called recentlyBought

.

Suppose, then, that the transaction that actually modifies the persistent data store fails to complete. Maybe the shopper doesn’t have enough limit left on his or her credit card.

Because Session beans are not transactional, the

ShoppingCart bean needs to know that it should reset its internal state back to the beginning of the transaction. In other words, it needs to move the contents of the recentlyBought list back over to currentContents

.

Obviously, there is no issue for stateful Session beans deployed under BMTD, because they are the owner of the transaction anyway and know when the tran.begin() and tran.commit() methods will be invoked. But for CMTD Session beans, there is an issue.

The EJB specification addresses this by extending the lifecycle of the bean. If a stateful

Session bean implements the javax.ejb.SessionSynchronization

interface, three additional lifecycle methods are defined and will be called at the appropriate points:

• afterBegin()

The transaction has just begun.

• beforeCompletion()

The transaction is about to be committed.

• afterCompletion(boolean)

The transaction has completed. The boolean argument has the value true to indicate that the transaction was committed, or false to indicate that the transaction was rolled back.

Figure 8.5 is a reworking of Figure 5.14 that you saw back on Day 5. It shows the stateful Session bean’s view of its lifecycle. (The client’s view and the actual lifecycle managed by the EJB container are unchanged).

11 0672323842 CH08 3/20/02 9:29 AM Page 353

Transactions and Persistence 353

F

IGURE

8.5

The

Session

Synchronization interface gives the stateful Session bean visibility to the transactions managed under

CMTD.

[timeout] pool too small/setSessionContext

Pooled

[surplus] remove/ejbRemove

Passivated create/ejbCreate

Bound to client

[too many active]

/ejbPassivate Ready non-TX business method

[business method or remove invoked]/ejbActivate commit/beforeCompletion afterCompletion(true)

[called in xactn]/afterBegin rollback/afterCompletion(false)

Ready in xact

TX business method

One common pattern for using this interface is to use the afterBegin() method to load any data from the data store (perhaps in the form of Entity beans), and then use the beforeCompletion() method to write any cached data that may have changed. One immediate application might be with respect to multi-valued cmr-field s. You will recall from yesterday that collections returned by the getter method of a cmr-field are valid only for the duration of a transaction. These two methods scope the duration that such a returned collection can be used.

There are analogies here also with SQL, where the afterBegin() corresponds to a

SELECT … WITH HOLDLOCK statement, and beforeCompletion() corresponds to the

UPDATE statements.

If the

SessionSynchronization interface is implemented by a Session bean, the only allowable values for the trans-attribute element in its deployment descriptor are

Required

,

RequiresNew

, or

Mandatory

. This is because these are the only attributes that can guarantee the presence of a transaction.

8

11 0672323842 CH08 3/20/02 9:29 AM Page 354

354 Day 8

Transactions: Behind the Scenes

The EJB specification starts off by listing nine goals. The second of these reads as follows:

“The Enterprise JavaBeans architecture will make it easy to write applications:

Application developers will not have to understand low-level transaction and state management details, multi-threading, connection pooling, and other complex lowlevel APIs.”

The EJB specification (downloadable from http://java.sun.com/products/ejb/index.html

) largely succeeds in addressing this goal—as a developer, you really do not need much knowledge about how transactions are managed. The fact that this book only covers transactions today is testament to that.

Nevertheless, like most technical topics, it can be helpful to have an insight as to what is going on “behind the scenes.” But if you want to skip this material and make a shorter day of it, please do so. You can always re-read at a later date if you find yourself wanting to know more.

Transaction Managers, Resource Managers, and 2PC

You already know about the terms resource manager and transaction manager (or transaction coordinator). In most EJB applications, an RDBMS will take the place of a resource manager, and the EJB container itself will be the transaction manager.

This division of responsibilities is required because in an EJB architecture, the data used by Session beans or represented by Entity beans may reside in more than one persistent data store. For example, one Entity bean might map onto an Oracle RDBMS, and another Entity bean may map onto a Sybase RDBMS, as shown in Figure 8.6. (The numbers will be explained shortly.)

F

IGURE

8.6

The J2EE platform separates resources and transaction managers.

local client session bean

1.

5.

2.

entity bean entity bean

3.

Syb

Ora transaction manager

4.

6.

7.

11 0672323842 CH08 3/20/02 9:29 AM Page 355

Transactions and Persistence 355

Leaving the transaction management responsibilities with the RDBMSs is not suitable.

Doing so would mean that each RDBMS would have its own transaction. If the first transaction succeeded but the second transaction failed, the logical data set represented by the Entity beans would no longer be consistent. The “C” of the ACID test would be broken.

Another case where only a single transaction is required is when interacting with JMS queues or topics. You can imagine that a queue might implement a To Do list. A task on the To Do list might be “invoice customer A for $20,” involving an update to an

RDBMS. If the task is removed from the To Do list as one transaction, and the update to the RDBMS performed as another transaction, there is again the possibility that the second transaction fails. In other words, the task is removed from the To Do list, but no invoice is raised.

The two phase commit protocol (more commonly called just 2PC) is the mechanism by which the transaction manager (EJB container) interacts with each of the resource managers (RDBMS or JMS queues and topics). In the EJB environment, it works as follows

(the numbers correspond to the steps in Figure 8.6):

1. The transaction manager within the EJB container creates a new transaction as needed. Generally, this will be when a Session bean’s method is invoked.

If the Session bean has been deployed under CMTD, the bean’s proxy will make this request to the transaction manager. If the Session bean is deployed under

BMTD, the bean itself will effectively make this request.

2. The Session bean interacts with Entity beans. The current transaction will be propagated through to them (assuming they are deployed with

Required or

Mandatory value for their trans-attribute element).

3. In turn, the Entity beans interact with the RDBMSs through an XA-compliant java.sql.Connection

. “XA-compliant” means supporting the two phase commit protocol (or 2PC); more on this shortly. If the Entity bean is BMP, the interaction with the RDBMS will be done by the bean itself; if the bean is CMP, the interaction will be by the generated subclass. Either way, it amounts to the same thing.

4. Each of the XA-compliant

Connection s registers itself with the EJB’s transaction manager. More correctly, a javax.transaction.xa.XAResource

representing and associated with the

Connection is registered as part of the current javax.transaction.Transaction

.

5. When all method calls to the Entity beans have been made, the Session bean indicates to the transaction manager that the transaction should be committed.

8

11 0672323842 CH08 3/20/02 9:29 AM Page 356

356 Day 8

6. The transaction manager performs the first “prepare” phase of the commit. It iterates over each of the

XAResource s that constitute the transaction and requests confirmation that they are ready to be committed. In turn, the

XAResource just delegates this request to its corresponding XA-compliant java.sql.Connection

.

7. When all resources have indicated that they are prepared, the transaction manager performs the second “commit” phase. It again iterates over each of the

XAResource s and requests them to commit.

The JTA API

An XA-compliant java.sql.Connection

, as previously described, is one that provides the ability to return an

XAResource for registering with the current transaction. The XA protocol is an industry standard, defined by the X/Open group, to allow a transactional resource manager to participate in a global transaction controlled by an external transaction manager. The JTA API is effectively a mapping into Java of this XA protocol.

In fact, the java.sql.Connection

interface does not mandate XA-compliance, so the previous description was a slight simplification. Instead, XA-compliance is provided by classes and interfaces in the javax.sql

package, part of the J2EE platform. Some of the more relevant classes of java.sql

, javax.sql

, javax.transaction

, and javax.transaction.xa

are shown in Figure 8.7.

F

IGURE

8.7

The javax.sql

and javax.transaction

packages together provide support for 2PC against RDBMSs.

interface

javax.sql.DataSource

getConnection

Some methods and variables not shown interface

…ConnectionPoolDataSource

getPooledConnection

java.sql.DriverManager

getConnection

!

interface

java.sql.Connection

createStatement prepareStatement prepareCall close

!

interface

javax.transaction.UserTransaction

begin commit rollback setRollbackOnly getStatus setTransactionTimeout interface

javax.transaction.TransactionManager

begin commit getStatus getTransaction resume rollback setRollbackOnly setTransactionTimeout suspend interface

javax.transaction.xa.Xid

interface

javax.sql.XADataSource

getXAConnection interface

…PooledConnection

getConnection close interface

…sql.XAConnection

getXAResource interface

javax.transaction.xa.XAResource

commit end forget getTransactionTimeout isSameRM prepare recover rollback setTransactionTimeout start interface

javax.transaction.Transaction

commit delistResource enlistResource getStatus registerSynchronization rollback setRollbackOnly

0..*

Delisted resources are not removed from this collection

As you know, the java.sql.Connection

interface represents a connection to an

RDBMS. In J2SE applications, java.sql.DriverManager

can be used to create a connection. Under J2EE, a javax.sql.DataSource

object is used.

11 0672323842 CH08 3/20/02 9:29 AM Page 357

Transactions and Persistence 357

In J2EE enterprise applications, reusing connections through some sort of connection pool is critical to ensuring performance and scalability. Many implementations of

DataSource provide built-in connection pooling. The connection returned by

DataSource.getConnection() is effectively a logical connection temporarily associated with an underlying physical connection. When the close() method on the logical connection called, the underlying physical connection is not closed but is, instead, returned to a connection pool.

J2EE also offers another approach for RDBMS vendors to provide connection pooling, through the javax.sql.ConnectionPoolDataSource

interface. This returns

PooledConnection s that are physical connections to the database. In turn, they provide a getConnection() method that returns a logical connection that wraps them, so the final effect is much the same as before.

8

Tip

If you want, you can think of the getConnection() method of

PooledConnection as leasing the connection from the pool. The close() method releases the

PooledConnection back into the pool to be used again.

Closely related to

ConnectionPoolDataSource is the javax.sql.XADataSource

, which returns javax.sql.XAConnection

s. This is a sub-interface of

PooledConnection

, so it works in the same way, providing the getConnection() to return a logical java.sql.Connection

that wraps it. However, it also provides the getXAResource() method that returns a javax.transaction.xa.XAResource

. Consequently, the

XAConnection acts as a bridge between the resource manager’s notion of connection and the transaction manager’s notion of resource.

A J2EE-compliant resource manager must be able to support each of these three different

DataSource interfaces (

XADataSource

,

ConnectionPoolDataSource

, and

DataSource itself).

The J2EE RI deploytool can be used to configure

XADataSource s against Cloudscape by using the

Tools, Server Configuration menu option. Alternatively, the resource.properties

file under

%J2EE_HOME%\config can be edited directly. The dialog box and required entries are shown in Figure 8.8. (You’ll also need to remove the previous definition of jdbc/Agency

, listed under

DataSources

/

Standard node of the

Explorer).

11 0672323842 CH08 3/20/02 9:29 AM Page 358

358

F

IGURE

8.8

deploytool

can be used to configure

XADataSource

s.

Day 8

As you saw earlier today, the EJB application’s interface into the EJB container’s transaction manager implementation is solely through the javax.transaction.UserTransaction

interface. Indeed, there is no direct way to access the javax.transaction.TransactionManager

object or the javax.transaction.Transaction

object that corresponds to the

UserTransaction

(though obviously they are available to the EJB container itself).

Tip

Some EJB containers make the

Transaction and

TransactionManager interfaces available—through JNDI. As ever, using these value-add features will compromise application portability.

Figure 8.9 shows an object instance diagram to indicate (some of) the objects that might be instantiated to represent the scenario shown back in Figure 8.6.

In practice, there probably would not be two objects for the

XAConnection and

XAResource interfaces ( oraConn

/ oraRes and sybConn

/ sybRes in the diagram). More likely, an RDBMS vendor would implement a concrete class that implements both of these two interfaces. The JTA API describes such a class as the

ResourceAdapter

.

Consequently, each transaction managed by the EJB container’s transaction manager will have a collection of

ResourceAdapter s, each also being a physical connection to some resource manager. Indeed, if you have used the Adapter design pattern, you’ll recognize that the

ResourceAdapter is well named, combining two orthogonal interfaces into a single instance.

11 0672323842 CH08 3/20/02 9:29 AM Page 359

Transactions and Persistence 359

F

IGURE

8.9

An object instance diagram showing

XAConnection s and

XAResource s.

ctxA:EJBContext ctxB:EJBContext

:TransactionManager proxyA delegate beanA

«uses» oraConn:XAConnection proxyB delegate oraPhysConn:Connection beanB

«uses» sybConn:XAConnection sybPhysConn:Connection

«creates»

«creates»

:UserTransaction delegate

:Transaction oraRes:XAResource sybRes:XAResource

8

What If It Goes Wrong?

You’ve seen how the 2PC protocol is intended to work. However, the whole point of 2PC is to ensure transactional consistency, even in the event of an unexpected failure. So, what happens when it goes wrong?

There are two cases to deal with. First, it could be that a resource manager enlisted into the transaction may no longer be available when the application (in an EJB context, the

Session bean or its proxy) decides to commit. It could be that the network has failed since the original interaction with the Entity bean that represents some data residing on that resource manager.

In this first case, the prepare phase of the 2PC protocol fails. Because the transaction manager has been unable to get an acknowledgement within its timeout, it will roll back the transaction. When the resource manager that failed is restarted, it will (as part of its so-called recovery process) automatically roll back any work done as a part of the transaction.

In the second case, a resource manager becomes unavailable after it has acknowledged the prepare, but before the commit phase. This rare case causes more problems because the transaction manager may already have sent the commit message to some other resource managers. Nevertheless, the transaction manager will continue to send the commit message to all other resource managers.

When the resource manager that failed is restarted, it will detect that it had acknowledged a prepare. It then contacts the transaction manager to determine whether the transaction was actually committed or was rolled back. It then performs the same (commit or rollback) as part of its recovery process.

11 0672323842 CH08 3/20/02 9:29 AM Page 360

360 Day 8

The prepare phase is more than the transaction manager checking that all resource managers are still available. It is also possible for resource managers to unilaterally decide to abort a transaction for some reason. When this happens, the transaction manager will send a rollback message to all participating resource managers, rather than a commit.

Tip If you have done any JavaBean programming, some of this will be starting to sound familiar. The java.beans.VetoableChangeSupport

class works in a very similar way.

In addition to failures of the prepare or the commit phase of the 2PC, there are also occasional cases when a resource manager may take a so-called heuristic decision that could be in conflict with the semantics of the transaction. For example, a resource manager could have a policy that, once prepared, it will commit if the transaction manager has not confirmed the outcome (commit or rollback) within a certain period. One reason that a resource manager might do this would be to free up resources.

If a heuristic decision is made, it is the responsibility of the resource manager to remember this decision. In an RDBMS, this is often stored in some sort of system table. Put bluntly, this information is required to allow the administrator to correct any issues with the data if the heuristic decision went against the transaction’s actual outcome. You might have noted that the

XAResource interface defines a forget() method; this allows the resource manager to finally forget that a heuristic decision was made.

This probably all sounds pretty arcane, but is needed if you want to understand the full set of status values defined by the javax.transaction.Status

interface. You’ll recall that a subset of these was presented in Table 8.2. Table 8.3 shows all of the constants.

T

ABLE

8.3

All of the Constants Defined in javax.transaction.Status

Constant Meaning Typical Actions

STATUS_

NO_TRANSACTION

No transaction is active.

tran.begin() to start new transaction.

STATUS_ACTIVE

STATUS_MARKED_

ROLLBACK

A transaction is active and can be used.

Use resource manager.

tran.commit() to commit.

tran.rollback() to rollback.

A transaction is active, but has been marked for rollback. Any attempt to commit it will result in a javax.

transaction.RollbackException

being thrown.

tran.rollback()

11 0672323842 CH08 3/20/02 9:29 AM Page 361

Transactions and Persistence 361

T

ABLE

8.3

Continued

Constant

STATUS_PREPARED

STATUS_

PREPARING

STATUS_

COMMITTING

STATUS_

ROLLING_BACK

STATUS_

COMMITTED

STATUS_

ROLLEDBACK

STATUS_UNKNOWN

Meaning Typical Actions

A transaction is active, and is in the Nothing; wait for transaction to process of being committed. The first complete.

“prepare” phase of the 2PC protocol has completed.

A transaction is active, and is in the process of being committed. The first

“prepare” phase of the 2PC protocol is in progress.

A transaction is active, and is in the process of being committed. The second

“commit” phase of the 2PC protocol is in progress.

A transaction is active, and is in the process of being rolled back.

The previous transaction has committed, Use administrative tool to forget but there is likely to be some heuristic heuristics after checking data is valid data available (otherwise, the status returned would have been

STATUS_NO_TRANSACTION

).

in resource.

tran.begin() to start new transaction, but heuristic data may be lost.

The previous transaction has rolled back, but there is likely to be some heuristic data available (otherwise, the status returned would have been

STATUS_NO_TRANSACTION

).

This is a transient status. Subsequent calls will resolve to another status.

Wait.

JTA Versus JTS

There are two Java APIs relating to transactions:

The Java Transaction API (JTA)—(Already introduced) The JTA classes and interfaces reside in the javax.transaction

and javax.transaction.xa

packages.

The Java Transaction Services API (JTS)—This is a Java mapping for the OMG’s

Object Transaction Service v1.1 to interoperate with CORBA ORB/TS standard interfaces. This includes on-the-wire propagation of transactions over CORBA’s

IIOP network protocol. The JTS classes and interfaces reside in the javax.jts

package.

8

11 0672323842 CH08 3/20/02 9:29 AM Page 362

362 Day 8

For its part, the JTS specification (downloadable from http://java.sun.com/products/jts/index.html

) mandates that any compliant Transaction Manager implementation must also provide complete support for the JTA API, so you can think of JTS as the

“back-end” of JTA. Some Java APIs (such as JDBC and JNDI) use the terms API and

SPI, where API is the application developer’s programming interface, and SPI is supporting the service-provider’s interface. For example, a JDBC driver written by Oracle Corp.

would be an implementation of the JDBC SPI. Using this terminology, JTS is the SPI portion of JTA.

The EJB specification mandates that the EJB container must provide access to the JTA

API, so some vendors will do this by implementing JTS. Such vendors can then offer transaction propagation between EJBs that reside on different EJB servers, because both such servers will effectively appear as CORBA servers implementing OTS. Figure 8.10

shows this.

F

IGURE

8.10

JTS support means transactions can be propagated between

EJB servers.

local client session bean entity bean transaction manager

Ora entity bean transaction manager

Syb

Obviously, both EJB servers need to support JTS, but they need not be implemented by the same EJB container vendor. The transaction manager on the called EJB server is enlisted as a resource of the transaction manager on the calling EJB server.

While the EJB specification mandates JTA support, it does not mandate that JTS be used to realize this support. In other words, the EJB container is free to provide any implementation of the interfaces in JTA, but it need not support the additional requirements

(principally CORBA interoperability) that make up the JTS API. Some vendors—perhaps those from a database or a Web application background—may implement JTA entirely

“within” their EJB container and offer no CORBA interoperability services. On the other hand, the EJB specification also indicates that if an EJB container vendor does elect to provide transactional propagation, it must do so by supporting OTS.

11 0672323842 CH08 3/20/02 9:29 AM Page 363

Transactions and Persistence 363

Caution In fact, if the vendor elects to support transactional propagation, the EJB specification requires support for OTS v1.2 (see for example section 19.6.1.1).

Strictly speaking, JTS 1.0 is a Java mapping only for OTS v1.1, so JTS 1.0 support does not in itself fully address the requirements of the EJB specification.

If the area of transaction propagation is of particular interest to you, you should make sure that your EJB container vendor can clarify its position to you.

Some EJB container vendors will also be vendors of CORBA products, and so are likely to implement JTA just by implementing JTS.

Overview of Persistence Technologies

The second main topic for today is to discuss persistence options and look at some of the

Java technologies available in this space. At this point, you might be thinking that this is a moot point; after all, you learned yesterday and on Day 6 how to develop Entity beans, so what else is there to address?

In response to that question, consider the following two points. First, you used JDBC to implement BMP Entity beans on Day 6. However, there are other technologies that may involve less work and could even lend themselves to code generation, or indeed, support persistence transparently. Second, many J2EE applications will not use EJBs. There are many successful J2EE enterprise applications built only with servlets, JSPs, and data access code. Moreover, commercial EJB containers can be costly to purchase, and this might also be a consideration for your organization.

The three technologies that you will learn more about are as follows:

JDBC—This is the most mature of the Java persistence technologies. There have been multiple versions over the last few years (sometimes renamed along the way), which can be confusing.

The JDBC API itself is not covered, because it is presumed that you are or have become familiar with it.

SQLj—This is actually three specifications that combine Java and SQL, either client-side or within the RDBMS. Some aspects of SQLj are supported through

JDBC (v2.0 and later).

At the time of writing, SQLj was being developed by a consortium of companies that includes Sun, Oracle, IBM, and Sybase. You can learn more at http://www.sqlj.org

.

8

11 0672323842 CH08 3/20/02 9:29 AM Page 364

364 Day 8

Java Data Objects (JDO)—This aims to make persistence transparent, either for small embedded applications or large scale enterprise applications. This latter objective means it can either replace or supplement Entity EJBs. (At the time of writing, this specification was still in draft.)

At the time of writing, JDO was being developed through the Java Community

Process, JSR-000012. You can learn more at http://access1.sun.com/jdo

. You can learn about its relationship with JDBC at http://java.sun.com/products/jdbc/related.html

.

This is by no means a definitive list. Specifically, there is nothing to prevent you from using a vendor-specific API to persist your data, as is used by most OODBMS vendors.

While OODBMSs are not as mainstream nor as prevalent as RDBMSs, they have many advocates who argue vociferously that the best place to store objects is in an objectbased database. Examples of OODBMS products include (in no particular order) O2,

Objectivity, ObjectStore, Versant, FastObjects (previously POET), Persistence, Jasmine,

Gemstone, and ozone. From a J2EE perspective, many of the OODBMS vendors have rebranded their products to be EJB application servers, while others have positioned their technology to provide a simple way to implement persistence (within a BMP Entity bean).

Yet another alternative is to use an object/relational mapping product. In a sense, these tools combine the familiarity of RDBMS with the intuitiveness of objects. The information that maps the object instances to the relational schema is usually held in a toolspecific repository of some sort, similar in concept to the information provided in a CMP

Entity bean’s deployment descriptor, although typically much more complex and sophisticated. These O/R mapping products usually have sophisticated caching algorithms (for example, predictively loading related persistent data) that can radically speed up performance. On the other hand, one downside is that there is a learning curve to effectively configure and maintain the mapping data; sometimes professional assistance is needed.

There are a number of O/R products around, including (in no particular order) TopLink,

CocoBase, JavaBlend, JRelay, OJB, DBGen, JDX, and ObjectDriver.

Whether you can use any of these technologies may depend on the standards and constraints in your organization. Certainly, the vendors of OODBMS and O/R mapping products claim substantial decreases in the time taken to develop code.

Note As you will see shortly, one of the implicit objectives of JDO is to create a standard Java API for OODBMS and O/R mapping products. This may well cause these products to be adopted by a wider audience.

11 0672323842 CH08 3/20/02 9:29 AM Page 365

Transactions and Persistence 365

On the other hand, another consideration for you is the requirement for EJB container vendors to support CMP 2.0. You may find that some EJB vendors address this requirement simply by cross-licensing one of the more established O/R mapping tools. This is arguably the best of both worlds; you have access to a mature O/R mapping technology, but configured using an industry-standard EJB deployment descriptor.

JDBC

In the beginning, there was JDBC v1.0. Although initially introduced as an addendum to

JDK 1.02, it was standardized as part of JDK 1.1 as the classes and interfaces that make up the java.sql

package. It remains an integral part of J2SE 1.3. In an effort to unravel

JDBC’s family tree, Table 8.4 lists the versions of JDBC, J2SE (previously JDK), and J2EE.

T

ABLE

8.4

JDBC Versions

JDBC

JDBC 1.0

JDBC 2.1 J2SE 1.2

Core API

JDBC 2.0

Package

JDBC 3.0

J2SE (JDK) J2EE

JDK 1.1

J2SE 1.4

N/A

Package

java.sql

Significant Features/Notes

DriverManager

,

Connection, Statement

,

ResultSet

Note: Formalized in JDBC 1.2 Spec.

J2EE 1.2

J2EE 1.3

java.sql

Scrollable and updateable

ResultSets

Batch updates

SQL1999 data types (

BLOB

,

CLOB

,

ARRAY

,

Structured Type,

REF

)

Mapping SQL UDTs to Java classes

Direct storing of Java objects

Note: When first introduced, was called the

JDBC 2.0 Core API, not 2.1.

J2EE 1.2

javax.sql

DataSource

,

ConnectionPoolDataSource

,

XADataSource

JNDI support

Rowsets

Note: When first introduced, was called the

JDBC 2.0 Standard Extension API.

J2EE 1.4

java.sql

javax.sql

Unifies JDBC 2.1 Core API and

JDBC 2.0 Optional Package

More thorough support for SQL1999 data types

DATALINK/URL data type (external data)

Savepoint support

Retrieval of auto-generated keys

Multiple open result sets

Define relationship to Connector architecture

Numerous other minor enhancements

8

11 0672323842 CH08 3/20/02 9:29 AM Page 366

366 Day 8

The JDBC 2.1 Core API is included in J2SE 1.2 and also in J2EE 1.2 and J2EE 1.3 platforms. J2EE 1.2 and J2EE 1.3 also require the JDBC 2.0 Optional Package. If all these different versions aren’t confusing enough, you may occasionally see documentation that refers to the “JDBC 2.0 API.” This refers to the combination of (what is now called) the

JDBC 2.1 Core API and JDBC 2.0 Optional Package. So, J2EE 1.2 and 1.3 effectively mandate the JDBC 2.0 API.

However, sanity is about to break out! At the time of writing, JDBC 3.0 was just coming out of draft, to be part of J2SE 1.4. This unifies both the Core API and the optional package. Given than J2SE 1.4 will include JDBC 3.0, it will also implicitly be part of

J2EE 1.4.

One notable feature about JDBC 3.0 is that it consolidates support for SQL1999 advanced data types. Support for these was introduced in JDBC 2.1 Core API, when

SQL1999 was still in draft and was called SQL3. Now that SQL1999 is a ratified standard, JDBC 3.0 has added some features that were, by necessity, previously omitted.

You can learn more about JDBC 3.0 (and download its specification) at http://java.sun.com/products/jdbc/index.html

.

The advanced data types defined by SQL1999 are as follows:

BLOB s are binary large objects. These can literally store anything, such as a JPEG image, a recording, a Java serialized object, and so on.

BLOB s are transparently accessed using SQL

Locators

. The

BLOB data isn’t stored along with the rest of the row; instead, a pointer is held. While this marginally slows down access, it means that

BLOB s can have very large size limits (2Gb or more).

The JDBC

ResultSet.getBlob() method and the

Blob interface provide access to data stored in

BLOB columns.

CLOB s are character large objects, similar to

BLOB s except that the data is treated as characters; as a result, conversion of data between locales is performed.

CLOB s are also transparently accessed via SQL Locators.

The JDBC

ResultSet.getClob() method and the

Clob interface provide access to data stored in

CLOB columns.

• SQL structured types are a mechanism to allow user-defined data types to be defined. They are somewhat similar to a class in Java that has only public fields.

The following SQL1999 command defines a structured type called

XY_POINT

:

CREATE TYPE XY_POINT AS (X FLOAT, Y FLOAT) NOT FINAL

SQL1999 allows columns to be defined of these types, and also allows tables to be defined consisting of instances of these types. Defining a table from a structure type is done using SQL syntax such as the following:

11 0672323842 CH08 3/20/02 9:29 AM Page 367

Transactions and Persistence 367

CREATE TABLE SCATTER_GRAPH OF XY_POINT (REF OID IS SYSTEM GENERATED);

The

OID is an identifier to a row in this table. The JDBC

ResultSet.getObject() method and the

Struct interface provide access to data stored in structured type columns (not structured type tables). To simplify implementation, custom mappings can be defined to convert the data of the SQL structured type to a Java class.

This is somewhat akin to implementing the java.io.Externalizable

interface for the target Java class; java.sql.SQLData

interface is the actual interface that must be implemented.

ARRAY s provide the ability to store vector data. In the case study, the

Applicant table defines two columns— address1 and address2

. Using SQL1999 types, these could instead be defined to be a single

ARRAY column. Usually

ARRAY s are accessed via SQL Locators, although it is possible for the data to be stored along with the rest of the data on the row.

The JDBC

ResultSet.getArray() method and the

Array interface provide access to data stored in

ARRAY columns.

REF s are a persistent pointer to an instance of a SQL structured type, defined when a table is created from an SQL structured type. You might think of them as an

SQL1999 equivalent to a foreign key.

The JDBC

ResultSet.getRef() method and the

Ref interface provide access to the SQL

REF

. This reference can be used to access data held in a table defined to be of a structured type (such as

SCATTER_GRAPH

). This is the way that data stored in structured type tables can be accessed.

These data types may be unfamiliar to you; after all, many RDBMS do not support them yet. However, JDBC 3.0 includes support for these data types because the JDBC specification authors expect RDBMS support for them will have become widespread within the next five years or so.

That said, there is some overlap in intent between SQL1999 data types and the objectives of SQLj. If SQLj proves more popular (because it is rooted more on Java technology rather than SQL), it could be that Java’s popularity may decelerate the adoption of

SQL1999 data types. Only time will tell. So, high time to look at this next technology,

SQLj.

SQLj

The SQLj initiative defines three ways to combine Java and SQL:

• SQLj Part 0 defines a mechanism for embedding SQL calls within Java code. The

SQL is converted into JDBC calls by using a preprocessor.

8

11 0672323842 CH08 3/20/02 9:29 AM Page 368

368 Day 8

• SQLj Part 1 inverts this, placing Java within SQL. It defines extensions to the SQL syntax to allow Java static methods to be called either as if they were SQL stored procedures or as user-defined functions within SQL statements.

• SQLj Part 2 also places Java within SQL, but here defining a mechanism for Java classes to be used to define SQL types. This allows table columns to be defined as a type of a Java class; a Java object instance can thus be stored directly in the column.

The SQLj Part 0 has already been adopted as an ANSI and ISO standard (ANSI ref:

X3.135.10-1998, ISO/IEC ref: 9075-10:2000), “Information Technology—Database

Languages—SQL—Part 10: Object Language Bindings (SQL/OLB).” If you see a reference to ANSI SQL Part 10, it just means SQLj Part 0.

The other parts have not yet been adopted by ANSI or ISO/IEC, but are being approved through NCITS, the (U.S.) National Committee for Information Technology Standards.

Most of the NCITS standardization activities result in national (ANSI) or international

(ISO/IEC) standards. SQLj Part 1 is also known as NCITS 331.1; SQLj Part 2 is NCITS

331.2.

The following sections look at each of these in turn.

SQLj Part 0

SQLj Part 0 is probably the most straightforward for database vendors to implement, because it an entirely a client-side technology. The developer, rather than writing a regular Java class whose file has a

.java

suffix, writes a hybrid class instead that has embedded SQL commands within it. Such a class resides in a file with a

.sqlj

suffix. A preprocessor (SQLj calls it a translator) converts the embedded SQL commands into JDBC equivalents, resulting in a

.java file that can be compiled in the usual way. The sqlj translator also creates a set of serialized

.ser

profile files that hold information about the embedded SQL.

Note

On a historical note, EJB 1.0 used

.ser

files to hold deployment descriptor information (as you know, XML files are now used instead). SQLj Part 0 was initially specified at around the same time as EJB 1.0 (1998 or so), so it is interesting to speculate if it were being specified now whether it would use

XML files to hold this supplementary information.

After the generated

.java

and

.ser

files have been created, they can be compiled and packaged up into an EJB JAR file in the usual way. The

.ser

profile files simply need to be part of the ejb-jar

. This overall process is shown in Figure 8.11.

11 0672323842 CH08 3/20/02 9:29 AM Page 369

Transactions and Persistence

F

IGURE

8.11

SQLj Part 0 uses a translator to convert

SQLj commands into

JDBC.

JobBean.sqlj

sqlj translator

Job.java

JobBean.java

.ser

profiles javac complier

Job*.class

Deployment

Descriptors

369

8

packager

JobBean.jar

The sqlj translator can optionally also check the embedded SQL against a target database to make sure that it is syntactically correct (that all tables, columns, and so on that are referenced do exist).

Listing 8.5 shows the BMP version of the ejbLoad() method for the

Job

Entity bean

(from Day 7), written using JDBC.

L

ISTING

8.5

JDBC Version of ejbLoad() for BMP

Job

Bean

1: public void ejbLoad(){

2: JobPK key= (JobPK)ctx.getPrimaryKey();

3: Connection con = null;

4: PreparedStatement stmt = null;

5: ResultSet rs = null;

6: try {

7: con = dataSource.getConnection();

8: stmt = con.prepareStatement(

9: “SELECT description,location

➥FROM Job

➥WHERE ref = ?

➥AND customer = ?”);

10:

11: stmt.setString(1, key.getRef());

12: stmt.setString(2, key.getCustomer());

13: rs = stmt.executeQuery();

14:

11 0672323842 CH08 3/20/02 9:29 AM Page 370

370 Day 8

L

ISTING

8.5

Continued

15: if (!rs.next()) {

16: error(“No data found in ejbLoad for “+key,null);

17: }

18: this.ref = key.getRef();

19: this.customer = key.getCustomer();

20: this.customerObj =

➥customerHome.findByPrimaryKey(this.customer); // derived

21: this.description = rs.getString(1);

22: String locationName = rs.getString(2);

23: this.location =

➥(locationName != null)?

➥locationHome.findByPrimaryKey(locationName):null;

24:

25: // load skills

26: stmt = con.prepareStatement(

27: “SELECT job, customer, skill

➥FROM JobSkill

➥WHERE job = ?

➥AND customer = ?

➥ORDER BY skill”);

28:

29: stmt.setString(1, ref);

30: stmt.setString(2, customerObj.getLogin());

31: rs = stmt.executeQuery();

32:

33: List skillNameList = new ArrayList();

34: while (rs.next()) {

35: skillNameList.add(rs.getString(3));

36: }

37:

38: this.skills = skillHome.lookup(skillNameList);

39: }

40: catch (SQLException e) {

41: error(“Error in ejbLoad for “+key,e);

42: }

43: catch (FinderException e) {

44: error(“Error in ejbLoad (invalid customer or location) for “+

➥key,e);

45: }

46: finally {

47: closeConnection(con, stmt, rs);

48: }

49: }

Listing 8.6 shows the same method, implemented using SQLj embedded SQL syntax.

11 0672323842 CH08 3/20/02 9:29 AM Page 371

Transactions and Persistence

L

ISTING

8.6

SQLj Version of ejbLoad() for BMP

Job

Bean

1: // assuming

2: // import sqlj.runtime.*;

3:

4: public void ejbLoad(){

5: JobPK key= (JobPK)ctx.getPrimaryKey();

6:

7: #sql context CustomConnectionContext;

8: CustomConnectionContext conCtx;

9: try {

10: con = dataSource.getConnection();

11: conCtx = new CustomConnectionContext(con);

12: #sql [conCtx]

13: { SELECT description,location

14: INTO :this.description, :this.locationName

15: FROM Job WHERE ref = :(key.getRef())

16: AND customer = :(key.getCustomer()) };

17:

18: this.ref = key.getRef();

19: this.customer = key.getCustomer();

20: this.customerObj =

➥customerHome.findByPrimaryKey(this.customer); // derived

21: this.location =

➥(locationName != null)?

➥locationHome.findByPrimaryKey(locationName):null;

22:

23: // load skills

24: #sql iterator SkillIterator

(String job, String customer, String skill);

25: SkillIterator skillIter =

26: #sql [conCtx]

27: { SELECT job, customer, skill

28: FROM JobSkill

29: WHERE job = :this.ref

30: AND customer = :(customerObj.getLogin())

31: ORDER BY skill };

32:

33: List skillNameList = new ArrayList();

34: while (skillIter.next()) {

35: skillNameList.add(skillIter.skill());

36: }

37: skillIter.close();

38:

39: this.skills = skillHome.lookup(skillNameList);

40: }

41: catch (SQLException e) {

42: error(“Error in ejbLoad for “+key,e);

43: }

44: catch (FinderException e) {

371

8

11 0672323842 CH08 3/20/02 9:30 AM Page 372

372 Day 8

L

ISTING

8.6

Continued

45: error(“Error in ejbLoad (invalid customer or location) for “+

➥key,e);

46: }

47: finally {

48: conCtx.close(ConnectionContext.KEEP_CONNECTION);

49: closeConnection(con, null, null);

50: }

51: }

You can see that SQLj binds host variables (that is, Java instance or local variables) or return values of expressions to the SQL statement using a

: prefix. You can see this in the first SQL statement (loading from the

Job table) where two function expressions are used in the

WHERE clause. Host variables can also be written to through the

INTO clause.

Again, in the first SQL statement, the

INTO clause is used to populate the location and description host variables. Contrast the use of the

INTO clause with the JDBC equivalent that has to iterate over a java.sql.ResultSet

.

The second SQL statement is a query against the

JobSkill table, and multiple rows are expected this time. A typesafe iterator,

SkillIterator

, is defined and is used to traverse the returned rows.

Connection information can be specified in SQLj in a variety of ways. The normal approach is to embed the connection information into the aforementioned

.ser

profile files, replacing the J2SE java.sql.DriverManager

approach. Obviously, this isn’t appropriate in a J2EE environment, because only logical connections should be acquired via javax.sql.DataSource

.

One solution is to use an explicit connection context to define the connection that the

SQL will be executed under. In Listing 8.6, the line

#sql context CustomConnectionContext; defines

CustomConnectionContext as a user-defined class, extending sqlj.runtime.ConnectionContext

and with a constructor accepting a java.sql.Connection

. The line

CustomConnectionContext conCtx; defines a reference conCtx of this class, and con = dataSource.getConnection(); conCtx = new CustomConnectionContext(con); instantiates an object of this class. The conCtx is used within the subsequent

#sql calls.

11 0672323842 CH08 3/20/02 9:30 AM Page 373

Transactions and Persistence 373

You may find that your vendor’s SQLj implementation offers the ability to make a

CustomConnectionContext the default context. Part of the SQLj Part 0 specification includes the concept of customizing profiles to a specific runtime environment, similar to the notion of adding the auxiliary deployment descriptor for EJBs. Your customizer may allow you to indicate that the default

ConnectionContext is one that is “J2EE-aware.”

This would avoid the need for any of the connection context code given in Listing 8.6.

SQLj Part 1

Whereas SQLj Part 0 embedded SQL within Java, the SQLj Part 1 specification inverts this and puts the Java within SQL. More properly, it allows Java static methods to be called as if they were SQL stored procedures. It also allows Java static methods to act as user-defined functions called from within SQL statements (such as

SELECT or

UPDATE

).

What this means is that, whereas SQLj Part 0 addresses Java and SQL for client-side code, SQLj Part 1 puts Java and SQL together in the server. Put another way, SQLj Part

1 can only be supported by RDBMS vendors that provide the capability to install this functionality.

8

Note There are two main mechanisms by which RDBMS vendors can support SQLj

Part 1. Many vendors have long provided the ability to hook user-defined functions into the RDBMS, where those functions are written using C or some other 3GL. Some such vendors offer SQLj Part 1 support by converting the Java static methods into corresponding C code and then uploading.

Alternatively, the RDBMS vendor can actually implement a JVM that runs within the RDBMS itself. When the Java static method is called, the SQL

“engine” within the RDBMS passes over control to the JVM to evaluate the result.

As an example, consider the

Applicant table from the case study. This table records each applicant’s address in the address1 and address2 columns. For the sake of illustration, imagine that it also has the state

, zip

, and country columns.

If the Job agency users want to do a mailshot, they will need to print some labels. The labels need to combine the information of the name

, address1

, address2

, state

, zip

, and country columns, with a newline between each, also dealing with the case where some of these columns are null. Listing 8.7 defines a utility class that will format the label string appropriately.

11 0672323842 CH08 3/20/02 9:30 AM Page 374

374 Day 8

L

ISTING

8.7

Static Methods Can Be Invoked as SQL UDFs

1: public class Utils {

2: public static String labelString(

3: String name, String address1, String address2,

4 String state, String zip, String country) {

5: StringBuffer label = new StringBuffer(name);

6: if (address1 != null) {

7: label.append(“\n”); label.append(address1);

8: }

9: if (address2 != null) {

10: label.append(“\n”); label.append(address2);

11: }

12: if (state != null) {

13: label.append(“\n”); label.append(state );

14: }

15: if (zip != null) {

16: label.append(“\n”); label.append(zip );

17: }

18: if (country != null) {

19: label.append(“\n”); label.append(country );

20: }

21: return label.toString();

22: }

23: }

This class would be compiled as usual and bundled up into a JAR file, say utils.jar

.

SQLj defines the following mechanism for installing classes as JAR files:

sqlj.install_jar (‘file:utils.jar’, utils_jar’ )

This gives the name utils_jar to the JAR file utils.jar

. Each RDBMS vendor will provide a tool that implements this mechanism.

With the code installed, the following defines the user-defined function label_for to be the

Utils.labelString() method: create function label_for( name varchar, address1 varchar, address2 varchar, state char(2), zip char(9) country varchar) returns varchar language java parameter style java external name ‘utils_jar:Utils.labelString’;

Finally, the following SQL can be executed:

SELECT label_for(name, address1, address2, state, zip, country)

FROM Applicant

11 0672323842 CH08 3/20/02 9:30 AM Page 375

Transactions and Persistence 375

Using a function alias means that access to this function can be granted or revoked as needed by using the SQL grant execute command. Additionally, SQLj specifies that security can be associated with the actual JAR file using grant usage on utils_jar to some_user

.

Some RDBMS vendors have relaxed the requirement to define function aliases and simply allow the Java static method to be called directly:

SELECT Utils.labelString(name, address1, address2, state, zip, country) AS label

FROM Applicant

8

Note

One of the major RDBMS vendors (Sybase) allows Java UDFs to read/write

BLOB

( image

) and

CLOB

( text

) columns as java.io.InputStream

and java.io.Reader

s, respectively. So, given a table of create table letter

( letter_id int, letter_text text ) and a static method such as public class TextUtils { public static int length(java.io.Reader reader) { int size = 0, read; char[] buf = new char[1024]; while( (read = reader.read(buf)) != -1) { size += read;

} return size;

}

} then SQL such as the following can be submitted:

SELECT letter_id, TextUtils.length(text)

FROM letters

Clearly, this opens up a lot of possibilities. Rather than just counting the number of characters in the

CLOB column, the Java method could search, apply regular expressions, run AWK scripts, or parse XML.

Indeed, the RDBMS vendor has developed an XML query engine implemented in precisely this fashion. As a result, developers can store XML files as

CLOB s and process them within the RDBMS server itself.

SQLj Part 1 also allows stored procedures to be defined as an alias for Java static methods. So, a Java client making a JDBC call such as the following

CallableStatement stmt = con.prepareCall(“{ call some_such_procedure ?, ?}”; stmt.execute();

11 0672323842 CH08 3/20/02 9:30 AM Page 376

376 Day 8 is actually invoking a Java static method aliased to some_such_procedure

. This is almost like performing a Java RMI call, except over a database network connection.

Such “Java stored procedures” can (somewhat arbitrarily) be split into two types—those that have embedded SQL calls implemented using JDBC or SQLj and those that do not.

Java stored procedures that do not have embedded SQL often perform some sort of complex computation that is not easily expressed in SQL. As a somewhat contrived example,

Listing 8.8 shows a reworked version of the

Utils.labelString

method that allows it to be called as a stored procedure.

L

ISTING

8.8

Static Methods Can Be Invoked as SQL Stored Procedures

1: public class Utils {

2: public static String labelString(

3: String name, String address1, String address2,

4: String state, String zip, String country,

5: String[] returnLabel) {

6: StringBuffer label = new StringBuffer(name);

7: if (address1 != null) {

8: label.append(“\n”); label.append(address1);

9: }

10: if (address2 != null) {

11: label.append(“\n”); label.append(address2);

12: }

13: if (state != null) {

14: label.append(“\n”); label.append(state );

15: }

16: if (zip != null) {

17: label.append(“\n”); label.append(zip );

18: }

19: if (country != null) {

20: label.append(“\n”); label.append(country );

21: }

22: returnLabel[0] = label.toString();

23: }

24: // code omitted

25: }

The stored procedure definition for this method is as follows: create procedure label_it

(in name varchar, in address1 varchar, in address2 varchar, in state char(2), in zip char(9), in country varchar, out returnLabel varchar) language java parameter style java external name ‘utils_jar;Utils.labelString’;

11 0672323842 CH08 3/20/02 9:30 AM Page 377

Transactions and Persistence 377

The following code will work to invoke this stored procedure from JDBC:

CallableStatement stmt = con.prepareCall (

➥”{call label_it ?, ?, ?, ?, ?, ?, ? }”); stmt.setParameter(1, someName); stmt.setParameter(2, someAddress1); stmt.setParameter(3, someAddress2); stmt.setParameter(4, someState); stmt.setParameter(5, someZip); stmt.setParameter(6, someCountry);

String theReturnedLabel; stmt.registerOutParameter(7, Types.VARCHAR ); stmt.setParameter(7, theReturnedLabel); stmt.execute ();

You may have noticed in Listing 8.8 the peculiar fact that the output parameter, returnLabel

, is defined to be an array of

String s, rather than just a

String

. This is no mistake; the method needs to be able to change the value of the parameter. The array passed in has precisely one element, the initial value of the returnLabel

, and can be changed.

Tip

If you are a C or C++ programmer, you might recognize this as the Java equivalent of “pointer to a pointer.”

You can probably think of some less contrived examples yourself.

The other type of Java stored procedure is that which does have embedded SQL. Here, you get the peculiar situation of a Java client calling SQL, which maps to Java calling

SQL. This brings to mind concentric rings of Java and SQL, as shown in Figure 8.12.

F

IGURE

8.12

SQL embedded within

Java, embedded within SQL, embedded within Java…

Java client

SQL Stored Proc

Java static method

SQL

8

11 0672323842 CH08 3/20/02 9:30 AM Page 378

378 Day 8

The outermost ring is a Java client (Session bean, CMP Entity bean, servlet, and so on).

The inner three rings are server-side, running within the RDBMS. The Java client invokes an SQL stored procedure, in turn aliased to a Java static method that then makes

SQL calls.

For the Java stored procedure to query the database, it must have a JDBC connection.

SQLj defines the URL string of jdbc:default:connection to be used as the parameter to java.sql.DriverManager.getConnection()

.

Not only can Java static methods query the database, they can also return the results.

This is done by allowing

ResultSet s to be returned as parameters, in a manner similar to the returnLabel that you saw earlier.

This second style of Java stored procedure is really just the re-location of a client-side application. In other words, with appropriate modifications to the mechanism to obtain the connection, the code could equally run in a regular Java client. Put another way, there is no difference in the amount of JDBC or embedded SQL code that must be written. As a result, it can seem that as a technology, this is a curiosity—nothing more. However, that would be overlooking the important point that running Java client-side code within the RDBMS itself means that there is no network traffic. Many batch processing jobs involve downloading large chunks of data, performing complex processing, and then uploading the results. Using Java stored procedures, all of this work can be done without incurring any costly network traffic at all.

By making Java static methods executable within the RDBMS, SQLj Part 1 offers some quite powerful functionality. It is a wonderful demonstration of Java’s oft-quoted “write once, run anywhere” ability.

SQLj Part 2

The last part of SQLj is possibly the most interesting of all. SQLj Part 2 allows Java classes to be defined as SQL abstract data types.

You will probably recall from the earlier discussion on SQL1999 advanced data types that there is overlap between SQL1999 and SQLj Part 2. The most obvious overlap is that Java classes can be used instead of SQL structured types.

Listing 8.9 shows a class that represents all the states of a

Job bean. This includes also the (names of the) job’s skills and the (name of the) job’s location.

11 0672323842 CH08 3/20/02 9:30 AM Page 379

Transactions and Persistence 379

L

ISTING

8.9

The

JobData

Class Represents All the State of a

Job

Bean

1: package data;

2: public class JobData implements java.io.Serializable {

3: public final static long serialVersionUID = 1;

4: private String ref, customer, description, locationName;

5: private String[] skillNames;

6: public JobData(String ref, String customer,

➥String description,

➥String locationName,

➥String[] skillNames) {

7: this.ref = ref;

8: this.customer = customer;

9: this.description = description;

10: this.locationName = locationName;

11: if (skillNames = null) { skillNames = new String[0]; }

12: this.skillNames = new String[skillNames.length];

13: System.arraycopy(skillNames, 0,

➥this.skillNames, 0, skillNames.length);

14: }

15: public String getRef() { return ref; }

16: public String getCustomer() { return customer; }

17: public String getDescription() { return description; }

18: public String getLocationName() { return locationName; }

19: public String[] getSkillNames() {

20: String[] skillNames = new String[this.skillNames.length];

21: System.arraycopy(this.skillNames, 0,

➥skillNames, 0, skillNames.length);

22: return skillNames;

23: }

24: public int getNumberOfSkills() { return this.skillNames.length; }

25: }

The class implements java.io.Serializable

and defines serialVersionUID to provide forward compatibility with future versions of the class.

This class can be bundled into a JAR and installed into the RDBMS, just as for SQLj

Part 1. This time though, a user-defined type can be created from the Java class, using an extension of the SQL1999

CREATE TYPE syntax:

CREATE TYPE JobType EXTERNAL NAME ‘data.JobData’ LANGUAGE java;

This example is incomplete because SQLj Part 2 is still in development. Some RDBMS vendors that have supported SQLj Part 2 have used alternative approaches. For example, the Cloudscape RDBMS uses the following syntax:

CREATE CLASS ALIAS JobType FOR data.JobData;

8

11 0672323842 CH08 3/20/02 9:30 AM Page 380

380 Day 8

Even then, this is an optional step in Cloudscape, as it is for the Sybase RDBMS. That is, the fully-qualified Java classname can just be used directly as a column type.

Consequently, the following could be used to define the

Job table:

CREATE TABLE Job

( job data.JobData

)

Each row in the table will hold a serialized instance of the data.JobData

class.

Performance-wise, this would be prohibitively expensive to access unless the RDBMS vendor has implemented a JVM within the RDBMS. If they have done this though, the cost of deserializing and serializing the objects is not expensive. The RDBMS has suddenly become an ORDBMS (object/relational database).

When the data in the table is actually a Java object, suddenly SQL queries become a lot more exciting:

SELECT job>>getCustomer(), avg( job>>getNumberOfSkills() )

FROM Job

GROUP BY job>>getCustomer()

This query will find the average number of skills needed for all of the jobs placed by a customer.

Note

In SQLj Part 2, the

>> operator replaces the conventional

.

notation.

The following query also works:

SELECT job

FROM Job

WHERE job>>getNumberOfSkills() > 2

The calling client’s JDBC is simplicity itself:

ResultSet rs = stmt.execute(

➥”SELECT job FROM Job WHERE job>>getNumberOfSkills() > 2”); while (rs.next()) {

JobData job = (JobData)rs.getObject(1);

}

11 0672323842 CH08 3/20/02 9:30 AM Page 381

Transactions and Persistence 381

Tip Although this is slightly off-topic, it is worth noting that SQLj Part 2 transparently supports super- and sub-types. One could define a subclass of

JobData and store instances of it within the

Job table. Moreover, any overridden methods would be called polymorphically. This feature alone could radically simplify many database schemas.

Turning back to the SQL1999 data types, you can probably see that Java classes can be used in lieu of a

BLOB

,

CLOB

, or

ARRAY

, as well as be replaced SQL structured types (for columns). The following is another definition of the

Job table, this time showing skills as a vector attribute:

CREATE TABLE Job

( customer VARCHAR, ref VARCHAR, description VARCHAR, location VARCHAR, skills java.util.List

)

One could then imagine the following query:

SELECT customer, ref

FROM Job

WHERE skills>>contains(“Cigar trimmer”)

8

Note

The Cloudscape RDBMS goes even further, providing implicit mapping of regular SQL types (such as

VARCHAR

) to their Java equivalents (such as java.lang.String

). Hence, the following query would be valid within

Cloudscape:

SELECT customer>>substring(0,2)>>concat(ref)>>length()

FROM Job

WHERE customer>>startsWith(“XYZ”)

Of the SQL1999 data types, only the

REF data type has no direct equivalent in SQLj.

Indeed, if you go back to the first alternate definition of the

Job table (defined in terms of the data.JobData

class), you can see that the job object has the name of the location, not a reference to the location. If one were to put this in EJB terms, one might say that

JobData holds the primary key to the job’s location, not a reference to

LocationLocal

.

11 0672323842 CH08 3/20/02 9:30 AM Page 382

382 Day 8

There are some other issues that you should be aware of:

• First is that, given the actual data is held as a Java serialized object, non-Java database clients will not be able to read that data.

Possible fixes for this issue are to

• Write code to read the (well-defined) Java serialization stream

• Implement java.io.Externalizable

and provide custom methods to write the state in some other format (XML, perhaps?)

• Second, the RDBMS’ query optimizer cannot index on the return value of method calls. For example, looking back at the

WHERE clause of one of the previous examples (

WHERE job>>getNumberOfSkills() > 2

), this can only be performed by deserializing every object and invoking the getNumberOfSkills() method.

One possible fix is to redundantly store the required information in a regular column, and maintain it using triggers. The query then uses this derived column.

Note True OODBMS do allow indexes to be defined on the return values of readonly methods.

• Last, this is new technology, so it will need to mature before organizations are ready to trust their valuable data to it.

How does SQLj (parts 1 and 2) fit with EJB and the J2EE platform? Well, the support in

SQLj Part 2 certainly makes implementing BMP Entity beans pretty straightforward, provided care is taken with relationships:

• If a BMP Entity bean is the parent of a one-to-many composite association, there is nothing to prevent that bean from simply persisting the dependent child data and dispensing with the child table in the RDBMS.

• Many-to-one associations, where the bean is a child, are more complex. In general, only the foreign key value should be persisted in the bean. This does incur extra cost for looking up the actual parent data. (There is an exception to this, in the special case where the parent is immutable. Then, the parent’s data can safely be stored in the bean itself.)

• Many-to-many associations can be treated like one-to-many associations, but only if the data that is held is the foreign key value to the other table, and also provided that the many-to-many link does not need to be traversed in both ways.

11 0672323842 CH08 3/20/02 9:30 AM Page 383

Transactions and Persistence 383

One of the previous examples stored the job skills as a java.util.List

of

String s, which seems elegant, but to identify which jobs require the “Cigar trimmer” skill necessitates instantiating every job instance; in other words, always coming at the

Job

/

Skill association from the

Job side.

Even with these provisos, one could imagine that it would be relatively easy (given a modern IDE or UML modeling tool) to automatically generate SQLj implementation code for BMP Entity beans.

On the other hand, the ability to deploy Java code that runs within the RDBMS itself

(using either SQLj Part 1 or Part 2) somewhat muddies the architectural water. You are by now intimately familiar with the n-tier architecture model. But, if Java can run in the

RDBMS, why need do you have a middle tier at all? This is a question that you are probably best advised to answer yourself. You may take the view that as long as you know which logical layer your Java code belongs to, it may not matter which physical tier

(Web server, EJB application server, Java-enabled RDBMS server) you choose to deploy it to.

JDO

The last persistence technology that you will be looking at, Java Data Objects (JDO), aims high and it aims low. That is, it is intended both for use within embedded devices

(J2ME) but also for use within J2EE environments. In a J2EE environment, JDO can either supplement or supplant Entity EJBs; if supplementing Entity EJBs, the application of JDO may be hidden if CMP is being employed (that is, the EJB vendor uses JDO but the bean provider is unaware of this), or it can be used by the bean provider directly if

BMP is in use.

JDO is the most recent of the Java persistent technologies and, at the time of writing, its specification was still in draft with only an incomplete reference implementation.

Moreover, as a technology it seems to be running in parallel with the various Java platform editions (J2ME, J2SE, and J2EE), and there appear to be no clear indications within which platform it will eventually reside. Nevertheless, JDO has raised some significant interest as an API, especially for vendors of OODBMS and O/R mapping tools. Indeed, the JDO expert group includes representatives from Versant, Poet, Object Design, and

Gemstone, among others. Because JDO completely hides the details of the data store internals, it is also suitable to access ERP systems; some ERP vendors (such as SAP) have committed to adopting it.

8

11 0672323842 CH08 3/20/02 9:30 AM Page 384

384 Day 8

Tip One way to think of JDO is as a vendor-neutral Java API to OODBMS and

O/R mapping tools, just as JDBC provides a vendor-neutral Java API to

RDBMS.

The overriding objective of JDO is to provide persistence transparency. In other words, the objects that are to be persisted—so-called persistent-capable objects—do not need to include any logic to make them persistent. Contrast this with EJB Entity beans that need to implement ejbLoad() and ejbStore()

, or with EJB Session beans (and for that matter servlets) that require reams of JDBC or SQLj calls. The only classes that need to use the

JDO interfaces and classes are those that manage the lifecycle of persistent objects.

As an example of transparent persistence, consider that persistent-capable objects can also have references to other objects. Assuming that the field that holds this reference has been marked as being persistent, these referenced classes will then also be made persistent. This is sometimes called “persistence by reachability.” Again, contrast this to EJB, where a relationship between beans required complex configuration in the EJB deployment descriptor, moreover being constrained to relate through the EJB’s local interface.

Note

It is perhaps a little misleading to claim that JDO does not need relationships between classes to be defined. Rather, it is not within the scope of JDO to be concerned about this. A JDO implementation provided by an OODBMS vendor will work in a different way than one provided by an O/R mapping tool vendor. Any mapping or other configuration information that might need to be done is performed entirely with vendor-specific tools.

Earlier, JDO was compared to JDBC. JDBC provides a standard API, but leaves

RDBMS vendors at liberty to implement their own data store and network protocols. Equally, JDO provides just an API and does not get involved in the internals. This makes JDO applications portable across JDO implementations, but obviously requires any vendor-specific configuration to be re-applied.

JDO Concepts

JDO’s approach to object persistence revolves around the concept of a cache. This cache belongs to a client, rather than the server; the objects in the cache are not shared among all clients. In a J2EE environment, a stateful Session bean would usually be a client; each active Session bean would have its own cache. At any given time, the cache that holds the objects is associated with at most one connection and at most one transaction.

11 0672323842 CH08 3/20/02 9:30 AM Page 385

Transactions and Persistence 385

Over time, that cache can be used with different transactions, and, for that matter, with different connections.

The JDO interface that controls the client’s cache is javax.jdo.PersistenceManager

. In a J2EE environment, an instance of this interface is obtained from a javax.jdo.PersistenceManagerFactory

that, in turn, is obtained via JNDI. The JDO specification describes how the J2EE Connector architecture is used to actually configure a

PersistenceManagerFactory into JNDI; you’ll be learning something about this on

Day 19, “Integrating with External Resources.” All you need to appreciate for now is that a JDO vendor (an OODBMS vendor, O/R mapping tool vendor, or ERP vendor) will have implemented the appropriate J2EE Connector interfaces such that a

PersistenceManagerFactory will be available for you to look up.

8

Note

JDO also integrates with XA, meaning that distributed transactions across multiple JDO implementations, and indeed RDBMS data stores, are supported.

JDO defines two main ways in which the cache can be used. All JDO implementations must support so-called data store transactions and can optionally support optimistic

transactions:

• When a persistent-capable object is in a cache through a data store transaction, it effectively prevents any other user from holding this object in his or her cache.

It is almost as if the object is “checked out” to the user’s cache, a little like checking out code from a source code control system. It remains there for that user’s exclusive use (to be read or modified) until he or she indicates that the transaction is complete. Any other user who wants to use the object must wait until the original user’s transaction has completed.

You may perhaps recognize this approach; it is often called pessimistic locking.

• Although not mandatory within the JDO specification, most JDO implementations suitable for use within a J2EE environment will also support optimistic locking.

Here, it is possible for a persistent-capable object to reside in two different users’ caches at the same time. As long as each only reads the data from the object, there are no issues (hence, “optimistic”).

If one user modifies the data and commits their optimistic transaction, the new state of the object will be transparently written back to the data store. The other user does not necessarily know about this (though he or she can request to refresh the object instance if needed).

11 0672323842 CH08 3/20/02 9:30 AM Page 386

386 Day 8

Of course, issues do arise when both users modify the data. In this case, the first user to commit his or her transaction will succeed. When the second user attempts to commit his or her transaction, the

PersistenceManager will throw a javax.jdo.JDOUserException

, detailing that the state of the object has been changed by some other user since it was first instantiated in the cache.

From the data store’s perspective, the optimistic transaction approach actually involves two data store transactions. The first is short-lived, lasting long enough just to read the data from the data store. The second will happen some time later (perhaps seconds, minutes, or longer) and again will be short-lived. During this second data store transaction, the persistent data will be updated with the modified data taken from the committing user’s cache.

The JDO specification also defines the notion of JDO identity. There are three different ways for a JDO vendor to implement JDO identity, depending on the underlying technology:

• For JDO implementations based on an O/R mapping tool, JDO identity basically corresponds to the primary key as defined in the RDBMS. The JDO specification terms this application identity or primary key identity.

• For JDO implementations based on an OODBMS, JDO identity corresponds to the object ID as assigned by the OODBMS itself. The JDO specification calls this data

store identity.

• The final JDO identity type is perhaps likely to be least often used; it is simply called non-data store identity. This relates to objects unique within the JVM, but that are only ever written to (not read from) a data store (entries in a log file, for example).

The term application (primary key) identity is used for RDBMS-based identity because it is effectively the application itself that defines the semantics of the primary key. This has echoes in EJB where you, as the bean developer, are required to implement a primary key class or identify the primary key field. The RDBMS data store is expected to enforce the notion of identity through the use of unique indexes.

JDO identity is not the same as Java object identity, because in a single JVM, there could be many active

PersistenceManager s; if optimistic transactions are used, multiple Java objects could be instantiated all with the same JDO identity. However, JDO identity does have some correspondence to Java’s notion of equality (that is, where two objects are considered the same if equals() returns true

). In particular, JDO requires that persistence-capable objects implement equals() in such a way that it returns true if and only if the JDO identity is the same. Unsurprisingly, this is exactly the requirement that EJB imposes on primary keys.

11 0672323842 CH08 3/20/02 9:30 AM Page 387

Transactions and Persistence 387

Note JDO is very clear about identity, recognizing that more than one object instance may be instantiated for a single instance in the data store. In contrast, EJB is quite casual in this regard. Indeed, the EJB specification explicitly states that EJB containers have latitude to, for example, implement precisely one Entity bean per instance in the data store (option A, section 10.5.9) or to have multiple instances (option C).

Such latitude is surprising. Option A effectively means that Entity beans reside in a server-side cache. One consequence of this is that deadlocks must be handled in the EJB container. Meanwhile, option C effectively means that

Entity beans reside in a client-side cache; any deadlocks arising are handled in the data store.

For any given persistence-capable object, only one of these different types of JDO identity applies. The JDO deployment descriptor (described briefly later) identifies the type of

JDO identity in use.

javax.jdo

Classes and Interfaces

The JDO API is defined by classes and interfaces in the javax.jdo

package. Some of the interfaces are intended to be implemented by the JDO vendor and some by the application developer (but more on this in a moment). Figure 8.13 shows the main classes of the javax.jdo

package.

The javax.jdo.PersistenceManagerFactory

, javax.jdo.PersistenceManager

, javax.jdo.StateManager

, and javax.jdo.Transaction

interfaces are all implemented by the JDO vendor. The

PersistenceManagerFactory has already been discussed. The

PersistenceManager is the most important interface, because it provides the methods to control the lifecycle of the persistence-capable objects. Relating this back to EJB, you might think of it as combining the functions of the EJB container and of an EJB’s home interface. The

PersistenceManager also provides access to the current javax.jdo.Transaction

. This allows the application developer to demarcate the transaction boundaries. In a J2EE environment, a CMTD EJB does not need to call the methods of this interface because the EJB container will do this work. If a BMTD Session bean is being developed to use JDO, the bean developer can use either the javax.jdo.Transaction

or the javax.transaction.UserTransaction

interface. The former is usually to be preferred however, because this allows a single

PersistenceManager to be used across multiple transactions. If a

UserTransaction is used, a

PersistenceManager must be acquired for each and every transaction. Finally, the

StateManager is used internally by the JDO implementation to keep track of changes to persistence-capable objects.

8

11 0672323842 CH08 3/20/02 9:30 AM Page 388

388 Day 8

F

IGURE

8.13

The significant classes and interfaces of the javax.jdo

package.

interface

…PersistenceManagerFactor

getPersistenceManager setConnectionFactory getConnectionFactory setMultithreaded getMultithreaded setOptimistic getOptimistic setRetainValues getRetainValues setNontransactionalRead getNontransactionalRead setNontransactionalWrite getNontransactionalWrite setIgnoreCache getIgnoreCache getMaxPool setMaxPool getMinPool setMinPool getMsWait setMsWait getProperties supportedOptions

com.vendor.PMFactoryImpl

«creates» interface

…jdo.PersistenceManager

isClosed close currentTransaction evict evictAll refresh refreshAll newQuery getExtent newSCOInstance newCollectionInstance newMapInstance getObjectById getObjectId getTransactionalObjectId makePersistent makePersistentAll deletePersistent deletePersistentAll makeTransient makeTransientAll makeTransientAll makeTransactional makeTransactionalAll makeNontransactional makeNontransactionalAll setUserObject getUserObject getPersistenceManagerFactory getObjectIdClass setMultithreaded getMultithreaded setIgnoreCache getIgnoreCache

com.vendor.PMImpl

1 interface

javax.jdo.StateManager

com.vendor.SMImpl

interface

…jdo.InstanceCallbacks

jdoPostLoad jdoPreStore jdoPreClear jdoPreDelete

1 interface

javax.jdo.Transaction

begin commit rollback isActive setNontransactionalRead getNontransactionalRead setNontransactionalWrite getNontransactionalWrite setRetainValues getRetainValues setOptimistic getOptimistic setSynchronization getSynchronization getPersistenceManager

com.vendor.TranImpl

0..* interface

…jdo.PersistenceCapable

READ_WRITE_OK

LOAD_REQUIRED

READ_OK

CHECK_WRITE

MEDIATE_WRITE

CHECK_READ_WRITE

MEDIATE_READ_WRITE jdoGetPersistenceManager jdoReplaceStateManager jdoProvideField jdoProvideFields jdoReplaceField jdoReplaceFields jdoReplaceFlags jdoCopyFields jdoMakeDirty jdoGetObjectId jdoGetTransactionalObjectId jdoIsDirty jdoIsTransactional jdoIsPersistent jdoIsNew jdoIsDeleted jdoNewInstance jdoNewInstance jdoNewObjectIdInstance jdoCopyKeyFieldsToObjectId jdoCopyKeyFieldsToObjectId

+ ObjectIdFieldManager

com.mycompany.ApplicationObject

com.mycompany.PersistenceAwareAppObject

Of the two remaining interfaces shown in Figure 8.13, the javax.jdo.PersistenceCapable

interface is implemented by the application developer.

Hang on though! Isn’t JDO meant to support transparent persistence? Doesn’t that mean that there should be no requirement for the application classes to implement any sort of interface at all? Well, yes…and no. It is true that the persistence-capable classes developed by the application developer neither need to call the classes in javax.jdo

, nor do they need to implement any of the interfaces in javax.jdo

. However, before a persistentcapable class can be deployed into a JDO Implementation, it must be “enhanced.” An enhancer is a tool provided by the JDO vendor that manipulates the byte code of the compiled application classes. The resulting enhanced byte code represents a version of the application class that does implement the

PersistenceCapable interface.

This whole process possibly sounds somewhat peculiar, but compare it to the approach used in EJB for CMP Entity beans. As you recall, the bean developer creates an abstract class with abstract getter and setter methods for each of the cmp-field s. The EJB container vendor’s deployment tools then generate an implementation for those methods and create the database access code. The enhancement process effectively does the equivalent of the first of these two tasks and also imbues the application class with a reference to a

StateManager instance. The

StateManager itself takes on the job of database access and persistence.

11 0672323842 CH08 3/20/02 9:30 AM Page 389

Transactions and Persistence 389

The final interface, shown in Figure 8.13, is the

InstanceCallbacks interface. The application developer can choose to implement this or not. If implemented, the methods give the application class visibility as to the transaction boundaries. You may have spotted that this is pretty similar to EJB’s optional

SessionSynchronization interface.

Time for some code! Listing 8.10 shows the basic steps to create a new persistencecapable

Customer

.

L

ISTING

8.10

Using JDO to Create a New

Customer

1: // import javax.jdo.*;

2: // import javax.naming.*;

3:

4: Context ctx = new InitialContext();

5: PersistenceManagerFactory pmf = (PersistenceManagerFactory)

6: ctx.lookup(“java:comp/env/jdo/SomePersistenceManagerFactory”;

7: PersistenceManager pm = pmf.getPersistenceManager();

8:

9: Transaction txn = pm.currentTransaction();

10: txn.begin();

11: Customer customer = new Customer(login, address1, address2, email, name);

12: pm.makePersistent(customer);

13: txn.commit();

That’s honestly all there is to it!

Customer is just a regular Java class that has been run through the JDO Enhancer.

Queries

As well as being able to create new objects, JDO also provides the facility to find existing objects. This is not surprising; after all, the home interface offers the finder methods as well as the create methods in EJB.

Rather than defining a declarative language, such as SQL or EJBQL, JDO uses a Java

API approach. Figure 8.14 shows the classes and interfaces of javax.jdo

that allow queries to be performed.

The

PersistenceManager.newQuery() method will instantiate a javax.jdo.Query

object. The set of candidate objects to be returned by the query is then configured using the setCandidates() method. This is overridden to accept either a java.util.Collection

or a javax.jdo.Extent

(more on

Extent shortly). A filter can also be defined using setFilter()

, as can parameters to the filter. Variables are used to declare iterators over multi-valued collection fields.

8

11 0672323842 CH08 3/20/02 9:30 AM Page 390

390 Day 8

F

IGURE

8.14

Classes and interfaces that support JDO queries.

Conceptually, all

Persistence Capable objects are partitioned into extents by class.

interface

…jdo.PersistenceManager

newQuery getExtent getObjectById

com.vendor.PMImpl

1 1

0..1

candidates interface

javax.jdo.Query

setClass setCandidates setFilter declareImports declareParameters declareVariables setOrdering setIgnoreCache getIgnoreCache compile execute executeWithMap executeWithArray getPersistenceManager close closeAll

<{java.lang.Class}>

0..* interface

javax.jdo.Extent

iterator hasSubclasses getCandidateClass getPersistenceManager closeAll close

«creates»

com.vendor.QueryImpl

com.vendor.ExtentImpl

Not all methods shown.

0..* active

0..* inExistence interface

…jdo.PersistenceCapable

0..* finds

The

Extent interface is principally used to identify all instances of some specified class in the persistent data store. Conceptually, this could be a very large set of objects, so commercial JDO implementations are not expected to fully materialize the set. Indeed, the JDO specification explicitly requires that JDO implementations must not cause an out-of-memory error when instantiating an

Extent

. The java.util.Iterator

returned by the iterator() method must be able to iterate over all instances if needed, but notice that other methods of the java.util.Collection

(such as contains() and isEmpty()

) are not present in the

Extent interface. In practice, the

Extent and

Query implementations will be closely coupled to provide efficient ways to identify the qualifying persistence-capable objects.

As an example, Listing 8.11 shows two queries. The first identifies all jobs that have a customer of

“winston”

; the second finds all jobs that require

“Cigar Trimmer” as a skill.

L

ISTING

8.11

Using JDO to Search for Jobs that Meet Some Criteria

1: // import javax.jdo.*;

2: // import javax.naming.*;

3:

4: Context ctx = new InitialContext();

11 0672323842 CH08 3/20/02 9:30 AM Page 391

Transactions and Persistence 391

L

ISTING

8.11

Continued

5: PersistenceManagerFactory pmf = (PersistenceManagerFactory)

6: ctx.lookup(“java:comp/env/jdo/SomePersistenceManagerFactory”;

7: PersistenceManager pm = pmf.getPersistenceManager();

8:

9: Transaction txn = pm.currentTransaction();

10: txn.begin();

11:

12: // query #1: all jobs that have a customer of “winston”.

13: Query query1 = pm.newQuery();

14: query1.setClass(Job.class);

15: Extent candidateJobs = pm.getExtent(Job.class, false);

16: query1.setCandidates(candidateJobs);

17: query1.declareParameters(“String nameParam”);

18: query1.setFilter( “customer == nameParam “);

19: Collection query1Res = (Collection) query.execute(“Winston”);

20: for(Iterator iter = query1Res.iterator(); iter.hasNext(); ) {

21: Job job = (Job)iter.next();

22: System.out.println(job.getRef());

23: }

24:

25: // query #2: all jobs that require “Cigar Trimmer” as a skill

26: Query query2 = pm.newQuery(Job.class, candidateJobs);

27: query2.declareVariables(“Skill eachSkill”);

28: query2.setFilter(

➥”skills.contains(eachSkill) &&

➥eachSkill.name == \”Cigar Trimmer\””);

29: Collection query2Res = (Collection) query.execute();

30: for(Iterator iter = query2Res.iterator(); iter.hasNext(); ) {

31: Job job = (Job)iter.next();

32: System.out.println(job.getRef());

33: }

34: txn.commit();

Other Features

There is much more in JDO than there is room to cover here. Some aspects worth a brief mention include the following:

Deployment descriptors—A deployment descriptor is used for each persistencecapable application class. This identifies the fields that are persistent rather than transient, the JDO identity type, and other information required for the JDO enhancer.

Second Class Objects (SCOs)—These are similar in concept to dependent value classes in EJBs, in that they are persistent only by virtue of being reachable from

First Class Objects (all objects previous discussed have been First Class Objects).

In particular, they do not have a JDO identity. If an SCO is modified, it must explicitly notify its containing First Class Object.

8

11 0672323842 CH08 3/20/02 9:30 AM Page 392

392 Day 8

The JDO specification identifies various classes in the JDK library packages that are first- or second-class, or that are not persistable at all (for example, java.net.Socket

).

Lifecycle—Persistent-capable objects go through different stages of their lifecycle.

For example, when an object is first instantiated, it is in the transient state. When the

PersistenceManager.makePersistent() method is called, the object transitions to persistent-new state. One state, hollow, is very similar to the EJB notion of a passivated Entity bean.

Transient transactional objects—These are persistent-capable objects acting in a manner akin to a

ShoppingCart

Session bean whose implementation supports automatic transaction recovery (implementing javax.ejb.SessionSynchronization

).

Gotchas

The following are some “gotchas” to help you with your implementation:

• If writing CMTD Session beans, resource manager transaction methods (such as connection.commit()

) must not be used.

• The ejbCreate() and ejbRemove() lifecycle methods for Session beans (and

Message-driven beans) are performed with unspecified transaction context.

• The collection returned by the getter method for a cmr-field cannot be used outside of the transaction in which it was materialized (see EJB specification, section

10.3.8).

• If using a SQLj Part 0 compiler, make sure that the vendor offers adequate support for the generated code to run within the J2EE environment.

• If using SQLj Part 1 or Part 2, be sure to do some performance benchmarking first.

This is a new technology and JVM-enabled features within the RDBMS are highly unlikely to perform as well as raw SQL.

• If the javax.transaction.UserTransaction

interface is used for transaction demarcation within BMTD Session beans that are using JDO for persistence, the javax.jdo.PersistenceManager

must be acquired after the transaction has been started.

Moreover, the

PersistenceManager cannot be used after the transaction has complete. It is better to use the javax.jdo.Transaction

that does not require a new

PersistenceManager for each new transaction.

• One for you to revisit after Day 9, “Java Messaging Service,” and Day 10,

“Message-Driven Beans”—the JMS request/reply paradigm cannot be used for transacted sessions (see EJB specification, section 17.3.5).

11 0672323842 CH08 3/20/02 9:30 AM Page 393

Transactions and Persistence 393

Summary

Curious day, today. If you’ve skipped over all the sections that discuss technologies that aren’t appropriate to you, you might still be feeling pretty fresh. On the other hand, if you’ve been trying to wrap your head around all these new and peculiar concepts, you could be completely worn out.

Anyway, today you’ve learned that EJB containers support container-managed transaction demarcation, but that your Session beans can take control using bean-managed transaction demarcation if needed. Behind the scenes, there are some complex goings on with the XA interfaces to support distributed transactions, using 2PC and XA-aware data store connections, such as javax.sql.XADataSource

.

JDBC is the de-facto way for implementing persistence of Java objects, and JDBC 3.0

unifies the various classes and interfaces in the java.sql

and javax.sql

packages.

JDBC 2.1 and 3.0 have fleshed out support for the most relevant features of SQL1999, specifically advanced data types.

The SQLj initiative addresses both client-side integration of SQL and Java (part 0) and server-side (that is, RDBMS) integration (parts 1 and 2). SQLj part 0 is more succinct than JDBC, but is not particularly well tailored to the J2EE environment, because for the most part, it pre-dates it. SQLj parts 1 and 2 offer some opportunities to radically change the way that databases are used, with SQLj part 2 operating in much the same space as the SQL1999 advanced data types.

JDO is a new specification that aims to make Java persistence totally transparent. It is a natural fit for OODBMS and O/R mapping tool vendors and also works as a front-end to

ERP systems. JDO primarily focuses on the API into the client-side cache of persistent objects, leaving much of the back-end configuration to the JDO implementation vendors.

Q&A

Q Why would it be pointless to deploy the getter of a multi-valued cmr-field using the

RequiresNew transaction attribute?

A This would be pointless because the returned collection cannot be used outside of the transaction in which it was materialized.

Q What happens to the current transaction when an EJB throws an application exception?

A The bean may or may not mark the transaction for rollback; consult the bean’s documentation.

8

11 0672323842 CH08 3/20/02 9:30 AM Page 394

394 Day 8

Q When using SQLj Part 2, why should the Java class acting as an abstract data type define a static variable serialVersionUID

?

A To provide forward compatibility with future versions of the class.

Exercises

A nice short exercise for you today.

The starting point for the exercise is a version of the today’s case study that uses the

BMTD for the

AdvertiseJob bean. Convert the

Register bean (that deals with applicants) to use BMTD rather than CMTD. The code to work on is under day08\exercise

.

The steps you should perform are as follows:

1. Make sure that you are still using the CMP version of the

Agency database.

Although you won’t be changing them, the Entity EJBs for the exercise are the

CMP versions from yesterday.

2. Update the implementation of the various methods of

RegisterBean class, basing it on

AdvertiseJobBean

. You will probably find it helpful to copy over the 3 helper methods ( beginTransactionIfRequired()

, statusAsString()

, and completeTransactionIfRequired()

).

3. Modify the dd\agency_session_ejbs-ejb-jar.xml

deployment descriptor, changing

RegisterBean to use transaction-type of

Bean

. You can also remove the

RegisterBean

’s container-transaction elements under the assemblydescriptor element because they will no longer be needed.

4. Build the enterprise application ( agency.ear

in the jar directory) and then load it into deploytool

. Deploy and complete any information missing from the wizard.

5. Test your program using the

AllClients client, run with run\runAll

.

The solution is in day08\agency

.

12 0672323842 CH09 3/20/02 9:23 AM Page 395

W

EEK

2

D

AY

9

Java Message Service

So far, you have learned about two types of EJB—Entity Beans and Session

Beans. Before introducing you to the third type—Message Beans (to be covered on Day 10, “Message-Driven Beans”), we will take a look at the purpose and use of messaging within enterprise applications, the Java Message Service’s

API (JMS), and its place within J2EE.

Messaging

Many enterprise applications are built from separate software components.

These components can reside on the same system or in a distributed or multitiered environment on several Java Virtual Machines.

Some method of communication is almost always required between the components in large systems. It is often a requirement that this communication should also be loosely coupled or asynchronous. In asynchronous communication, the sender sends the message and continues execution without waiting for a reply, and the receiver can retrieve the message at any time after it has been sent. In a loosely coupled system, the sender does not need to necessarily know who the recipient is; the communication itself may or may not be asynchronous. This

12 0672323842 CH09 3/20/02 9:23 AM Page 396

396 Day 9 communication between software components is called messaging.

Without support for a messaging system, the programmer would typically use a sockets interface for inter-application communication. With sockets, both the sender and receiver need to agree on the socket address, and both applications need to be running at the same time. Likewise, if Remote Method Invocation (RMI) is used, the sender (or calling application) needs to know about the receiver’s (or remote application’s) methods. With messaging, the sender and receiver only have to agree on the message format and where to send it. The sender and receiver do not need to know anything about each other, nor do they both need to exist at the same time.

Messaging should be used in preference to a tightly coupled API such as RMI when some or all of the following conditions exist:

• The components interfaces are not known or not published.

• Not all of the components will be running at the same time.

• A sender needs to communicate with multiple receivers.

• No response is required.

Messaging should not be confused with electronic mail (e-mail). E-mail is used to communicate between people, whereas messaging is the mechanism used to communicate between applications.

Messaging is used in various environments and many applications; it is not unique to

J2EE. A well-known example of a messaging service is IBM’s MQ Series. While there are numerous messaging APIs, the only one supported by J2EE is JMS.

Message Passing

There are several models of message passing, but JMS only supports two—point-to-point and publish/subscribe. Both of these are known as a push models. The sender of the message is the active initiator, and the receiver is a passive consumer.

With point-to-point, the sender and receiver agree on a message destination, also known as a queue. The sender leaves the message and the receiver picks up the message at any time thereafter. The message remains in the queue until the receiver removes it. The following are some real-life examples of point-to-point communication:

• Sending a fax

• Dropping letters at a hotel desk to be picked up later by a hotel guest

• Leaving a voice mail message

Figure 9.1 illustrates point-to-point messaging.

12 0672323842 CH09 3/20/02 9:23 AM Page 397

Java Message Service 397

F

IGURE

9.1

Point-to-point messaging model.

Sender sends message to queue

Point-to-Point

Message

Message

Message

Message

Receiver

Consumes message and sends acknowledgement

Queue

With the publish/subscribe model, the sender, now called a publisher, again sends messages to an agreed destination. The destination is, by convention, known as a topic and this time there may be many receivers and these are called subscribers. Messages are immediately delivered to all current subscribers and are deleted when all the subscribers have received the message. Figure 9.2 portrays the publish/subscribe messaging model.

F

IGURE

9.2

Publish/Subscribe messaging model.

Publish/Subscribe

Publisher publishes message to topic

Topic

Subscriber

Consumes message and sends acknowledgement

Subscriber

Consumes message and sends acknowledgement

Subscriber

Consumes message and sends acknowledgement

Within JMS, these models are called message domains. Code examples and further details on the JMS implementation of these message domains are presented in the next section.

Java Message Service API

The JMS API is a Message-Oriented Middleware (MOM) API. It was designed by a collaboration of several companies, including Sun Microsystems and IBM. Version 1 of the

API was released in August, 1998. The purpose of JMS is to enable applications to transmit and receive messages in an asynchronous and reliable way. The JMS API also

9

12 0672323842 CH09 3/20/02 9:23 AM Page 398

398 Day 9 defines interfaces to allow applications written using JMS to communicate with other messaging APIs.

The JMS designers’ aims were as follows:

• To minimize the messaging concepts that a programmer needs to understand

• Provide communication that is loosely coupled, asynchronous, and reliable

• Have a consistent API that is independent of the JMS provider

• Maximize the portability of JMS applications

The JMS API provides messaging that is

Loosely coupled—The sender and receiver may have no information about each other or the mechanisms used to process messages.

Asynchronous—Receivers do not request messages. Messages can be delivered as they arrive. The sender does not wait for reply.

Reliable—A message is sent and received once and only once.

Note the JMS API does not specify how to control the privacy and integrity of messages.

The type and level of message security is left to the JMS providers and is configured by administers not by J2EE clients.

JMS and J2EE

Prior to J2EE 1.3, it was not necessary to implement the JMS API. From version 1.3, the

JMS API became an integral part of the J2EE requirements.

Support for JMS within the J2EE platform provides the following functionality and features:

• Message beans for the sending and receiving of asynchronous messages (message beans are covered on Day 10)

• Support for distributed transactions

• Concurrent consumption of messages

An additional goal is to provide loosely coupled, reliable, asynchronous communication with legacy systems.

12 0672323842 CH09 3/20/02 9:23 AM Page 399

Java Message Service 399

Programmatically, JMS can be considered to be a container-managed resource, similar to a JDBC connection. Application clients, EJBs, and Web components can all utilize the

JMS API to send and receive messages. Note, however, that Applets are not required to support JMS.

The addition of the JMS API to J2EE simplifies enterprise software development and provides access to existing Enterprise Information Systems (EIS) and other legacy systems.

JMS API Architecture

Before launching into the JMS API programming model, there are some details of the

JMS API architecture that need to be covered. Table 9.1 contains a list of the J2EE components that are found in a JMS application.

T

ABLE

9.1

JMS Components

Component

JMS Provider

JMS Clients

Description

This is the messaging system that implements the JMS API interfaces and provides certain control features. All J2EE implementations from J2EE

1.3 and later must include a JMS provider.

System components or programs that send or receive messages.

Messaging Domains JMS supports both the point-to-point and publish/subscribe message domains. Both must be implemented in J2EE 1.3 and later.

Messages These are the objects used to communicate between JMS clients.

Queues

Topics

Used to hold messages in the point-to-point domain.

Used to hold messages in the publish/subscribe domain.

Administered Objects These are pre-configured JMS objects used to create connections with a

JMS Provider (a connection factory) or to specify the target or source of messages (queues or topics).

Connection A client’s active connection to the JMS provider.

Session

Native Clients

One or more sessions can be created for each connection. Used to create senders and receivers and administer transactions.

Clients that use a non-JMS messaging API.

Figure 9.3 is a UML activity diagram of how the components fit together in a JMS application.

9

12 0672323842 CH09 3/20/02 9:23 AM Page 400

400

F

IGURE

9.3

JMS application components.

Day 9

Message

Generator

JMS create

Connection

JNDI lookup

Connection

Factory create Session lookup Queue/

Topic create Sender/

Publisher create Message send Message

No more messages

More messages

Message Domains

JMS supports two types of message domains—point-to-point and publish/subscribe, giving application developers both choice and flexibility in message handling.

JMS Point-to-Point Messaging Domain

With point-to-point messaging the application is built around message queues, with a one-to-one relationship between a sender and a receiver. Each sender addresses the message to, and the receiver removes messages from, a queue. Many receivers can access the same queue, but only the first to pick up the message will receive it. The sender may set an expiration time on a message, after which it will be deleted from the queue.

There is no mechanism to send a message to a particular receiver. If this one-to-one relationship is required, the message must be sent to a queue that has only one receiver.

12 0672323842 CH09 3/20/02 9:23 AM Page 401

Java Message Service 401

A JMS point-to-point message domain has the following characteristics:

• Each message is produced by the sender and consumed by one and only one receiver.

• Messages are either consumed by the receiver or they time out and are deleted by the JMS provider.

• Receivers can consume the message any time after it has been sent.

• The receiver does not need to exist when the message is produced.

• The receiver cannot request a message.

• The receiver must acknowledge receipt of the message.

The JMS API does not allow a single message to be sent to multiple queues. If you need to send a message to multiple receivers, you should consider using the publish/subscribe message domain.

JMS Publish/Subscribe Messaging Domain

The JMS publish/subscribe messaging domain has a completely different delivery model from point-to-point. With the publish/subscribe model, senders post messages to a topic; many receivers can register interest in a topic by subscribing to it.

Messages in a topic are distributed to all subscribers and only exist in the topic for as long as this process takes. A receiver can only receive messages posted after it has subscribed to a topic, and the receiver must be active at the time the message is posted to receive it. This introduces a timing dependency between the sender and the receivers that is not present in point-to-point messaging.

To circumvent this timing dependency, the JMS API allows receivers to create durable subscriptions. Durable subscriptions will be covered in more detail in the section titled

“Creating Durable Subscriptions,” later on in this chapter.

A JMS publish/subscribe message domain has the following characteristics:

• Each message is produced by a publisher and consumed by zero or more subscribers.

• Messages are immediately distributed to the existing subscribers.

• Subscribers must exist at the time the message is published to receive the message.

• Durable subscriptions can be used to allow subscribers to receive messages published to a topic when the subscriber was inactive.

• The subscriber must acknowledge receipt of the message.

Table 9.2 lists the JMS objects in the point-to-point domain.

9

12 0672323842 CH09 3/20/02 9:23 AM Page 402

402 Day 9

T

ABLE

9.2

Components in the JMS Point-to-Point Message Domain

Component

QueueConnectionFactory

QueueConnection

Description

Administered object used to create an object that implements the

QueueConnection interface

Active connection to a JMS provider

QueueSession

QueueSender

QueueReceiver

Queue

QueueBrowser

Provides methods for creating objects that implement the

QueueSender QueueReceiver or

QueueBrowser interfaces

Used by a client to send a message to a queue

Used to receive messages sent to a queue

Encapsulates the JMS provider queue name

Used to look at messages on a queue without removing them

Developing JMS Applications Using JBoss

1

Shortly, you will be developing a simple JMS application. As with all the code examples in this book, the application will be developed using J2EE RI. Although J2EE RI provides a good environment for learning about JMS, it is not intended to support commercial applications. Before developing this JMS application, there will be a short digression to show the JMS features available with JBoss.

JBoss is in an open-source implementation of J2EE developed by the JBoss Group.

JBoss is available to download for free from www.jboss.org

.

JMS Implementation in JBoss

JBoss contains a JMS provider called JBossMQ. JBossMQ is fully JMS API-compliant and can be used for standalone JMS clients. JBossMQ has a number of configuration files, the most important of which is called jboss.jcml

. This file resides under the JBoss installation in the conf/default directory. To create a message application in Jboss, you first have to read or edit this file to

1. Look up the names of the available connection factories

2. Add destinations (queues and topics)

Additionally, you can optionally add client identifiers (JBossMQ users) to the jbossmqstate.xml

file (also in conf/default

) for authentication. The purpose and use of authentication is to associate a client connection and its objects with a state maintained by the JMS provider. At the current time, the only state defined is that required to support durable subscriptions.

12 0672323842 CH09 3/20/02 9:23 AM Page 403

Java Message Service 403

Administered Objects

Connection factories and destinations are the administered objects that are configured by the JMS administrator.

Connection Factory

The purpose of a connection factory is to encapsulate connection configuration. Clients use a pre-configured connection factory to create connections to the JMS provider.

JBoss includes several different connection factories with varying characteristics, as shown in Table 9.3.

T

ABLE

9.3

Connection Factories Provided in JBoss

JNDI Name Description

ConnectionFactory

The default factory. A fast two-way socket-based communication protocol.

XAConnectionFactory

RMIConnectionFactory

RMIXAConnectionFactory java:/ConnectionFactory java:/XAConnectionFactory

UILConnectionFactory

UILXAConnectionFactory

Also a fast two-way socket based protocol that also has support for XA transactions.

Uses RMI to implement communication mechanism.

Uses RMI and also has support for XA transactions.

Very fast in-VM protocol that does not use sockets. Available when the client is in the same virtual machine as JBossMQ.

Fast in-VM protocol that also. supports XA transactions.

Protocol multiplexed over one socket. Used when going through firewalls or when the client is not able to look up the server IP address correctly.

Same as

UILConnectionFactory but also has support for XA transactions.

You should choose the connection factory appropriate for your application. The default connection factory in JBossMQ is

ConnectionFactory

, which is implemented over a two-way, socket-based communication protocol.

Note that other JMS providers may have different connection factories with different characteristics and different names.

Destinations

Destinations define the address parameters for queues and topics. New queue and topic destinations are added to the jboss.jcml

file with the following:

9

12 0672323842 CH09 3/20/02 9:23 AM Page 404

404 Day 9

<mbean code=”org.jboss.mq.server.TopicManager”

➥ name=”JBossMQ:service=Topic,name=YourTopicName”/>

<mbean code=”org.jboss.mq.server.QueueManager”

➥ name=”JBossMQ:service=Queue,name=YourQueueName”/>

You have now finished looking at JBoss. The following examples illustrate how to connect to queues and topics using J2EE RI.

Programming a JMS Application Using

J2EE RI

In this section, you will develop a simple point-to-point application that sends and receives a text message. Although this code has not been written for any one particular

JMS implementation, you will need to be aware that the mechanism for setting up the administered objects is implementation specific. Also, the JNDI names may be different for different J2EE environments.

J2EE RI Connection Factories

With the J2EE RI, you will use the default connection factory objects, named jms/QueueConnectionFactory and jms/TopicConnectionFactory

.

Adding Destinations in J2EE RI

J2EE RI provides a command-line program called j2eeadmin to list, create, modify, and delete queues and topics. There is also a screen in the J2EE RI Application Deploy Tool that provides an interface to j2eeadmin for the same purpose.

Creating a Queue in J2EE RI

The point-to-point application uses a queue for communication. To create a queue do the following:

1. Ensure that J2EE server is running.

2. Use j2eeadmin or deploytool to create the queue.

Create a Queue Using

j2eeadmin

To see the existing queues and topics, use the following: j2eeadmin –listJMSDestination

The J2EE RI has a default queue predefined— jms/Queue

.

12 0672323842 CH09 3/20/02 9:23 AM Page 405

Java Message Service 405

To add a queue called jms/firstQueue

, use the following: j2eeadmin –addJMSDestination jms/firstQueue queue

The j2eeadmin command works silently, so to check that your queue has been created, run

J2eeadmin -listJMSDestination once more. Figure 9.4 demonstrates the use of these two commands to create a queue called jms/firstQueue

(this is the queue you will use in your first example).

F

IGURE

9.4

Using j2eeadmin to add a JMS queue to

J2EE RI.

9

Create a Queue Using

deploytool

You can also use deploytool to list and add queues. Start up deploytool and select Server

Configuration from the Tools menu, as shown in Figure 9.5.

F

IGURE

9.5

Using deploytool to configure installation.

12 0672323842 CH09 3/20/02 9:23 AM Page 406

406 Day 9

This will bring up the Configure Installation screen. Select JMS Destinations in the panel on left to display the screen in Figure 9.6.

F

IGURE

9.6

Adding JMS destination in deploytool

.

Click the Add button in the panel at bottom right titled JMS Queue Destinations and add a new queue called

jms/firstQueue

.

Point-to-Point Messaging Example

You are now in a position to start coding. For all the following code examples, you will need to import the javax.jms

package. This contains all the classes for creating connections, sessions, queues, and topics.

To send a JMS message, a number of steps must first be performed to obtain a connection factory, establish a session, and create a

QueueSender object. Although the example is in the point-to-point message domain, the same steps are required for publish/subscribe topics.

The following steps show how to create a queue used later to send a simple text message.

1. Obtain the JNDI initial context.

Context context = new InitialContext();

2. Contact the JMS provider, obtain a JMS connection from the appropriate

ConnectionFactory

, and create a connection for a queue. The following code uses a connection factory registered against the default JNDI name jms/QueueConnectionFactory

.

QueueConnectionFactory queueFactory =

➥ (QueueConnectionFactory)context.lookup(“jms/QueueConnectionFactory”);

QueueConnection queueConnection = queueFactory.createQueueConnection();

12 0672323842 CH09 3/20/02 9:23 AM Page 407

Java Message Service 407

The createQueueConnection() method throws a

JMSException if the JMS provider fails to create the queue connection due to some internal error.

3. Establish a

QueueSession for this connection. In this case, the

QueueSession has transactions set to false

(transactions are covered later) and

AUTO ACKNOWLEDGE of receipt of messages. This will throw a

JMSException if the

QueueConnection object fails to create a session.

QueueSession queueSession = queueConnection.createQueueSession(false,

➥ Session.AUTO_ACKNOWLEDGE);

9

Note Although sent messages are not acknowledged, a session can be used to receive messages created by its own connection. This is why an

Acknowledge mode must be specified on a session, even if the queue is only used to send messages.

4. Obtain the queue destination using its JNDI name as defined using j2eeadmin or deploytool

. The lookup() method can throw a

NamingException if the name is not found. queue = (Queue)context.lookup("jms/firstQueue");

5. Finally, create a queue sender that will be used to send messages. This will throw a

JMSException if the session fails to create a sender, and an

InvalidDestinationException if an invalid queue is specified.

QueueSender queueSender = queueSession.createSender(queue);

Note how the connection factory hides all the implementation details of the connection from the client. It does the hard work of creating resources, handling authentication, and supporting concurrent use.

Now you have a queue that is ready to send messages. But before that, you need to know a little more about JMS messages.

JMS Messages

JMS messages consist of three parts:

A header—Used to identify messages, set priority and expiration, and so on and to route messages.

Properties—Used to add additional information in addition to the message header.

Message body—There are five message body forms defined in JMS—

BytesMessage

,

MapMessage

,

ObjectMessage

,

StreamMessage

, and

TextMessage

.

Note that only the header is a required component of a message; the other two parts, including the body, are optional.

12 0672323842 CH09 3/20/02 9:23 AM Page 408

408 Day 9

Message Header Fields

The JMS message header contains a number of fields that are generated by the JMS provider when the message is sent. These include the following:

JMSMessageID

A unique identifier

JMSDestination

Represents the queue or topic to which the message is sent

JMSRedelivered

Set when the message has been resent for some reason

The following three header fields are available for the client to set:

JMSType

A string that can be used to identify the contents of a message

JMSCorrelationID

Used to link one message with another, typically used to link responses to requests

JMSReplyTo

Used to define where responses should be sent

Other header fields may be set by the client but can be overridden by the JMS provider with figures set by an administrator:

JMSDeliveryMode

This can be either

PERSISTENT or

NON_PERSISTENT

(the default is

PERSISTENT

).

JMSPriority

Providers recognize priorities between 0 and 9, with 9 being the highest (default is 4). Note that there is no guarantee that higher priority messages will be delivered before lower priority ones.

JMSTimestamp

This contains the time the message was sent to the JMS provider by the application. Note that this is not the time the message is actually transmitted by the JMS provider to the receivers.

JMSExpiration

An expiration time

Each header field has associated setter and getter methods that are fully described on the

JMS API documentation.

Message Properties

As you have seen, there is not a great deal of scope for clients to add information to JMS header fields, but additionally JMS messages can incorporate properties. These are name/value pairs defined by the client.

Property values can be boolean

, byte

, short

, int

, long

, float

, double

, or

String and are defined using the appropriate

Message.setProperty

method. For example: message.setStringProperty (“Type”, “Java”); sets the message object’s property called

“Type” to the string

“Java”

. A corresponding getProperty method is used to retrieve a message’s property by name. The getPropertyNames() can be used if the names are not known.

12 0672323842 CH09 3/20/02 9:23 AM Page 409

Java Message Service 409

The prefix JMSX is used for JMS-defined properties. Inclusion of these JMSX properties are optional; refer to your JMS provider documentation to determine what properties are supported in your JMS implementation.

JMS Body Types

JMS supports five types of message body. Each is defined by its own message interface.

Each type is only briefly described in Table 9.4. Refer to the JMS API for more information on the message body types.

T

ABLE

9.4

JMS Message Body Types

Message Body Type

BytesMessage

MapMessage

ObjectMessage

StreamMessage

TextMessage

Message Contents

Un-interpreted byte stream.

Name/value pairs

Serializable Java object

Stream of Java primitives

Java String

Creating a Message

For the example given here, the default header properties will be used. Also, to keep this example straightforward, a

TextMessage body will be used (this is also the most common message body type). A

TextMessage object extends the

Message interface and is used to send a message containing a

String object.

A text message body is created using the createTextMessage() method in the

QueueSession object.

TextMessage message = queueSession.createTextMessage(String);

The content of the a text message is added with message.setText()

:

String msg = “some text”; message.setText(msg);

Having created a message, you are ready to send it.

Sending a Message

A message must be sent to a queue

QueueSender object as follows: queueSender.send(queue, message);

It’s as simple as that.

9

12 0672323842 CH09 3/20/02 9:23 AM Page 410

410 Day 9

Closing the Connection

Connections are relatively heavyweight JMS objects, and you should always release the resources explicitly rather than depending on the garbage collector. The sender, the session, and the connection should all be closed when no more messages need to be sent.

queueSender.close(); queueSession.close(); queueConnection.close();

With the J2EE RI implementation, prior to releasing the

QueueSender object, you must send a empty message to indicate no more messages will be sent. To do this, add the following line before the

QueueSender object is closed.

queueSender.send(queueSession.createMessage());

Note This code may not be required in other JMS implementations.

Send JMS Text Message Example

The code for the entire point-to-point sender example is shown in Listing 9.1.

L

ISTING

9.1

Complete Listing for Point-to-Point Queue Sender

1: import javax.naming.*;

2: import javax.jms.*;

3:

4: public class PTPSender {

5: private QueueConnection queueConnection;

6: private QueueSession queueSession;

7: private QueueSender queueSender;

8: private Queue queue;

9:

10: public static void main(String[] args) {

11: try {

12: PTPSender sender = new PTPSender();

13:

14: System.out.println (“Sending message Hello World”);

15: sender.sendMessage(“Hello World”);

16: sender.close();

17: } catch(Exception ex) {

18: System.err.println(“Exception in PTPSender: “ + ex);

19: }

12 0672323842 CH09 3/20/02 9:23 AM Page 411

Java Message Service 411

L

ISTING

9.1

Continued

20: }

21:

22: public PTPSender() throws JMSException, NamingException {

23: Context context = new InitialContext();

24: QueueConnectionFactory queueFactory =

➥ (QueueConnectionFactory)context.lookup(“jms/QueueConnectionFactory”);

25: queueConnection = queueFactory.createQueueConnection();

26: queueSession = queueConnection.createQueueSession(false,

➥ Session.AUTO_ACKNOWLEDGE);

27: queue = (Queue)context.lookup(“jms/firstQueue”);

28: queueSender = queueSession.createSender(queue);

29: }

30:

31: public void sendMessage(String msg) throws JMSException {

32: TextMessage message = queueSession.createTextMessage();

33: message.setText(msg);

34: queueSender.send(message);

35: }

36:

37: public void close() throws JMSException {

38: //Send a non-text control message indicating end of messages

39: queueSender.send(queueSession.createMessage());

40: queueSender.close();

41: queueSession.close();

42: queueConnection.close();

43: }

44:}

To send messages to the queue, run this program from the command line. The next example program will retrieve messages from that same queue for display onscreen.

Consuming Messages

There are two ways of consuming queued messages with JMS.

Synchronously—An explicit call to the receive() method.

Asynchronously—By registering an object that implements the

MessageListener interface. The JMS provider invokes the onMessage() method in this object each time there is a message queued at the destination.

Figure 9.7 is a UML sequence diagram showing the difference between synchronous and asynchronous message consumption.

9

12 0672323842 CH09 3/20/02 9:23 AM Page 412

412 Day 9

F

IGURE

9.7

Synchronous and asynchronous message consumption.

client receive()

Synchronous

Message

Asynchronous setMessageListener(this)

:Receiver method call blocks until message arrives next message arrives onMessage(Message)

The following example will demonstrate how to receive a message from a queue using a synchronous call.

Simple Synchronous Receiver Example

The code for a simple synchronous receiver is very similar to that of the sender method already presented. There are two differences in constructing the

QueueReceiver object.

The first one is obvious; a

QueueReceiver is created instead of a

QueueSender

. Like createSender()

, the createReceiver() method throws a

JMSException if the session fails to create a receiver, and an

InvalidDestinationException if an invalid queue is specified.

The second difference is that this time there is a call to the connection’s start() method, which starts (or restarts) delivery of incoming messages for this receiver. Calling start() twice has no detrimental effect. It also has no effect on the connection’s ability to send messages. The start() method may throw a

JMSException if an internal error occurs. A receiver can use

Connection.stop() to temporarily suspend delivery of messages.

The message is received using the synchronous receive() method, as shown next. This may throw a

JMSException

.

Message msgBody =queueReceiver.receive(); if (msgBody instanceof TextMessage) {

String text = ((TextMessage) msgBody).getText();

}

12 0672323842 CH09 3/20/02 9:23 AM Page 413

Java Message Service 413

If there is no message in the queue, the receive() method blocks until a message is available. There are two alternative versions of the receive() method:

• receiveNoWait()

Retrieves the next message available, or returns null if one is not immediately available

• receive(long timeout)

Retrieves the next message produced within the period of the timeout

Receive JMS Text Message Example

The code for the entire point-to-point receiver example is shown in Listing 9.2.

L

ISTING

9.2

Complete code for Point-to-Point Queue Receiver

1: import javax.naming.*;

2: import javax.jms.*;

3:

4: public class PTPReceiver {

5:

6: private QueueConnection queueConnection;

7: private QueueSession queueSession;

8: private QueueReceiver queueReceiver;

9: private Queue queue;

10:

11: public static void main(String[] args) {

12: try {

13: PTPReceiver receiver = new PTPReceiver();

14: System.out.println (“Receiver running”);

15: String textMsg = receiver.consumeMessage();

16: if (textMsg != null)

17: System.out.println (“Received: “ + textMsg);

18: receiver.close();

19: }

20: catch(Exception ex) {

21: System.err.println(“Exception in PTPReceiver: “ + ex);

22: }

23: }

24:

25: public PTPReceiver() throws JMSException, NamingException {

26: Context context = new InitialContext();

27: QueueConnectionFactory queueFactory =

➥ (QueueConnectionFactory)context.lookup(“jms/QueueConnectionFactory”);

28: queueConnection = queueFactory.createQueueConnection();

29: queueSession = queueConnection.createQueueSession(false,

➥ Session.AUTO_ACKNOWLEDGE);

30: queue = (Queue)context.lookup(“jms/firstQueue”);

31: queueReceiver = queueSession.createReceiver(queue);

32: queueConnection.start();

33: }

9

12 0672323842 CH09 3/20/02 9:23 AM Page 414

414 Day 9

L

ISTING

9.2

Continued

34:

35: public String consumeMessage () throws JMSException {

36: String text = null;

37: Message msgBody = queueReceiver.receive();

38: if (msgBody instanceof TextMessage) {

39: text = ((TextMessage) msgBody).getText();

40: }

41: return text;

42: }

43:

44: public void close() throws JMSException {

45: queueReceiver.close();

46: queueSession.close();

47: queueConnection.close();

48: }

49: }

Run this program from the command line. If you have previously used

PTPSender to put messages in the queue, these will now be displayed.

Asynchronous Messaging

For many applications, the synchronous mechanism is not suitable and an asynchronous technique is required. To implement this in JMS, you need to register an object that implements the

MessageListener interface. The JMS provider invokes this object’s onMessage() each time a message is available at the destination.

The receiver example will now be extended to support asynchronous messaging. Because this is a simple example, the listener is implemented in the same class. public class PTPListener implements MessageListener {

The message listener is registered with a specific

QueueReceiver by using the setMessageListener() method before calling the connection’s start() method. The following extra line is required in the constructor: queueReceiver.setMessageListener(this);

Messages might be missed if you call start() before you register the message listener.

To actually receive the messages, the

MessageListener interface provides a single onMessage() method. The JMS provider calls your implementation of this method when it has a message to deliver. The following is an example onMessage() method: public void onMessage(Message message) { try { if (message instanceof TextMessage) {

12 0672323842 CH09 3/20/02 9:23 AM Page 415

Java Message Service 415

}

}

} catch(JMSException ex) {

System.err.println(“Exception in OnMessage: “ + ex);

}

String text = ((TextMessage) message).getText();

System.out.println(“Received: “ + text);

The onMessage() method should handle all exceptions. If onMessage() throws an exception, the signature will be altered and, therefore, not recognized.

In the main() method, the

QueueReceiver object is initialized as before, but this time there is no call to a synchronous receive() method. public static void main(String[] args) {

System.out.println (“Listener running”); try {

PTPListener receiver = new PTPListener();

Thread.currentThread().sleep(2000); receiver.close();

} catch(Exception ex) {

System.err.println(“Exception in PTPListener: “ + ex);

}

}

A sleep has been placed in the main body to allow time for messages to be handled.

Normally, the main() method would sleep for a long period or do other processing and therefore requires some way to determine when message processing is finished.

Typically, a shutdown message is sent or the user interface is used to close the application. For brevity, this code is not included here.

Asynchronous Exception Handling

If you register an

ExceptionListener with a connection, your application will be asynchronously informed of problems. If a client only consumes a message, this may be the only way it can be informed that the connection has failed.

The JMS provider will call the listener’s onException() method, passing it a

JMSException describing the problem.

The Publish/Subscribe Message Domain

So far, you have seen how to create a simple point-to-point application with a single sender and receiver. Now you will build a simple bulletin board application to demonstrate the features of the publish/subscribe model.

9

12 0672323842 CH09 3/20/02 9:23 AM Page 416

416 Day 9

For this example, a

TopicPublisher will be produced that creates messages and publishes them to a bulletin board. A single

TopicSubscriber will asynchronously listen for messages. The messages will be printed to the screen—one at time—in the order they were received. The program then exits.

Remember that, unlike messages in a queue, messages in a topic are immediately distributed to all subscribers. As a result, the timing of the publisher and the subscriber becomes important. Apart from this, the publisher/subscriber code is very similar to the sender/receiver code. In fact, the publisher code is a copy of the previous sender code with all references to

Queue<object> changed to

Topic<object>

.

Table 9.5 lists the JMS objects in the publish/subscribe domain.

T

ABLE

9.5

Components in the JMS Publish/Subscribe Message Domain

Component

TopicConnectionFactory

TopicConnection

Description

Administered object used to create an object that implements the

TopicConnection interface

Active connection to a JMS provider

TopicSession

Provides methods for creating objects that implement the

TopicPublisher and

TopicSubscriber interfaces

Used by a client to publish a message to a topic

TopicPublisher

TopicSubscriber

Used to receive messages sent to a topic

Durable

TopicSubscriber

Used to receive messages published when the subscriber is inactive

Topic

Encapsulates the JMS provider topic name

Because subscribers only receive messages when they are active, it would be nice to be able to test for active subscribers before publishing to a topic. Unfortunately, this is not possible.

Publish/Subscribe Messaging Example

Now, you will build a simple bulletin board application. For this example, the bulletin board publisher program will generate 10 simple messages. The subscriber will be a

Swing application that will display the messages as they arrive.

The bulletin board used a topic called jms/bulletinBoard

. This must be created using the Configure Installation screen in deploytool or using j2eeadmin as follows: j2eeadmin –addJMSDestination jms/bulletinBoard topic

12 0672323842 CH09 3/20/02 9:23 AM Page 417

Java Message Service 417

Bulletin Board Publisher

The same mechanism is used to create a topic as a queue, so Listing 9.3 should appear very similar to that in the point-to-point receiver example, except that all references to a queue are replaced with topic.

L

ISTING

9.3

Bulletin Board Publisher Program

1: import javax.naming.*;

2: import javax.jms.*;

3:

4: public class BulletinBoardPublisher {

5: private TopicConnection topicConnection;

6: private TopicSession topicSession;

7: private TopicPublisher bulletinBoardPublisher;

8: private Topic bulletinBoard;

9:

10: public static void main(String[] args) {

11: try {

12: BulletinBoardPublisher publisher = new

BulletinBoardPublisher(“TopicConnectionFactory”,”jms/bulletinBoard”);

13:

14: System.out.println (“Publisher is up and running”);

15:

16: for (int i = 0; i < 10; i++) {

17: String bulletin = “Bulletin Board Message number: “ + i;

18: System.out.println (bulletin);

19: publisher.publishMessage(bulletin);

20: }

21: