Eclipse Live: A Guide to Creating Java Applications Using Eclipse

288

Transcript of Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Page 1: Eclipse Live: A Guide to Creating Java Applications Using Eclipse
Page 2: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

by Bill Dudney

Copyright © 2004 by SourceBeat, LLC.

Cover Copyright © 2004 by SourceBeat, LLC.

All rights reserved.

Published by SourceBeat, LLC, Highlands Ranch, Colorado.

Managing Editor: James Goodwill

Technical Editor: Michael Orazi

Copy Editor: Brent Barkley, Rebecca Lyons

Layout Designer: Amy Kesic

Cover Designer: Max Hays

ISBN: 0974884316

Many designations used by organizations to distinguish their products are claimed as trademarks. These trademarked names may appear in this book. We use the names in an editorial fashion only with no intention of infringing on the trademark; therefore you will not see the use of a trademark symbol with every occurrence of the trademarked name.

As every precaution has been taken in writing this book, the author and publisher will in no way be held liable for any loss or damages resulting from the use of information contained in this book.

Page 3: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Dedication . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viiAcknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . viiiAbout the Author . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ixPreface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xIntroduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi

Chapter 1: Introducing Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Getting Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2Installing and Running Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3Eclipse Default UI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

Package Explorer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6Text Editor View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9Problems Viewer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11Outline View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12Hierarchy View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14Adding New Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15Workspace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17Getting Help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

Eclipse Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22Plug-ins with OSGi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23Eclipse User Interface - SWT/JFace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

Chapter 2: Editing and Navigating Java Source Code . . . . . . . . . . . . . . . . . . . 24

Quick Start - For the Impatient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25Major Aspects of the JDT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

Perspectives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

Java Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44Editing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44Navigation Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45Source Manipulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46Local History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55

Searching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

Chapter 3: Running and Debugging Java Applications . . . . . . . . . . . . . . . . . . 62

Running Java Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63Launch Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63Launch Configuration Options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65Other Launch Configurations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76

External Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77Debugging Java Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

Quick Start for the Impatient . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82Debug Perspective . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

Table of Contents

Page 4: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Table of Contents iv

Chapter 4: Ant Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95

Ant Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96Ant File Editor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98Ant Outline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100Ant View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103Ant Build . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

Main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108Refresh . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111Targets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113Classpath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115JRE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117Common . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

Ant as a Project Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125

Chapter 5: Working in a Team with Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . 126

CVS Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127CVS Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130

Putting Your Project into a CVS Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131Getting a New Project from a CVS Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138

The Package Explorer Team Menu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140Getting Files in and out of the Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141Managing Versions of Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144File Types in CVS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152Edit File Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154CVS State of the Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156

Chapter 6: Eclipse JUnit Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157

Unit Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158JUnit Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159

JUnit Basic Type – Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159JUnit Basic Type – TestCase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160JUnit Basic Type – TestSuite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161JUnit Pattern Usage – Composite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163JUnit Pattern Usage – Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164JUnit Pattern Usage – Decorator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165

JUnit Quick Start . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166JUnit Integration Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

New TestCase Wizard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173New TestSuite Wizard . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186

Page 5: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Table of Contents v

Chapter 7: Refactoring Java Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187

Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188Refactoring with Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193Change Method Signature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194

AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194Apply the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196

Convert Anonymous to Nested . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201Apply the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

Extract Constant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205AntiPattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206

Extract Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210AntiPattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212

Extract Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215AntiPattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218

Inline Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222AntiPattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223

Move . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224AntiPattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

Rename . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227AntiPattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230

Chapter 8: Refactoring Java Code: Part 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

Refactoring with Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232Extract Local Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233

Extract Local Variable – AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233Extract Local Variable – Apply the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234

Inline Constant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237Inline Constant – AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237Inline Constant – Apply the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239

Inline Temp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242Inline Temp – AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242Inline Temp – Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243

Convert Local Variable to Field . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244Convert Local Variable to Field – AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245Convert Local Variable to Field – Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246

Pull Up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247

Page 6: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Table of Contents vi

Pull Up – AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247Pull Up – Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248

Push Down . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253Push Down – AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253Push Down – Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254

Encapsulate Field . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256Encapsulate Field – AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256Encapsulate Field – Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257

Use Supertype Where Possible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259Use Supertype Where Possible – AntiPattern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259Use Supertype Where Possible – Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 263

Chapter 9: Refactoring Java Code: Part 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . 264

Refactoring with Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265Generalize Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266

Generalize Type – AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266Generalize Type – Apply the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267

Introduce Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270Introduce Factory – AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271Introduce Factory – Apply the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272

Introduce Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274Introduce Parameter – AntiPattern Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274Introduce Parameter – Applying the Refactoring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275

Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277

Page 7: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

To my wife Sarah, for her unending support and love.

Dedication

Page 8: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

First of all I’d like to thank God for sending Christ who has made my life so much more than it would have been. I’d also like to thank my family for putting up with my long hours and lack of skiing and other fun stuff I should have been doing instead of writing. You are the best I could possibly hope for and more!

I’d also like to thank James & Matt for lunch and the opportunity to get involved in SourceBeat early. I am very excited about this new model and can’t wait to see it play out. This book would be hard to fol-low without the expert input from the SourceBeat team. First is Mike Orazi, his comments and feedback on what was missing and what need clarification were invaluable! Rebecca also did a great job of turn-ing my near English into proper English, it’s a much more readable book because of her hard work.

Finally I’d like to thank Mike Cymerman for introducing me to Eclipse and forcing me to sit through his endless evangelism of the tool until I finally relented and tried it for a month. I’ve never looked back.

Acknowledgments

Page 9: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Bill Dudney is a senior java architect with Dallas based OSG. He has been building large distributed sys-tems for almost 15 years most of that with Emacs until that one fateful day…

In addition to his professional experience, Bill has written several other books including Jakarta Pit-falls, AntiPatterns in J2EE and Mastering JavaServer Faces. More information on Bill can be found at his website http://bill.dudney.net.

About the Author

Page 10: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Most of my computing life has been spent in Emacs starting back in 1990 building Obj-C applications for school. I fell in love with the multiple buffers the way I could quickly navigate back and forth between them. I could even run a make and have compiler errors jump me to the offending code, how cool is that! Well it was cool in 1990. Through the years I tried various IDE’s (JBuilder, Visual Café, NetBeans, etc) but could never make the leap out of Emacs. I just liked not having to go to the mouse, I liked integrated features and besides I had the key strokes burned into my gray matter so it was hard to write code without them. Then along came Eclipse. I downloaded it in November/December of 2001 and decide it was not for me. I liked the look of it and the hype sounded good but I turned on the Emacs key bindings and could not switch between buffers, enough said I’m not going there. Then about eight months later I meet Mike who convinced me to give it another go this time without the Emacs key bind-ings.

I was fairly resistant but after Mike showed me the distributed debugger I was hooked. I still used Emacs from time to time but the more I got into Eclipse the more I loved it. My hope in writing this book is that you will have an easier time getting into Eclipse that I did, most of my learning about the tool came from trial and error, trying something and seeing if it worked. I have laid out much of the functionality that drew me into Eclipse and kept me there even when I really wanted that Emacs key binding. I hope that you can get over the hump and be productive right out of the gate with Eclipse as a result of what you read here.

Secondly I’m really excited about the SourceBeat model. I love the idea and I can’t wait to have a dia-logue with you the reader about this work. Undoubtedly there are things I did not document or things that I messed up. With the online presence that we will share at SourceBeat I can adapt the book and make it better, areas that are missing can be added, typo’s can be fixed. I can’t wait!

Finally, I hope this book helps you to enjoy writing code more. I know that Eclipse has made me more productive and I’m excited to hear how you become more productive through using it as well.

Thanks again and best of luck,

Bill Dudney

Preface

Page 11: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

In writing this book I have assumed that you are familiar with java and have experience writing Java programs. The Java code in the book is simplistic so that we can focus on the tooling provided by Eclipse instead of spend-ing time digging into some interesting but tangential aspect of Java or J2EE development. Also this book focuses almost exclusively on Java and J2EE development. Eclipse (especially with 3.0) has grow way beyond its roots as a Java IDE and is now capable of assisting you in the development of C++, Cobol and many other languages as well as becoming a generic application hosting environment. This book however does not go into the details of the aspects of Eclipse that do not focus on some aspect of Java development.

Chapter 1: Introducing Eclipse gives a very quick overview of what Eclipse, where it came from and what the default UI looks like. You will gain a basic understanding of what the Java Development Tools plug-in (JDT) has to offer from this chapter.

Chapter 2: Editing and Navigating Java Source Code offers an in-depth look at the tools available in Eclipse for editing Java source code as well as navigating through your code. You will understand the tools that help you to write and understand Java code from this chapter.

Chapter 3: Running and Debugging Java Applications covers the launch configurations used to run and debug java applications as well as all the great tools available to help you to find a squash bugs. You will have an in-depth understanding of how to run and debug java programs as well as how to use the Debug perspective after reading this chapter.

Chapter 4: Ant Integration discusses how the Ant build system is integrated in the JDT. You will understand how to use the integration to control Ant builds of your application from within Eclipse.

Chapter 5: Working in a Team delves into the team support provided by the JDT. From this chapter you will understand how Eclipse integrates with CVS specifically as well as understanding of the process of configuration management as presented through Eclipse.

Chapter 6: Testing Java Apps with JUnit discusses the integration of JUnit into the JDT and how you can take advantage of that to test your application. You will gain a basic understanding of test first development from this chapter and an in-depth understanding of how the JUnit integration is used to test your applications.

Chapter 7: Refactoring Java Code introduces and explains refactoring in general. It then discusses the first 8 of the 23 refactorings supported by Eclipse. The discussion focuses on what you must do in Eclipse to perform the refactoring rather than a lengthy discussion of the list of mechanisms to accomplish the refactoring. At the end of the chapter you will understand refactoring in general, as well as how to apply this list of 8 refactorings.

Introduction

Page 12: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Chapter 1

Eclipse 3 Live

Introducing Eclipse

Eclipse is taking the IDE world by storm; this chapter will give you a quick overview of its major components and how Eclipse came to be.

Unless you have been sitting in the corner with your fingers in your ears yelling, “I’m not listening!” for the last two years, you have probably heard of Eclipse. Because you’ve bought this book, you have probably come down off the fence and decided to get your hands dirty with the tools and start finding your way around. Congratula-tions! Your efforts will be rewarded. Eclipse is a full-featured IDE that has gained huge market penetration for good reason. Before we get into the details of Eclipse, however, let’s look back over the history of the tool.

Eclipse.org was founded in November 2001 principally by IBM in the form of a donation of forty million dol-lars’ worth of software. The organization is led by a “board of stewards,” which is responsible for setting the overall direction of the Eclipse platform. The main focus of Eclipse, in contrast to other open-source consor-tiums, is as a robust platform for building commercial tools. Many of the tool vendors who are part of the Eclipse board of stewards have commercial products based on Eclipse (IBM – WSAD, Metonology – MDE for J2EE, etc.). Whereas other open-source communities have no problem with commercial entities using their software, they were not formed with this as a main goal, as Eclipse.org was. The question then becomes: what does this mean to us as consumers of Eclipse? The biggest gain is a managed-release cycle that more or less meets the advertised dates. Many of the contributors to Eclipse get to work on the code as their full-time job instead of hav-ing to make time outside of work. Because many of the members have products that depend on a robust infra-structure, they are financially motivated to have their employees work on Eclipse. All in all, this is a very cool setup for consumers of Eclipse, for not only is there a great group of volunteers but there is also a lot of financial clout behind the ongoing development of the platform.

Page 13: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Getting Eclipse 2

Getting Eclipse

To get started using Eclipse to develop Java code, download and install the default IDE. All of the currently available versions of Eclipse are on the Eclipse download page, http://www.eclipse.org/downloads/index.php.

A Word about Versions: The Eclipse site has four versions of Eclipse available at any given time. The downloads categorized as “Latest Release” are currently the 2.1.x series. We will not be discussing this version in particular. If, however, you are looking for a very stable version with almost no surprises, this is the version to get. The “3.0 Stream Stable Build” is a tested, but not feature-complete, version and is probably your best bet to get the 3.0 features with very little risk of having major problems. The “3.0 Stream Integration Build” is where you will find the latest version of Eclipse that has “stable” com-ponents integrated into the build. This version has had less testing than the “stable build” and therefore is more likely to have problems. Use this build if you are looking for a particular feature or bug fix that is not available in the “stable build.” Finally, there is the “3.0 Stream Nightly Build.” This build is the least tested of them all (the only tests run against this build are the automated JUnit tests), and is intended mostly for developers actually working on Eclipse. The recommended version is the “3.0 Stream Stable Build,” which is where most of the content of the book will be focused. As of this writing, 3.0M6 has been released.

Once you have selected a build from the main download page (see the note above on versions), that build’s spe-cific download page displays. This page provides many different options of related packages to download. The typical option is to download the “Eclipse SDK.” For convenience, this group is located at the top of the page. The SDK is the most common way to use Eclipse; this package has the Java Development Tools (JDT) and the Eclipse Plug-in Development Environment (PDE) plug-ins already loaded. The download options are listed in a table. The first column indicates the status of the build (the definition of the status depends on which version you are downloading, but typically a green check is good and a red X is bad). The second column is the platform/OS for which the build is intended. The third column lets you download by http or ftp. The final column is the file name that you will download.

Page 14: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Installing and Running Eclipse 3

Installing and Running Eclipse

Now that you have your desired version of Eclipse, install it by unzipping it into the directory of your choice. In Windows, I suggest C:\DevTools\Eclipse-version, and on the Macintosh, I suggest /Users/Shared/Application/Eclipse-version. In Windows, you need to have the java.exe file somewhere in your path for Eclipse to start up. If you don’t, Eclipse will fail to start, and a message will appear stating that it can’t find a JRE to run within. If that happens, just click OK and add the JRE & JDK bin directory to your system path.

Now that the IDE is installed, adjust the runtime of Eclipse. The instructions for various platforms on how to specify runtime options are listed in Table 1.1.

Once you have the OS-specific place to put the options figured out, they are the same on every OS. Generally, you specify the options with the following format: eclipse [platform options] [-vmargs VMOptions]. The plat-form options are passed to Eclipse, and the VMOptions (everything past –vmargs) are passed to the JVM that is running Eclipse. The options for the JVM depend on the JVM that you are running. The one you will most likely have to change is the amount of memory allocated to the VM. You do that with the –Xms<size> and –Xmx<size> options on most JVMs (see the note). Eclipse 3.0 is shipping with a default setting of 150MB max-imum memory used, which is fine for just getting started and for small projects, but for serious development you should increase the memory to 256MB. To do so, specify –Xmx256MB on the command line.

JVM Memory Options: Even though the memory-related JVM options are “nonstandard,” they do seem to be widely adopted across many JVMs. If you are not sure about your VM, use java –X to get help for these options.

Table 1.1: Eclipse Runtime Configurations For Various Platforms

Platform ProcessWindows Create a shortcut for eclipse.exe (right-click on eclipse.exe and select Create

Shortcut), select Properties from the context menu, and add the command line arguments to the end of the Target: text field. If you are going to change the location of the workspace directory, specify the Start In: directory as the parent directory of your workspace (for example, if your workspace is c:\home\workspace, then specify c:\home as the Start In: directory).Click OK.

Macintosh Go to the Eclipse.app bundle in the finder, select Show Package Contents, then open the file Content/Info.plist.

Linux Create an alias in your shells startup script (that is, for bash.bash_profile) that contains the eclipse executable and the options.

Page 15: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Installing and Running Eclipse 4

The Eclipse options are listed in Table 1.2.

Users of Eclipse often use the –data option to specify an alternate place for Eclipse to store their projects. Chang-ing this location allows you to put your projects into a directory that will be backed up on a regular basis. Whether or not you use the default location for your projects, make sure to back up the entire workspace direc-tory on a regular basis. Eclipse keeps important metadata in the .metadata directory in your workspace. If some-thing goes wrong with this data, Eclipse sometimes won’t start, and you will have to set up all of your projects again to continue to use Eclipse.

Table 1.2: Eclipse Platform Options

Option Description-application [App ID] Specifies the application to run and defaults to the Eclipse workbench. This

option will become particularly important for development with and use of the Rich Client Platform.

-boot [boot code path] Allows you to change the location of Eclipse’s startup classes, which are con-tained in boot.jar and startup.jar. The typical developer using Eclipse will not use this option.

-consolelog Puts the error log onto the console in addition to writing it to Eclipse’s internal log. If you are having a problem starting Eclipse, this is a good option for debugging what has gone wrong.

-data [workspace path] The location of the workspace. This is where Eclipse keeps its meta-data and is the most likely option to be specified by developers using Eclipse.

-debug [options path] Specifying this option with a path to an options file will allow you to specify debugging options for the Eclipse platform. This option file is very useful if you are developing plug-ins for Eclipse.

-dev [classpath entries] Allows you to add classpath entries to the classpath of every plug-in.-nosplash Keeps the Eclipse splash page from showing up on startup.-os [os-id] You only need to specify this option if Eclipse can’t figure it out on its own.

This usually happens when a new OS is added to the list of supported OSs. Most installations will not have to specify this option.

-vm [vm-path] This option allows you to specify an alternate JVM to use for running Eclipse. If you do not specify this in Windows, Eclipse will use the first java.exe it finds in the System PATH variable.

-ws [ws-id] You only need to specify this option if Eclipse can’t figure it out on its own. This usually happens when a new windowing system is added to the list of supported windowing systems. Most installations will not have to specify this option.

Page 16: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 5

Eclipse Default UI

The first run of a newly installed Eclipse will take a bit longer than normal because it’s creating the workspace directory where projects and metadata are stored. The workspace directory also stores preferences and other data about Eclipse and the projects you are creating. After that initial startup, Eclipse will be in the “Resource Per-spective.” We will talk more about perspectives later, so just think of a perspective for now as a collection of views and tools. The Resource Perspective is not particularly suited for doing Java development, so we won’t spend more time discussing it. Once you have Eclipse started, you will want to switch to the Java Perspective. Open the Java Perspective by selecting the “Window” menu and then choosing “Open Perspective->Java.” You should see a window similar to that shown in Figure 1.1.

Figure 1.1 The Initial Java Perspective

This perspective initially has five views (only four are visible) starting from the top left. The four visible views are “Package Explorer,” “Text Editor,” “Problems,” and “Outline.” The fifth view (hidden below the Package Explorer) is the “Hierarchy View.” Let’s go over each view and what you should expect to see in them.

Page 17: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 6

Package Explorer

The Package Explorer shows you a package-oriented picture of your project. In Figure 1.2, you can see a small example application displayed in the Package Explorer.

Figure 1.2 The Package Explorer

Page 18: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 7

The example shown in Figure 1.2 contains a single-source directory called src. There is a single package within that directory called “example.” The grey arrows (in Windows these are instead a + [plus] and – [minus]sign in a box) beside each element expand and collapse the element. When the arrow is pointing down the element is expanded, and when it is pointing to the right the element is collapsed. In Figure 1.2, you can see that the DBEx-port class is expanded. From this view you can get the editor to open the class with the element selected by dou-ble clicking on the element. You will also notice in Figure 1.2 that, in addition to the java packages, you can also see all the jar files that are part of the project. There are five jar files in the classpath of this project: the JRE, junit.jar, exml.jar, dtparse.jar, and dbunit.jar. With the Package Explorer, you can inspect the content of a jar file. In Figure 1.2, you can see the inside of the junit.jar file.

Figure 1.3 Looking into junit.jar with the Package Explorer

Page 19: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 8

As you can see, the junit.awtui package has four classes in it. Notice that the AboutDialog class is expanded in the same way that java classes in your project can be expanded. Another cool feature of the Package Explorer is the ability to see the definition of the class in the editor pane by double-clicking on one of the classes in a jar file. If you have source code for the content of the jar you can even associate the source with the jar, and the edi-tor will take you to the source code. Notice that in Figure 1.3 the junit.jar file has a little page of text on its “jar” icon. This indicates that source code (or javadoc) is associated with the jar file. We will discuss how to specify the source and javadoc for a jar file later when we discuss the Java Development Tools in Chapter 2.

You will also notice some buttons across the top of the Package Explorer. These buttons provide quick access to some of the more commonly used features and preference settings of the Package Explorer. The arrow pointing down is a menu for accessing more features; you can see the menu activated in Figure 1.4.

Figure 1.4 Package Explorer Menu

Let’s look at each button starting with the leftmost and moving to the right. The left-pointing button takes you one step back through the most recently displayed state of the Explorer. The right-pointing button takes you one step forward in the most recently displayed state of the Explorer. If these buttons seem familiar, that’s because they follow the same metaphor as a browser’s back and forward/next buttons. The button with an up arrow on a folder takes you one step up in the hierarchy that is being displayed in the Explorer. The next button collapses all the elements in the Explorer. The double-arrow button links the Package Explorer with the editor. Any class selected in the Explorer will be shown in the editor window and vice versa. The next button brings up the menu. Finally, the X will close the view. If you inadvertently close a view, you can always get it back with the Window->Show View menu item. The X “button” closes the Package Explorer menu and is after the button that brings up the menu.

This has been a whirlwind tour of the Package Explorer. For those wanting more detail about the Package Explorer features and what they do for you in your java development, see Chapter 2. Its features will be addressed in much more detail there. Next up in our overview is the Text Editor view.

Page 20: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 9

Text Editor View

You will spend most of your time in this view when writing Java code. This view is a context-sensitive Java text editor that understands Java syntax and, along with the Eclipse incremental compiler, can warn you of compila-tion errors before you even compile. (Eclipse is sophisticated enough so that you almost never have to compile manually). Figure 1.5 shows the editor open on a simple JUnit test example.

Figure 1.5 Text Editor View

This figure shows the default highlighting and colorization, but there are many ways to customize the way the code appears. The Java Editor has many features that can make your life easier.

Page 21: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 10

In this same space, you can see any other editor that you are currently using. For example, if you open a build.properties file (a customization file for Ant; we will talk more about Ant in Chapter 4), the Build Properties Editor will open and load your .properties file, as can be seen in Figure 1.6.

Figure 1.6 Build Properties Editor

Many other editors come with Eclipse, and thanks to the huge open source community that has formed around Eclipse, there are lots of additional editors to download and use. Next up is the Problems Viewer.

Page 22: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 11

Problems Viewer

In the Problems Viewer, you will see a list of issues that affect the compilation of your project. Figure 1.7 shows an example with two problems listed.

Figure 1.7 Problems Viewer

This first item in the list is a compile error, and the second is just a warning. Because Eclipse has an incremental compiler that recompiles as you type, you will see items pop up in here from time to time and then disappear as you finish typing a line. The second item is just a warning or informational message that java.util.BitSet is imported but never used in the file. One of the best features of the Problem Viewer is the ability to perform Quick Fixes. A quick fix is something that Eclipse can do for you to fix the problem, simply right click and choose “Quick Fix” from the context menu, and Eclipse does the rest (sometimes prompting you to choose from a list of possible fixes). Most of the problems that show up in this view have a quick fix associated with them. The list of problems can be filtered, too, with the Filters button (the three right-pointing arrows) to show you only the items you want to see, e.g., to show only errors or to limit the number that show up. Filtering comes in handy when you are migrating to Eclipse in the middle of a development cycle. There are probably many small warnings (like the unused import statement) that can be annoying in the midst of a deadline. With the filters, you can get rid of par-ticular kinds of problems until you have the time to deal with them.

Page 23: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 12

Outline View

The Outline View allows you to look at a java class individually in the same way that you can with the Package Explorer. In addition, there are many filters and sorting capabilities built in to let you focus on what you need/want to focus on and hide the rest. Figure 1.8 is an example of the Outline View focused on the SubjectTest class.

Figure 1.8 Outline View

Page 24: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 13

This view is rooted at the class and lists all the features of that class starting with the package the class is in. The buttons, from the left, are

• “Go into top level,” which focuses the view on the class hiding the package and import statement items

• “Sort members,” which alphabetically sorts the members of the class either ascending or descending

• “Hide fields,” which will focus on just the methods for the class

• “Hide static members,” which hides all the static members from view

• “Hide nonpublic members,” which hides from the view all of the members that are not public

• “Hide local types,” which removes any anonymous inner classes from the Outline View

• “Link with editor,” which keeps the selection in Outline View in sync with the location of the cursor in the Java editor

Page 25: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 14

Hierarchy View

Last in our whirlwind overview of the default JDT UI is the Hierarchy View, which allows us to view a Java class in light of its inheritance hierarchy. Figure 1.9 shows the Hierarchy View in action, focused on the SubjectTest class.

Figure 1.9 Heirarchy View

Notice that the top half of the view shows the SubjectTest and its lineage, and the bottom half shows the mem-bers of SubjectTest. The buttons on the top half of the Hierarchy View allow you to change the focus of the tree. The first button (the one selected in Figure 1.9) allows you to see all the immediate super classes of the cur-rent class. The next button essentially inverts that, roots the tree at the selected class (SubjectTest in this case), and shows all the type hierarchy as children instead of parents. The final button shows only the subtypes of the selected class (again in this case its SubjectTest, which has no subtypes). The bottom half of the window allows you to filter the members of the selected class like you can in the Package Explorer. You can filter out

Page 26: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 15

static members, fields, etc. You can also choose to see all the inherited members or just the ones local to this class.

That sums up the default UI that Eclipse first loads with the Java Perspective. This has been a very brief over-view of the views that are loaded. We will view more of the details as we cover the Java Development Tools in Chapter 2. Now let’s move on to some other features built into Eclipse.

Adding New Views

In addition to the default set of views, there are many others that you can use to build your projects. Many of them will open automatically for you when needed. For example, when you run a java application that prints to the console, a view called Console View will appear and show the output, whereas some other views will have to be explicitly opened. To open a view, select the Window->Show View->View Name menu item. For example, you can open the Ant view via Window->Show View->ant. Ant is covered in detail in Chapter 4. Figure 1.10 shows the Window->Show View menu with the list of views that are available. The Ant view is selected.

Figure 1.10 Show View Menu

Plug-ins are allowed to add views to this list as well, so expect this list to grow as you add plug-ins to your instance of Eclipse

Page 27: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 16

The Other… item brings up a window with the views grouped around functionality. Figure 1.11 shows this win-dow with the first three groups expanded.

Figure 1.11 Show View Window

Once you get a group of views open, you may want to rearrange the views to fit the way you work. Eclipse has a UI that allows you to reorganize the views in your workspace. Simply click on one of the tabs and drag the tab to the spot where you want the view to appear. The cursor will change as you move the view around. The cursor that looks like a group of file folders indicates that the view will be added to the current location as a new tab. One of the black arrows means that the view will be docked as a new region with this view as the only one in the region. A circle with a line through it means the view cannot be dropped into the current region.

Page 28: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 17

Workspace

The workbench is the general name for the user interface of Eclipse (there is more to it than that, but you can think of it as what you see when you start up Eclipse). Into the workbench plug-ins register perspectives, views, tools, menu items, etc., basically anything that is needed to implement the plug-in. The topmost organizing item in the workbench is the Perspective. The Perspective defines which views are visible, what regions (called fold-ers internally to Eclipse) they live in, etc. You have seen two perspectives so far in our discussion: the Resource Perspective (which is the default on initial startup) and the Java Perspective. Figures 1.12 and 1.13 illustrate the same project (Simple in this case) in two different perspectives.

Figure 1.12 Simple in the Resource Perspective

Page 29: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 18

In the Resource Perspective, you still have access to the Java tools (like the editor seen in this figure), but because the perspective is not Java aware, you won’t see packages, for example (although you could open the Package Explorer if you wanted to). Figure 1.13 shows the same project in the Java Perspective.

Figure 1.13 Simple in the Java Perspective

We have gone over the list of default views already, so we won’t review them again here, but notice the differ-ences in the presentation of the project between the two perspectives. It is subtle, but the Java Perspective is much more organized for Java development than is the Resource Perspective. Thus, the perspective is the orga-nizer that sits behind the set of views and editors that are visible to you as a programmer.

Page 30: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 19

Earlier, when we were looking at the way to open a view, you might have noticed a group of items related to per-spectives. With these commands you can customize a perspective and then save the customizations so they are always there. You can also create a new perspective (as a copy of an existing one) on the fly and build an envi-ronment suited just for you. Figure 1.14 shows the Window menu with Save Perspective As… highlighted. With this option you can create your own perspectives customized any way you need or want, which are always avail-able simply by choosing Open Perspective.

Figure 1.14 The Save As Perspective Menu Item

With the Customize Perspective item, you can specify which of the pieces of the current perspective are visible. For example, you can add menu items, tool bar short cuts, etc. The Reset Perspective item is handy if you feel like you’ve gone a bit overboard on customization and need to get back to “factory settings.” The other two items (Close Perspective and Close All Perspectives) close either the current perspective or all the perspectives, respec-tively.

Page 31: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 20

Getting Help

The help system is yet another plug-in that comes bundled with Eclipse. On the back end, it’s an integrated instance of tomcat. To you as a user, however, it looks like a huge set of html files all waiting to inform and edu-cate you. To activate help, choose the Help->Help Contents menu item. Your default browser starts up, and you should see a page that looks like Figure 1.15.

Figure 1.15 Help Contents

Help Contents consist of three basic panes. The top pane provides some anchors for commonly used commands. Contents will collapse all the collections of help and return you to a page that looks like it does not. Search allows you to search the content of the help system for keywords or phrases. Links provides a list of help items related to a particular item in the Eclipse UI. Finally, the Sync button selects the currently visible document in the left-hand side hierarchy.

The Top part looks pretty different on IE. It contains a Search: text entry field, and a Search Scope: link on the left side; on the right side there are a handful of useful shortcuts (forward, back, TOC, bookmark, print). The contents title bar contains a Refresh icon.

Page 32: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Default UI 21

The hierarchy view allows you to browse help by topic. Thus, you can look through the topics under the JDT Plug-in Developer Guide, for example, and see all the topics in a hierarchy. Once you select an article or topic, you will see the content in the right-hand pane. Clicking the Search button will replace the hierarchy with the search page. Finally, the right-hand pane is where the actual help content will be displayed. This is where all the actual content will be shown for you to read.

Search works differently on Windows/IE.

Page 33: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Architecture 22

Eclipse Architecture

The architecture of Eclipse is focused on extensibility. Almost all of the design decisions made, as well as the layout of the API, is intended to make it easy to extend Eclipse. The primary means of extending Eclipse is through adding a plug-in. Figure 1.16 shows the major pieces of the Eclipse runtime as well as a few plug-ins.

Figure 1.16 Overview of the Eclipse Architecture

As you can see in the diagram above, the IDE consists of several components. The core of the application is the Platform Runtime (PR). The extensibility starts with this component. The platform has an implementation of the OSGi (Open Services Gateway Initiative) framework. The OSGi framework allows plug-ins to be dynamically loaded and unloaded, to declaratively state their dependencies and extension points, etc. Almost every aspect of the Eclipse IDE is a plug-in to this framework.

When you download Eclipse, it has the JDT (Java Development Tools) plug-in installed and configured. This plug-in provides most of what we will be discussing in this book. It is important to realize, though, that the JDT is just another plug-in to Eclipse (a very important plug-in, but still just a plug-in). The other plug-ins shown on the diagram simply illustrate the rather generic nature of Eclipse. If you were to download the CDT (C/C++ Development Tools), Eclipse would become a C/C++ IDE.

Page 34: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Eclipse Architecture 23

The great thing about the layout of the Eclipse architecture is that all plug-in builders get equal access and input into what Eclipse looks like. For example, the CDT plug-in makes Eclipse look like a C++ environment by pro-viding a new perspective (recall that a perspective is a collection of views and tools), a C/C++ editor, a set of tool bar icons and buttons, and several other items that transform Eclipse from its default Java IDE into a C/C++ IDE. In fact, the underlying Eclipse runtime is not even dependent on its being an IDE. The Eclipse Rich Client Plat-form allows just about any type of application to be built with the Eclipse infrastructure. As we saw in Figure 1.16, the Eclipse architecture is intended to allow flexibility. Almost everything typically seen in Eclipse is actu-ally from a plug-in instead of an implemented part of Eclipse. Next, we will take a look at how some of this cool functionality works. Most of the next two sections covers how Eclipse works from an architectural perspective (the frameworks and libraries that sit behind the scenes). Feel free to skip to Chapter 2 if you’re not interested in how Eclipse works behind the scenes. Those who want to know some of the nuts and bolts of Eclipse, read on.

Plug-ins with OSGi

One of the most significant changes in Eclipse 3.0 is the change of the plug-in architecture to be conformant with the OSGi API. So what does that mean to you? Well, OSGi adds really cool features to the way plug-ins are loaded and managed. In the older versions of Eclipse, plug-ins were statically loaded. In other words, if you added a plug-in, you had to restart Eclipse to get at the new feature set. With OSGi, you can add and remove plug-ins to your heart’s content and never have to restart Eclipse. This is just the most user viable feature of hav-ing the OSGi backend for the plug-ins. If you develop your own plug-ins, you will discover a whole host of easy-to-use APIs for your plug-in to use. If you’d like to see more in this book about developing plug-ins, please let me know, a chapter about building plug-ins is on the list of potentially additional chapters post version 1.0.

Eclipse User Interface - SWT/JFace

Probably one of the most controversial things about Eclipse (apart from the name that is) is the use of a replace-ment for the Java standard UI framework combination of AWT and Swing. Eclipse has a completely different approach to the user interface. SWT is a replacement of AWT. Instead of being implemented independently of the underlying OS, SWT is aware of the underlying OS’s windowing widgets and is, thus, claimed to perform better than the AWT equivalent. JFace is then built on top of SWT in the same way that Swing is built on top of AWT. The advantages of having SWT/JFace is that the user interface of Eclipse much more closely matches the OS that it’s running on in the way that the widgets work. A disadvantage of SWT/JFace is the new learning curve needed to become proficient at writing Java UIs. Love it or hate it, SWT/JFace is going to be around for the fore-seeable future. If you plan to write any plug-in code, you will have to learn enough SWT/JFace to build your user interface. Whole books could be written on SWT, and a full in-depth discussion of it is beyond the current scope of this book. This is another area that could become an additional chapter. Again, if you are interested, please let me know.

There are many other facets to the Eclipse architecture that are worth discussion, but this book is about how to use Eclipse rather than how to extend it. So, we will leave this discussion for now and move on to the JDT.

Page 35: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Chapter 2

Eclipse 3 Live

Editing and Navigating Java Source Code

The Java Development Tools (JDT) plug-in is the heart of what most Java developers use Eclipse for. The Java-centric view of the world comes from JDT. This chapter will delve in detail into the Java editor that is part of the JDT to give you a firm understanding of its features and their functions.

Now that we have gone over the out-of-the-box default UI for Eclipse and the JDT, it is time to delve into the specifics. For the impatient among us, though, let’s dive into a real project (OK, a contrived project, but we will still have some execution happening!). Because “Hello World” has been done thousands of times, let’s instead create a simple program with three classes of the UI class (text UI in this case), a “Hello” vendor class, and a “World” vendor interface. We will expand this project as we move through this chapter.

Page 36: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Quick Start - For the Impatient 25

Quick Start - For the Impatient

To create a new project in Eclipse, you need to invoke the “New Project” wizard from the Package Explorer or use the “New Project” button. We will start with the context menu invocation in the Package Explorer. Within the Package Explorer, invoke the context menu (right click in Windows; control click on the Mac), then select New->Project. The first page of the new project wizard appears on the screen (see Figure 2.1).

Figure 2.1 New Project Wizard – First Page – Project Type Selection

Page 37: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Quick Start - For the Impatient 26

If it’s not already selected, choose Java -> Java Project, as shown in Figure 2.1, then choose the Next > button. The second page of the wizard will appear. Enter the name of the project (HelloWorld in this simple case), as shown in Figure 2.2.

Figure 2.2 New Project Wizard – Second Page – Project Name and Location Specification

Page 38: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Quick Start - For the Impatient 27

In this page, you will specify the name of the project and its location. The Use Default checkbox is on by default because Eclipse expects that your typical development will reside in the workspace directory. This is fine for new development, but for existing development where you will be creating a project with an existing code base, you will want to uncheck this box. Importing will be covered in more detail later. For now, leave the checkbox checked. Notice that the location is the path to the Eclipse workspace plus the project name. If you ever need to view your project using a program other than Eclipse, you will find it in this directory. You can also finish from here and be done with the wizard. For many simple projects it will be fine to finish here and go directly to the code. In many cases, however, you will want to move on to the next page to set up the class path for the project, as well as other options. We will learn more about this in the project configuration section. Click the Finish but-ton on this wizard, and your first project will show up in the Package Explorer.

Because we never want to leave code in the default package in a real Java project, the first step is to make a new package into which to put the code. I named my package com.sb.eclipselive, but, subject of course to the naming conventions of java, you may choose whatever name you like for your package. The New Package dialog box allows you to specify the name. In the project explorer, use the context menu item New->Package to bring up the dialog and create the package. Now that the new package is created, Eclipse should look like Figure 2.3.

Figure 2.3 HelloWorld Project With Package

Now that we have a project and a package in which to place our source code, it’s time to start creating some Java code. Recall that our HelloWorld project is to print out “Hello World” using a total of three classes: the text based UI, the hello provider, and the world provider. We will go through the creation of the code one class at a time.

Page 39: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Quick Start - For the Impatient 28

First we create the HelloWorld class, which is the main class in our application. It is responsible for gathering the hello and world from their prospective providers and putting them together into a string to print out. Eclipse has a couple of ways to build new classes. First, you can choose New->File from the context menu in the package explorer and then specify a file with the .java extension. Although this way will work, it does not take full advan-tage of the JDT. The second way is to use the new class wizard invoked from the context menu New-> Class item. Figure 2.4 shows the first and only page of this wizard. (It could be called a dialog, I suppose, but why split hairs?)

Figure 2.4 New Class Wizard

Page 40: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Quick Start - For the Impatient 29

We want to create a new class called HelloWorld with a main method that will kick off our program. Check the options and type in HelloWorld as it’s shown in Figure 2.4. We will cover more of the specific options later in this chapter. The next step is to modify the code to do what we want it to do. Before it will really work, however, we need to have the HelloProvider and the WorldProvider. Invoke the wizard two more times to create these two provider classes, but uncheck the main method check box. You should now have three classes to add the follow-ing code to HelloProvider:

public String getHello() {return “Hello”;}

Add the following code to the WorldProvider class:

public String getWorld() {return “World”;}

In the main class of your HelloWorld class, you could add the following code to implement the requirements.

public static void main(String[] args) {HelloProvider hp = new HelloProvider();WorldProvider wp = new WorldProvider();String hello = hp.getHello();String world = wp.getWorld();System.out.println(hello + “ “ + world);}

Page 41: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Quick Start - For the Impatient 30

After you copy and paste this code into your class, just for fun try out the code completion feature of the JDT. Delete the last half of getHello() call. With your cursor just past the t in “get,” hit the auto complete key sequence. (On the Mac, it’s command-shift-x; in Windows it’s Ctrl+Space). Notice a pop-up selection box that looks like Figure 2.5.)

Figure 2.5 Code Completion in Action

Page 42: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Quick Start - For the Impatient 31

You may select an option by using either your mouse or the arrow keys. This feature works with almost all java elements. For example, if you wanted to add a List to the main method you could delare it with java.util.L and then hit the Content Assist feature. It would fill in the list as well as add an import.

You are finally ready to run your first java program in Eclipse! To run applications in Eclipse, you must have a Run Configuration that tells Eclipse how to invoke your application. Most of the time, however, it’s easy to set one of these up with Eclipse. Figure 2.6 shows the Run Configuration menu activated with the Run As->Java Application item selected.

Figure 2.6 Invoking a Java Application

Once you click this menu item, you should see the console appear, and your message “Hello World” should be displayed. We will go into the details of the rest of the options under Run As later in this chapter, but for now think of this as shortcuts to tell Eclipse how to run your application. Notice that we did not have to go through a compile step. Eclipse is sophisticated enough to know that your project should be compiled before it’s run, so it builds all the code before the invocation actually happens.

You now know how to create a new project, create some classes, and run the program. Congratulations! Although we have not excercised any great depth of functionality, this is the basic life cycle of a project in Eclipse. The rest of the chapter goes into detail about each of the features we have touched on in this quick start overview.

Page 43: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 32

Major Aspects of the JDT

Before we can get into the specifics for particular tools provided by the JDT, we need to cover some of its spe-cific features. Some of what follows would usually fall into a more generic discussion of the Eclipse workbench, but because we are really focusing on Eclipse as a Java IDE, the generic aspects of the following features are dis-cussed as they relate to the JDT. The more generic features that do not directly apply to Java development will not be covered.

Perspectives

As we said earlier, a perspective is a grouping of tools and views to help you focus on a particular kind of task. A perspective is highly customizable and can be saved so that it can be used again in a different invocation of Eclipse. The perspectives are so customizable that you can almost make one indistinguishable from another. Because the different perspectives are intended to give us a different view of the world, we won’t customize them quite that far. The JDT contains two perspectives: the Java Perspective and the Java Browsing Perspective. Each perspective provides a default set of views that will show up as well as the “regions” they will show up in. For example, as we saw in Chapter 1 – Figure 1.1, the Java perspective has four regions. As new views are added to the perspective, they become attached to a particular region by default. Once the new views are opened, they can be moved around and the new layout saved so that the customized layout is shown the next time the perspective is opened. Perspectives are powerful tools that allow you to make Eclipse look the way you want it to. You can open the Java Browsing perspective by selecting Window->Open Perspective->Java Browsing. On some ver-sions of Eclipse, this perspective does not show up by default; to open it, select Window->Open Perspective->Other….

Page 44: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 33

The Java Browsing perspective is different from what most IDE users have come to expect. Smalltalk tools inspired this perspective. Smalltalk tools have always provided a way for you to see your class one method at a time, and the browsing perspective tries to recreate this feeling. Figure 2.7 shows the HelloWorld project in the Java Browsing perspective.

Figure 2.7 HelloWorld in the Java Browsing Perspective

There are four coordinating views across the top of the perspective that work together to show the code in a method-centric way. Starting on the left-hand side you see the Projects view, which lists the projects in your workspace and is optionally filtered by a working set (we will talk more about a working set later). If you select HelloWorld, you will see the list of packages in the Packages view. In our simple case, there is only one package, com.sb.eclipselive, to show. If you select a package, you will see the list of types in the Types view. Finally, when you select a type (which could be an interface or class), you will see its members in the Members view. Of course, for interfaces you’d only see methods and static fields; for classes there would be methods and fields. This set of views are all “connected” to each other. In other words, as you change the selection in one view, all the others are updated. To kick it all off, double click on one of the Types. Finally, as you select members in the Members view, notice that the Editor is updated and focused on the particular member selected.

Page 45: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 34

The bottom line on perspectives is that they are one way to organize the information that we need as developers. The important thing to keep in mind is that you should use the perspective in which you are more productive. If you are new to Eclipse and from a Smalltalk background, then the Java Browser perspective is probably best. If you come from a more traditional IDE background, or you have no IDE background, then you should probably use the Java perspective. Unfortunately, most people come from some background other than Smalltalk. (It really is a great language and environment for learning Object Oriented Programming.) Thus, the rest of the book will focus on the Java perspective. Almost everything applies equally well to both perspectives, though, so if you are an adventurous soul feel free to continue with the Java Browsing perspective. Following is a more detailed dis-cussion of the other views typically used during Java development.

Views

In Chapter 1, we covered the default set of views available in the Java perspective, which includes Package Explorer, Hierarchy, Outline, Problems, and the Java editor. Other commonly used views also come with the JDT. We will discuss some details about these other views that were not covered in Chapter 1. We touched briefly on the Quick Fix feature in Chapter 1. Let’s do a quick demo of how that works so that you can start using it right away.

Problems View

Switch back to the Java perspective if you have not already done so and select the Problems view. You should not have any problems at this point, so we are going to introduce some to demonstrate the Quick Fix feature. With the HelloWorld java file open in the editor (double click on the file in the Package Explorer), type an extra import, for example import java.util.List;. After you save the file, a problem will show up in the Problem view warning you that the import is never used. To invoke the Quick Fix feature, right click on the problem in the Problem View and select the Quick Fix item, as shown in Figure 2.8.

Figure 2.8 Superfluous Import

Page 46: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 35

As with many features in Eclipse, there is more than one way to invoke the feature. With Quick Fix, you can also right click on the marker in the left side of the editor. The marker is a light bulb and a warning sign as shown in Figure 2.9.

Figure 2.9 Editor Warning and Idea Marker

If you position your mouse over the marker and invoke the context menu (right-click in Windows, control-click on Mac), Quick Fix will be one of the choices. We discuss the left-hand side and its markers in the Java Editor section later in this chapter. However you choose to invoke the Quick Fix, you will be presented with a list of choices to fix the problem. In this case, Eclipse has two options to resolve this warning: either Remove the par-ticular offending import or Organize the imports. If you choose to remove the import, the warning disappears. The Organize Imports option in this case, although it will do exactly the same thing, is also capable of much more. If you have multiple imports that are not used, your imports are not sorted in any particular order, and you have many package level imports, then Organize Imports is indeed your friend. Once you get the hang of this feature you will never again type an import statement. You may spend some time playing with the feature. We will discuss it in more detail with some examples later in the section on the Java editor. Covered next is a view that will help you understand your code base.

Page 47: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 36

Call Hierarchy View

The Call Hierarchy view allows you to see all the calls made in your project from or to a method. This feature and view can be most helpful in understanding your code base, especially if you are new to a project and want to understand how a particular bit of code is used. You invoke the view by selecting the “Open Call Hierarchy” item in the context menu with a method name selected. Figure 2.10 shows the item selected for the getHello() method on the HelloProvider class from our simple example.

Figure 2.10 Invoking the Call Hierarchy View from the Editor

Page 48: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 37

Once you open the Call Hierarchy, you can see all the places in the code that call getHello() directly. Of course, in our simple example here, there is only one caller, the main(String[]) method on the HelloWorld class. In addition to being able to see what other classes call a particular method, you can also see what methods a partic-ular method calls. Go to the HelloWorld class in the java editor window, not the just-accessed call hierarchy window, and open its main(String[]) method in the Call Hierarchy view. Once it is opened, select the “Show the Callee Hierarchy” button in the Call Hierarchy. Figure 2.11 shows the view with the mouse over the correct button.

Figure 2.11 The Callee Hierarchy for HelloWorld.main(String[])

This feature is useful for new development but is particularly helpful if you are migrating onto a project and need to understand a large complex code base quickly. As with most views in the JDT, you can navigate to a java ele-ment by simply double-clicking on it. For example, in a more complex situation (let’s look at the println(String) call), you might want to see what is happening three or four levels deep in the call stack. Select the println(String) call in the Call Hierarchy view and click on the expansion icon (the little right-pointing arrowhead). Notice that it shows all of the methods called by println. – Expand the print(String) – java.io.PrintStream line and double click on write(String) – java.io.PrintStream. You are now look-ing at the invocation of write in the print method of java.io.PrintStream. This feature is incredibly useful for navigating a large source base. There are few things better to help you understand a code base you are new to than being able to see how the code is invoked and to trace it through the various call stacks. Covered next is another great tool to help you better understand your code.

Page 49: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 38

Declaration View

The Declaration View allows you to see the declaration of any selected java element in the Java Editor. For example, in Figure 2.12 you can see that the HelloWorld class is being edited in the editor, the Declaration View is open, and System.out.println is selected. In the Declaration View, you can see the declaration of the println method.

Figure 2.12 The Declaration View

This is a great tool for tracing through your code (especially code new to you that you are trying to figure out). The “open input” button on the Declaration View allows you to open the class that the declaration is part of in the Java Editor. With the println method still selected, click the button. You will be taken into the PrintStream class and the println method will be selected. As you move down into the code in println, the Declaration View still shows the declaration of the selected Java element. When you move down into one of the method calls inside println, its declaration will show up; you can then open that class. Following these steps, you can navigate through your code and gain a deeper understanding of what it’s doing. Next up is a great feature for large projects that helps you organize your code and only view what you need for the task at hand.

Page 50: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 39

Working Sets

As you’ve worked with Eclipse, you might have noticed the menu item Select Working Set… This item allows you to specify a subset of your project and hide the rest from view. This feature is very handy for big projects or for when you have several projects in your workspace. Let’s start with the Package Viewer, which is usually where I kick the dialog off to set up a working set. Invoke the pull-down menu in the title bar of the Package Explorer and choose Select Working Set…. A window with a blank selection box and several buttons will appear. Click the New… button. The New Working Set wizard appears. You can now specify your working set. Because we are working with a Java project, we want to make a Java working set, so choose Java from the list, and click the Next > button. After expanding the HelloWorld project as well as the com.sb.eclipselive package, your screen should look like Figure 2.13.

Figure 2.13 The Element Selection Page of the New Working Set Wizard

Page 51: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 40

Notice that the wizard page is asking you to enter a name for the working set before proceeding. In Windows, this doesn’t show up as a warning. The dialog box tells you to enter a Working Set Name, but there is no red X, and the language is, ‘Enter a working set name and select the working set elements.’ The finish button is simply deactivated until you enter a name. This Wizard seems to use Finish only and never activates the Next button. After you enter a name, the wizard will then tell you that at least one resource must be checked. This is a com-mon feature of wizards in Eclipse. They analyze your input while you are on the page and let you know what is wrong before you hit the next button. Let’s get back to the working set. Select one of the classes in the com.sb.eclipselive package. I chose HelloWorld.java and called the working set My Stuff. After clicking the Finish button, my Package Explorer view looks like Figure 2.14. If you want to see all of your code again, simply choose the Deselect Working Set item from the pull-down menu.

Figure 2.14 The Package Explorer with Content Limited by the My Stuff Working Set

Page 52: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 41

For this very small project, this feature’s value is limited, but in a large project, where you are responsible for only a small fraction of the code, this feature allows you to focus on the code that you need to edit. In many instances, you can use the working sets you create, too. For example, you can direct the program to search only in a working set. Figure 2.15 shows My Stuff as the selected working set for the search.

Figure 2.15 Using a Working Set to Focus a Search

The Choose… button brings up the same panel that Select Working Set… in the Package Explorer brings up, so from here you can use My Stuff or you can create a new working set. The working set is not really a view, but considering all of the views that it can affect this seems an appropriate place to discuss it.

In this section we have covered some of the major aspects of the JDT: the two perspectives that are shipped with the JDT and many of the views that will make your life as a Java developer easier.

Page 53: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 42

Classpaths

One last item should be considered before we move on to editing Java code. The classpath for your project is managed via project properties. To assess the properties, invoke the context menu with the project selected in the Package Explorer. Choose Properties from the menu and then select Java Build Path from the browser on the left-hand side of the window. You should see a screen that looks like Figure 2.16 below.

Figure 2.16 Project Properties

The first page is the Source page, which allows you to specify directories in the projects that contain source code. On big projects it is typical to have source code in one directory and test code (with the same package structure) in another directory. On this page you can specify that the two directories are source directories. The Default Output Folder field allows you to specify where Eclipse should put the compiled classes for your project.

Page 54: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Major Aspects of the JDT 43

The next page allows you to add other projects in your workspace to the classpath. This is a very helpful feature for a large project that is broken into several different subsections of code. The next page allows you to specify jar files that belong on your path. You can see the classpath editor in Figure 2.17.

Figure 2.17 Classpath Editor

The first button, Add JARs…, allows you to specify jar files that are part of your project as members of the class-path. The next button, Add External JARs…, allows you to specify jar files that reside outside the project on your file system. Use caution when using external jar files because the project will store absolute file names, which can become difficult to work with for other members of your team. Add Variable… allows you to work around the down sides of using external files by allowing you to set up path variables that are not part of the project, so each developer working on the project can have a different location specified for the variable. Next is the Add Library… button. It allows you to add a group of jar files to the classpath. The JRE is an example of a Library that can be added. When building plug-ins, you also can add other plug-ins to the classpath of your project. The next button is Add Class Folder…. It allows you to specify the folders that will be on the classpath. Folders on the classpath are useful when you have resources (like .jpg image files or .properties file for configuration) that must be accessible at runtime, although you don’t want to make them part of any jar just yet. Finally, you can Edit or Remove an entry from the classpath. Editing lets you specify a new location for a particular jar file, and removing the jar file removes it from your classpath. Now let’s continue on to editing java files.

Page 55: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 44

Java Editor

You will use the Java Editor to write Java code. The editor has many built-in tools that work with the rest of the views as well as tools in the JDT to make your life easier and allow you to focus on writing your application instead of messing with the tool set. In this section we will become familiar with most of the features of the edi-tor.

Editing

The first thing you will do with the Java editor, of course, is edit Java. Open a new file (context menu in the Package Explorer New->File) and name it MyClass.java. Make sure to put the file into the com.sb.eclipse-live package. Normally you will use the new class wizard, but just to demonstrate how the editor works I want you to begin by creating a simple file with a .java extension. If you still have the working set selected that we cre-ated earlier, you won’t see the file in the Package Explorer. To see your new file, use the Deselect Working Set menu item in the pull-down menu in the title bar of the Package Explorer. The editor should now be open and displaying the MyClass.java file, and you should see it in the Package Explorer view. Next, type in the follow-ing code and save the file. (The problem View doesn’t show up until the file is saved.)

public class MyClass {public String getTheValue() {return “the value”;}}

You should see a problem show up in the Problem View stating that the package declared in the source does not match the directory structure that the file lives in. Choose the context menu item Quick Fix to get a list of options for fixing the problem. Choose Add package declaration “com.sb.eclipselive;” from the list, and Eclipse will automatically put the package declaration for you. This is just the fancy stuff behind the Eclipse editor. It is syntax aware, so when you enter code it knows whether or not it’s going to compile and warns you if it isn’t.

Of course, all the typical features we have come to expect from an editor are here, too, e.g., copy/paste, etc. All of these features, however, are integrated into the syntax awareness of the editor. For example, if you are pasting a method into your code (no, you should never do this, but let’s go with it for a minute), and you accidentally paste the method in after the last curly brace, the editor will inform you right away that this is an error. Covered next are the powerful navigation features built into the editor.

Page 56: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 45

Navigation Tools

The navigation tools built into the JDT are almost a substitute for running the debugger. With these powerful fea-tures you can move through a call trace without ever having to fire up the program. We saw a brief introduction to this earlier when we discussed the Call Hierarchy and Declaration view. The editor has most of this function-ality built into it. For example, invoke the context menu with a Java element selected. Figure 2.18 shows the getHello() method selected and the context menu showing the Open Declaration item selected.

Figure 2.18 Navigation Sub-menu with Open Declaration Selected

If you invoke this menu item you will be taken directly to the getHello() methods declaration. This is exactly what happens in the Declaration view when you hit the Open Input button in the title bar. The focus of the editor becomes the getHello() method. The other items in the Navigation submenu invoke other features to help you navigate through your source code. We have already covered the Type Hierarchy and Call Hierarchy views in Chapter 1, which is where the next items will take you if you select them. The Open Super Implementation menu item will take you to the super class’s implementation of the currently selected method, if there is one. Finally, choosing Show in Package Explorer will select the currently active class in the Package Explorer. With these tools you can move through your code simply and powerfully. Next covered is the source manipulation tools found in the JDT.

Page 57: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 46

Source Manipulation

The JDT has a very powerful set of tools to help you write and change your source code. We will start with the formatting tools, which can be found on the context menu in the editor. Figure 2.19 shows the context menu with the Source submenu visible.

Figure 2.19 Source Manipulation Menu Items

We have already gone over the first two sections (navigation and general editing) of the context menu. This third section is all about formatting and manipulating code. The first three items on the menu (Shift Right/Left and the Source submenu) will be covered in this section. The Shift Right item moves the selected code one indentation level to the right and, no surprise here, the Shift Left moves the selected code to the left. The more interesting fea-tures are in the Source submenu.

Page 58: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 47

The first six items are concerned with moving around the code you already have, and the first four will comment or uncomment the current selection. The next two commands, Format and Correct Indentation, are complemen-tary features to help you keep your code conformant with your coding standards. Correct Indentation will ana-lyze the context of the current selection and indent it correctly. Format provides proper newlines and argument arrangement. Notice that Format is context sensitive and does not always show up in the menu. The Format menu only makes sense when a “formatable part” of a class is selected. A formatable part of the code is anything that could reasonably be formated. An example of a selection that cannot be formatted would be from the second letter of class to the first letter of public in the getHello() method. The formatter does not know what to do with a selection like that, thus, the item does not show up. Now on to the import management features.

One of my favorite features of Eclipse when I first got started was Organize Imports. I have never worried about an import since I made the switch. Not only does this feature keep import statements in logical order and grouped by package, but it will find a referenced class in the class path and insert an import statement for you. To illustrate, open the HelloWorld.java class, add a variable typed List, set it equal to a new instance of ArrayList, and save the file, Figure 2.20 shows the editor after these edits have been performed.

Figure 2.20 Unknown Classes “List” and “ArrayList”

Page 59: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 48

Notice in the Problem Viewer that both classes are listed as unknown. We could use the Quick Fix feature to address both of these problems individually. The Organize Imports, however, was created for just such an occa-sion. Invoke the context menu and select Source->Organize Imports. You will be presented with a dialog win-dow that lists all of the definitions of List that are available and which asks you to choose which one you want used in this class. After you specify (java.util.List in this case), an import will be placed into your code. Notice that it does not prompt you for which definition of ArrayList to use because the classpath contains only one ArrayList. This is one of the features that has helped me to justify my move from Emacs after nearly 15 years. I hate having to wait for a compile to find out I’ve forgotten to import some class or another. The Add Import item also adds imports but only one at a time. To invoke it, you must have the unknown type selected (i.e., double-click on List or ArrayList before invoking the command). Next up are the source code generation menu items.

Page 60: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 49

Override/Implement Methods

This feature allows you to either override methods that are implemented in your super class or implement meth-ods for an interface. To invoke the one-page wizard, select the menu item. To see how useful this feature can be, lets make HelloWorld implement the List interface (just type Implements List and then use the Organize Imports feature). After you save the file, you should see several problems that relate to not implementing all the methods that the List interface requires. In the old days you’d have to look at the javadoc for List and type fran-tically. With this feature, however, you only have to click a couple of buttons. Invoke the Override/Implement Methods… menu item, and you will see a window that looks like Figure 2.21.

Figure 2.21 Override/Implement Dialog

Page 61: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 50

Each selected method will be generated for you. Any unselected methods will not be generated. By default the dialog will select all of the methods that must be implemented to get the class to compile. Because we added List to the interfaces that HelloWorld should implement but none of the methods are implemented, the dialog has selected all of the methods from that interface. The insertion point can be specified with the pull-down menu. Each existing method in the HelloWorld class is in the pull-down menu as well as two special cases: “Last Method” and “First Method.” The generated code will be placed below whatever selection you make. You can also specify whether or not you want Eclipse to generate a comment for the method. Regardless of your choice, Eclipse will place a TODO comment into the body of the method. One more quick item on this dialog deserves note. The button on the top right-hand side of the selection list lets you view the methods in one of two ways: in a hierarchy (as in Figure 2.21) or flattened in alphabetical order.

Page 62: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 51

Generate Getters and Setters

This feature generates JavaBean compliant get and set methods for the fields in a class. To illustrate, add a field to the HelloWorld class and invoke this menu item. A dialog appears that looks like Figure 2.22.

Figure 2.22 Getters and Setters

The insertion point can be set and works the same way that the Override/Implement Methods feature works that we saw in the last section. The Sort By pull down allows you to sort the generated methods according to get/set pairs or alphabetically (i.e., the getFoo and setFoo method will be sorted together or all the gets and all the sets will be together). You can also set the modifiers (visibility, etc.) of the methods that will be generated. The but-tons on the top right provide a quick way to select just the getters or setters.

Page 63: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 52

Generate Delegate Methods

This feature makes implementing the Delegate Pattern a snap. Let’s go back to our List implementation example for a moment. Let’s say that you want to implement the List interface, but all you really want to do is implement the add(Object) method in a special. The rest of the implementation of ArrayList is just fine for your needs. You can take advantage of the implementation of ArrayList by simply forwarding all calls related to the List interface to a private instance of ArrayList in your code. Eclipse makes it easy. To illustrate, let’s walk through the example. Start by creating a new class, ExampleDelegator. Next, add a field of type List called delegate. Now you are ready to invoke the Generate Delegate Methods feature. You should see a dialog that resembles Figure 2.19. Select the methods you want Eclipse to generate for you and the location they should be inserted into and select the Finish button. The ExampleDelegator will have a code added that looks something like this.

public boolean add(Object o) {return delegate.add(o);}

If you checked every method in the dialog, you could even mark ExampleDelegator as an implementer of the List interface. Next up are overriding constructors.

Add Constructors from Superclass

The JDT code generator can look at the super class (through reflection or the internal model) and provide a default overriding constructor. Let’s go back to the ExampleDelegator and override the default constructor from Object. Click anywhere in the class and invoke Add Constructors from Superclass (context menu Source->Con-structors from Superclass). Again, the dialogue looks a lot like Figure 2.21. The single constructor should already be selected. You can also specify that the typical call to super() should be omitted if you want to com-pletely override the super classes implementation.

Page 64: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 53

Generate Constructor using Fields

This feature allows you to create constructors using any number of the fields in a class. To illustrate, let’s go back to the ExampleDelegator class. Add a single field of type String called field1. Next, invoke the context menu and select the Source->Generate Constructor using Fields item. This will bring up a dialogue similar to Figure 2.23.

Figure 2.23 Generating Constructors from Fields

Page 65: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 54

You can specify the constructor you want to override (leave it on Object() and elect to omit the call to super if you are making a new constructor) and the location to insert the constructor.

Add Javadoc Comment

This feature lets you insert javadoc comments into your code. It is smart about the element you are adding the comment to in that it will add @param directives for each parameter and an @return for the returned value and an @throws for each Exception. All that is left for you to do is to add the content of the comment.

Surround with try/catch Block

The last feature we will cover under the source generation menu section adds try/catch blocks to your code. This is a fantastic feature for lazy programmers. Instead of having to look up the method and all the Exceptions it throws, you can simply invoke the method and have Eclipse look up everything for you. Consider, for example, if you typed the following line of code into one of your classes.

DriverManager. getConnection(“jdbc:mysql://localhost:3306/book”);

Normally you’d have to know that the SQLException is thrown, but since Eclipse is syntax aware it knows about all the exceptions and will warn you. This feature is unique in this set of features because it can also be invoked via a Quick Fix in the problem viewer, or you can use the context menu Source->Surround with try/catch Block. Either way accomplishes the same thing: the method call is surrounded by the try/catch block. After invoking this feature, the code will look like this.

try { DriverManager. getConnection(“jdbc:mysql://localhost:3306/book”);} catch (SQLException e) {// TODO Auto-generated catch blocke.printStackTrace();}

If getConnection(String) had thrown more than one exception, then more than one catch block would have been introduced.

In this section, we have seen several of the code generation features built into Eclipse. Together they make a powerful group of features that can take most of the drudgery out of writing code. A lot of the generic boilerplate code is generated through a quick and easy wizard-type interface. Spend some time experimenting with these features. The time invested will be paid back many times over in the productivity gains you will get. The next section covers the really cool history functionality built into the JDT.

Page 66: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 55

Local History

Did you ever make an edit, then wish you hadn’t, but you’ve already quit your editor and lost the undo stack trace? Did you ever wish you could make some changes to your code and be able to back it out without having to check it into a source repository like CVS? With the JDT, every time you save a file, Eclipse is saving a diff of the file for use in a function called “Local History.” With this feature you can do a lot of experimentation with your code that we used to have to rely on a version control system for in the past.

The features related to Local History are accessed through the context menu under the Local History submenu.

Page 67: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 56

Compare With…

The Compare With… feature allows you to compare what is currently in the buffer with an instance of the same file in the Local History of the file. For example, earlier in this chapter we discussed adding methods through delegation, and we looked at how this is done on the HelloWorld class. Go back to that class, delete the code that was added by the code generator (i.e., all the methods that delegate to the list), and save the file. Then invoke the context menu and select Local History->Compare With…. The dialog you see will look something like Figure 2.24.

Figure 2.24 Compare With an older Version of the File

The particulars of the histories on my file and yours are probably different unless you are working through this in the middle of the night. The top pane lists the versions stored by date and then by time. The bottom pane is a comparison view that shows the delta between two files (basically a visual diff). The golden up and down arrows on the right-hand side of the comparison view move you from one diff to the next, up or down in the file. No golden arrows, but there is similar functionality in windows. With this feature you can see changes that you’ve made without ever invoking the version control system, which can be very handy if you are experimenting with a breaking change and don’t want to check it in until you are sure you want to make it.

Page 68: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 57

Replace w/previous

This feature allows you to go back one version in the Local History to the previously saved version of the file. This comes in handy when you notice that you’ve accidentally saved the “select all,” “delete,” and “save” com-mands all in a row. There is no dialog for this feature; it simply pulls the previously saved version of the file and replaces the content of the buffer with that version.

Replace with…

Replace With… is a bit more sophisticated than the previous feature. This feature is basically a combination of Replace and Compare. When you invoke this feature, you should see something like Figure 2.24. The only dif-ference is that the Close button is replaced by two other buttons: Cancel and Replace. You can browse through all the old versions of the class and see the diff, and then choose the version that you want to go back to. This is another great way to recover from an experiment gone awry.

Page 69: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Java Editor 58

Restore from…

Restore from… allows you to “bring back” Java elements that were part of the selected class in a previous save. For example, if you delete a method and then decide that you want it back, you can choose to restore the method with this feature. To illustrate, go back to the HelloWorld class that has all the methods in List implemented, delete one or more of the methods, and save the file. Now invoke this feature. You will see a dialog that looks like Figure 2.25.

Figure 2.25 Restore Java Elements From the Local History

The left select box displays elements that are in the history but no longer in the class. The right-hand side shows the history of these elements (in this case, there is only one, but there could be many). As you change the selec-tion, the bottom pane will show the content of the element.

Local History is a very powerful feature in the JDT. With it, you can experiment with relative confidence that you can back out of unwanted changes and not have to lose changes that you want to keep.

Page 70: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Searching 59

Searching

The searching features built into the JDT are very powerful. You can search for just about anything you can think of that has to do with Java. To invoke the searching features go to the main menu and select Search->Java… You should see a window that looks like Figure 2.26.

Figure 2.26 Searching for Java Elements

Let’s go through the window and explore the various options. The first option is the string you want to search for. This string is aware of Java syntax, so you can do very specific, very powerful, searches. For example, if you double click on “println” and then invoke the Java search, the search string will display java.io.Print-Stream.println(String) and will search for invocations of this very specific method. The check box, as the name implies, specifies whether the search should consider the case of the search string or not. (Doing the println example, the checkbox is there, but greyed out.)

Page 71: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Searching 60

The Search For set of radio buttons allows you to specify the kind of Java element you are looking for. The Limit To set of radio buttons allows you to limit the extent of the search to a particular kind of occurrence of the ele-ment. For example, if you choose Reference, then the search will find the places where a method is invoked and not the implementation. If you choose Declarations, then a search for a method will only return the spots where the method is declared. These selection-limiting buttons work identically with all the Java elements. It does not make sense, however, to limit a search to a certain type of Java element. For example, when searching for a java method, it does not makes sense to limit the search to Read Access because methods are invoked, not read. In these cases (most of the time, anyway), the Limit To choices will be limited by the type of Java element the search is for. The Scope section allows the search to be limited to a certain subset of the Java classes in the project. You can limit it to the Workspace (which is everything), only to Selected Resources, which applies to items selected in the Package View typically, or you can specify a Working Set.

You might also have noticed in the main Search menu that there is a list of choices that repeat the Limit To radio buttons. This list is a set of shortcuts to specifying the options on the Search page. With them you can highlight Java elements in the editor and then search for the references, declarations, etc.

As you can see, the searching capabilities of the JDT are very powerful. Like the rest of the tools, the search is context and syntax aware and makes full use of the underlying knowledge that the JDT has about the Java code in your project.

Page 72: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Summary 61

Summary

In this chapter, we have covered the various features of the JDT that provide a rich editing and navigation envi-ronment. The next chapter will cover how we can run and debug our applications in Eclipse.

Page 73: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Chapter 3

Eclipse 3 Live

Running and Debugging Java Applications

The Launch Configuration concept built into the JDT makes running and debugging your code easy. The tools built into the debugger make it a snap to find and fix the bugs. In this chapter, we will cover both the Launch Configuration and the debugging tools in detail.

Now that we have gone over the JDT in detail and you’ve seen the tools you have at your finger tips to make Java code, we will delve into how to make the code that you’ve worked so hard on run. We will also go over in detail the various views and tools that you can use to track down and get rid of the occasional bug.

Page 74: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 63

Running Java Applications

First up is a discussion of how to run Java applications. The functionality to run applications is quite powerful. You can specify that the application should run in the same JVM as Eclipse, a different JVM, or even a different version of the VM. You can set up a special classpath, or you can use the one that you’ve created for your project. There are a lot of other options that we will go over in the discussion of Launch Configurations.

Launch Configuration

Before we go any further, let’s look at the ‘quick start’ options that Eclipse has to offer for running your java applications and run the very simple HelloWorld example we’ve been looking at. Open the HelloWorld class if you have not already done so, and select the editor (select anything in the file). We will run this class via the Run menu that is shown in Figure 3.1.

Figure 3.1 The Run Menu Activated

Page 75: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 64

This menu is almost always the beginning of setting up a Launch Configuration. The items in this submenu set up a Launch Configuration for the currently selected class with a set of default options that make sense for the type of configuration being created. Navigate to the Run As item and notice the submenu items that are shown. Each of these represents a type of Launch Configuration used to launch Java programs of various types. The name and a description of the type of configuration are listed in Table 3.1.

These configurations give you a lot of power in creating all kinds of Java applications. New items can even be added to this by plug-ins.

Let’s focus on running our example application now. Select HelloWorld in the text editor (if it’s open, select the tab; if it’s not open, then double- click the Package Explorer). Then, invoke the Run->Run As->Java Application item. After the code runs, you should see the Console view appear with Hello World printed out. Notice now that if you select the Run pull-down again, HelloWorld shows up as an item on the menu. This is a short cut to invok-ing the application again. In fact, to start the most recently run application, you don’t even have to invoke the Run pull-down; just click the button and it will run HelloWorld again. This is an extremely convenient feature when you are testing out an application that you have to run over and over again. Now that we have seen how to invoke an application, let’s drill into the detail of the Launch Configuration and see what options we have for set-ting up the environment that our applications run in.

Table 3.1: Launch Configuration Types

Launch Configuration Type DescriptionJava Applet Creates a Launch Configuration suited to running Java Applets. This configu-

ration lets you specify the Applet Viewer class and the parameters that would normally be found in the HTML file that invokes the applet.

Java Application Creates a Launch Configuration suited to running Java Applications. This configuration lets you specify the class to run from the current project or a class from one of the JAR files in the classpath. Using a class from a JAR file can be very useful if you are extending a framework and building code that will be invoked from that framework.

JUnit Creates a Launch Configuration suited to running JUnit Tests. We will go over JUnit tests in more detail in Chapter 6, Testing Java Apps. This configu-ration lets you specify a particular JUnit test class or all the tests in a particu-lar directory.

JUnit Plug-in Test Creates a Launch Configuration to invoke JUnit tests against an Eclipse plug-in. This configuration is specific to plug-ins, and since they are not being covered in the first edition of the book, we won’t go into any more detail here. If you are interested in plug-ins, please let me know; it is on the list of possi-ble new chapters.

Run Time Workbench Creates a Launch Configuration for a new instance of Eclipse. This configu-ration is also intended to give you a spot to run your plug-ins, but instead of running JUnit tests against them, you can actually use them in a live running copy of Eclipse.

Page 76: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 65

Launch Configuration Options

We have seen Eclipse build a ‘default’ run configuration for our very simple application. In many cases that default configuration will be sufficient to run your app. However, in some cases you will need more control over the environment that your application runs in. In these cases, the Launch Configuration management pages are where you need to go to customize the configuration. To edit the configuration select Run->Run… You should see a window that looks something like Figure 3.2.

Figure 3.2 The Initial Launch Configuration Options Screen

Page 77: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 66

With this window, you can specify high-level options about your program, as well as access all the functions related to customizing the Launch Configuration. On the left is the list of configurations in your project. Notice that this is the same list that we see under the Run->Run As menu. In this view, you can add and remove various configurations. If you’d like to create a Java Application configuration, select it or any existing Java Application configuration and hit the New button at the bottom of the view. This will create a new blank configuration (as opposed to the default one we got from using Run->Run As->Java Application earlier). You can also duplicate and modify an existing configuration by invoking the context menu (right click on windows, control click on the mac) and selecting Duplicate. You will notice that the New and Delete functions are avalible on the context menu as well. The buttons along the bottom right hand side allow you to ‘save’ the changes you make as well as run the application once the changes are made. Once you make a change to any of the options, the Apply and Revert but-tons will become selectable. They do what you’d expect: the Apply button saves the changes that have been made, and Revert changes the values back to what they were. The Run button launches the application just as if you selected the Run->Configuration Name menu item. If you have unsaved chagnes and you select Run, they will be saved before the app is launched. The Close button closes the window; if you have unsaved changes, it will prompt you to save the changes before closing the window. This set of buttons is constant throughout all the pages of this dialogue, but the content of the page changes from section to section.

Main Options

Now focus on the Main page that is currently visible in Figure 3.2. We will add a new configuration shortly. The Name field allows us to specify the name of the configuration. This is the item that will apper in the Run menu, so name your configurations in a way that you can recognize what they are for. The default configuration built by Run As-> always uses the class name without the package name. You can of course change the name. In this screen shot (in Figure 3.2), the Main configuration options are selected. These options let you specify the high-level options related to your application. Project is the name of the project this configuration is associated with. When you are editing a configuration that was created by the Run As menu items, you will not typically change this field. However, in new configurations you must specify the project to add it to. The Main class field allows you to specify the Java class to run. The class that you specify here must have a method that conforms to the Java spec for an executable class, namely public static void main(String[]). The Search button to the left of the Main class field will bring up a list of all the known classes that have a main method that conforms to the standard interface. The first check box allows you to run the main method from a class in your classpath instead of just from the classes in your project. This can be really handy in cases where you are extending an existing application and are therefore not writing the main method of your application. Finally, the Stop in main toggle lets you specify if the application should stop in main when launched. This is intended to make it easy to debug by stopping the program when it starts in debug mode. I find though that I’d rather set breakpoints and run to them than have the app stop whenever I start it. We will talk more about debugging and setting breakpoints later in this chapter. Next up is how to specify arguments to the program.

Page 78: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 67

Arguments

Change the selection from Main to Arguments. The content part of the window should change to look very much like what you see in Figure 3.3.

Figure 3.3 The Arguments Page

On this page you can specify the arguments that Eclipse should pass to the program and/or the JVM when it launches your application. Both the Program arguments and VM arguments sections are free form text, and you can type anything you need/want to in these sections. However, when you run your application, Eclipse will build the command line equalvilent of the following:

java VM arguments Main class Program arguments

VM arguments and Program arguments come from this page, and Main class is specified in the previous screen of the dialogue (back in Figure 3.2). So even though you can type anything you want in these boxes, keep in mind how they are used and use valid arguments. The Working directory section allows you to specify the direc-tory that the application should start in. For many (if not most) situations you won’t need to specify anything here, and you can use the default directory (the root directory of the project). If your application uses files though, it might make sense to test its behavior when starting up in various directories. If you uncheck the Use default working directory check box, you can then specify either a Local Directory on your file system or a direc-tory in the Workspace. Either way, the Browse buttons that are to the right of the fields bring up browsers rooted in their respective worlds.

Page 79: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 68

Since HelloWorld currently does not take any options, let’s change the code to print one of the arguments. Change the main method to look like this:

public static void main(String[] args) { HelloProvider hp = new HelloProvider(); WorldProvider wp = new WorldProvider(); String hello = hp.getHello(); String world = wp.getWorld(); if(args.length > 0 && args[0] != null) { System.out.print(args[0] + “ - “); } System.out.println(hello + “ “ + world);}

This code will print its first argument plus the content provided by the HelloProvider and WorldProvider classes. In the Program arguments field, type a single word like Foo. You should see output that looks like Figure 3.4 in the console.

Figure 3.4 Visible Effects of Program arguments

As you can see, the Program arguments work the same way they do on the command line. Next up is the JRE page.

Page 80: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 69

JRE

This page allows you to specify the JRE to use in running your application. This is another great feature of Eclipse. You can run Eclipse with one JVM and use another to run the application you are building. This is espe-cially useful for testing your application when it’s run in various JVMs. For example let’s say you want to test HelloWorld on both JDK 1.4.1 and JDK 1.3.1. To do this, you should duplicate the HelloWorld configuration by invoking the context menu on the configuration as seen in Figure 3.5.

Figure 3.5 The JRE Page

Page 81: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 70

Once the duplicate configuration is made, you can change the name to something more descriptive like Hel-loWorld 1.3.1. Now that you have two configurations, one will execute the application in the 1.4.1 JVM (the default one built by Run As->Java Application) and the other will execute the application in the 1.3.1 JVM. In the HelloWorld 1.3.1 configuration, choose an alternate JRE by selecting the radio button and then selecting the JRE to use from the pull-down. If you don’t see an alternate JRE, you can add one via the Installed JREs… but-ton. Once you have the alternate JRE specified, click Apply. After you have applied the changes, the JRE page should look something like Figure 3.6.

Figure 3.6 Updated JRE

With these two configurations you can run HelloWorld in two JVMs. HelloWorld is very simple so it will run in both VMs. However, this example shows you how to set up your application to run in a different VM than the VM you are using to run Eclipse. Next up is the Classpath page.

Page 82: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 71

Classpath

The Classpath page allows you to set the classpath that will be used to run your application (very surprising, I know). The classpath will default to the classpath for the project, but you can customize it and add more JAR files, directories and other resources that need to be available to your application at runtime. Figure 3.7 shows the Classpath page with both entries expanded and the project selected.

Figure 3.7 Managing the Classpath

This classpath is the runtime classpath and defaults to the projects classpath (which is the build classpath). You might want an additional classpath entry for runtime so that resources can be found, such as configuration files, etc. Lets go through the various tools available on this page to manage the classpath. The first two buttons Up and Down let you specify the order of the classpath. Entries closer to the top are further up the classpath than entries further down. The Remove button takes a resource out of the classpath. If you have multiple projects in your workspace, you can add one to the classpath with the Add Projects… button. If you add a project to your classpath, all the classes in that project will be added to your classpath as well as any exported JAR files. The next button Add JARs… allows you to add JARs in your project to the classpath. The browser that this button brings up will only allow you to choose files that are within your project. The next button Add External JARs… allows you to choose JAR files that are outside your project. If you are working on a team, it is not a good idea to add external JARs to the classpath because this uses the absolute path to the JAR file. It is better to use a variable that represents a path. We will talk about path variables next in the discussion about the Advanced… button.

Page 83: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 72

I’m not sure why this is called Advanced other than it’s a way to group a bunch of options for specifying class-path entries that are not JAR files in your project. Regardless of why it’s called advanced, this button will bring up the dialogue box shown in Figure 3.8.

Figure 3.8 The Advanced Options

From here you can choose to add various kinds of resources to your classpath. Clicking the OK button brings up another window that allows you to choose the type of resource. The first radio button, Add Folders, allows you to add one or more folders to the classpath. This option only allows you to add folders that are in your project. The next radio button, Add External Folder, as expected, lets you add a folder from anywhere in your file system. Again though, use caution when adding external resources; the absolute path is used in the project and that path might not match the path your coworkers are using to store the same thing.

The next button, Add Variable, allows you to add a path variable to the classpath. Recall from Chapter 2 that path variables allow you to refer to external paths through a name instead of the absolute path. In this way, all the peo-ple on your team can install external dependencies wherever they want and still use the same project. All they have to do is set up their path variables to point to their installation. The runtime classpath uses the same set of variables that you set up in Chapter 2. The Attach Source choice allows you to attach the source code for a JAR file (selected before the Advanced… button was pushed). And finally, you can add a container with the Add Con-tainer: choice. (Container is inconsistent with the name in the build classpath, in the 3.0M6 build, that we dis-cussed in Chapter 2. There, the same concept is referred to as Library—bug #50453 was filed for this inconsistency) The container is just a grouping of JAR files, and there are two types, a JRE and a set of plug-ins. Finally, you can get rid of all the changes and revert to the build classpath with the Restore Default Entries but-ton. The next page of the dialogue window is Source.

Page 84: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 73

Source

This page allows you to add resources that contain source code. The JDT will look in these locations for source code during debugging. This page and the previous page have basically the same functionality; here you specify the source code that sits behind the classes that are in your classpath. Figure 3.9 shows the Source page.

Figure 3.9 Specifying the location of Source Code

The buttons here have basically the same functions as described earlier in the discussion on the classpath. If you have the Use default settings check box checked the rest of the buttons on the page are inactivated and the runt-ime source will match the build source. The Search for duplicate check box is only important in a debugging ses-sion. If this button is checked, the debugger will search for duplicate source files and ask you to choose from the duplicates if you step into code that is not part of your project. If this check box is not checked, the debugger will take you to the first one it finds. Next up is the Environment page.

Page 85: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 74

Environment

This page lets you specify environment variables for your Java program. Figure 3.10 shows the page with a sin-gle variable foo and its value is set to bar.

Figure 3.10 Managing Environment Varaibles

The radio buttons allow you to specify whether Eclipse should append these variables to the environment vari-ables that were present when Eclipse was launched, or if the environment variables listed should replace the exit-ing environment. Either way, these environment variables you enter in this window will be available to your program at runtime. Next up is the Common page.

Page 86: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 75

Common

This page is named Common because it has the options that are common across all the Launch Configurations. Figure 3.11 shows the page.

Figure 3.11 The Common Options

The typical Launch Configuration is local to the project you are working on. But if your configuration is particu-larly difficult or prone to cause problems and if it’s not just right and you are working on a team, it might be a good idea to share the configuration. A shared configuration can be used by more than one developer. This is par-tiuclarlly important if you are on a team and the configuration for your application is complex.

This is the Java Application launch configuration options. As you can see there are quite a few ways to customize the way your application is launched. Just about anything you can imagine doing on the command line with a java command can be set up here in the pages of the Launch Configuration.

Page 87: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Running Java Applications 76

Other Launch Configurations

As we saw earlier in this chapter, there are several different types of Launch Configurations—each one particu-larly suited to the type of Java application that it launches. A full discussion of the other options is currently out-side the scope of the book. However, if you’d like to see more about one of the configurations in particular, please let me know. Next up is a discussion of running external tools inside and with Eclipse.

Page 88: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

External Tools 77

External Tools

An external tool is any tool that you need to run that is not part of Eclipse. Usually these tools involve building or generating some part of the application. There are two types of tools currently configurable in Eclipse: Ant Builds and Programs. As you would expect, Ant Builds relate to the Ant build program. We will go over Ant integration in Eclipse in detail in Chapter 4. Programs is anything else that you need to run to correctly build your application. The External Tools menu is invoked by clicking the icon just to the right of the Run menu. The proper button is highlighted in Figure 3.12.

Figure 3.12 The External Tools Menu

Page 89: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

External Tools 78

The configuration of an external tool is very similar to the Launch Configurations we saw in the last secetion. Open the external tools configuration tools by selecting the External Tools... item from the menu, and select the Program element from the browser on the left side of the window. You should see a window that looks like Fig-ure 3.13.

Figure 3.13 The External Tools Menu

The content on the right allows you to specify what perspective should be invoked when the external program is run. You would specify a particular perspective in cases where the external program is going to make some changes to resources (such as generating code) that are best viewed in a particular perspective. Notice that the window does not change between the Ant Build and Program elements.

Page 90: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

External Tools 79

For an example, we will add Java 2 HTML as an external tool. If you are not familiar with Java 2 HTML, it is a great program that converts java source code to HTML so you can browse the code with a browser. I find it espe-cially useful for doing code reviews. Let’s get back to our regularly scheduled discussion here though.

With the Program element selected, push the New button at the bottom of the browser. Change the name of the run configuration to ‘Java 2 HTML’. This is a Java program so we need to specify the JVM in the Location field. I’m not sure why Location is used here; what is meant is the Location of the executable that this run configura-tion will run. For the HelloWorld project, we can set the Working Directory to the root of the project. Without going into great detail of how to use Java 2 HTML, you need to supply the JAR file (-JAR j2h.jar), the location of the output (with -d), the location of the java source code (with –js) and that you’d like to see line numbers (with –m #). The number associated with the margin tells Java 2 HTML how many characters to allocate for line numbers. After you have specified all this, your window should look something like Figure 3.14.

Figure 3.14 Java 2 HTML as an External Tool

Page 91: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

External Tools 80

Now that you have a configuration set up, let’s run it. Click the Run button in the bottom-right side of the win-dow. You should see some messages from Java 2 HTML about the files it is generating. Once it’s done, you have HTML made from your code. One small problem is that you can’t see it in the package explorer yet. In order to see the new HTML, invoke the context menu in the Project Explorer and choose the Refresh item. You should see the html and html.com.sb.eclipselive directories. Refreshing after each run will get to be a bit tiresome, so let’s go back to the configuration editor and change the Refresh options. Choose External Tools->External Tools… and then select the Refresh page and check the Refresh resources upon completion check box. You should see a window that looks like Figure 3.15.

Figure 3.15 Refresh Options

Page 92: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

External Tools 81

You can choose what to refresh, from everything in the workspace down to specific resources. The other pages to set up an external tool allow you specify environment variables, and arrange how the external tool appears in the menu. The flexibility provided by having this functionality integrated into Eclipse is fantastic. You can continue to use external tools that provide code generation or other needed features but build all your java code in Eclipse.

Now that we have gone over running Java applications with Launch Configurations and invoking external tools with the very similar configuration tool, it’s time to move on to debugging Java applications.

Page 93: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 82

Debugging Java Applications

No matter how good we become at writing code, we always end up with bugs. No matter how careful we are to test and retest, we still get unexpected behavior and sooner or later we will have to figure out how to make it all work. That is where the debugger in Eclipse comes along.

Quick Start for the Impatient

The debugger uses the same Launch Configurations that we saw in the previous section. Once you have a launch configuration set up, you can simply choose the same configuration under the Debug menu. Figure 3.16 shows the Debug menu expanded.

Figure 3.16 The Debug Menu

Page 94: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 83

Notice that the HelloWorld configuration is selected. If you left the Stop in main option checked in the Main page of the Launch Configuration editor, then selecting the HelloWorld item will cause the application to startup in the debugger and stop on the first line of HelloWorld.main(String[]) as show in Figure 3.17.

Figure 3.17 HelloWorld Stopped in main(String[])

The HelloWorld program is executing, stopped in the main thread on the line with the blue arrow. We are now ready to debug the program. This is a quick introduction of how to launch into a debugging session. There are lot of details that we will cover in the upcoming sections, but the important take home point is that debugging uses the same Launch Configurations that running applications use. Next up is a detailed discussion of the Debug Per-spective and if you were paying close attention, you will notice that we have been put into the Debug Perspec-tive.

Page 95: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 84

Debug Perspective

The Debug Perspective is like any other perspective—it’s made up of views and tools. You can customize the Debug Perspective by adding and removing views, moving the views to different locations, etc. All the various customizations we have discussed thus far apply here to this perspective as well. I have found that after trying out many different customizations that I actually prefer the default layout of the Debug Perspective. I would encour-age you to get to know the default layout and then work on customization instead of customizing early. In this section we will cover each of the views in the perspective as well as the tools associated with them. First up is the Debug view.

Debug

This view shows you the current process that is being debugged as well as a history of each debugging session until it is cleared. Figure 3.18 shows the Debug view again with the HelloWorld program stopped at the first line of Main.

Figure 3.18 Debug View

In Figure 3.18 you can see that HelloWorld has been run three times and that the third time is still active in the main thread. Each execution of a program is kept around until it is explicitly removed. This is a great feature that allows the history of the output of the debugging run to be kept. You can click back through this history to see the output of each run. Since HelloWorld is a very simple program there is not a lot to see, but in a more typical application there would be quite a bit of logging to look through. With this history at your fingertips, you can go back over the output to see how the efforts to squash the bug have progressed. All this history of the output is kept in the console (which is at the bottom of the window in Figure 3.17).

Page 96: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 85

Now that we have talked about the main part of the view, let’s look at the set of functions available in this view. The buttons across the top of the view allow the execution of the program to be controlled. We will go over each button starting on the left-hand side and moving right. The first button (the green arrow) runs the program to the next breakpoint. Since we don’t have any breakpoints set in this program, clicking this button will cause the pro-gram to run to completion. You can try this out a couple of times (hitting the debug button after running to com-pletion) to get the same looking window as what is shown in Figure 3.18. The next button causes a running program to pause wherever it’s at. Since HelloWorld is so simple we only have one thread of control. So as soon as that thread exits, the program will end. In more sophisticated applications such as those with a Swing based GUI, the program will continue running until it is explicitly stopped. It is these types of programs that make the pause button useful. With it you can stop a program and examine the state then continue for further debugging. The next button is the red square. It will stop the currently executing program. The disconnect button is next, and it is used to disconnect the debugger from a remote debugging session. We will cover remote debugging later in this chapter. The next button clears out the history of all terminated launches. I find it helpful to do this from time to time to clean up, especially after debugging for a long time leaves a long trail of terminated launches.

The next group of buttons relates to stepping through the running application. The first of these is the Step with Filters button. It steps into the next line of code but skips all the ‘filtered out’ classes. This is a particularly useful feature. With this feature any classes you specify can be skipped in a ‘step into’ command. For example, the fol-lowing line of code would be very hard to ‘step into’ because of all the string concatenation that is happening (each ‘+’ causes some method calls on String).

System.out.println(hello + “ “ + world);

Page 97: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 86

So a simple ‘step into’ on this line of code would take us through several stack traces of building the final string that is passed to println (which is where we want to step into). However, if the Step with Filters functionality is used, then all the calls into the String class will be skipped if the String class is filtered out. To specify which classes to filter out when Step with Filters is used, the preferences must be set. To activate the preferences panel choose Window->Preferences from the main window. Once there navigate to Java->Debug->Step Filtering. Fig-ure 3.19 shows the preferences page with java.lang.String and java.lang.StringBuffer added to the fil-tered out classes.

Figure 3.19 Filtering Preferences

Page 98: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 87

I added these classes to the default list by selecting Add Class and then typing the class names and selecting the class from the list. With these two filters in place hitting the Step with Filters button when on the line above will step directly into java.io.PrintStream(String), skipping all the calls to String and StringBuffer. The next button is Step Into and it will ignore the filters and step into the method call on the current line of code. The next button is Step Over and will call the method on the current line but will not step into it (unless a breakpoint is encountered that is). Finally, the Step Return button will cause the program to run to the end of the currently executing method and return. The application will stop at the next line after the current method returns. The pull-down lets you specify whether or not the system threads should be thrown and whether or not fully qualified type names should be shown. And of course the X will close the Debug view. This wraps up the discussion of the tools available in the Debug view. With this view you can examine and control each thread of execution in your application with the various functions that are described. Next up is the Variables view where you can examine all the variables that are present on the current stack.

Variables

This view allows you to see all the variables on the current stack frame (assuming that the code was compiled with debug mode). Figure 3.20 shows the Variable view with the last line in HelloWorld.main(String[]) hav-ing been executed and the local variable wp selected.

Figure 3.20 Variables View

Page 99: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 88

The top part of the view shows the list of variables. For types that have internal structure an arrow is shown. This arrow can be clicked to expand the view of the variable to the internals (i.e., you can browse through the fields of each variable as well). The bottom part of the view shows the details of the selected variable. Typically this amounts to calling to String() on the variable and showing the return value. For some instances, the output is more sensible. The buttons across the top of the Variable view allow you to look at the variables in different ways. The first button toggles showing the type of the variable. The next button toggles showing the ‘logical’ structure of the variable. The logical structure is very handy for examining things like hash maps and other java.util collections, instead of showing the full detail of all the internal variables the view will only show the ‘logical’ pieces. Try it out by adding the following code to the main(String[]) method.

Map map = new HashMap();map.put(“ONE”, “TWO”);

Then, restart the debugger, step over these two lines, examine map in the Variable view and toggle the ‘logical structure’ button. Notice how much simpler the internal view of the map is when the logical structure is turned on. The next button collapses all the expanded variables. The pull-down lets you change the layout of the two parts of the Variable view and filter what types of variables are shown (static, constants etc.). Next up is the breakpoint viewer.

Breakpoints

A breakpoint is the ‘spot to stop’ for the debugger so that you can examine the program and find bugs. The Breakpoints view allows you to look at the breakpoints that have been set, remove breakpoints and change their properties. Figure 3.21 shows the single breakpoint that I have set in the HelloWorld.main(String[]) method.

Figure 3.21 Breakpoints View

Page 100: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 89

The first two buttons remove either a single breakpoint (the selected breakpoint) or all the breakpoints respec-tively. The next button, that looks like gears, toggles seeing all the breakpoints or just the breakpoints that are rel-evant to the currently selected debug target in the Debug view. The next button will take the editor window to the line that the breakpoint is set on. The next button links the Debug view with the Breakpoint view so that the cur-rent breakpoint (where the program is stopped) will be selected in the Breakpoint view. The next button allows you to specify breakpoints on Java exceptions. For example, your program might be throwing a NullPointer-Exception and you want to track it down. Rather than go to the line of code and set a breakpoint, you can spec-ify that the debugger should always stop on a NullPointerException. Figure 3.22 shows the Exception breakpoint window.

Figure 3.22 Selecting an Exception to break on

Page 101: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 90

Notice that you can specify that the debugger should break on either caught, uncaught or both kinds of excep-tions. This is useful especially when trying to track down an exception that is caught and rethrown as another exception. The pull-down (the last button before the X that closes the view) allows you to specify that the fully qualified names of classes should be used in the display.

Finally the breakpoints have a context menu that allows the properties of the breakpoint to be set. Invoke the con-text menu on the breakpoint and select Properties… (each of the items in the menu are supported on the proper-ties page). You should see a window that looks a lot like Figure 3.23.

Figure 3.23 Breakpoint Properties

Page 102: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 91

In addition to laying out information about the breakpoint, this window allows you to specify other not-so-obvi-ous properties about the breakpoint. You can specify the Hit Count which basically means that the breakpoint will not stop until it is hit that number of times. On that count either the thread or VM will suspend. Afterwards the breakpoint will be disabled until the hit count is reset. This feature is extremely useful for those times when your program is going through a long loop and you don’t want to stop on each iteration, but you do want to stop from time to time. For example, say you have a loop that is going to execute one hundred times and you don’t want to step through it each time, but you want to stop every 10 times through the loop to check on its progress. You could set the hit count to 10, and after the program steps over the breakpoint nine times, it would suspend on the tenth, allowing you to examine the state of the program. When you are done checking looking at the state, reset the hit count to 10 and hit the continue button. The program will stop the next time the line is hit the tenth time.

The next section of the window allows you to set conditions for this breakpoint. You can specify just about any Java code. If the expression evaluates to a Boolean, you can use the Susspend when condition is ‘true’ button. Otherwise use the Suspend when value of condition changes button. You will want to use conditional breakpoints in much the same way you would use the hit count. With a condition in place, the breakpoint will not suspend the program unless the condition changes or evaluates to true.

Finally, you can specify that either the thread or the entire JVM is suspended when the breakpoint is activated. When running in a multi-threaded environment, it is often useful to suspend the whole VM so that you can be sure nothing is changing out from under you while you attempt to find the bug you are looking for.

Breakpoints are the cornerstone of debugging; without them we are reduced to using logging statements. This section has given you a look into all the powerful features that the Java debugger has to offer for breakpoint. With a firm grasp of these options, you will have great control of how your program stops so that you can find bugs quickly. Next up is the Expressions view.

Expressions

The Expressions view keeps track of Java expressions that are evaluated while the application is running and the results are displayed. This is another great feature that can be used while running through a long loop. If all that is needed during the execution of this long loop is to examine a variable, then an expression to watch that vari-able can be created in the Expressions view. As an example, add the following loop to the Hel-loWorld.main(String[]) method.

Map map = new HashMap();for (int i = 0; i < 100; i++) { String key = String.valueOf(i); map.put(key, “value-”+i);}

Page 103: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 92

Make sure your launch configuration is set to stop in main (on the launch configuration editor’s main page) and then activate the debugger. With the debugger activated, double-click on the map variable and select Watch from the context menu. This will add map to the Expressions view and start watching the expression. In other words, it will recalculate the value of the expression at every breakpoint or step. Add a breakpoint on the last line of the main method, then have the program continue. When the next breakpoint is hit (at the last line), the map expres-sion shows all 100 members in the details pane of the Expression view. Figure 3.24 shows the Expressions view with the map expression selected.

Figure 3.24 Expression Evaluation

Notice that this particular expression is not any different than what we saw in the Variables view. (You can switch back and see for yourself.) However, in the Expression view, you have a lot more freedom to examine values than you do in the Variables view. For example, invoke the context menu on the map expression and choose Edit Watch Expression. In the dialogue window that pops up, change the expression to be map.get(“14”) and hit OK. Notice that the details change form the whole map to just the single entry found at “14”. The buttons along the top of the view do the same things that their similar looking counterparts do in the other views. The first button toggles showing type names, the second toggles showing the ‘logical’ view of the evaluated expression and the third collapses all expanded elements. The blue boxes remove the selected expression or all expressions respec-tively. Also, notice that the pull-down is the same as the pull-down in the Variables view. Next up is the Display view.

Page 104: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Debugging Java Applications 93

Display

The Display view gives us a spot to type in snippets of Java code and evaluate them to either examine the values they produce or effect the state of the program. Figure 3.25 shows the Display view with a fragment of code typed in and highlighted.

Figure 3.25 Display View

Select the magnifying glass button to examine the result of evaluating the get method call. (Don’t forget to high-light the text before evaluating it.) You will be taken back to the Expression view, and the expression typed into the Display view will be added to the watched expressions. Selecting the next button will evaluate the expression and display the result of the expression. The next button evaluates the expression but does not display any result. This button is a way for you to affect the state of your running application and can be very handy in finding and fixing bugs. I use this feature to force the program to go down a particular branch so that I can get to the code where I think the bug is. Or I change the state of the program (as if the code were different) and examine the result to see if the bug is fixed. As an example of what you can do, type in map.put(“14”, “new value”) into the Display view and select the Evaluate the Selected Text button (the third button from the left). When it com-pletes select the map.get(“14”) line again and evaluate it. The result is different (it should be ‘new value’ instead of ‘value-14’). With this feature, you can dramatically cut down debugging time by forcing the program down particular branches or checking the result of changes to the code while debugging.

Page 105: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Summary 94

Summary

We have examined the Debug perspective in detail here and seen the views and tools that are at our disposal while debugging Java applications with the JDT. There are many powerful and easy to use tools available that make finding and fixing bugs much easier than with the simple debugger that ships with the JDK. In the next chapter, we will examine the integration of Ant into the JDT.

Page 106: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Chapter 4

Eclipse 3 Live

Ant Integration

Ant is now the de-facto standard build tool for all things Java. You can hardly find an open-source Java project that is not built using Ant, and most enterprises are now using Ant to deploy their applications. As you will see in this chapter, the Ant integration in Eclipse is outstanding.

Building an application is available in Eclipse even without Ant, so why should you worry about another build system? Well for starters, Eclipse is a great IDE, but you probably don’t want to fire up Eclipse to build and deploy a large application. Most systems need to be built and tested in an automated way via something like a cron job on a regular basis. It is unlikely that you’ll want to use Eclipse to build the application from a shell script. Given that you are not going to run Eclipse from a shell script, Ant is the way to go.

Page 107: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Overview 96

Ant Overview

In this chapter, it is largely assumed that you are familiar with Ant and how it works. If you are not familiar with Ant, then read this section and if you want more info, you can start at the Ant web site. You can also pick up a copy of the book ‘Java Development with Ant’ by Erik Hatcher and Steve Loughran, which is the standard on the subject. However, since some might be new to Ant, I’ll give a fifty-thousand foot view of what Ant is and how you can use it to build your projects.

With Ant, you provide an XML declaration of what your build process is to look like. What that means is the XML file (build.xml typically) declares or specifies what needs to be done and in what order the steps should be performed to build your application. The XML typically consists of four major parts: project, properties, target, and tasks. The project is the root element, and it contains many properties and targets. A property is basically a variable that can be used to customize the way the build happens (the location of the source code for example). A target is an element that describes some part of the build that needs to be done like ‘compile’ or ‘deploy’, and it accomplishes its objective via a list of tasks. Ant tasks are the actual workers in the build process; they’re the steps needed to produce the desired output.

Take a look through this quick and simple example that serves to illustrate the way Ant works by turning the HelloWorld project into an executable JAR file. In order to create an executable JAR file, you need to first compile the source code, and then put the .class files into a JAR file along with a manifest entry that specifies the ‘main class’ to run. Figure 4.1 shows a graphical representation of the targets needed to accomplish this.

Figure 4.1: A Simple Ant Build File

compile

build-jar

clean

run

Page 108: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Overview 97

In this diagram, you can see the targets and their dependencies. To run the application, build-jar must be invoked and for build-jar to work, compile must be invoked. The clean target will get rid of the generated artifacts (i.e., the .class files and the .jar file). Listing 4.1 has the source code for this build.xml file.

<project name="Eclipse Live Examples" default="run"> <property name="build.dir" value="build"/> <target name="compile" description="Compiles the code"> <mkdir dir="${build.dir}/classes"/> <javac srcdir="src" destdir="${build.dir}/classes"/> </target> <target name="build-jar" depends="compile" description="Makes an executable jar file"> <jar basedir="${build.dir}/classes" destfile="${build.dir}/HelloWorld.jar"> <manifest> <attribute name="Main-Class" value="com.sb.eclipselive.HelloWorld"/> </manifest> </jar> </target> <target name="run" depends="build-jar" description="Runs the jar file"> <java jar="${build.dir}/HelloWorld.jar" fork="true"/> </target> <target name="clean" description="deletes all the Ant generated stuff"> <delete dir="${build.dir}"/> </target></project>

Listing 4.1: build.xml

This has been a fifty-thousand foot fly by of Ant. If it doesn’t make sense to you, don’t worry—Ant is easy to pick up once you get over the initial hump. Eclipse makes editing Ant files almost as easy as editing Java code, so you will be up and running in no time. Start with the manual on the Ant site and then buy a copy of Erik & Steve’s book if you still need more info. Read on for more information about Eclipse’s support for Ant.

Page 109: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant File Editor 98

Ant File Editor

The Ant file editor is part of the JDT and is a context aware (i.e., Ant DTD/Schema), specialized XML editor. The code listed in Figure 4.1 is shown here in Figure 4.2 in the editor.

Figure 4.2: Editing the build.xml file

Page 110: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant File Editor 99

The editor provides several features that make editing build files easier. The first one is syntax coloring or highliting. As you can see in Figure 4.2, the editor colorizes the code. You can customize the colors at Window->Preferences->Ant->Editor. Preferences are discussed later in this chapter.

Another cool feature about the Ant editor is its ability to do Code Assist on the tags as you type. This feature closely mimics the Code Assist feature in the Java editor. By default ‘<’, ‘$’, and ‘{‘ trigger the assist feature (customizable in the preferences). To see how it works, create a new build.xml file (context menu with project selected New->File... type in build.xml) and type in <j. Notice that several options come up. After you choose one, Eclipse will place all the required attributes as well as the closing piece of the XML. This feature is new in 3.0 and is a huge time saver compared to having to go back to the manual repeatedly.

Also new in 3.0 is an Ant file formatter tool that understands the way that the XML should be formatted. The formatter is used in the same way that the Java formatter is used (context menu->Format), and it basically will put a four-space indent between nested XML elements. In addition to being able to auto-format, you can also shift the code left or right with the same menu (context menu->Shift Right, Shift Left). As expected, these commands move the selected code four spaces to the right or left. Next up is the Ant Outline.

Page 111: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Outline 100

Ant Outline

The Ant Outline arranges the Ant file in a hierarchal way that makes visualization of the properties and tasks simpler than looking at the raw file. The outline is very similar to the Java Outline that we saw in Chapter 2. If you have the Outline View up and you open a build file, then the content of the outline changes from the Java outline to the Ant Outline. You can see the outline in action in Figure 4.3.

Figure 4.3: Ant Outline

The Ant Outline is synchronized with the Ant editor. Notice in Figure 4.3 that the javac task is selected in the outline and is also highlighted in the editor. The reverse selection does not synchronize the two views, and if you change the selection in the editor, the selection in the outline is not updated. I suspect that there will be a Synchronize button (like in the one in the Java outline) before 3.0 final ships.

Page 112: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Outline 101

The outline shows as top-level elements the properties, types, and tasks found in your Ant build file. In this example, there is a property called build.dir and four targets (compile, build-jar, run, and clean). Notice that the targets can be expanded to see the tasks that make them up. In this small example, the outline is not as critical since you can see the whole build file in one window. However, on big projects it is very useful to be able to visualize the Ant file in this way. The targets can show up in one of the three icons. The all green icon shown in Figure 4.3 with the build-jar target means that the target is public. The blue icon shown with the run target in Figure 4.3 denotes that run is the default target. Finally, in Figure 4.4 the clean target has been reworked so that it depends on two private targets. The two private targets (clean-jar and clean-classes) have green and white target icons next to them.

Figure 4.4: Public, Private, and Default Target Icons

Public vs. Private Ant Targets: A target is made public by including a description attribute on the target element. In a large build system, it is typical to have some shared piece of build script (something like a function) that is reused via either antcall or depends. It’s also typical to make these targets private (i.e. no description) so that they are not called out of context. Only public targets show up when you use the projecthelp flag.

Page 113: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Outline 102

The first button on the top of the outline view allows you to sort the top-level elements (properties, targets, and types) alphabetically. This feature is not as sophisticated as the one in the Java outline in that it won’t let you toggle the sort between ascending, descending, and off. This is just another sign of the fact that much of the Ant Outline view is new in 3.0. As the final approaches, I expect that the sophistication of the Java Outline will make its way into the Ant Outline as well. The next two buttons allow you to hide the private targets and properties respectively.

The last feature of the Ant Outline that I want to cover is the ability to run the selected target from the context menu. In the outline, select the run target and invoke the context menu; then, choose Run. Figure 4.5 shows the run target selected with the context menu up.

Figure 4.5: Running Ant Targets from the Outline

This feature is also new in version 3.0-M7. The functionality behind the Run Ant… menu item will be discussed later in this chapter. Next up is the Ant View.

Page 114: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant View 103

Ant View

The Ant View is focused on letting you keep all your Ant files organized and accessible so that you can run any part of your build quickly and easily. Figure 4.6 shows the Ant View with a single build.xml file loaded. (You will see how to load the file in just a couple of paragraphs.)

Figure 4.6: Ant View

Unlike the Outline View that was discussed in the previous section, this view is focused on running the build instead of editing the build file. One thing that is missing that you might expect is a way to synchronize the selection in this view with the editor. So, typically I use the Ant Outline for editing and testing out the build and the Ant View for running the build. Now, explore the features available in this view.

Page 115: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant View 104

The first two buttons on the top of the view allow you to add build files to the view. The first button brings up a browser that lets you choose the build files manually. The browser is shown in Figure 4.7.

Figure 4.7: Build File Selection Browser

The browser will only allow you to choose files with .xml as an extension. You also must select a directory before you can see any of its contents. In Figure 4.7, you can see that the root of the project is selected, and the single build.xml file is visible on the right hand side. Select the build.xml file and then press the OK button. This will add the build.xml file to the Ant View.

Page 116: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant View 105

You can also add build files via searching. The second button allows you to do a search for build files using any critieria you specify. The search screen is shown in Figure 4.8.

Figure 4.8: Build File Search Screen

Notice that in addition to searching the workspace you can search any of your working sets. For more info on working sets, see Chapter 2.

The next button allows you to hide any private targets. When this button is toggled on, none of the private targets are shown in the view. This can be very helpful for large build files that are difficult to visualize when all the detail is present.

The next button allows you to run the selected target. You can also run a target from the context menu, but I find it very convenient to use the button in the title bar during debugging a build file. Finally, the last two buttons allow you to remove build files from the list of files in the view. The first button removes only the selected file, and the second removes all the files.

The context menu in the Ant View allows you to perform all the commands available in the title bar of the view, as well as run a target. In addition, you can open the build file in the Ant editor if it’s not already open. Double-clicking on a target causes it to run as if you chose the Run button. You can also select the Run Ant… menu item to get at Ant launch configurations. You will take a closer look at this functionality in the next section.

Page 117: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 106

Ant Build

Eclipse provides a way to run Ant as an external tool. The ability to run Ant externally (in another JVM instead of inside the JVM that is running Eclipse) is very helpful especially when running big builds. In particular, some of the third party Ant tasks that can be integrated in the build can cause Eclipse to run out of memory, which is always painful to recover from. When the build is run in a separate JVM, it cannot cause Eclipse to run out of memory because the JVM is started and stopped as a separate resource. You access the external tools via the External Tools menu. Figure 4.9 shows the menu item activated.

Figure 4.9: Run External Tools

Page 118: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 107

When the External Tools... dialogue is first displayed, there’s one configuration for the build that you have already run (assuming that you have run the Ant build already). Once you activate the dialogue, select the Ant target as shown in Figure 4.10.

Figure 4.10: External Tools Dialogue

This screen allows you to specify the perspective to switch to when an Ant launch configuration is run. This switch will take place regardless of which Ant configuration is run. I find that I don’t want to switch to another perspective most of the time so I usually do not specify a perspective to switch to, but instead I switch manually when needed.Once an Ant configuration is selected, you are able to fully customize the way Ant runs in your environment. The next several sections will lay out the options various pages of the dialogue and how they are used to customize the way Ant runs.

Page 119: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 108

Main

The Main page allows you to specify which build file to run as well as the directory the process should be run from. This directory becomes the basedir of the build file, so make sure the directory you choose matches up with the expectation of the build file. The Main page is shown in Figure 4.11.

Figure 4.11: Main Run Ant Page

Page 120: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 109

You can also specify any arguments that your Ant build needs to function properly in the arguments text field. The arguments can be any one of the standard Ant arguments (like –debug or –emacs for example). The really interesting feature here is that Eclipse allows you to also use variables that will be substituted at run time by Eclipse before invoking Ant.

Table 4.1 Ant Argument Variables

Variable Descriptionbuild_type The type of build being performed. This can have one of four values: ‘full’,

‘incremental’, ‘auto’, or ‘none’. A full build is a complete rebuild of the whole project; an incremental build will only build what has changed since the last build; an auto build is either incremental or full; and ‘none’ denotes that the user instantiate the Ant build apart from the Eclipse build system.

container_loc The absolute path to the folder where the build file resides. This location is the platform-specific absolute path. In other words, it will start with the drive letter on Windows and ‘/’ on UNIX boxes. This is a way you can get at an absolute path, like the location attribute on the property task, from Ant. This variable and the two that follow become more useful in the case of using an Ant build as part of your project’s builders. When the variables are used in an automatic build, the resources are specified by Eclipse and passed into these variables. You will learn more about this in the next section.

container_name This variable expands to the name of the folder that the specified resource lives in. This variable will expand through ‘links’ in the project. So, for example, if you specify an external resource as one of the folders in your project, this variable would expand to the actual path and not the project relative path. You must specify an argument after a ‘:’ to select a resource for the variable to work properly. Example: ${container_name:/HelloWorld/src/com/sb/eclipselive/HelloWorld.java} will evaluate to ‘eclipselive’.

container_path This variable expands to the workspace relative path of the specified resource. Just as with the previous variable, following the variable name with ‘:’ specifies the resource.

env_var This variable expands to the name of the environment variable specified. For example, you could get to the installed location of Tomcat with ${env_var:CATALINA_HOME} if you had specified that variable in your environment.

file_prompt This variable will be replaced by the user’s choice from a file browser. In other words, this variable will cause Eclipse to pop up a file chooser. The users specified file from the chooser will replace the variable.

folder_prompt This variable will be replaced by a chosen folder in the same way that file_prompt works.

project_loc This variable provides the absolute file path to the current project.resource_loc This variable is replaced by the absolute path of the resource that is selected in the

workspace. In the case of multiple selection, the ‘last’ selected resource becomes the value of this variable. The specific resource will vary depending on the active viewer. If the Java editor is active, then only one resource can be ‘selected’ at a time (the resource in the active editor). In the case of the Package View, however, multiple resources can be selected. The file that is alphabetically last is the resource when more than one resource is selected in the Package View.

Page 121: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 110

You can use these variables in your build script as properties and then specify the values via the –D option in the Arguments: field. For example, if you’d like to run certain targets if the build_type is ‘full’, you could specify a property with –Dbuild.type=${build_type}. Then, your conditional targets would contain if="build.type" attributes. Clicking the Attributes button will show the list of variables with a short reminder of what the variable is used for so that you don’t have to memorize Table 4.1.

resource_name This variable evaluates to the selected resource. The same description of ‘last’ applies to this variable as the resource_loc variable.

resource_path This variable evaluates to the workspace relative path of the selected resource. The same description of ‘last’ applies to this variable as the resource_loc variable.

selected_text This variable evaluates to the currently selected text. This variable is intended more for use in other external tools instead of Ant.

string_prompt This variable will cause the user to be prompted for an input value. This is similar to Ant’s input task.

workspace_loc This variable evaluates to the absolute path of the workspace.

Table 4.1 Ant Argument Variables

Variable Description

Page 122: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 111

Refresh

The Refresh page allows you to specify if and/or when the workspace should be refreshed after the build is run. This feature is particularly useful when an Ant target invokes some sort of code generation. Figure 4.12 shows the Refresh page selected.

Figure 4.12: Refresh Page

The Refresh resources upon completion check box allows you to turn on or off the refresh. If it’s off, the bottom part of the page is disabled; when on, you can specify one of five different groups of resources to refresh. The entire workspace will cause every project in the workspace to be refreshed. A refresh can take a long time to complete, so choose wisely here. If you have several large projects in the same workspace, choosing to refresh the whole workspace can take a very long time.

Page 123: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 112

Next, you can choose to refresh only the selected resource. The selection could be a folder, or a project, or anything else in the project. I typically do not like this option because you have to remember what to select before running your build, unless the build acts specifically on the selected resource. This can still be an expensive operation if you happen to have a large project selected when you invoke the build.

The next option, The project containing the selected resource, is what I typically use because the project is the thing I most frequently need to update after running a build, and I don’t want to have to worry about what I’ve got selected. Since I’m typically working with the content of one project at a time, this option usually works out well. The next option will refresh only the folder that the currently selected resource is in. Again, this choice causes me to think too much about what I’ve got selected before I can run the build, so I don’t typically use it.

The next option Specific resources allows you to build a working set (see Chapter 2 for more on working sets) to refresh after the build. This option is particually useful if your build performs code genration into a particular directory. You can merely put the folder that is generated to in the refresh working set, and that will be all that is refreshed. This option is the most focused and the least likely to cause performance problems. In the next section, you will learn about the Targets page.

Page 124: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 113

Targets

The Targets page allows you to specify which target to use when the Ant build is run. The page is shown in Figure 4.13 below.

Figure 4.13: Targets Page

You can check one or more targets to execute at a time. If you do select more than one, then you can specify the order with the Order… button at the bottom of the page. If you select the Hide internal targets check box, you will only see the targets that have a description (the same way that this works in the Ant View). The Sort targets check box causes the visible targets to be sorted alphabetically; otherwise, they appear in the same order as in the build.xml file. You would want to select multiple targets in the case where you have one set of targets that need to be executed at one time and another set for other times. For example, in the case of doing a local deploy to a J2EE server verses a remote deployment, both cases need to have the code compiled, but one deployment is a simple copy and the other is an ftp of the files.

Page 125: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 114

Classpath

The classpath allows you to specify the classpath that Ant executes with. As expected, the Ant classpath will determine which custom tasks are available. For example, the default Ant download comes with several JAR files with optional Ant task definitions. Without these additional JAR files, you would not be able to use the tasks defined in these JAR files. The bottom line is that if you are using custom Ant tasks that you have written, then you must add the JAR file containing the classes to the Ant runtime classpath here. Figure 4.14 shows the page.

Figure 4.14: Classpath Page

The page allows you to add JAR files, folders containing classes, or Zip files. The other two ‘folders’ of classpath entries are defined in the Ant preferences. The classpath entries you add here will be apart from the classpaths entries specified in the preferences. In other words, the preferences set classpath entries across all Ant launch configurations (kind of like a default), but each launch configuration can specialize the classpath according to its own needs. This is useful for putting your own tasks into your build files.

Page 126: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 115

Properties

The Properties page allows a set of properties to be set for the Ant build. You can either specify individual properties or you can use property files. Either way, the properties specified on this page are passed into Ant at runtime. Figure 4.15 shows the Properties page with the Use global properties as specified in the Ant runtime preferences check box checked. When this check box is checked, you can only view the properties—not update them. If you’d like to specify additional properties, uncheck this option.

Figure 4.15: Properties Page

Page 127: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 116

The initial list of properties is added by Eclipse and allows you to know the location of the Eclipse instance that is running. You can also use the eclipse.running property to determine if Eclipse is running or not. In some build files, these properties can be helpful, especially if you are trying to write a build file that works with Eclipse-specific tasks and runs both in Eclipse as well as out of Eclipse. I usually shy away from making my build files Eclipse aware because most of the time the build is run on a client machine (Windows or Mac) and on a deployment/build machine (Unix-based server), and I don’t want to mess with installing Eclipse on a box without a monitor. You can also specify property files with the list on the bottom of this page. Here you can add property files to be imported by Ant when it runs. Make sure to keep in mind that if the launch specification is set to use the project wide defaults, then you won’t be able to change anything on this page. Next up, learn how you can specify the JVM to use to run Ant.

Page 128: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 117

JRE

This page allows you to specify which JVM to use to run Ant. One of the great things about Eclipse is that it can run in one JVM, and a project can be compiled and executed in another JVM. Your projects could still require a J2SE 1.3 JVM, but Eclipse requires J2SE 1.4.1. With this page, you can easily specify that Ant should run in the J2SE 1.3 JVM. Figure 4.16 shows the page displayed.

Figure 4.16: JRE Page

The first set of radio buttons allow you to choose the default of running in the same JRE as Eclipse. For many projects this will be fine. You can also choose an alternate by either choosing from the pulldown or selecting the Installed JRE... button to add a JRE. The Now button tells Eclipse to update the runtime classpath of Ant right away so that you can see any if there are any conflicts. VM arguments allow you to specify any needed arguments. A good example of needing to specify an argument is setting the max heap size when using XDoclet (a code generation tool). It is not unusual for XDoclet to use a lot of memory, and it is often required to set the max heap size for Ant in order for XDoclet to be able to run. Finally, you can specify the working directory to have Ant start in (this working directory will be the default ${basedir}). You can choose to use the default

Page 129: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 118

setup in the Ant preferences (which is the root directory of the project in the workspace), or you can specify a different one if needed. If you choose to specify a directory, then you can choose a local directory or a directory in the workspace. The local directory will be an absolute path and the workspace directory will be a relative path. Next,take a look at the common configuration options for external tool launch configurations.

Page 130: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 119

Common

The Common page allows you to set an external tool launch configuration as visible or not visible in the local or shared directories. In other words, am I the only one who uses this launch configuration or do I need to share it? If it’s shared, then specify a networked directory that everyone on the project has access to. Figure 4.17 shows the Common page.

Figure 4.17: Common Page

You can also specify that this launch configuration should show up in the External Tools menu. This behavior works just like the Run or Debug menus. Finally, you can choose to launch Ant in the background, which is recommended so that the UI of Eclipse is as responsive as possible. However, it is not recommended that you start editing your Java code after you kickoff a build, since there is a slim chance that you could end up in an inconsistent state and have to run the build all over again.

Page 131: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant Build 120

In this section, you have learned how to run Ant inside Eclipse and all the various options you have to customize the way it runs. In the next section, you will see how to integrate Ant even more tightly with Eclipse and the build process.

Page 132: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant as a Project Builder 121

Ant as a Project Builder

In addition to being able to kickoff an Ant build as an external tool like we saw in the previous section, you can also make Ant part of the incremental build process that Eclipse does. In order to add an Ant build as one of the project builders, choose the project’s properties (context menu->Properties, with the project selected in the Package Viewer) and then choose Builders from the list of options. Figure 4.18 shows the Builders page of the Options Panel.

Figure 4.18: Project Builders

Page 133: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant as a Project Builder 122

Currently, the project only has the default builder in use (the Java Builder). The Java Builder can’t be removed or edited—it’s the Eclipse incremental compiler. You can, however, add an Ant launch configuration to the list of builders. Select the New… button to bring up a chooser. Double-click the Ant Build option from the panel that pops up. From there, you should see a familiar sight. Figure 4.19 shows the Ant builder configuration dialogue.

Figure 4.19: Ant Builder Configuarion Dialoge

Page 134: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant as a Project Builder 123

This set of pages is very similar to the External Tool configuration pages that you saw in the last section. However, since the build will be run internally, to Eclipse there is no JRE page, and there is no Common page since this launch configuration is only for use by the Eclipse incremental builder. The Main, Refresh, Targets, and Properties pages are the same as the ones you have already seen. The last page, Build Options, allows you to specify in what way this builder participates in the Eclipse incremental builder. Figure 4.20 shows the Build Options page selected.

Figure 4.20: Build Options Page

Page 135: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Ant as a Project Builder 124

You can specify if the Ant build should run in the background or not. It is more typical to have this kind of build run in the background since the rest of the build will run in the background. The next set of check boxes let you specify which kind of build you want the Ant build to participate in. The values here correspond to the build_type variable discussed in Table 4.1. In addition to specifying which kind of build to participate in, you can also restrict the build to be invoked for certain resources (typically Java files).

This feature comes in very handy when using a code generation framework like XDoclet. Any reasonable discussion of XDoclet is beyond the scope of this book, but if you already use XDoclet, you should consider setting up an Ant builder that specifies only the Java files that use XDoclet. Specify that the builder should be used for Full, Incremental, and Auto builds, and specify the working set for the builder to watch. Then, XDoclet will update your generated files each time Eclipse performs a build. This comes in especially handy when you are using XDoclet to generate the remote or local interfaces of EJBs since your code likely depends on these interfaces. It’s very convenient to have them updated continually by Eclipse.

Page 136: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Summary 125

Summary

In this chapter, you have looked in detail at the way Ant integrates into Eclipse. You should have come away with an understanding of how to use Ant as both an integrated part of the build cycle as well as using Ant as an external tool. The next chapter will go into the details of how to work with a team of people in Eclipse with CVS.

Page 137: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Chapter 5

Eclipse 3 Live

Working in a Team with Eclipse

Eclipse ships with support for teams in the form of CVS, the de facto standard for open source projects. The support that is available is almost as extensive as the command line options that are available in CVS itself. In this chapter, you will learn how to use Eclipse in a team environment.

If you are already using CVS, then the support Eclipse has for CVS will be a pleasant surprise. If you are not using a version control system, you are not alone. A vast number of developers never use an ‘organized’ version control system. Their days consist of making tar balls of their code, and naming them with date stamps, or copying the ‘latest’ stuff out to a server, hoping beyond hope that no one else copies over the ‘latest’ stuff. With the CVS support built into Eclipse, even the uninitiated will be able to safely manage their projects’ artifacts.

Page 138: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Overview 127

CVS Overview

This section is for those who are new to CVS. CVS stands for Concurrent Version Control System, a means to keep track of every version of a set of files. The versions and files are kept in a repository that is able to respond to requests like, “give me version 1.3 of file XXX.java.” Figure 5.1 shows a conceptual diagram of how a file is versioned through time.

Figure 5.1: Versoning of MyClass.java

For the sake of this explanation, assume that you are working with MyClass.java. When the file is new, it is ‘added’ to the repository, which basically means that the file becomes known to the repository, and from that point forward, the repository will keep track of the file and all its versions. After adding the file, some edits are made, and then those edits are ‘checked in’ to the repository, which instructs the repository to calculate the difference, store the difference, and then make the checked in file the current copy of the file. After you have checked in your changes, the file’s version becomes 1.1, and any other user that has access to the repository can ‘update’ their local copy of the files to get version 1.1 of MyClass.java. Over time, the file continues to be edited and versions are checked in. Since the repository has the file and all the old versions (stored in the form of diffs), you are able to ask it for MyClass.java 1.2, even when the most recent version is 1.19.

MyClass.java

Versions of MyClass.java over time

1.1 1.2 1.3 1.4 1.5

Repository

edit edit edit edit edit edit

Check In

MyClass.java 1.1

MyClass.java 1.2

MyClass.java 1.3

...

Add File

Page 139: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Overview 128

In addition to having single files versioned, everything in the repository can be marked as part of one version with a feature called a ‘tag’. A tag is basically a name for a single version of the project as a whole instead of any particular file. For example, MyClass.java and build.xml could be two files in one project, but MyClass.java’s latest version is 1.3 and build.xml’s latest version is 1.2. These two different versions can be tagged as part of ‘RELEASE_1.0’. Then, at any point later in time, you could ask the repository for all the files tagged RELEASE_1.0, and you’d be back to MyClass.java 1.3 and build.xml 1.2. Figure 5.2 shows the files progressing past the tag. At any point later in development, you can get back to the tagged versions.

Figure 5.2: Tagged Versions of MyClass.java and build.xml

Add File

MyClass.java

1.1 1.2 1.3 1.4 1.5

Repository

edit edit edit edit edit edit

Check In

MyClass.java 1.1

MyClass.java 1.2

MyClass.java 1.3

...

Add File

build.xml

1.1 1.2 1.3

edit edit edit edit

RELEASE_1.0

Page 140: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Overview 129

This has been the whirlwind tour of CVS and some of what it can do. The important thing to remember is that CVS will keep all the old versions of your files for you. No longer do you have to leave very old unused code in place, just in case you want to revert. Instead, add a tag to CVS and then delete that unwanted code with abandon. If you mess up your project, you can always revert.

To get CVS set up on your computer, go to CVS’s home on the net at CVS Home to see all about the various packages you can download and install to get started with CVS. If you are using a Mac with OS X and you installed the developer tools, you already have CVS installed, so you don’t need to download anything—just set up the server according to the directions found at CVS Home.

Page 141: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Quick Start 130

CVS Quick Start

Now that you have done a very quick overview of what CVS is and why you should use it, study this practical ‘quick start’ of how to get going with CVS on one of your projects. There are two tasks you will quick start: the first is getting one of your new projects put into CVS, and the second is to get an existing project into Eclipse from a CVS repository.

Page 142: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Quick Start 131

Putting Your Project into a CVS Repository

To begin, you will put your simple ‘HelloWorld’ project into a CVS repository. In order to make this possible, you need a CVS repository that you have permissions to add new projects to. The easiest way to do that is to set up CVS on your local computer. I will assume that you have a CVS server set up on your machine.

To get started, select the project and invoke the context menu. Then, select Team->Share Project. Figure 5.3 shows the selected menu.

Figure 5.3: Invoking the Share Functionality

Page 143: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Quick Start 132

Once you’ve clicked Share Project, you are presented with a window that asks you to specify a CVS repository location. If you have set up a CVS repository location, you will be presented wth a choice between using one of the existing locations or creating a new one. Since I’m assuming that you’ve just gotten started, let’s go through as if there was not an existing repository location. Figure 5.4 shows the new repsoitory location dialog box.

Figure 5.4: Invoking the Share Functionality

This dialogue allows you to set up the information needed so that Eclipse can connect to the CVS repository. Eclipse will not connect to the server unless you ask it to, so you don’t have to worry about any performance problems from supplying this information.

Page 144: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Quick Start 133

The dialog box is broken into three parts: the location of the repository, authentication information, and finally the information needed to connect to the repository. The location information tells Eclipse which server to contact and the directory to use. If you have CVS set up on the computer that Eclipse is running on, then you can use localhost as the server name; otherwise, you need to know the name of the server. The repository path is the location on the remote server that the repository lives in. If you have access to the server CVS is running on, then you can look in the /etc/inetd.conf file or the /etc/xinetd.d/cvspserver file for the location of the repository. If you don’t know the location of the repository, you will have to find out from whoever maintains the repository.

The next step is to set up the authentication part of the dialog box. User and Password are exactly what you expect them to be—the user’s username and password. Recall that CVS does not have to use system authorization (i.e., the usernames and passwords on the server machine), so you should check the username and password you should use. If you have set up CVS on your local machine, then you can set the usernames and passwords in the $CVSROOT/CVSROOT/passwd file and use one of them in this dialog box.

Page 145: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Quick Start 134

Finally, you need to tell Eclipse how to connect to the server. Typically, the pserver protocol is used for anonymous or read-only access, and one of the secure methods is used if you will be committing to the repository. To keep this simple and not spend too much time going into the details of how CVS works, stick with the simple pserver method in this discussion. There are numerous resources on how to set up CVS to work with one of the secure methods at www.cvshome.com or from a search at google.com. The port should almost always be the default port. The only time you’d want to change this is if the server is listening on a different port. If you have set up your own CVS server, you almost certainly have the default port configured on the server—so choose pserver and the default port in that group. Figure 5.5 shows my set up completed.

Figure 5.5: Completed CVS Connection Dialog Box

Page 146: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Quick Start 135

After hitting the Next button, the Module Name page prompts you to name the module before you add it to CVS. This will become the name that others check out the project under. It is fine to use the name of the project as the name of the module for CVS, so you can leave the default selected and just click Finish. The last page of the wizard basically just says that Eclipse has what it needs to share the project, so you can skip it every time.

Once these steps are complete, your project is ready to be put into the repository. Before it is committed though, Eclipse has you verify what you are about to add in a view called Synchronize. (Actually, it’s a whole Perspective, but this will be covered in more depth later.) In this view, you can choose what to do with your project resources. Quickly, there are a couple of things I want to point out before you continue. In the Synchronize view, you will see something like what is shown in Figure 5.6.

Figure 5.6: Syncronize View on Hello World

Specifically, I want you to notice the build directory. It is the first entry under the project in Figure 5.6. This directory is a result of running the Ant build (from back in Chapter 4). You do not want to version the results of a build, so you need to get rid of this directory from what is added to the repository. Invoke the context menu with the build directory selected, and choose ‘Add to .cvsignore’. A panel will pop up asking you what to add to the .cvsignore file—leave the default ‘Resource(s) by name’ selected. In the text field at the bottom should be the name of the directory (i.e., build). Choose OK, and the build directory should go away. Do the same for the html directory, since that was result of running Java2Html on the source code, and you don’t want to version control those files either. After you are done adding html and build to the .cvsignore file, your Synchronize view should have the src directory, the build.xml file, and the three hidden files, .classpath, .cvsignore, and .project. These are the files that will end up under source code control in CVS.

Page 147: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Quick Start 136

You are now finally ready to add the files to the repository. Invoke the context menu with the project selected, and choose ‘Commit’. A dialog box will pop up asking you if you want to continue and add the items to the repository. Click the Yes button, and enter a comment into the next dialog box that pops up. (My favorite comment for adding files to CVS is ‘initial commit’, but you can type anything you’d like). When Eclipse is finished adding all the files to the repository, the Synchronize view should be empty. You can now close the Team Synchronize perspective for now and switch back to the Java perspective. (You don’t have to close the Team Synchronize perspective.)

Congratulations, your project is now under source code control in CVS. If you have switched back to the Java perspective, you might notice that the Package Explorer looks a bit different. Each file has annotations attached to it that tell you something about the file’s state with respect to CVS. Figure 5.7 shows the Package Explorer with the src directory expanded.

Figure 5.7: CVS Annotations on Files

Notice the little yellow disk icons on the bottom-right side of each file that’s under CVS control. Another very useful feature of the CVS annotations is the version number and the keyword expansion state (ASCII –kkv, which is the default for text files). Keywords are a great feature that allow CVS to put pertinent information about a file inside the file so that you can see that information without having to ask the server. For example, the $Revision: $ keyword in CVS expands out to the revision number in the file. So, you could put this keyword into the Class’s JavaDoc comment block, and it would turn into $Revision: 1.2$ on the next checkout, and the version number of the file would be in the file for you to see without asking CVS. The CVS preferences have a lot of options to manage the way that keyword expansion works, as well as many other CVS preferences.

Page 148: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Quick Start 137

Next, try editing one of the files.Notice that after you save your changes, the annotations on that file change to let you know what files have changes. Figure 5.8 shows the Package Explorer again with the HelloWorld.java file edited.

Figure 5.8: Edited CVS Annotation in Package Explorer

Notice that the HelloWorld.java file has a ‘>’ character to the left of its name. This annotation is to let you know that HelloWorld.java has changed and needs to be committed back to the repository at some point. There are other annotations as well to let you know which files are not in the repository.

This has been a very quick look at all the things that CVS and the Eclipse integration can do for you. You have taken a simple project that you built and got it checked into a CVS repository, and you have also seen how to interpret the CVS annotations. The next section will show you how to get a new project from an existing CVS repository.

Page 149: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Quick Start 138

Getting a New Project from a CVS Repository

Often projects that you become part of will already have a CVS repository in place with the project checked in. The typical open source project, for example, has its code available through an existing CVS repository. You will create a new project from the Eclipse CVS servers to see how Eclipse allows you to create a new project from a repository. Open the CVS Repository Exploring perspective. (Window->Open Perspective->Other, or you can just click on the Open Perspective button.) Once you have the perspective open, the CVS repository’s view should have replaced the Package Explorer view. Invoke the context menu in the CVS repository’s view and choose New->Repository Location. The dialog box that comes up will look exactly like the one you looked at earlier in Figure 5.5, except that the connection information will be different. Figure 5.9 is the completed dialog box for getting to the Eclipse source code.

Figure 5.9: Repository Location for Eclipse Code

Page 150: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

CVS Quick Start 139

Once the location is confirmed, click on the expansion icon to the left of the repository location string. Then, expand HEAD as well. There will be far too many subprojects to show in one window, but you should see something that looks like Figure 5.10 if everything is working properly.

Figure 5.10: Eclipse Projects on dev.eclipse.org

This list represents all the projects that are publicly avalible from the Eclipse team—the beauty of open source! Choose one so you can get the code. For this example, choose jdt-core-home. Invoke the context menu on jdt-core-home, and select Check Out As from the menu. This will bring up a wizard that will walk you through the project creation process. If you click Finish from the first page, Eclipse will create a new project named the same as the folder in CVS (i.e., jdt-core-home). Alternatively, you can choose Check Out from the context menu, and it will do the same thing as the wizard does if you choose all the default options. Getting all the code for jdt-core-home can take a while, depending on your connection speed.

Once the process of downloading jdt-core-home is complete, you will have a new project in your workspace that is connected to CVS, so all the annotations will show up. The difference between Hello World and jdt-core-home is write access. You have full access to Hello World, but read-only access to jdt-core-home. What that means is that you are free to make changes to the code in jdt-core-home, but you are not able to commit any of the changes because you are a read-only user. Now, you have seen a quick start to setting up projects with CVS from creating a whole new project in CVS, by getting an existing Eclipse project, and creating a project by checking out the source code from a CVS server. Now you should dig into the details of the Team menu in the Package Explorer.

Page 151: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 140

The Package Explorer Team Menu

Most of the functionality related to version control can be accessed via the Team menu under the context menu in the Package Explorer view. In fact, most of my interaction with CVS is through this menu. In order to make this a meaningful exchange, pick one of the Java files in the Hello World project and edit it. The edit in particular is not that significant. Choose something simple like adding a private one-line method. This small change will help the rest of the examples and discussion make a more sense. Figure 5.11 shows the Team menu opened on the HelloWorld.java file.

Figure 5.11: Team Menu

The items in this menu are divided into four groups. The first group is focused on getting files to and from the repository. The second group is focused on managing and interacting with file versions. The next group allows files to be managed with respect to how CVS handles them. For example, you can toggle from binary to text format or back. (CVS handles binary files differently than text files.) The next group asks the server about specific files. You will go through each group in detail in the next section.

Page 152: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 141

Getting Files in and out of the Repository

The first menu item in this group kicks off synchronization with the repository.When you invoke this menu item, the Team Synchronization perspective will be opened, as well as the Synchronize view in ‘outgoing’ mode. This means that the Synchronize view will be focused on what is going out of the workspace and into the server. The project will be at the root of the Synchronize view. Select the project and invoke the context menu. Then, select Expand All. You should see the file that you changed earlier. (I changed HelloWorld.java by adding a private void method and making a slight formatting change inside the main() method.) Double-click the file that you changed (or invoke the context menu and select Open In Compare Editor). You should see the Compare Editor view. Figure 5.12 is the Compare Editor view for the changes that were made to HelloWorld.java.

Figure 5.12: The Syncronize View and Compare Editor

Page 153: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 142

The Compare Editor shows the structural changes on the top pane and the two different versions of the file on the right and left side. The structure compare is particularly useful for seeing what changed. If you look closely at the arrow icons to the left of the structural differences, (the methods that have changed in this case) you will notice that the wozer() method has been added (denoted by the +), and that main(String[]) has simply had a change (no +). This structural comparison is quite handy. Instead of seeing just a bunch of lines of code with + and – in front of them, you can see exactly what changed, and it’s presented in a much clearer format that the old CVS diff command. Double-click one of the added methods. In the bottom half of the screen, you will see the difference between the two versions. Since the method was added to the new version the old version will be blank. Notice that this is a logic comparison between the two versions instead of a line-by-line comparison. With older tools, you had to sort through lines that were prefixed with + and – to tell what had been added or removed from a file, and there was now way to distinguish logically insignificant differences. To illustrate the logic comparison , go ahead and commit (context menu Commit menu item) the changes you’ve made to the file, then move the private void method to the top of the file (above main for example). Then, choose the Synchronize with Repository again. You should see something similar to Figure 5.13.

Figure 5.13: Revised Comaprison

Page 154: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 143

Notice that the comparison tool knows that wowzer() is not a new method. (There is no + in the Outgoing icon.) This tool makes it much easier to tell exactly what the differences are, which really does make for safer commits.

The next menu item in the Team menu on the Package Explorer is Commit. Earlier, when you chose the Commit menu item in the Synchronize view, the same functionality was invoked. Basically, the file was ‘committed’ back to the repository. In other words, the file was put back into the repository along with the differences so that you can get back to the version of the file without the wowzer() method, if need be.

The next menu item (from Figure 5.11) is the Update item. This functionality gets all the latest files from the repository. Throughout the day, team members typically commit changes to files. The Update menu item is how you can get the latest changes. This command will not overwrite any files that you have locally edited, so you don’t have to check in your changes before updating. In fact, the best way to interact with a repository (i.e., getting files from the repository, updating the files, and checking them back into the repository for others to get) is to first update, recompile, and run your tests to make sure that you have not made any incompatible changes with other’s changes.

Next up is the Create Patch item. This tool allows you to create a ‘patch’ file that others can use to update their local copy of a file without anyone having to make changes to the repository. This functionality goes hand in hand with the next command, Apply Patch. Apply Patch takes the output of Create Patch and updates the file with the changes. This functionality is especially useful when working on open source projects where you find and fix a bug but don’t have commit privileges. You can use Create Patch to make a patch file to send to one of the committers, and they can use Apply Patch to apply your changes to the source code.

Page 155: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 144

Managing Versions of Files

The first item in this group is the Tag as Version menu item. With this feature, you can specify a named version of a file, a group of files, or even the whole project. This functionality is key in being able to go back to a particular release. In other words, at the end of each release cycle, a tag can be applied to the whole project. Then, if you need to get back to the state of the project as it was when it was released, you can do so by synchronizing to that tag.

To help clear this up, consider this example. In the previous discussion of the Synchronize View, you made a small change to the HelloWorld.java file. When you committed that file, it bumped the version number to 1.2. All the rest of the files in the project are probably still at version 1.1. Suppose that everything works; you have tested the project and you are ready to release HelloWorld to the world so they can enjoy it as much as you have. You want to tag the whole project to invoke the context menu on the Hello World project, and choose Tag as Version. A dialog box will appear asking you for a name for the tag. Type in a name (I used ‘EVERYTHING_WORKS’, but it is more typical to name the tags after the version number of the project, i.e., HelloWorld_1.0), and click the OK button. Now, you have a tag in place and can revert to that version at any time. You can also tag individual files or groups of files and revert to the version of those files at any time. You will see how to revert to these tagged versions later in this chapter.

The next two menu items go hand in hand (like the Create/Apply patch that you learned about earlier). Branch allows you to create a branch of your code. A branch is a place to perform edits that do not immediately go into the main area of the repository, where most of the team members are working. (This is usually called the HEAD in CVS or, more generally, the main trunk of development.) In other words, a branch can be a place to experiment with a change that is isolated from the rest of the team, or a branch can be used to apply fixes to a previous release. Merge is used to take the changes in a branch and apply them to another branch (typically the trunk).

Note: Branches, Trunks & the HEAD: A tree is often used as the metaphor to describe development in a repository. The main branch is called the trunk, and that is where most of the development happens and where new releases come from, etc. Typically, as each release is made, a branch is created. Branches are also used, as said earlier, as a place to experiment with changes you do not want on the trunk. The branches can be abandoned or they can be merged back into the trunk. Experimental branches are often abandoned. Release branches typically have critical bug fixes applied, and then the changes are merged back into the trunk. In CVS, the trunk is referred to as the HEAD.

Page 156: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 145

To make this discussion a little more concrete, you can practice what you’ve learned by branching HelloWorld, making some changes, and then merging them back into the trunk. Start by selecting Branch with the project that is currently selected. (Make sure to select the project and not a file for this example.) You can name the branch whatever you want, (I named mine Exp1) and choose the tag that you made earlier (I named mine EVERYTHING_WORKS). Figure 5.14 shows the branch creation dialog box.

Figure 5.14: Creating a Branch

Page 157: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 146

Make sure that you have checked the Start working in branch check box, and then click the OK button to create the branch. Now, make a few changes in the branch by adding a method or two, adding some comments, etc. The particular changes are not important. I added a private void method to HelloProvider, HelloWorld, and MyClass. Now, commit your changes by invoking the context menu on the project again and choosing Team->Committ, Then, enter a comment and click the OK button. Figure 5.15 shows the branched files; notice that the version numbers have gone to four digit numbers (i.e., 1.1.2.1).

Figure 5.15: Changed Files

Notice also that the CVS decoration of the project has the name of the branch you are working on in addition to the name of the host the server is on. So, for the sake of the example, consider that the tag EVERYTHING_WORKS was a release branch, and some users reported a security hole in that release, so you had to fix it and release a new dot version of the product (i.e., version 1.1). Since the changes are important, you want the changes that you made in the branch to be pushed into the trunk. (I know there is no way to have a security hole in Hello World, but go with it for the sake of the example). You need to merge the changes in the branch into the trunk of the development tree. To make things more complicated, you could make some changes in the trunk and commit them before you do the merge. That is probably a more realistic scenario, but I don’t want to get lost in the intricacies of doing a particular file merge when the focus is on how to get a whole branch merged back into the development tree. With that being said, proceed to the merge.

Page 158: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 147

To merge, you first need to get the version you want to merge the changes to into the workspace. Since you want to merge your changes into the trunk (aka HEAD), you need to get the trunk into the workspace. Select Replace With->Other Branch Or Version from the context menu (remember to have the project selected again), and choose Head from the list of branches. Make sure the Recurse into sub-folders check box is checked, and click the OK button. Now that you have the trunk in the workspace, you are set to merge. Select Team->Merge from the context menu. Choose the tag ‘EVERYTHING_WORKS’ (or your tag name if you chose something else) from the list, and click the Next button. The next page of the wizard wants you to choose the branch that changes should come from, and then click the Finish button. You should now be back in the Team Synchronize perspective, and the Hello World project should be the root of the Synchronize view. Figure 5.16 shows the files with incoming changes (these are the merging changes for the file), as well as the comparison for HelloWorld.java.

Figure 5.16: Merging with the Team Synchronize Perspective

Page 159: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 148

As you can see in the branched version (the one on the bottom left), I added a method called gak(). In order to merge the changes into the trunk, you need to click the Copy All Changes from Left to Right button. The button is highlighted in Figure 5.17.

Figure 5.17: The Copy All Changes from Left to Right Button.

After you have merged all the changes from each file, the branch changes will be in your edited local copies of the files. Then, switch back to the Java perspective. Notice, the Synchronize view is empty once all the merge points have been reviewed and applied. The next step is to check in the files, but before you do that, you need to make sure to run all the unit tests for your system. (If you don’t have unit tests, you will learn how and why to create them in Chapter 6.) In this way, you limit the breakage such merges can cause. Running the tests will ensure that others that update from the repository will be able to compile, and most if not all of the expected functionality will still be there.

Page 160: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 149

The next item allows you to see the annotations for the file. After selecting the Team->Show Annotation from the context menu, the perspective will switch back to the CVS Repository Exploring perspective, with the CVS Annotate and CVS Resource History views showing. The CVS Annotate view shows you all the versions of the file. You can click through the list of versions and see the lines that were changed as a result of that version. I find that the really useful aspect of this view is the link with the CVS Resource History view. As you click through the various version numbers, the CVS Resource History updates to select that version. The CVS Resource History view allows you to see all the versions of the file as well, but in addition, it lets you compare any two versions of the file. Figure 5.18 shows the CVS Repository Browsing perspective with version 1.3 of the HelloWorld.java file selected.

Figure 5.18: CVS Annotate View in the CVS Repository Browsing Perspective

Page 161: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 150

Notice that the Editor view has the changed lines for version 1.3 selected. This can be very useful, but it tends to make less and less sense the further the file gets away from the version that is selected. Lines get deleted and moved around so much that eventually the lines recorded for version 1.3 won’t make any sense to the current version of the file. The really interesting and useful feature is that it will show you the same version in the CVS Resource History view (History view for short). From the History view, you can see the commit messages and any relevant tags for the version. You can also select two different versions of the file and compare them. For example, select the 1.3 and 1.1. versions of the HelloWorld.java, and bring up the context menu. Select Compare (Your file names might vary—if you’re not following directly along, just select any two versions of the file and select Compare from the context menu if you’re not using the Hello World project). The HelloWorld.java file should show up in the edit space with HelloWorld.java being the only visible file. Double-0click the file, and the compare view will display. You should see something like what is displayed in Figure 5.19.

Figure 5.19: Compare View Open on HelloWorld.java

Page 162: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 151

From the Compare view, you can see all the changes between the two versions. If you notice, in the right hand margin of the Compare view there are black boxes at each change. For large files with several changes, these boxes can be used to navigate between the changes.

Finally, the Team->Show in Resource History menu item brings the CVS Resource History view into the Java perspective instead of taking you to the CVS perspective. This is the full view, so you can see the history, of course. But you can also see the commit messages and any branches or tags that the version is a part of in the bottom of the view. The next section will cover file types for CVS and how you can mange them with the CVS integration in Eclipse.

Page 163: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 152

File Types in CVS

The Change ASCII/Binary Property menu item tells CVS to treat the file differently. When you add a file to CVS, it is assumed to be ASCII text. This is important because it effects how CVS treats the file on check-in, specifically how diffs are calculated. Recall that when a file is checked into CVS that the latest version is kept, plus the differences needed to arrive at the old version. When CVS was built, the problem of calculating differences between binary files was considered too difficult of a task to tackle. As a result, CVS just keeps every version of the binary file. When you check in a file marked as binary, CVS does not attempt to calculate a diff; it simply takes the new version and copies it into the repository as the latest version. This can take up a lot of space, so you want to avoid marking a file as binary when possible. However, when a file is binary, it must be marked that way or the CVS check in and check out process will damage the file. That is where the Change ASCII/Binary Property menu item becomes useful. If you notice that a binary file is corrupt (a Word document not opening etc.), then you can mark it as binary and check in a fresh copy of the file. CVS will then stop damaging the file on check –in and check out.

Page 164: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 153

It’s difficult to manually manage this binary/ascii switch for all the files in your project, but fortunately, the Eclipse CVS integration has a set of default file extensions that are automatically recognized as binary. The list is managed via the Team preferences page (Window->Preferences) and can be seen in Figure 5.20.

Figure 5.20: Binary File Extension Preferences Page

The list that ships with Eclipse contains the standard binary files (.doc, .jar, etc.), but you can add any other file extension that you need. If you are adding a project to CVS for the first time, it is a good idea to make sure that the list of binary files in the project are in this list so that when Eclipse adds them, they will get set to binary properly.

The next two menu items in the list are Add to Version Control and Add to .cvsignore. These two items allow you to specify what Eclipse should do with a file that is not known to CVS. If a file is unknown to CVS and you choose to add it to version control, then Eclipse will add it to the CVS repository on the next commit. If you instead choose to add it to the .cvsignore file, then the file will be ignored by CVS.

Page 165: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 154

Edit File Commands

The CVS server is capable of keeping track of who is editing a file and sending an e-mail message when an editor is added to a file. This support is turned off by default in Eclipse, but it is easily turned on. There are two places to configure this preference: in the workspace preferences (Window->Preferences) or on the projects CVS properties page. If you want all projects to have the editing commands enabled, then you should set the workspace preference. After this preference is changed, it will effect all new projects checked out from CVS. Since your current project is already checked out though, you will have to modify its CVS properties. Select the project, and invoke the context menu. Then, choose Properties and navigate to the CVS page of the project’s properties. You should see something that looks like Figure 5.21.

Figure 5.21: CVS Project Properties

Select the Enable watch/edit for the project check box to turn on the Edit functionality for this project.

Page 166: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

The Package Explorer Team Menu 155

Now that the project is set up to understand the CVS edit/watch functionality, the files will be pulled from the repository in read-only mode. When you start to edit a file, an edit notification will be sent to the CVS repository. If there are other editors, you will receive a notification via a pop-up window that warns about the other editor. You can also see the list of editors by selecting Team->Show Editors, with the file of interest selected. Also, if you’d like to explicitly add your self to the list of editors of a file, you can do that by selecting the Team->Edit menu item. This will send an edit notification to the CVS server in the same way that just typing does. The cool thing about this edit functionality is that it’s integrated into the JDT. For example, if you were to refactor a method and the refactoring in turn changed four files, all four would have an edit notification sent to the CVS repository. Finally, if you decide that you want to back out of your edit, you can choose Team->Unedit. This feature will cause the file to be reverted to where it was when you issued the edit. In other words, all your changes will be deleted and you will have the same version of the file that you had when you started your edit.

CVS State of the Project

The last group of items on the Team menu are only visible when the project is selected. The first is Share Project. Since the Hello World project is already connected to a CVS server, this item is disabled. You went through sharing Hello World earlier in this chapter. For more detail, return to that section ‘Putting Your Project into a CVS Repository’. The last menu item is Disconnect, which will disconnect the project from the CVS server. When this item is invoked, a dialog box appears asking if you want to delete all the CVS related data from your project. If you do not remove this information, it will be easier to reconnect the project later if you change your mind. You can always get another copy of the project from CVS by invoking the Checkout item from the context menu in the CVS Repositories view.

Page 167: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Summary 156

Summary

In this chapter, you have covered the various aspects of how to share a project among many different users at the same time with the CVS version control system. The Eclipse integration offers powerful tools to help you manage team development with CVS. In the next chapter, you will cover testing Java applications with JUnit and the integration within Eclipse.

Page 168: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Chapter 6

Eclipse 3 Live

Eclipse JUnit Integration

Eclipse ships with fantastic support for unit testing your Java code. The JUnit integration is built deep into JDT and provides for sophisticated building and usage of JUnit. In this chapter, you will look at how to use Eclipse’s JUnit integration.

JUnit is the de facto standard testing framework. Just about anywhere you find unit testing, you find some extension of JUnit involved. There are JUnit extensions for testing everything from EJBs to .NET. Of course, with Eclipse, you will be doing Java development, so this chapter is focused on making you productive in building and executing JUnit tests for Java code. This chapter starts with a brief introduction to unit testing, followed by an introduction to JUnit, and finishes up with a detailed discussion of the JUnit integration found in Eclipse.

Page 169: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Unit Testing 158

Unit Testing

Unit testing, at its core, is focused on identifying a well-defined part of a program (the unit) that can be asserted to be functioning correctly. Generally, a unit test involves three basic steps. The first step is to set up a contrived world for the test subject to run in. The contrived world is the environment (other objects, data, etc.) that the subject needs to function. As an example, some objects require data from a database. In the set up phase, you would put known data into the database (either valid for positive test or invalid for negative tests). Another example of setting up a contrived world would be creating mock objects that act in the place of the subject’s dependents. The mock objects are simple and act in very predictable ways so that the subject can be asserted to be correct. In other words, a mock object can help you find a bug in the subject, knowing that wrong behavior must be coming from the subject since its dependents are simple mocks. The next step is to invoke the functionality being tested on the subject (i.e., execute the unit). This can be as simple as executing a method on a Java object. The final step is to assert that the expected result happened. Assertions are the most important part of a unit test. Without an assert, there is no test. Tests that do not have an assert are not really testing anything at all (except maybe that no exceptions are being thrown). The test should really assert that whatever state change was expected actually happened.

You might have noticed that I have been a bit vague about the definition of a unit. That vagueness is intentional. There is much debate around the community of unit testers about what constitutes a unit. Some argue that a unit must be defined in the smallest possible terms. For example, a single isolated method or even one aspect of a single method is one possible unit that is very small. Others argue that a unit should be defined in a broader way to include integrated sets of objects; these types of tests are sometimes referred to as ‘integration unit tests’. I don’t want this chapter to digress into an attempt to convince you that one kind of unit is better than another. In my opinion, you should do both kinds of tests (as well as others). The more kinds of tests, the better the chances of finding and fixing bugs before an application makes it into the hands of a user. In fact, most testers agree that everyone needs many kinds of tests, so the debate in the testing community usually boils down to the definition of a unit rather than any sort of philosophical problem with one kind of test or another. The discussion in this chapter will focus on the broader definition of a unit. For example, the example tests will not attempt to mock the implementation of java.util.List just to isolate the subject from all possible dependencies.

For those readers that are familiar with this debate between tests that seek to isolate their subjects and the tests that do not, the ‘integration unit tests terminology’ is based on work done by Vincent Massol on the Cactus project. There are several very practical discussions of the differences of the two approaches and when it is appropriate to do one type of testing or the other.

Page 170: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Overview 159

JUnit Overview

First and foremost, JUnit is a framework for building unit tests for Java classes. The typical unit is one method. The test asserts the state change, that the method is supposed to make, happens as expected. So, what does all this mean anyway? Why is JUnit called a framework, and how does that make testing easier for your Java classes? The rest of this section will answer these questions by giving you an understanding of what JUnit is, how it is put together, and what extension points are available to make testing easier.

As a user of JUnit, you will need to understand the three basic types in JUnit and how you will typically interact with them. In addition to these types, you must also understand the patterns that are used to put them together. In the next six sections, the basic types, as well as the patterns used to group them, will be covered.

JUnit Basic Type – Test

The Test is the basic unit of functionality in JUnit. This is the type used to run all tests in JUnit. TestCase and TestSuite, which will both be covered shortly, implement the Test interface. The API defined by the Test interface consists of two methods, run(TestResult):void and countTestCases():int. The run() method (as expected) causes the test to be executed. The TestResult object that is passed into the method is used to collect the results of the test. The countTestCases():int method counts the number of tests that will be run by this method. For a simple TestCase, there will be only one test; for TestSuites, the number will be the number of tests added to the suite. The section below about the patterns in JUnit will go into more detail about the ways that Test, TestCase, and TestSuite interact.

Page 171: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Overview 160

JUnit Basic Type – TestCase

The TestCase is the class that you will most often deal with in your everyday usage of JUnit. Instances of this class are responsible for setting up the test subject (also sometimes called the fixture), executing the test, and then cleaning up the subject. The major APIs to be concerned with in this class are setUp():void, tearDown():void, and runTest():void. The setUp() method is responsible for setting up the contrived world for the test subject, and usually this method creates one or more subject instances as well. The tearDown() method is responsible for cleaning up after setUp(). As an example, consider an object that gets some of its data from a database. The setUp() method places made-up data into the database, and the tearDown() method removes the made-up data. Finally, the runTest() method is responsible for actually running the test. You will not typically implement this method yourself; instead, you will rely on the implementation in TestCase, so it is important to understand what this method does. The runTest() method takes the name (set in the constructor) of the TestCase instance and finds a method with the same name (through reflection). It then executes that method. The power and usefulness of this implementation will become more evident in the following section on the TestSuite. Another important fact to know about TestCase is that it subclasses from Assert. The Assert class provides dozens of methods to let you assert the values involved in the test. The TestCase inherits all these methods making it very straightforward to assert things about the subject and the state changes that are supposed to occur during the test.

Page 172: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Overview 161

JUnit Basic Type – TestSuite

The TestSuite class’s main function is to aggregate TestCase instances into groups that can be run together. One of the TestSuite constructors takes a class as an argument. The class is expected to be a subclass of TestSuite. Each method defined on the class that begins with ‘test’, takes no arguments, and has a void return type that is used as the name of an instance of the TestCase. For example, here is a simple subclass of TestCase that has two test methods.

public class ExampleTest extends TestCase {

public static void main(String[] args) { junit.textui.TestRunner.run(ExampleTest.suite()); }

public static Test suite() { return new TestSuite(ExampleTest.class); } protected void setUp() throws Exception { super.setUp(); }

protected void tearDown() throws Exception { super.tearDown(); }

public ExampleTest(String name) { super(name); } public void testOne() { // perform some testing here }

public void testTwo() { // perform some other testing here }}

Listing 6.1: ExampleTest.java

Page 173: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Overview 162

Creating a TestSuite (as in the suite() method seen in the listing) with this class as the argument in its constructor would result in a TestSuite with two instances of ExampleTest: the first named ‘testOne’ and the second named ‘testTwo’. When suite is run, it will call the run():void method on both instances, which invoke the test methods, testOne() and testTwo(). This happens as a result of the default implementation of runTest(). Recall the earlier discussion about this method. When runTest() is called, it looks for a method that matches the name of the instance, and then invokes it if found. This combination of TestSuite and TestCase makes it very convenient to get all the tests in a single class loaded into a single suite. In the next section (the discussion of the composite pattern), there will be a more detailed discussion of TestCase and TestSuite and the way that suites are run.

Another thing to note here is that the ExampleTest class has a main method and thus, can be run from the Java command line. When run, this class will use the textui to run the tests and display the output on the console (because of the textui) that ran the tests. There is a Swing and an AWT user interface that can also be run instead of the textui. All three UIs provide the same information, just in a different format.

Page 174: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Overview 163

JUnit Pattern Usage – Composite

The Composite pattern describes the way that part-whole hierarchies are designed and built. Figure 6.1 shows the JUnit classes involved in the Composite pattern and labels each class with its respective role.

Figure 6.1: Composite Pattern in JUnit

The component role is filled by the Test interface. The component role is the abstract representation of either the leaf or composite roles. In other words, the component is the definition of the API that can be assumed on all the objects that make up the ‘whole’ in the whole-part equation. The important idea to remember from this pattern is that both TestCase and TestSuite are both subtypes of Test, so they can be freely composed into suites. In other words, you can have a suite of suites or a suite with both suites and test cases in it.

<<component>>Test

run()countTestCases()

<<composite>>TestSuite

addTest(Test)

<<leaf>>TestCase

setUp()runTest()tearDown()

tests

0..*

Page 175: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Overview 164

JUnit Pattern Usage – Command

The Command pattern describes a way to encapsulate very different behaviors behind a common API. For example, the Test interface in JUnit has the run() method. Since that piece of common API is shared across TestCase and TestSuite, it’s easy to get the test encapsulated in instances of either class to run. Figure 6.2 shows the core JUnit classes and how they are involved in this pattern.

Figure 6.2: Command Pattern in JUnit

In TestSuite, the run() method invokes the run() method of all contained tests in TestSuite, either TestCases or TestSuites (i.e., you can have suites of suites). In TestCase, the run() method invokes the runTest() method. By issuing the simple command run, you can run all the composed tests without having to know if the Test is a single test case or a suite of tests. This feature leads to a very simple and straightforward way to run and compose the tests.

<<command>>

Test

run()

<<macro-command>>

TestSuite

run()

<<concrete-command>>

TestCase

run()

tests

0..*

Page 176: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Overview 165

JUnit Pattern Usage – Decorator

Another pattern that’s used in the implementation of JUnit is the Decorator pattern. Figure 6.3 shows the classes in JUnit involved in the Decorator pattern.

Figure 6.3: Decorator Pattern in JUnit

The TestDecorator class allows behaviors to be attahced to a test or suite of tests without you having to override any of the methods in TestCase and TestSuite to achieve the new behavior. A typical way to use a decorator is to do some expensive setup before a suite of tests is run and then clean up aftwards. For example, you might have a group of objects that depend on some data in a database. It is common to have a decorator populate the database with test data, and then delete that test data when the test is finished. This way, the data is inserted into the database once and deleted once instead of having to be done with each test. Now that you’ve seen an overview of how JUnit works, it’s timeto see a quick example of how to use JUnit within Eclipse.

<<component>>Test

run()countTestCases()

<<decorator>>TestDecorator

setUp()run()tearDown()

<<concrete-component>>TestCase

setUp()run()tearDown()

test

1

Page 177: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Quick Start 166

JUnit Quick Start

In order to see how JUnit works, you first need a subject to test. A very simple class will be used as the subject so you can focus on JUnit and not the complexity in the subject. In the example you will use, a very simple Calculator class that accepts any of the primitive number types as inputs, performs some calculator functions on the inputs, and returns the result. The code is listed in Listing 6.2 below.

public class Calculator { public int add(int a, int b) { return a + b; } public int subtract(int a, int b) { return a - b; } public int multiply(int a, int b) { return a * b; } public int divide(int a, int b) { return a / b; }}

Listing 6.2: Calculator.java

Page 178: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Quick Start 167

This is a very simple class; it takes two integer inputs and returns the result of some mathematical operation on the two arguments. Now, you want to test this class to make sure you got it right. Start by selecting the class in the Package viewer. Invoke the context menu and select New->Other from the menu. You should see a dialog box that looks like Figure 6.4.

Figure 6.4: New Item Dialogue

Page 179: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Quick Start 168

Select the Java->JUnit->TestCase item as shown in Figure 6.4, and click the Next button. This will take you to the main wizard for creating a new TestCase. The wizard is shown in Figure 6.5 below.

Figure 6.5: The New TestCase Page

I usually accept the defaults except for checking the setUp, tearDown, and constructor check boxes. Checking these boxes will have Eclipse generate a lot of the mundane code for you. After you change the options to the way you want them, click the Finish button. (We will go over the Next button later.) If you chose the same options that I did, your code should look like the code in Listing 6.3 below.

Page 180: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Quick Start 169

public class CalculatorTest extends TestCase {

public static void main(String[] args) { junit.textui.TestRunner.run(CalculatorTest.suite()); }

protected void setUp() throws Exception { super.setUp(); }

protected void tearDown() throws Exception { super.tearDown(); }

public CalculatorTest(String arg0) { super(arg0); }}

Listing 6.3: CalculatorTest.java

You will probably notice right off the bat that this code will not compile because there is no suite() method defined on CalculatorTest. This is a bug in the M7 code base and will hopefully be fixed before the final 3.0 release. To make the code correct, remove the suite() method call and replace it with the class. (The runner auto-generates a test suite if passed a subclass of TestCase) Change the main method to look like this:

public static void main(String[] args) { junit.textui.TestRunner.run(CalculatorTest.class);}

Now you have a runnable test class. However, there are no tests to run. Recall that the TestSuite will use reflection to find any method that starts with ‘test’, has no arguments, and has a void return type. So, you need to add a test method for the Calculator.add(int, int) method. The code is listed below.

public void testAdd() { Calculator cal = new Calculator(); assertEquals(11, cal.add(4, 7));}

This test asserts that the Calculator returns 11 when adding 4 and 7.

In this section, you have covered the basic structure and function of JUnit, including a very simple example of writing your own tests. In the next section, you will set the JUnit integration (built into Eclipse) in action.

Page 181: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 170

JUnit Integration Details

JUnit is integrated into the JDT plugin, and thus is an integral part of Java development in Eclipse. To see how the integration works, you need to create a new project. Call the project Money. Add a source directory (src), and specify the output directory as classes. The final page of the wizard should look like Figure 6.6.

Figure 6.6: Final New Project Wizard Page

For more detail on creating projects, refer back to Chapter 2, specifically the quick start near the beginning of the chapter.

Page 182: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 171

The subject for this section will be a group of classes related to managing money. The code is the sample code found in the JUnit distribution. There are three types of classes. The first type is Imoney, and it’s the base interface. IMoney has methods that allow for adding Money objects together, doing multiplication of Money objects, and other useful methods. The next type is the Money class, which knows the amount as well as the currency so that conversion factors can be taken into account. Finally, the MoneyBag class allows a set of Money instances to be stored together. These three classes will be the subjects of the example tests in the rest of this chapter. Figure 6.7 shows a UML diagram of the three classes.

Figure 6.7: The Money Hierarchy

The source code for these classes can be found on the book’s web site or in the JUnit distribution. If you use the code from the JUnit distribution, you have to repackage the code into the money package from the junit.samples.money package. You will create three TestCases—one for each class.

IMoney

Money MoneyBag

monies

0..*

Page 183: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 172

There are many different ways to package test code. The tests can be in a separate package (money.test for example), the tests can be in the same package alongside the subject, or the tests can be in a different directory but the same package. The third choice is the approach you will take in the examples. This approach allows the tests to have access to package private members of the subject, but it doesn’t clutter the package with the test code. In real world application, this also has the advantage of being able to easily distinguish the ‘real’ code from the tests code with build scripts. To create the alternate directory to store the tests in, invoke the context menu in the Package view with the project selected, and choose the New->Source Folder menu item. Figure 6.8 shows the dialog box with the name test already typed in.

Figure 6.8: Creating a new Source Code directory

Next, the money package must be added to the test directory. Again, invoke the context menu, this time with the test directory selected, and choose New->Package. Name the package money, and click the Finish button. You should now have two packages called money, one in the src directory and one in the test directory.

Page 184: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 173

New TestCase Wizard

Now that you have all the directories and packages set up, you can finally create a test case for one of the subject classes. Use the New TestCase Wizard to create the test. Select the Money class in the Package viewer, and invoke the context menu. Then, choose New->Other. Select Java->JUnit->TestCase from the dialog box that appears. Figure 6.9 shows the dialog box with the TestCase selected.

Figure 6.9: New TestCase Wizard

Page 185: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 174

After selecting TestCase, click the Next button. This page of the wizard is where you specify all the structural things about the test, including the name of the test, the directory the source will be in, and the class to test (to name a few). You should change the directory to be Money/test, and select the main, setUp, tearDown, and constructor check boxes if they are not already checked. Figure 6.10 shows this page of the wizard selected and the values changed appropriately.

Figure 6.10: Page two of the New TestCaseWizard

Page 186: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 175

After setting the values, click the Next button. The third and final page of the wizard should now be visible. Here, you can specify which methods you want to test by selecting the appropriate check boxes. It’s typical to have at least one test method for each method of the subject, so I usually check all the methods on this page. Make sure to only check the methods in Money and not in Object. (I think it’s fair to assume that the methods in java.lang.Object are well tested.) Figure 6.11 shows the final page of the wizard with all the methods on Money selected.

Figure 6.11: Page three of the New TestCaseWizard

Page 187: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 176

I typically leave the two check boxes at the bottom of this page left unchecked. I rarely subclass a TestCase anyway, so there is no real reason to make the test methods final. (Methods marked final cannot be overridden.) I also do not like to have auto-generated TODO items because I find them cluttering. Experiment with these two check boxes though to see which way you are most productive. After you click the Finish button, the MoneyTest.java file should appear in the editor. The file will not compile because of the missing suite() method (the bug discussed earlier in this chapter), so change the suite() method call to MoneyTest.class work around the bug. Notice that you have a test method for each method on Money that you checked in the third page of the wizard. These methods are the starting point for testing each method in the Money class.

The MoneyTest is now ready to run, although it won’t do much to test the Money class. In order to test the class, the code has to be written to create instances of the test subject and to assert that the various expected state changes happen as expected. A complete discussion of how to write good unit tests is beyond the scope of this book. However, Vincent Massol’s excellent book JUnit in Action [Massol, Husted Manning, 2003] is a great resource for learning how to write good tests.

To illustrate the way running JUnit tests is integrated into Eclipse, you will need a couple of tests to run. You can get started by testing the method isZero(). This method is supposed to tell you if the Money object has a value of zero (with a return of true) or not (a false return). To make this test, the code in testIsZero() would look like this:

public void testIsZero() { Money subject = new Money(0, "USD"); assertTrue(subject.isZero());}

This test creates a subject and then tests that the isZero() method returns the expected value. In this test, you are only testing the positive outcome. You should add another test that makes sure the method returns false when it should. Add another test method that looks like this:

public void testNotZero() { Money subject = new Money(100, "USD"); assertFalse(subject.isZero());}

Page 188: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 177

This test will make sure the isZero() returns false when the amount is not zero. Now that you have a couple of legitimate tests, run them to see the Eclipse integration. In the Run menu, you can choose to run the class as a JUnit Test. Figure 6.12 shows the menu item selected.

Figure 6.12: Run As JUnit Test menu item

Select this menu item. When the tests are finished, the JUnit view should be open (if it was not already). Unless a failure occurs, the view will stay in the background. Select the JUnit view so you can see the output of the tests. Figure 6.13 shows the JUnit view with all 15 tests successful.

Page 189: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 178

Figure 6.13: Clean and Green Test Run

Page 190: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 179

There are several interesting things to take note of in this view. First, you should notice the green bar across the top of the view. If this bar is green, all the tests passed; if it’s red, at least one failed. You will intentionally introduce a failure to see this shortly. Next, is a pair of panes that allow you to see the tests that were run. The first tab (Failures) shows only the failed tests. The second tab (Hierarchy) shows all the tests, pass or fail. In the Hierarchy view, passing tests have a small green check on their icons, and tests that did not pass have an x. If the test failed an assertion, the x is black. If there was an unexpected exception, the x is red. Both views are integrated with the Java editor. If you double-click on any test in either view, the editor opens that test. The bottom pane of the JUnit view shows the stack trace of the selected failed test. Notice also that the results are printed across the bottom of the Eclipse window. This line of text appears even if the JUnit view is not brought to the foreground, so you can tell that all your tests passed even if you don’t go to the JUnit view.

Now that you have seen what a positive test run looks like, take a look at a failing test run. Change the expected outcome in the testIsZero() method. To do that, change the code to look like this:

public void testIsZero() { Money subject = new Money(0, "USD"); assertFalse(subject.isZero());}

Now, run the tests again by clicking the Run button. The JUnit view should pop to the front (if it’s not already) since there was a failure. The JUnit view should look like Figure 6.14.

Page 191: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 180

Figure 6.14: Failed Test

Page 192: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 181

There are several differences to notice here. First, the bar at the top of the view is red now since one of the tests failed. The failed test is in the list of failures, and since it’s selected, the stack trace to the point of assertion failure is shown. Also, note that the text at the bottom of the window is now red and shows that one of the tests failed. If you click on any frame in the trace, the editor opens that method. This is a great tool for helping you track down what went wrong in a test.

Next up is the support built into Eclipse for TestSuites. Since a suite is meant to aggregate multiple TestCases, you can create one now. Create a test for the MoneyBag class as well. Follow the same steps in the wizard outlined earlier for the Money class. You can add code to the tests or not—for the illustration of creating a TestSuite,it won’t matter.

Page 193: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 182

New TestSuite Wizard

Recall that a TestSuite is an aggregate of other Tests (either TestCases or TestSuites). Eclipse has built-in support to help you to create suites of your tests. To get started, select the money package in the test source folder, and invoke the context menu. Select New->Other and then Java->JUnit->Test Suite from the dialog box that appears. Click the Next button, and you should see a dialog box like Figure 6.15.

Figure 6.15: Create New TestSuite Wizard

Page 194: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 183

Change the name of the suite to MoneyTests, and select the main and ui check boxes to generate a main method that will invoke one of the JUnit user interfaces. Then, click the Finish button. You should be presented with the MoneyTests class that looks like the code in Listing 6.4

public class MoneyTests {

public static void main(String[] args) { junit.textui.TestRunner.run(MoneyTests.suite()); }

public static Test suite() { TestSuite suite = new TestSuite("Test for money"); //$JUnit-BEGIN$ suite.addTestSuite(MoneyTest.class); suite.addTestSuite(MoneyBagTest.class); //$JUnit-END$ return suite; }}

Listing 6.4: MoneyTests.java

When this class is run, it will invoke all the tests in the MoneyTests class as well as the tests in MoneyBagTest. And the really cool part (because of the Composite nature of Test, TestCase, and TestSuite) is that this looks like any other JUnit test to Eclipse. From the Run menu, you can select Run As->JUnit Test again to get all the tests to run. Figure 6.16 shows the result of running this TestSuite.

Page 195: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 184

Figure 6.16: Results of Running MoneyTests

Page 196: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

JUnit Integration Details 185

Notice that instead of 15 tests, 29 were run. That is the total number of tests in each of the test classes that were aggregated into this TestSuite.

Page 197: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Summary 186

Summary

This wraps up the discussion of JUnit integration in Eclipse. You have learned what unit testing is, how JUnit fits into the requirements of unit testing, and how JUnit makes use of patterns to make it easier to use. In addition, you have seen the way that JUnit is integrated into Eclipse to make building tests and suites of tests simple.

Page 198: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Chapter 7

Eclipse 3 Live

Refactoring Java Code

Refactoring is becoming a standard approach to maintaining software. Eclipse has great, built-in support for many different refactorings and is also extensible so that third parties can add other refactorings. In this chapter, we will cover in detail the built-in support for refactoring that ships with Eclipse.

Thanks in part to the momentum of agile methodologies (XP, etc.) and the support found in tools like Eclipse, refactoring is becoming a standard part of the software development process. Fowler defined refactoring as a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior [Refactoring, Fowler, Addison Wesley 2000]. In my experience, a refactoring leads to increased understandability but does not always lead to making the code more maintainable. Eclipse provides great support for refactoring your Java code. In this chapter, we will cover the currently supported list of refactorings available in Eclipse, along with a simple example of when you might use the refactoring.

Page 199: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Refactoring 188

Refactoring

Now that we have a very brief intro to refactorings, let’s get right into an example of how to do refactoring and why you should. One of my favorite examples to get people off the ground as quickly as possible with refactoring is called Extract Method. The basic premise of Extract Method is that you have a method body that is too long to follow, and you want to break it up into several smaller methods.

Rather than bore you with some contrived source code that I made up to fit the situation, I figured I’d start with some code that is currently working great in the real world. Listing 7.1 is some of the source code that I’ve been working on in the MyFaces project. This is part of the startup configuration code that sets up all the JSF factories for use in the application. I will take you through one of the actual refactorings I did while adding a new feature to the way JSF starts up.

private void parseFacesConfigFiles(FacesConfig facesConfig, ExternalContext context) throws FacesException { InputStream stream; //First of all load the standard faces-config stream = ClassUtils .getResourceAsStream(STANDARD_FACES_CONFIG_RESOURCE); if (stream == null) { throw new FacesException( "Standard faces config " + STANDARD_FACES_CONFIG_RESOURCE + " not found"); } if (log.isInfoEnabled()) { log.info("Reading standard config " + STANDARD_FACES_CONFIG_RESOURCE); } parseStreamConfig(facesConfig, stream, STANDARD_FACES_CONFIG_RESOURCE, new FacesConfigEntityResolver()); // look for service definitions in the main jar file Set factoryNames = new HashSet(); factoryNames.add(FactoryFinder.APPLICATION_FACTORY); factoryNames.add(FactoryFinder.FACES_CONTEXT_FACTORY); factoryNames.add(FactoryFinder.LIFECYCLE_FACTORY); factoryNames.add(FactoryFinder.RENDER_KIT_FACTORY); //Search for factory files Set services = context .getResourcePaths(META_INF_SERVICES_LOCATION); // this performs the intersection of the factory names // that we are looking for the ones found, only the // services found that match the expected factory // names will be retained services.retainAll(factoryNames);

Page 200: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Refactoring 189

Iterator itr = services.iterator(); FactoryConfig config = new FactoryConfig(); while (itr.hasNext()) { String factoryName = (String) itr.next(); String resourceName = META_INF_SERVICES_LOCATION + factoryName; InputStream is = context.getResourceAsStream(resourceName); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String className = null; try { className = br.readLine(); } catch (IOException e) { throw new FacesException( "Unable to read class name from file " + resourceName, e); } try { Class.forName(className); } catch (ClassNotFoundException e) { throw new FacesException("Unable to find class " + className, e); } config.setFactory(factoryName, className); } facesConfig.setFactoryConfig(config);

//Search through JAR files Set jars = context.getResourcePaths("/WEB-INF/lib/"); if (jars != null) { for (Iterator it = jars.iterator(); it.hasNext();) { String path = (String) it.next(); if (path.toLowerCase().endsWith(".jar")) { parseJarConfig(facesConfig, context, path); } } }

//context initialization parameter config list String configFiles = context .getInitParameter(CONFIG_FILES_INIT_PARAM); if (configFiles != null) { StringTokenizer st = new StringTokenizer(configFiles, ",", false); while (st.hasMoreTokens()) { String systemId = st.nextToken().trim(); stream = context.getResourceAsStream(systemId); if (stream == null) { log.error("Faces config resource " + systemId

Page 201: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Refactoring 190

+ " not found"); continue; }

if (log.isInfoEnabled()) log.info("Reading config " + systemId); parseStreamConfig(facesConfig, stream, systemId, new FacesConfigEntityResolver(context)); } } //web application config String systemId = "/WEB-INF/faces-config.xml"; stream = context.getResourceAsStream(systemId); if (stream != null) { if (log.isInfoEnabled()) { log.info("Reading config /WEB-INF/faces-config.xml"); } parseStreamConfig(facesConfig, stream, systemId, new FacesConfigEntityResolver(context)); } }

Capture 7.1: FacesConfigFactoryBase.parseFacesConfigFiles(...)

I reworked the formatting to make it look better in the book, but other than that change, this is the code. There are five basic things done by this code. First, the default set of configuration parameters is loaded, and then it searches for the factory configuration. Then, any JAR files are searched, the context initialization files are searched, and finally the faces-config.xml file is parsed.

This code works fine and presents no problem to the compiler or at runtime; there are no bugs in it. So the next question that should be brewing in your head is ‘why fix what ain’t broken?’ Well, the code works, but it’s ‘broken’ in a subtle way that might not be obvious at first. Since the method is so long, it’s hard to know exactly what the method is supposed to do. There is so much going on in the method that the original intent of the method is not clear. That is where refactoring comes in— specifically, Extract Method refactoring.

We can take the code that is in this method and break it down into at least five smaller, easier-to-understand methods and have parseFacesConfigFiles call these methods. The perceived functionality of the parseFacesConfigFiles method will not change, but the understandability will change considerably. So let’s consider the first application of Extract Method. Here is the code for ‘loading the standard faces-config’ stuff.

Note: Please don’t get hung up on the particulars of what is happening here. The code is complex, makes several calls, and has several local variables. Keep focused on the big picture here. We are extracting a group of statements into a smaller method, and then calling that smaller method from the original method so that the original method is easier to understand.

InputStream stream;

Page 202: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Refactoring 191

//First of all load the standard faces-configstream = ClassUtils .getResourceAsStream(STANDARD_FACES_CONFIG_RESOURCE);if (stream == null) { throw new FacesException( "Standard faces config " + STANDARD_FACES_CONFIG_RESOURCE + " not found"); }if (log.isInfoEnabled()) { log.info("Reading standard config " + STANDARD_FACES_CONFIG_RESOURCE);}parseStreamConfig(facesConfig, stream, STANDARD_FACES_CONFIG_RESOURCE, new FacesConfigEntityResolver());

If we want to apply the refactoring manually, we have to go through the following list of steps as outlined in [Fowler 2000].

1. Create a new method named after the intent of the code—in this case, we would use something like loadStandardConfiguration().

2. Copy the extracted code from the source method into the new method.

3. Scan the extracted code for references to any variables that are local in scope to the source method. These become local variables or parameters to the method. Even without the Eclipse tooling to perform this refactoring, we would still get a lot of help from Eclipse in the form of red squiggly lines under the unknown variables.

4. If any of the identified variables are used only within the new method, then declare them as local variables.

5. A single, locally modified variable can be returned to the caller. If more than one variable is modified, then more work needs to be done upfront to make this code suitable for Extract Method. (Fortunately for the current example, we don’t have to worry about this.)

6. Any remaining variables that are not defined in the new method should be passed in as parameters.

7. Replace the original code with a call to the new method.

8. Compile and test.

So, to follow these steps for the parseFacesConfigFiles code, we would first create the new method, private void loadStandardConfig(), and copy the code from the original method to this new method. Once done, you will notice that the facesConfig variable is undefined, so this becomes a possible parameter or local variable. (Since this variable is defined outside the code we copied, it should be a parameter.) Notice also that the stream is not only used in the new method, but also in the old method. The declaration of the stream should remain in both places for the time being. Next, we need to replace the original code (except the declaration of stream) with a call to our new method. Here is the refactored code (with the bulk of parseFacesConfigFiles elided for brevity).

Page 203: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Refactoring 192

private void loadStandardConfig(FacesConfig facesConfig) { InputStream stream; //First of all load the standard faces-config stream = ClassUtils. getResourceAsStream(STANDARD_FACES_CONFIG_RESOURCE); if (stream == null) { throw new FacesException("Standard faces config " + STANDARD_FACES_CONFIG_RESOURCE + " not found"); } if (log.isInfoEnabled()) { log.info("Reading standard config " + STANDARD_FACES_CONFIG_RESOURCE); } parseStreamConfig(facesConfig, stream, STANDARD_FACES_CONFIG_RESOURCE, new FacesConfigEntityResolver());}

private void parseFacesConfigFiles(FacesConfig facesConfig, ExternalContext context) throws FacesException { InputStream stream; loadStandardConfig(facesConfig); ...}

This is the first step of making the original method parseFacesConfigFiles() shorter and easier to understand. Assuming that we have a good set of unit tests in place, we would compile this change and run our unit tests over the updated code. We would expect exactly the same behavior, since all we have really done is move some code around. Assuming that we did get the same behavior, we would move on to the next group of statements that can be extracted and follow the same steps again. As you can see, this is a tedious and error prone procedure that is likely to be faulty the first time. In addition, we have only touched one of the five things this method was doing. Fortunately, Eclipse provides great support to make this much easier.

Page 204: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Refactoring with Eclipse 193

Refactoring with Eclipse

Eclipse currently supports 23 refactorings in an automated way. In the following sections, we will cover 8 of these refactorings, how to apply them, and an example of doing so. In some cases, I will be using code from the MyFaces project as example material so that you can see some real world code and how it is affected by refactoring. In other cases, I’ll be using homegrown examples.

Note: This is just a quick note about the code in the MyFaces project. I chose to use this code because I am starting to work with it in detail again, and thus, I am getting a feel for it. The code in the project is great, well-tested, and works like a champ. Any project that is more than a year old with thousands of lines of code can easily have refactoring opportunities pointed out. So, I just want to make sure that everyone knows that I’m not picking on the MyFaces code.

The remainder of this chapter will be a catalog of each of these refactorings. Each section will document one refactoring and have the following elements.

Element DescriptionRefactoring Name The name of the refactoring also the title of the sectionRefactoring Description A brief definition of the refactoring as well as a discussion of the motivation

behind using the refactoringExample AntiPattern requiring Refactoring

A typical situation in which the need to perform the refactoring might arise and there is code that needs to be refactored

Refactoring Example Discussion of applying the refactoring

Page 205: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Change Method Signature 194

Change Method Signature

This refactoring allows you to change the signature of an existing method by changing any of the aspects of the method that make its signature. You can change the access rights (public, private, etc.), you can change the name as well as add or remove parameters, and you can add or remove exceptions that are thrown from the method.

The most typical use of this refactoring is to add or remove parameters from a method. Over time, you might discover that a method would be better designed or easier to understand or implement if it took an additional parameter. Another reason to use this refactoring is that during the process of refining/refactoring your code, you might discover that you no longer need one of the parameters that is being passed in. In addition to adding and removing parameters, this refactoring is also helpful in changing the access privileges on methods. I most commonly change access rights to be more restrictive (i.e., public to protected or private). Sometimes for retrofitting unit tests onto existing code, however, I change privileges to be less restrictive. This refactoring will change all the subclass implementations of the method to match the new access privilege.

AntiPattern Example

During some recent development on the MyFaces project, I implemented a method and then realized that I needed an additional parameter in order to make the method work properly. Specifically, I added some code to handle some of the factory lookup functionality. At first, I was only interested in getting the algorithm correct and not on how to make sure the actual configuration was set properly. The initial method is shown in Listing 7.2 below.

protected FactoryConfig performMetaInfFactoryConfig( ExternalContext context) throws FacesException { Set factoryNames = FactoryFinder.getFactoryNames(); // keyed on resource names, factory name is the value Map resourceNames = expandFactoryNames(factoryNames); //Search for factory files in the jar file Set services = context .getResourcePaths(META_INF_SERVICES_LOCATION); // retainAll performs the intersection of the factory // names that we are looking for the ones found, only the // services found that match the expected factory names // will be retained services.retainAll(resourceNames.keySet()); Iterator itr = services.iterator(); FactoryConfig config = new FactoryConfig(); while (itr.hasNext()) { String resourceName = (String) itr.next(); InputStream is = context .getResourceAsStream(resourceName); InputStreamReader isr = new InputStreamReader(is);

Page 206: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Change Method Signature 195

BufferedReader br = new BufferedReader(isr); String className = null; try { className = br.readLine(); } catch (IOException e) { throw new FacesException( "Unable to read class name from file " + resourceName, e); } try { Class.forName(className); } catch (ClassNotFoundException e) { throw new FacesException("Unable to find class " + className, e); } config.setFactory((String) resourceNames .get(resourceName), className); } return config; }

Listing 7.2: Initial performMetaInfFactoryConfig

So after completing this code and testing it, I realized that the configuration was not actually being updated. Following the standard of the other code in this class that did similar things, I decided to pass in the configuration object that maintained the overall configuration. And instead of just making the change and then tracking down the compilation problems, I applied the refactoring.

Page 207: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Change Method Signature 196

Apply the Refactoring

The first step in using any of the refactorings is to invoke it via the context menu in the Java editor. For this refactoring to work, you must select a method signature or any line in the method body. You can select anything in the method signature before invoking the context menu. Keep in mind that for a particular refactoring to be available, you must have an element selected that the desired refactoring can effect. Figure 7.1 shows the refactoring menu item selected over the selected method name.

Figure 7.1: Change Method Signature

I almost always end up double-clicking on the method name just to make sure that I’m invoking the refactoring on the correct element.

Page 208: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Change Method Signature 197

After invoking the refactoring, you will see a dialog box that looks like Figure 7.2.

Figure 7.2: Change Method Signature Dialog

Page 209: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Change Method Signature 198

There are two things that need to be changed to have this method match the rest of the methods in the class. First, the return type must be changed to void and an additional parameter of type FacesConfig must be added. Since this method returns a value and the return type is being changed to void, Eclipse will complain that the result of the refactoring will not compile. You can safely hit Continue and then remove the return statement after the refactoring is complete. Figure 7.3 shows the updated values.

Figure 7.3: Change Method Signature Dialog Updated

Notice that the default value for the new parameter must be specified and that it is defaulted to null. For simple values like String or int, you can specify the value in this dialog box. For more complex values, you will have to do some work after refactoring. Now that we have the values changed and specified, click the OK button. After doing some internal checking, Eclipse will report that a void method should not return a value. As stated

Page 210: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Change Method Signature 199

earlier, it is OK to ignore this warning and click the Continue button. After the refactoring completes, the return statement can be deleted. There is one more small change to make; we need to apply the factory configuration to the overall configuration object facesConfig. The refactored code is listed in Listing 7.3

protected void performMetaInfFactoryConfig( ExternalContext context, FacesConfig facesConfig) throws FacesException { Set factoryNames = FactoryFinder.getFactoryNames(); // keyed on resource names, factory name is the value Map resourceNames = expandFactoryNames(factoryNames); //Search for factory files in the jar file Set services = context .getResourcePaths(META_INF_SERVICES_LOCATION); // retainAll performs the intersection of the factory // names that we are looking for the ones found, only the // services found that match the expected factory names // will be retained services.retainAll(resourceNames.keySet()); Iterator itr = services.iterator(); FactoryConfig config = new FactoryConfig(); while (itr.hasNext()) { String resourceName = (String) itr.next(); InputStream is = context .getResourceAsStream(resourceName); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String className = null; try { className = br.readLine(); } catch (IOException e) { throw new FacesException( "Unable to read class name from file " + resourceName, e); } try { Class.forName(className); } catch (ClassNotFoundException e) { throw new FacesException("Unable to find class " + className, e); } config.setFactory((String) resourceNames .get(resourceName), className); } facesConfig.setFactoryConfig(config); }

Listing 7.3: Refactored performMetaInfFactoryConfig

Page 211: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Change Method Signature 200

Once again, it’s important to keep in mind that you don’t need to fully understand all the code shown here. Keep focused on the fact that this refactoring allows you to update the signature of a method by adding or removing parameters, changing the name of the method, updating the access privileges, or adding or removing an exception type.

Page 212: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Convert Anonymous to Nested 201

Convert Anonymous to Nested

This refactoring comes in handy when you are building code with frameworks that encourage the use of anonymous inner classes. An example framework would be Swing. The main motivation behind using this refactoring is to make an anonymous class that is getting prohibitively large into an inner class to ease maintenance and understandability. Over time you might even want to migrate the code into an independent class, but that is another refactoring.

AntiPattern Example

A typical place to use (overuse) anonymous inner classes is in supplying ActionListeners for Swing applications. In many situations, this is a fine approach, but often the amount of code in the actionPerformed(ActionListener) method becomes unwieldy and should be moved to a more manageable and maintainable location. Listing 7.4 has an example bit of code that is way too long to be part of an anonymous class.

public SimpleSwingGui() { JButton hello = new JButton(); hello.setText("Hello World"); hello.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { Sequencer sm_sequencer = null; Synthesizer sm_synthesizer = null; InputStream stream = this.getClass() .getClassLoader().getResource( "com/sb/eclipselive/bach.mid").openStream(); Sequence sequence = null; sequence = MidiSystem.getSequence(stream); sm_sequencer = MidiSystem.getSequencer(); if (sm_sequencer == null) { System.err .println("SimpleMidiPlayer.main(): can't " + "get a Sequencer"); return; } sm_sequencer.open(); sm_sequencer.setSequence(sequence); sm_synthesizer = MidiSystem.getSynthesizer(); sm_synthesizer.open(); Receiver synthReceiver = sm_synthesizer .getReceiver(); Transmitter seqTransmitter = sm_sequencer

Page 213: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Convert Anonymous to Nested 202

.getTransmitter(); seqTransmitter.setReceiver(synthReceiver); sm_sequencer.start(); } catch (InvalidMidiDataException t) { t.printStackTrace(); } catch (IOException t) { t.printStackTrace(); } catch (MidiUnavailableException t) { t.printStackTrace(); } } }); getContentPane().add(hello); }

Listing 7.4: Anonymous Action Listener Class

This code creates a very simple UI with a single button. When the button is pushed, a midi file is played. This code is bad because the length of the method is way too long to justify an anonymous class. I generally try to shy away from anonymous classes anyway, but I especially dislike anonymous classes that have large amounts of code in them.

Page 214: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Convert Anonymous to Nested 203

Apply the Refactoring

There are two options to refactor this code. We could apply Extract Method on the bulk of the music playing code so that it’s in a place all its own and the actionPerformed method could simply invoke that code, or we could extract this anonymous inner class into a class all its own. I prefer making the anonymous class into an inner class because it prepares the way for more refactoring in the future.

In this example, we will apply the Convert Anonymous to Nested refactoring. The first step is to select the inner class. (Your cursor can be anywhere inside the class.) Next, invoke the context menu and invoke the Refactoring -> Convert Anonymous to Nested menu item. You should see a dialog box that looks like Figure 7.4.

Figure 7.4: Convert Anonymous to Nested Dialog

All you need to do is to specify the name of the new nested class and click Complete. Again, if there are any compilation errors, the refactoring machinery in Eclipse will warn you about it (but let you do it anyway). In this example, there are problems, so it will complete without further intervention.

The refactoring will take the existing anonymous class, turn it into a nested class, and replace the anonymous code with a call to the default constructor for the new nested class. Listing 7.5 has the refactored code listed for review.

Page 215: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Convert Anonymous to Nested 204

public SimpleSwingGui() { JButton hello = new JButton(); hello.setText("Hello World"); hello.addActionListener(new MusicPlayingActionListener()); getContentPane().add(hello); }

private final class MusicPlayingActionListener implements ActionListener { public void actionPerformed(ActionEvent e) { try { Sequencer sm_sequencer = null; Synthesizer sm_synthesizer = null; InputStream stream = this.getClass() .getClassLoader().getResource( "com/sb/eclipselive/bach.mid").openStream(); Sequence sequence = null; sequence = MidiSystem.getSequence(stream); sm_sequencer = MidiSystem.getSequencer(); if (sm_sequencer == null) { System.err .println("SimpleMidiPlayer.main(): can't " + "get a Sequencer"); return; } sm_sequencer.open(); sm_sequencer.setSequence(sequence); sm_synthesizer = MidiSystem.getSynthesizer(); sm_synthesizer.open(); Receiver synthReceiver = sm_synthesizer .getReceiver(); Transmitter seqTransmitter = sm_sequencer .getTransmitter(); seqTransmitter.setReceiver(synthReceiver); sm_sequencer.start(); } catch (InvalidMidiDataException t) { t.printStackTrace(); } catch (IOException t) { t.printStackTrace(); } catch (MidiUnavailableException t) { t.printStackTrace(); } } }

Listing 7.5: Refactored Code

Page 216: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Constant 205

Extract Constant

This refactoring is very useful for moving constant values out of your code and instead declaring them as static variables in your class. This is especially important when your code uses the same value in multiple places. A typical constant value that would be a good candidate for applying this refactoring is something like a constant number in a calculation. For example, in a math equation, if your code has 3.14159 in several places then you should probably consider this refactoring. If you ever need to increase the precision of the number, you’d have to change it in all the places it’s typed instead of just once at the constant. Another great use of this refactoring is to get rid of String constants in your code. Instead of having the same String in several places in your code, this refactoring can be used to make the String into a constant that can be defined once and used again and again.

AntiPattern

A classic case of this AntiPattern is typing in a mathematical constant repeatedly. Listing 7.6 has the source code for a class caught in this AntiPattern.

public class Circle {

private double radius;

public Circle(double radius) { this.radius = radius; }

public double getCircumfrence() { return 2.0 * 3.14159 * radius; }

public double getArea() { return 3.14159 * radius * radius; }}

Listing 7.6: Stuck In an AntiPattern

This is a very simple class that keeps the radius of a circle and will calculate the area and circumference of the circle. The problem here is the repeated use of ‘3.14159’ for the constant . If the precision ever needed to be updated, the value would have to be changed in all the places it’s used. This is likely to lead to bugs because the value will probably be changed in many but not all the places required.

Page 217: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Constant 206

Applying the Refactoring

So now that we have seen the code in a bad state, let’s apply the refactoring and watch Eclipse help us clean up the code. The first step is to select the value and activate the refactoring. Figure 7.5 shows the values selected and the refactoring menu item selected.

Figure 7.5: Extract Constant Refactoring Invocation

Page 218: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Constant 207

After applying the refactoring, you will be presented with a dialog box that will ask you for a constant name. Figure 7.6 shows the dialog box.

Figure 7.6: Extract Constant Dialog

This dialog box allows you to not only specify the name of the constant but also the access privileges and the way to apply the refactoring. Typically, when doing this refactoring, you will want to check the ‘Replace all occurrences of the selected expression with references to the constant’ option so that the new constant is used everywhere in your code. The last option, ‘Qualify constant references with class name’, will (as the name suggests) put the class name on the constant to qualify it. In this example, every occurrence of 3.14159 will be replaced with ‘Circle.PI’.

Page 219: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Constant 208

If you click the ‘Preview >’ button, you will be shown the changes before they are actually made. Figure 7.7 shows the preview for this example with the changes that will be made in getCircumference selected.

Figure 7.7: Preview of Extract Constant

Page 220: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Constant 209

Notice that each of the changes can be checked or unchecked. This is another great feature of the refactoring support; almost every refactoring has a preview avalible. The preview shows you the list of changes and what the code looked like before and after if you choose to apply that particular change. If you don’t want Eclipse to make a change, you can selectively remove certain changes. Once you are through reviewing the changes, you can click the OK button and all the selected changes will be made. If you choose not to preview the changes, then every change will be made. The updated code is shown below.

public class Circle {

protected static final double PI = 3.14159;

private double radius;

public Circle(double radius) { this.radius = radius; }

public double getCircumference() { return 2.0 * Circle.PI * radius; }

public double getArea() { return Circle.PI * radius * radius; }}

Listing 7.7: Circle – Refactored

The test for this class should be updated as well so that it makes use of the constant. The refactoring does not know about test classes (nor is it clear that it should know about them), so it is up to us to refactor the test class. We have two choices: we can change all occurrences of 3.14159 in CircleTest to Circle.PI, or we can apply the refactoring again and then change occurrences of CircleTest.PI to Circle.PI. Both approaches have about the same margin of error, so I usually opt for using find and replace over another application of the refactoring.

Page 221: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Interface 210

Extract Interface

This refactoring allows you to create an interface from the methods in an existing class. This refactoring is useful in several different circumstances. First, you might have several classes that have either the same or a very similar API fulfilling the same responsibility. As an example, consider the Circle from the earlier example in the discussion of the Extract Constant refactoring. Assume this class is part of a drawing application that can draw circles or squares. On an initial, simple implementation, you might have a hierarchy that looks like Figure 7.8.

Figure 7.8: Shape Hiearachy

Since the two shapes share a common piece of API, you might consider moving that piece of API into a common interface that both classes could implement.

Another way you might consider applying this refactoring is the case where a class has several different roles for different clients. Going back to the Circle class, in one piece of code the circle could be used as a shape that can calculate its own area, and in another it might be viewed as a persistent object that is able to return an XML stream of its persistent state. You might consider pulling the two responsibilities into interfaces to make the code that requires this responsibility clearer.

Finally, a class might be to restrictive for its client code. In other words, the client code might be better structured if it interacted with the class through an interface instead of a concrete reference to the class. If the client referenced an interface instead of a class, the implementation of the interface could vary, but the client code would remain unchanged. Let’s go into an example to make these ideas a bit clearer.

Circle

getArea() : doublegetCircumference() : double

Square

getArea() : double

Page 222: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Interface 211

AntiPattern

As an example, let’s go back to the Circle and Square shapes in figure 7.8. Both implement getArea in their own way, but there is no abstract way to address this functionality. For example, let’s say that the drawing application must calcualte the total area taken up by a group of shapes. Listing 7.8 has an example implementation that would result from the hierarchy in Figure 7.8.

public class AreaCalculator { public double getTotalArea(List shapes) { double value = 0.0; Iterator itr = shapes.iterator(); while(itr.hasNext()) { Object shape = itr.next(); if(shape instanceof Square) { value += ((Square)shape).getArea(); } else if(shape instanceof Circle) { value += ((Circle)shape).getArea(); } } return value; }}

Listing 7.8: Area Calculator Example

Hopefully your reaction to this code is negative. In particular, think about what happens over time as shapes are added to the mix. For each new type of shape that is added, a new else if statement must be added. This code is very ugly indeed, and it will be very hard to maintain over time. In fact, all the code that interacts with the shapes in this application will have similarly ugly code. If we were to add persistence-based logic to the shapes, the code that handled saving the classes to persistence would require very similar case logic.

Page 223: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Interface 212

Applying the Refactoring

To fix the AntiPattern discussed earlier, we only need apply this refactoring. The refactoring will create an interface that client code can interact with instead of having to apply case logic. In applying the refactoring, the getArea method will be pulled out into an interface called Area. Figure 7.9 is a UML diagram of the refactored classes.

Figure 7.9: Refactored Shape Hiearachy

Circle

getCircumference() : double

Square

AreagetArea() : double

Page 224: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Interface 213

The process will start with Square, although it does not really matter which class we start with. First, we have to apply the refactoring to the Square.getArea() method. Double-click on the getArea() method and then invoke the context menu and select Refactor -> Extract Interface. Figure 7.10 shows the refactoring dialog box.

Figure 7.10: Extract Interface Dialog Box

You should change all references from Square to Area where possible so that the code will get updated in the appropriate places. You can preview and see all the files and code that will change if you’d like to. After the refactoring takes place, a new interface will be created, listed in Listing 7.9, and the new AreaCalculator code is listed in Listing 7.10.

Page 225: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Interface 214

public interface Area { public double getArea();}

Listing 7.9: Area Interface

public class AreaCalculator { public double getTotalArea(List shapes) { double value = 0.0; Iterator itr = shapes.iterator(); while(itr.hasNext()) { Object shape = itr.next(); if(shape instanceof Area) { value += ((Area)shape).getArea(); } else if(shape instanceof Circle) { value += ((Circle)shape).getArea(); } } return value; }}

Listing 7.9: AreaCalculator

Notice that instead of Square the code uses Area now. This is a result of selecting the update everywhere possible check box in the refactoring dialog box. Next, we need to refactor the Circle class to make it implement the Area interface as well. All we need to do is simply add implements Area to the declaration of Circle. Now that that is done, we can further refactor the code in AreaCalculator to use just the Area interface. Listing 7.11 shows the fully refactored AreaCalculator.

public class AreaCalculator { public double getTotalArea(List shapes) { double value = 0.0; Iterator itr = shapes.iterator(); while(itr.hasNext()) { Area shape = (Area)itr.next(); value += shape.getArea(); } return value; }}

Listing 7.10: AreaCalculator

There are a lot of other ways this refactoring can be used, but through this relatively short example you have seen the general flow of applying the refactoring.

Page 226: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Method 215

Extract Method

This refactoring allows you to pull out some lines of code from a longer method and turn them into a standalone method that is then called from the original method. This refactoring is very useful for cleaning up code bloat in methods. The main purpose behind this refactoring is to make methods shorter so that they are easier to understand.

When applying this refactoring, it is very important to think through the naming of the additional methods that will be created. If method1, method2, and method3 come out of this refactoring, then the code is in worse shape than before the refactoring was applied. Make sure to name the methods after what the method is doing.

As you probably recall, this refactoring was applied in the introduction of this chapter to the very long method. In this section, the steps needed to perform this refactoring in Eclipse will be covered instead of the manual process outlined earlier.

AntiPattern

Long methods are the chief indicators of code that needs to have Extract Method applied to it. The underlying reasons can be many. First, the method might not be well-formed (in other words, the method is trying to do too much). For example, a method called saveChanges() should not have code in it that sends e-mail. Another reason for code to be stuck in this AntiPattern is a method that grows over time to fulfill its responsibilities becomes too long to follow. The example at the beginning of this chapter is such an example. The code is listed again here in Listing 7.11 for ease of reference.

private void parseFacesConfigFiles(FacesConfig facesConfig, ExternalContext context) throws FacesException { InputStream stream;

//First of all load the standard faces-config stream = ClassUtils .getResourceAsStream(STANDARD_FACES_CONFIG_RESOURCE); if (stream == null) { throw new FacesException( "Standard faces config " + STANDARD_FACES_CONFIG_RESOURCE + " not found"); } if (log.isInfoEnabled()) log.info("Reading standard config " + STANDARD_FACES_CONFIG_RESOURCE); parseStreamConfig(facesConfig, stream, STANDARD_FACES_CONFIG_RESOURCE, new FacesConfigEntityResolver()); Set factoryNames = FactoryFinder.getFactoryNames(); // keyed on resource names, factory name is the value

Page 227: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Method 216

Map resourceNames = expandFactoryNames(factoryNames); //Search for factory files in the jar file Set services = context .getResourcePaths(META_INF_SERVICES_LOCATION); // retainAll performs the intersection of the factory // names that we are looking for the ones found, only the // services found that match the expected factory names // will be retained services.retainAll(resourceNames.keySet()); Iterator itr = services.iterator(); FactoryConfig config = new FactoryConfig(); while (itr.hasNext()) { String resourceName = (String) itr.next(); InputStream is = context .getResourceAsStream(resourceName); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String className = null; try { className = br.readLine(); } catch (IOException e) { throw new FacesException( "Unable to read class name from file " + resourceName, e); } try { Class.forName(className); } catch (ClassNotFoundException e) { throw new FacesException("Unable to find class " + className, e); } config.setFactory((String) resourceNames .get(resourceName), className); } facesConfig.setFactoryConfig(config);

//Search through JAR files Set jars = context.getResourcePaths("/WEB-INF/lib/"); if (jars != null) { for (Iterator it = jars.iterator(); it.hasNext();) { String path = (String) it.next(); if (path.toLowerCase().endsWith(".jar")) { parseJarConfig(facesConfig, context, path); } } }

//context initialization parameter config list String configFiles = context .getInitParameter(CONFIG_FILES_INIT_PARAM);

Page 228: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Method 217

if (configFiles != null) { StringTokenizer st = new StringTokenizer(configFiles, ",", false); while (st.hasMoreTokens()) { String systemId = st.nextToken().trim(); stream = context.getResourceAsStream(systemId); if (stream == null) { log.error("Faces config resource " + systemId + " not found"); continue; }

if (log.isInfoEnabled()) log.info("Reading config " + systemId);

parseStreamConfig(facesConfig, stream, systemId, new FacesConfigEntityResolver(context)); } }

//web application config String systemId = "/WEB-INF/faces-config.xml"; stream = context.getResourceAsStream(systemId); if (stream != null) { if (log.isInfoEnabled()) log .info("Reading config /WEB-INF/faces-config.xml"); parseStreamConfig(facesConfig, stream, systemId, new FacesConfigEntityResolver(context)); } }

Listing 7.11: FacesConfigFactoryBase.parseFacesConfigFiles(...)

This method is too long, but all of the content needs to be there for parseFacesConfigFiles to function according to its requirements. This is a classic example of where I use Extract Method.

Page 229: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Method 218

Applying the Refactoring

Now that we have a concrete example, let’s walk through how we would apply the refactoring. First, let’s examine the code again to get a better understanding of what it’s doing. There are basically five steps to configuring a JSF application, and each step involves a location for configuration files and configuration information that the JSF implementation (MyFaces in this example) is supposed to look for. Each of the pieces of code is relatively self-contained and is easy to extract. Let’s start with the first section that handles the standard faces configuration files. The code is repeated here for ease of reference.

//First of all load the standard faces-config stream = ClassUtils .getResourceAsStream(STANDARD_FACES_CONFIG_RESOURCE); if (stream == null) { throw new FacesException( "Standard faces config " + STANDARD_FACES_CONFIG_RESOURCE + " not found"); } if (log.isInfoEnabled()) { log.info("Reading standard config " + STANDARD_FACES_CONFIG_RESOURCE); } parseStreamConfig(facesConfig, stream, STANDARD_FACES_CONFIG_RESOURCE, new FacesConfigEntityResolver());

Page 230: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Method 219

In Eclipse, we would select this code and invoke Extract Method refactoring. After invoking the refactoring, you should see the dialog box shown in Figure 7.11.

Figure 7.11: Extract Method Dialog Box

Page 231: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Method 220

From this dialog box, you can specify the signature of the method before it’s generated. Eclipse is very good at getting the parameter list correct, but you have to supply the name. As recommended earlier, make sure you use a descriptive name. After you click OK, the code in the original method will be replaced with a call to your newly created method. If you choose to preview the code, you can see exactly what will be changed. Figure 7.12 shows the preview.

Figure 7.12: Preview of Extract Method

Page 232: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Method 221

To completely clean up this code, you can apply the refactoring to all five sections of the code. After applying the refactoring five times, the code looks something like Listing 7.12.

private void parseFacesConfigFiles(FacesConfig facesConfig, ExternalContext context) throws FacesException { InputStream stream;

// load the standard config stuff from my-faces performStandardConfiguration(facesConfig);

// this might need to use some of the code from the search // through jar files performMetaInfFactoryConfig(context, null); performJarFileConfig(facesConfig, context);

performContextSpecifiedConfig(facesConfig, context);

performWebAppConfig(facesConfig, context); }

Listing 7.12: Refactored Code

Next, let’s look at the opposite method of this refactoring.

Page 233: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Inline Method 222

Inline Method

Inline Method is useful to get rid of excessive Extract Method use. Another case that makes this refactoring useful is when a one-line method is added to a class and used in only one place. If the body of the method is just as easy to understand as the method name, then there is no real reason to have the method.

This refactoring can also be useful as an intermediate step in doing a larger refactoring. For example, consider the case where a method is calling several poorly factored methods. Rather than try to refactor all the poorly factored methods, in many cases it’s easier to pull all the code into one larger method and then refactor that piece of code. After the code is factored, the Extract Method can then be applied to the larger method to extract several smaller well-factored methods.

AntiPattern

As a really simple example of using this refactoring, I will change the implementation of Circle just a little bit. Instead of directly referring to the field radius, an accessor (i.e., getRadius) will be used. The new code is shown here:

public double getCircumference() { return 2.0 * Circle.PI * getRadius();}private double getRadius() { return radius;}

The use of an accessor in this case does not make the code for getCircumference any easier to read; in fact, it kind of makes it harder to read.

Page 234: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Inline Method 223

Applying the Refactoring

So let’s apply Inline Method. Start by double-clicking on the call to getRadius and invoke the Inline Method refactoring (context menu, Refactor->Inline…). You should see a dialog box that looks like Figure 7.13.

Figure 7.13: Inline Method Dialog

I changed the default to change all invocations and then delete the method declaration because the method is pointless. You can click OK or Preview if you want to see what Eclipse will do before it actually does it (probably a smart idea in most cases if you are deleting the method).

After this refactoring is complete, you might notice that the set method is still there. You can use this refactoring to inline ‘set’ method calls as well. Click on the setRadius(radius) code in the constructor and invoke the Inline Method refactoring. Again, I choose to replace all occurrences and delete the method. When this second invocation of the refactoring is complete, Circle will look like it did in Listing 7.7.

Page 235: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Move 224

Move

I use this refactoring probably more than any other in Eclipse. This refactoring will apply to files, classes, methods, or just about any resource in Eclipse. The most typical way that I use it is to move classes that are in the wrong package. The motivation behind using this refactoring varies widely. However, it usually boils down to a class being in the wrong package. It might be that the classes functionality is out of place in its current package, or that the class references many classes in another package and few in the package it’s currently in.

AntiPattern

Classes that need to be moved do not necessarily fall into the category of AntiPattern. Many times, the placement of a class in a particular package is a matter of taste or a total judgment call on the part of the developer. However, a class can come to be more closely aligned with several classes in an alternate package over time. If that is the case, then the class is a good candidate for moving. More often than not, I find myself applying this refactoring because I made a mistake when I created the class but didn’t notice until I’ve already put lots of references to the class in the rest of my code.

Page 236: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Move 225

Applying the Refactoring

To apply this refactoring, select the member you want to move and then select the menu item for the refactoring (context menu->Refactor->Move). You can move classes, methods, and fields in a class. You can also select a file or a package in the Package View and choose Refactor->Move from the context menu. You will get a dialog box like Figure 7.14 if you invoke this refactoring on a class.

Figure 7.14: Move Class Dialog

Page 237: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Move 226

Almost any element or resource in Eclipse can be moved safely through this refactoring. One cool feature of the way this is implemented is that a drag and drop of a class in the Package View is actually an invocation of this refactoring behind the scenes. In fact, you can even move methods around via this refactoring from the Package Viewer. You need to be careful moving methods though because you can end up with uncompilable code. I almost always use the Package Explorer to invoke this refactoring by selecting the class I want to move and dragging it to the new package. Behind the scenes, this refactoring is updating all the import statements and other references that have been made to the class from other files in the workspace.

Page 238: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Rename 227

Rename

This refactoring let’s you change the name of almost any resource or element in Eclipse. The most common use I make of this refactoring is to change the names of methods. The reasons for renaming elements are vast. Mostly, it seems that names are a matter of taste. There are, however, some names that should always be changed. Don’t name things method1 or fielda. These names are very difficult to maintain for developers that come later to keep the code maintained.

AntiPattern

Code with method or field names that are not descriptive should have this refactoring applied. It is very hard to maintain a program where values are named x, a, b, c, etc. I hope that most people are not getting stuck in this AntiPattern anymore. Where I usually use this refactoring is to rename methods that have been changed over time so much that they no longer are named after what is happening in the body of the method.

Page 239: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Rename 228

Applying the Refactoring

As was said earlier, you can rename almost anything with this refactoring. In this example, I will apply the refactoring to a method name. In the test code for the AreaCalculator, there is one test method. The code is listed in Listing 7.13.

public class AreaCalculatorTest extends TestCase { public static void main(String[] args) { junit.textui.TestRunner.run(AreaCalculatorTest.class); }

protected void setUp() throws Exception { super.setUp(); }

protected void tearDown() throws Exception { super.tearDown(); }

public AreaCalculatorTest(String name) { super(name); }

public void testGetTotalArea() { List shapes = new ArrayList(); shapes.add(new Circle(10.0)); shapes.add(new Square(10.0)); AreaCalculator subject = new AreaCalculator(); BigDecimal value = new BigDecimal(subject .getTotalArea(shapes)).setScale(5, BigDecimal.ROUND_HALF_EVEN); BigDecimal expected = new BigDecimal( 100.00000 + 314.15900).setScale(5, BigDecimal.ROUND_HALF_EVEN); assertEquals(expected, value); }}

Listing 7.13: AreaCalculatorTest

Tests are some of my most renamed methods. As I grow the number of tests, the old names have to be reworked to better reflect their more focused nature. For example, let’s say I wanted to test some other cases of shape count to really hammer out if getTotalArea worked in all cases. In particular, I want to add a test that only adds one shape, another test that calls the method with an empty list, and yet another test that calls with a null argument. I need to at least change the name of the existing test to better reflect that it’s sending two shapes to the method.

Page 240: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Rename 229

You invoke this refactoring from the context menu Refactor->Rename. You should see a dialog box like Figure 7.15.

Figure 7.15: Rename Method Dialog

After typing in the new name, you can either preview the changes or just click OK. As usual, Eclipse will update any references to this method in any file in the workspace that references it.

Page 241: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Summary 230

Summary

This chapter has served as an introduction to eight of the refactorings that I use most commonly. I hope that this knowledge will enable you to understand the power of refactoring, as well as how you might apply these eight refactorings to some of your existing code. In the next two chapters, I will finish out the remainder of the Eclipse refactorings. Stay tuned to my blog for updates on when to expect the new chapters.

Page 242: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Chapter 8

Eclipse 3 Live

Refactoring Java Code: Part 2

In Chapter 7, refactoring was introduced as a means to improve the design of existing code without impacting the clients of that code. Eight refactorings were covered, and eight more will be covered here.

This chapter will cover the following eight refactorings:

• Extract Local Variable

• Inline Constant

• Delete

• Promote Temp to Field

• Pull Up

• Push Down

• Self Encapsulate Field

• Use Supertype Where Possible

Page 243: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Refactoring with Eclipse 232

Refactoring with Eclipse

The remainder of this chapter will catalog each of these refactorings. Each section will document one refactoring and have the following general outline.

• Refactoring Name – the name of the refactoring and the title of the section

• Refactoring Description – A brief definition of the refactoring, as well as a discussion of the motivation behind using the refactoring

• Example AntiPattern Requiring Refactoring – A typical situation when the need to perform the refactoring might arise and the code that needs to be refactored

• Refactoring Example – Discussion of applying the refactoring

The refactorings are in alphabetical order.

Page 244: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Local Variable 233

Extract Local Variable

I find the Extract Local Variable refactoring to be particularly useful in cleaning up messy code that has far too much happening on one line. I tend to use this refactoring after I’ve hacked together an implementation of some requirement in a quick and dirty fashion. Once the code is done and I’ve tested it to make sure it works, I then apply this refactoring to clean up the code so I’m not embarrassed to check it into the repository.

Another interesting way this refactoring can be used is to delete redundant code from a method. Consider a call to trim an incoming String argument. In many cases, the result of trim is used inline instead of being made a local variable. If the argument needs to be trimmed for another method call, then trim will have to be called again on the argument. If you find code like this, then using this refactoring is a great way to clean things up quickly and easily.

Extract Local Variable – AntiPattern Example

Consider the following method from the tests for the AreaCalculator (started in the last chapter). This test is asserting that the calculator is responding with the correct total area of the two shapes that are passed into getTotalArea(List).

public void testGetTotalAreaTwoShapes() { List shapes = new ArrayList(); shapes.add(new Circle(10.0)); shapes.add(new Square(10.0)); AreaCalculator subject = new AreaCalculator(); BigDecimal value = new BigDecimal(subject .getTotalArea(shapes)).setScale(5, BigDecimal.ROUND_HALF_EVEN); BigDecimal expected = new BigDecimal( 100.00000 + 314.15900).setScale(5, BigDecimal.ROUND_HALF_EVEN); assertEquals(expected, value); }

Listing 8.1: Test method in need of ‘Extract Local Variable’

There are a couple of places that could stand to be cleaned up in this code. It’s hard to see the actual method that’s being tested because of all the code related to BigDecimal. To make things clearer, the invocation of getTotalArea(List) could be extracted so that the value is in a local variable and then converted to BigDecimal instead of having all that code on one line. It’s also not very clear how the expected value was calculated. This piece of code could be extracted into two local variables (one for the circle and one for the square) to make the code clearer.

Page 245: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Local Variable 234

Extract Local Variable – Apply the Refactoring

This refactoring requires an expression to be selected in order to work properly. The expression can be any valid Java expression. To tie it into the testing example above, see Figure 8.1, where the invocation of getTotalArea(List) is selected and the refactoring is being invoked.

Figure 8.1: Invoking the refactoring

Page 246: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Local Variable 235

The expression ‘subject.getTotalArea(shapes)’ is selected. Eclipse will use the value of this expression to assign the value of the new local variable that is created. Once the refactoring is invoked, a dialog box appears allowing the name of the new variable to be specified. See Figure 8.2.

Figure 8.2: Naming the variable with the dialog box.

There are a couple of really cool things to notice about the way Eclipse handles this refactoring. First, notice the name that it defaulted to, totalArea. This is a result of the method call, getTotalArea. Eclipse is smart enough to look at what you are extracting and default to a decent name—very cool! In addition, it knows the return type of getTotalArea, so the refactoring understands how to type the new variable.

The first check box allows you to replace every occurrence of the expression with the new local variable. This is particularly useful in cases like the trimming of String parameters that were discussed earlier in the AntiPattern section. The variable can also be declared as final if there is a need or desire to do so.

Page 247: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Extract Local Variable 236

If the refactoring is being applied to a large method and the ‘replace all’ option is checked, it is probably better to use the Preview feature so that the changes can be reviewed before being applied. The updated code is listed in Listing 8.2.

public void testGetTotalAreaTwoShapes() { List shapes = new ArrayList(); shapes.add(new Circle(10.0)); shapes.add(new Square(10.0)); AreaCalculator subject = new AreaCalculator(); double totalArea = subject.getTotalArea(shapes); BigDecimal value = new BigDecimal(totalArea).setScale(5, BigDecimal.ROUND_HALF_EVEN); double expectedTotalArea = 100.00000 + 314.15900; BigDecimal expected = new BigDecimal(expectedTotalArea) .setScale(5, BigDecimal.ROUND_HALF_EVEN); assertEquals(expected, value);}

Listing 8.2: Refactored test code

This refactoring could also be applied to the circle and square that are created in the third and fourth lines of code respectively. However, the code would not improve in readability and, in fact, could be more cluttered if these two expressions were turned into local variables. You could apply Extract Method to convert the double values into scaled BigDecimals and greatly improve the readability.

public void testGetTotalAreaTwoShapes() { List shapes = new ArrayList(); shapes.add(new Circle(10.0)); shapes.add(new Square(10.0)); AreaCalculator subject = new AreaCalculator(); double totalArea = subject.getTotalArea(shapes); BigDecimal value = convertToBigDecimal(totalArea); double expectedArea = 100.00000 + 314.15900; BigDecimal expected = convertToBigDecimal(expectedArea); assertEquals(expected, value); }

Listing 8.3: Final form of test code

This example again brings up the fact that multiple refactorings can be applied to greatly affect the readability of the code without changing the behavior.

Page 248: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Inline Constant 237

Inline Constant

Sometimes while writing code, it’s easy to become convinced that a particular value will be needed more than once, so it’s placed in a static final value. After some time of using the class, it becomes apparent that, in fact, the constant is only used in one place anyhow, so the extra static constant value is just cluttering up the code. Another example of when you might want to use this refactoring is if the use of the constant makes the code harder to read than without it.

Inline Constant – AntiPattern Example

Code that needs this refactoring is not really stuck in any formal AntiPattern. The code will work, and even be maintainable over time, with an unneeded constant. The reason you would apply this refactoring is for extra readability in the code. While some may argue that the code is always more readable with the constant, others think the opposite. So if you fall into the camp that says a constant is never a waste, then this refactoring is probably not for you. If you are somewhere in the middle, however, then read on.

One project I was on had defined a rigorous set of coding standards, and one of the points in the standards was to never put a constant value in the code; always pull that constant out into either a public or private static final field of the class where the code lived. Over time, that guideline was seen as pedantic, so it was relaxed. One of the methods I wrote looked like the code shown in Listing 8.4.

public class CheckerBoard implements Area { private static final int SQUARE_SIZE = 3; private static final int SQUARES_PER_SIDE = 8; private final static int SQUARE_COUNT = 64; private List squares; public CheckerBoard() { initalizeBoard(); }

private void initalizeBoard() { squares = new ArrayList(SQUARE_COUNT); for (int i = 0; i < SQUARES_PER_SIDE; i++) { addRow(squares, i); } }

private void addRow(List squares, int rowNum) { boolean even; if (rowNum % 2 == 0) {// even even = true; } else {// odd

Page 249: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Inline Constant 238

even = false; } for (int i = 0; i < SQUARES_PER_SIDE; i++) { if (i % 2 == 0) { // black - red if (even) { squares.add(new Square(SQUARE_SIZE, Color.BLACK)); } else { squares.add(new Square(SQUARE_SIZE, Color.RED)); } } else { // red - black if (even) { squares.add(new Square(SQUARE_SIZE, Color.RED)); } else { squares.add(new Square(SQUARE_SIZE, Color.BLACK)); } } } }

public double getArea() { return (new AreaCalculator()).getTotalArea(squares); }

List getSquares() { return squares; }}

Listing 8.4: Over-used constants

Note: Listing 8.4 is from a contrived example class, CheckerBoard, that keeps the 64 squares for a checker or chess board contained in one object and has nothing to do with my previous project.

In order to make the array big enough to hold all the squares, I declared a constant SQUARE_COUNT. However, that constant is used in only that place, so it’s overkill to have a constant declared for that. In the end, it tends to make the code less readable. This example is a bit contrived—I could use SQUARES_PER_SIDE * SQUARES_PER_SIDE instead of SQUARE_COUNT; however, it does illustrate how we can get overzealous with using constants.

Page 250: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Inline Constant 239

Inline Constant – Apply the Refactoring

Now that we have covered an example using this refactoring, let’s apply it to the code for CheckerBoard. To invoke this refactoring successfully, a constant must be selected. To do so, double-click SQUARE_COUNT and then invoke the refactoring (context menu -> Refactoring->Inline). Figure 8.3 shows the refactoring being invoked.

Figure 8.3: Invoking Inline Constant refactoring

Page 251: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Inline Constant 240

Keep in mind that many things can be ‘inlined’ with this refactoring, and the actual refactoring that’s invoked depends on the context (i.e., what’s selected). If a method is selected, you will be inlining a method. If a constant is selected, then the constant will be inlined, etc. Figure 8.4 shows the dialog that appears to guide you through the refactoring.

Figure 8.4: The Inline Constant dialog box

Page 252: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Inline Constant 241

You can specify to replace either all the references to the constant or just the one selected. In this example, all references will be replaced. Since we only have one reference, there is no real difference in what will happen with the use of the constant. However, all references were chosen since selecting all references enables the check box to delete the constant, as well as replace the references to it. It’s usually a good idea to use the Preview button with this refactoring in real world code because there could be instances of use of the constant that we are unaware of, and the preview of the changes will alert us to theses uses. Figure 8.5 shows the Preview window for this refactoring, and as expected, we are only replacing one instance and deleting the constant.

Figure 8.5: Preview of changes

Remember that you can selectively choose which changes are made with the Preview window. (The Refactoring Preview was covered in more detail in Chapter 7.)

Page 253: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Inline Temp 242

Inline Temp

This refactoring is very similar to the previous refactoring in that it allows you to delete a variable in favor of taking its constant value inline. The motivations for using this refactoring are more or less the same as well. However, I often use this refactoring to undo the creation of a temporary variable. Sometimes I apply the Extract Local Variable refactoring to turn an expression into a local variable, and I then later realize that applying that refactoring made the code harder to understand. When I notice that I’ve complicated the code, I apply the Inline Temp refactoring to undo the wrong.

Inline Temp – AntiPattern Example

In the discussion of Extract Local Variable earlier, we stopped applying the refactoring before we got to the two shapes that are added to the list. For the sake of this example, let’s take the code one step further and make square and circle variables. The code is changed to look like this.

public void testGetTotalAreaTwoShapes() { List shapes = new ArrayList(); Circle circle = new Circle(10.0); Square square = new Square(10.0); shapes.add(circle); shapes.add(square); AreaCalculator subject = new AreaCalculator(); double totalArea = subject.getTotalArea(shapes); BigDecimal value = convertToBigDecimal(totalArea); double expectedTotalArea = 100.00000 + 314.15900; BigDecimal expected = convertToBigDecimal(expectedTotalArea); assertEquals(expected, value); }

Listing 8.5: Over-used local variables

The addition of the two local variables circle and square arguably makes the code harder to read. In addition to over-applying the Extract Local Variable refactoring, sometimes I find myself declaring local variables instead of just making them inline to start with. Both cases are good uses of this refactoring.

Page 254: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Inline Temp 243

Inline Temp – Applying the Refactoring

In order to make the code from listing 8.5 more readable, we will apply the Inline Temp refactoring to clean things up. The first step is to highlight the use of the temporary variable. Figure 8.6 shows the item selected and the refactoring being invoked.

Figure 8.6: Invoking Inline Temp

You may have noticed that this is the same menu item that was invoked to inline constants in the previous refactoring. As you might be coming to realize the Inline menu item can be used to inline many different Java elements. The important thing to keep in mind is that the selected element determines the underlying refactoring that is invoked. If you are not sure what was selected when the refactoring was started, you can always use the Preview to make sure what you expect to be inline is going to be made inline.

Page 255: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Convert Local Variable to Field 244

Convert Local Variable to Field

This refactoring is very useful when you find that you are passing a temporary variable from method to method. For example, you might have a method that performs a calculation and passes the result to another method. In many cases, it might make sense to store that calculated value as a field variable on the class instead of passing it from method to method.

In another case, you might want a value to remain between method invocations on an object. In the case that this value is not stored as a field, the client of the object's API must keep the value and pass it back to the object with each invocation. This makes for very sloppy code and breaks the encapsulation of both the object and its clients. This refactoring will allow the value to be promoted to a field.

Page 256: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Convert Local Variable to Field 245

Convert Local Variable to Field – AntiPattern Example

A class that requires its clients to keep track of part of its state is breaking its own encapsulation, as well as forcing the clients to be incoherent as well. Listing 8.6 shows the Circle class with a method added called calculateCircumference() and another method called calculateArcLength(). Since the arc length requires the circumference to be passed in, the clients of Circle will be forced to keep the circumference.

public class Circle implements Area { protected static final double PI = 3.14159;

private double radius;

public Circle(double radius) { this.radius = radius; }

public double getCircumference() { return 2.0 * Circle.PI * radius; }

public double getArea() { return Circle.PI * radius * radius; }

public double calcualteCircumference() { double circumference = Circle.PI * 2.0 * radius; return circumference; }

public double calculateArcLength(double angle /* degrees */, double circumference) { return (circumference / 90.0) * angle; }}

Listing 8.6: Circle

Any client then needing to calculate an arc length must first ask for the circumference and then pass it to the arc length calculation.

Page 257: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Convert Local Variable to Field 246

Convert Local Variable to Field – Applying the Refactoring

In order to apply this refactoring, a local variable must be selected. Invoking the refactoring is like invoking any of the other refactorings—invoke the context menu and then select Refactoring -> Convert Local Variable to Field. This will bring up the dialog box seen in Figure 8.7.

Figure 8.7: Convert Local Variable to Field dialog box

The dialog box uses the name of the local variable as the name of the field. You can specify the access for the new field that defaults to the expected private. You can also specify where to initialize the variable. Since in this case the value only depends on radius and we are passed radius in the constructor, I chose Class constructor. In addition, the field can be declared as final. Select Preview to inspect the changes that Eclipse will make. After you are satisfied with the changes, select OK to have Eclipse make the changes. However, we are not quite done with cleaning up this code. Since the circumference is now a field, we can use it in the calcualteArchLength() method. In order to change this method, we should invoke the Change Method Signature to remove the circumference parameter. Change Method Signature was discussed in detail in Chapter 7.

Page 258: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Pull Up 247

Pull Up

The Pull Up refactoring allows you to pull existing fields or methods into a superclass. The main motivation behind this refactoring is to reduce duplication across members of the same hierarchy. Consider the class hierarchy in Figure 8.8.

Figure 8.8: Financial Instrument Model

The symbol field is common between the two subclasses (Stock & MutualFund). This field should be moved to the superclass to remove the redundant code. In many cases though, the redundancy was not placed into the classes up front, and the fact that the fields are duplicates is not always so clear. Usually, this is the last step in a much better refactoring. The fact that name is shared between the two classes could very well not be obvious at first. For example, the field Stock.name could have initially been called cusip. So in order to perform this refactoring, you must often look at the code closely and determine that two different variables are being used in the same way and then rename one or the other with the Rename refactoring.

Pull Up – AntiPattern Example

The main AntiPattern calling for this refactoring is redundant code. Commonly, it’s obvious that this refactoring needs to be applied. However, as mentioned in the previous section, it’s not always clear. I find that I usually need to apply this refactoring as part of a bigger refactoring effort. Once I’ve simplified a large or deep class hierarchy, I often spot places to pull either methods or fields up into the superclass. Figure 8.8 is a good example of the result of a large scale refactoring to reduce the complexity of a class hierarchy.

Stock

symbol

MutualFund

symbol

FinancialInstrument

Page 259: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Pull Up 248

Pull Up – Applying the Refactoring

Assuming that we start with the hierarchy shown in Figure 8.8 lets walk through the process of pulling up the field and methods required to make FinancialInstrument a proper superclass. The code for Stock is listed in Listing 8.7, and the code for MutualFund is listed in Listing 8.8.

public class Stock extends FinancialInstrument { private String symbol; public Stock(String symbol) { this.symbol = symbol; } public String getSymbol() { return symbol; }

public void setSymbol(String symbol) { this.symbol = symbol; }}

Listing 8.7: Stock

public class MutualFund extends FinancialInstrument { private String symbol; public MutualFund(String symbol) { this.symbol = symbol; } public String getSymbol() { return symbol; }

public void setSymbol(String symbol) { this.symbol = symbol; }}

Listing 8.8: MutualFund

Page 260: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Pull Up 249

Notice how strikingly familiar the two classes are. Indeed, they are exactly the same. This is intentional to show the point of the refactoring. Again, in the real world, your code is likely to be much more complex and opportunities to apply Pull Up will be harder to spot. The first thing we need to do is pull symbol up into the FinancialInstrument class. To start the refactoring, double-click on symbol in either the MutualFund or Stock class and invoke the context menu -> Refactoring -> Pull Up. You will be presented with the first page of the Pull Up refactoring wizard shown in Figure 8.9.

Figure 8.9: The first page of the Pull Up wizard

Page 261: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Pull Up 250

Notice that it also gives you the option to pull up the accessor methods in addition to the field. Choose both the get and set methods to be part of this refactoring and then hit the Next button. Figure 8.10 shows the next page in the wizard.

Figure 8.10: The second page of the Pull Up Wizard

Page 262: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Pull Up 251

The wizard brings up all the existing subclasses of FinancialInstrument and allows you to pick and choose the items to remove in the subtypes. Also, notice that the wizard shows you the code in the respective elements so that you can compare and make sure that you are not removing some important code that is unique to one of the subclasses. Click around on the various elements so you can get a feel for what you are seeing. Also, make sure to select all the elements in the hierarchy. The next page of the wizard is show in Figure 8.11 and is the same as the Preview window that can be invoked from other refactorings.

Figure 8.11: The final page of the Pull Up Wizard

Page 263: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Pull Up 252

From this page, you can see all the changes that are going to be made and selectively remove some of them if you wish. To finsh the refactoring, hit the Finish button. The final code for FinancialInstrument is lsted in Listing 8.9.

public class FinancialInstrument { protected String symbol; public String getSymbol() { return symbol; }

public void setSymbol(String symbol) { this.symbol = symbol; }}

Listing 8.9: FinancialInstrument

In our example, the constructor could be moved up into this class as well. In a more ‘real world’ example, only a portion of the code in the Stock and MutualFund constructors could be pulled up.

Page 264: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Push Down 253

Push Down

This refactoring is basically the opposite of Pull Up. With this refactoring, you move fields or methods from a supertype into a subclass because the behavior or data don’t make sense to all the subclasses. This refactoring is often used in conjunction with other refactorings in the same way that Pull Up is.

An interesting twist to this refactoring is that sometimes the behavior is required of all subclasses, but it cannot be implemented in the superclass because it’s too specific to the subclasses. In cases when a method is pushed down to one or more subclasses, it should be left as abstract in the superclass.

Push Down – AntiPattern Example

Methods or fields that are unique to a particular subclass are the most typical signs that this refactoring needs to be applied. For example, Figure 8.12 shows the FinancialInstrument hierarchy extended a little to include a market index (i.e., Dow Jones, NASDAQ, etc.) However, it’s not possible to own part of an index, so there can never be a gain associated with an index. The getGain method will have to somehow be made a no-op in the Index subclass.

Figure 8.12: Extended Financial Instrument hierarchy

This group of classes is not well designed, since the getGain method does not apply to all subclasses. The hierarchy needs to be refactored.

Stock MutualFund

FinancialInstrument

symbolgetGain()

Index

Page 265: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Push Down 254

Push Down – Applying the Refactoring

To use this refactoring, either a field or method must be selected. The refactoring is started in the usual way (invoke the context menu->Refactoring->Push Down). Figure 8.13 shows the dialog box used to guide us through this refactoring.

Figure 8.13: The Push Down refactoring dialog box

Page 266: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Push Down 255

Since we are only moving the getGain method, make sure that only that check box is selected. The Edit button will allow you to specify if the method should be removed from the superclass altogether or remain as an abstract method. In this example, it should be moved instead of left as an abstract method. When using this refactoring, you almost always want to use the Preview so that the method is pushed down only to the classes that actually should have the functionality.

Figure 8.14: The Push Down Preview

Make sure to deselect the Index class, since the whole point of this refactoring was to get rid of that method from that class.

Page 267: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Encapsulate Field 256

Encapsulate Field

Depending on your attitude toward private fields, this refactoring is basically a code generator for get/set method pairs or a major boon to assist in achieving encapsulation of data in classes. A typical use of this refactoring is to make fields inaccessible outside the class they are a part of. If you are in the habit of never exposing the fields of your classes, you might think this refactoring is not useful. On the contrary, I use this refactoring often to encapsulate functionality related to a field. For example, some fields have particular ‘valid’ ranges associated with them. Instead of having this information spread over the whole class, I put the validation into a private set method and then call the set method internally from the class any time the value needs to change.

Encapsulate Field – AntiPattern Example

The most common AntiPattern needing this refactoring is using public fields on classes. One of the main tenants of Object Oriented Programming is encapsulation of data behind behavior. If fields are exposed as public data members, then there can be no encapsulation or guarantee of consistency. Listing 8.10 shows the code for a slightly modified FinancialInstrument. Most notably, the symbol field is now public.

public class FinancialInstrument { // should be restricted to 6 characters and no more public String symbol;... public String toString() { return symbol; }...}

Listing 8.9: FinancialInstrument

Since the field is public, there is no need for the accessor pair, so the get/set method pair has been deleted. Even though the longest possible symbol in the system is six characters, there is no way to enforce that because symbol is public.

Page 268: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Encapsulate Field 257

Encapsulate Field – Applying the Refactoring

To activate this refactoring, you need to have a field selected either at the declaration or in any spot that it’s used. Here is some code that makes use of the public symbol field on FinancialInstrument.

public class StockReport { private List portfolio; public String stocksInPortfolio() { String report = null; for(int i = 0;i < portfolio.size();i++) { report += ((Stock)portfolio.get(i)).symbol; if(i + 1 < portfolio.size()) { report += ", "; } } return report; }}

The refactoring is invoked in the usual way with the context menu ->Refactoring->Encapsulate Field. Figure 8.15 shows the dialog box used to guide us through this refactoring.

Figure 8.15: Encapsulate Field dialog box

Page 269: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Encapsulate Field 258

The dialog allows the get and set methods to be named as well as the location in the file for the method to be placed. Also, in the declaring class, you can choose to replace all the instances of the field with a call to the get or set method. I typically leave the internals of the class to use the field, but this is a matter of taste. Figure 8.16 shows a preview of the changes this refactoring will make.

Figure 8.16: Encapsulate Field preview

Notice that the StockReport will be updated to use the get method instead of public access. As was said in Chapter 7, this is the real power of using Eclipse to do refactorings. Since Eclipse knows everything that is involved in making a change from public to private, it can make all the changes itself.

Page 270: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Use Supertype Where Possible 259

Use Supertype Where Possible

This refactoring allows you to make your code use a superclass when all the methods you are invoking are available on the superclass. The motivation behind doing this refactoring boils down to being able to have more than one type respond to the API you are invoking.

Use Supertype Where Possible – AntiPattern

The AntiPattern behind needing to use this refactoring is similar to the AntiPattern behind Extract Interface. However, there is a more abstract type that could be used in this case—it's just not being used. Consider the CheckerBoard example we looked at earlier, but let’s modify it slightly. Instead of delegating to the AreaCalculator class, the CheckerBoard calculates the area on its own. Listing 8.10 has the code for the new CheckerBoard.

public class CheckerBoard implements Area { private static final int SQUARE_SIZE = 3; private static final int SQUARES_PER_SIDE = 8; private List squares; public CheckerBoard() { initalizeBoard(); }

private void initalizeBoard() { squares = new ArrayList(64); for (int i = 0; i < SQUARES_PER_SIDE; i++) { addRow(i); } }

private void addRow(int rowNum) { boolean even; if (rowNum % 2 == 0) {// even even = true; } else {// odd even = false; } for (int i = 0; i < SQUARES_PER_SIDE; i++) { if (i % 2 == 0) { // black - red if (even) { squares.add(new Square(SQUARE_SIZE, Color.BLACK)); } else { squares.add(new Square(SQUARE_SIZE, Color.RED)); } } else { // red - black

Page 271: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Use Supertype Where Possible 260

if (even) { squares.add(new Square(SQUARE_SIZE, Color.RED)); } else { squares.add(new Square(SQUARE_SIZE, Color.BLACK)); } } } }

public double getArea() { double value = 0.0; Iterator itr = squares.iterator(); while(itr.hasNext()) { Square shape = (Square)itr.next(); value += shape.getArea(); } return value; }

List getSquares() { return squares; }}

Listing 8.10: CheckerBoard

In the getArea method, the content of squares is explicitly cast down to Square when it could just as easily be cast down to Area instead. This would become important in the case where the content of the CheckerBoard changes over time. For example, we might want to use Circles someday just for fun. With this code, we’d get ClassCastExceptions being thrown.

Page 272: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Use Supertype Where Possible 261

Use Supertype Where Possible – Applying the Refactoring

You must have a type name selected to start this refactoring—in this case, select Square. The easiest way to do this is to select Square in the getArea method and then navigate to Square (press the F3 key on PCs and Macs, and click context menu->Go To Declaration on all platforms). Invoke the refactoring in the typical way (context menu->Refactoring->Use Supertype Where Possible). Figure 8.17 shows the dialog box that will guide us through the application of this refactoring.

Figure 8.17: Use Supertype Where Possible dialog box

Page 273: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Use Supertype Where Possible 262

Notice that we can specify a particular supertype to apply to the refactoring. The list is actually the same as the Hierarchy view, so if we had a deeper or more complex hierarchy, we could choose from any one of the supertypes that the type selected descends from. In this case, we want to use Area. Click the preview button and you will see a window that looks something like Figure 8.18.

Figure 8.18: Use Supertype Where Possible Preview

After the refactoring is applied, the CheckerBoard will be able to have circles used as the places to move too, and it will still be able to calculate the area.

Page 274: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Summary 263

Summary

This chapter is the second of three on the refactoring support available in Eclipse. From this list of refactorings, you have gained an understanding of the power of refactoring as well as how you can apply these eight refactorings to your existing code. In the next chapter, I will cover the remainder of the Eclipse refactorings. Stay tuned to my blog for updates on dates to expect the last chapter.

Page 275: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Chapter 9

Eclipse 3 Live

Refactoring Java Code: Part 3

In this chapter the coverage of refactorings that are available in Eclipse is completed with the documentation of these final three refactorings.

This chapter covers three refactorings:

• Generalize Type

• Introduce Factory

• Introduce Parameter

Page 276: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Refactoring with Eclipse 265

Refactoring with Eclipse

Each section of this chapter will document one refactoring and have the following outline.

1. Refactoring Name – The name of the refactoring and the title of the section

2. Refactoring Description – A brief definition of the refactoring, as well as a discussion of the motivation behind using the refactoring

3. Example AntiPattern Requiring Refactoring – A typical situation when the need to perform the refactoring might arise and the code that needs to be refactored

4. Refactoring Example – Discussion of applying the refactoring

Page 277: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Generalize Type 266

Generalize Type

The Generalize Type refactoring is intended to help you to use more abstract types instead of their less abstract subclasses. Using a more abstract type rather than a concrete subclass is usually a better design because it leads to looser coupling between types, especially when an interface can be used. Another time to consider using this refactoring is when you are extracting interfaces from your existing classes with the Extract Interface refactoring documented earlier in Chapter 7. When a refactored class mentions many of its methods in an interface, it’s natural to apply this refactoring to the code that used the concrete class before the interface was introduced.

Generalize Type – AntiPattern Example

In many cases, a concrete type might be specified, but only methods from a more abstract supertype are invoked. Here is some code that uses the java.util.ArrayList class when it could easily use the java.util.List interface instead.

public String stocksInPortfolio(ArrayList portfolio) { String report = ""; for(int i = 0;i < portfolio.size();i++) { report += ((Stock)portfolio.get(i)).getSymbol(); if(i + 1 < portfolio.size()) { report += ", "; } } return report; }

The portfolio argument is of type java.util.ArrayList, which imposes a burden on the caller that forces the argument passed in to be of a particular concrete type. Since only the size() and get(int) methods are called, there is no reason to use the concrete type when the more abstract java.util.List type would suffice. If we make this change, the caller is free to substitute any implementation that makes sense. Using the more abstract type (especially with an interface) is much better in many cases because it allows the caller the flexibility to choose the implementation instead of letting the API dictating it.

Page 278: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Generalize Type 267

Generalize Type – Apply the Refactoring

In order to apply the refactoring, you select the variable you want to change the type of then use the keyboard shortcut (Alt-Cmd-T for Macs and Alt-Shift-T for Windows) to bring up the Refactoring submenu. Since this menu knows the context in which it was invoked, only the relevant refactorings are available. Naturally, you want to choose the ‘Generalize Type’ refactoring. See Figure 9.1 to see the menu item open and selected.

Figure 9.1: Invoking the Refactoring

Page 279: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Generalize Type 268

After the refactoring has been invoked, a Type Hiearchy window appears showing the list of possible supertypes you can choose. In this case, since methods are invoked from the java.util.List interface, that’s the highest up the inheritance chain we can go. Therefore, your list should look something like Figure 9.2.

Figure 9.2: Choosing the new Type

As a short aside, let’s redo the example code to only invoke methods from the java.util.Collection interface so that it looks like this.

public String stocksInPortfolio(ArrayList portfolio) { String report = ""; Iterator itr = portfolio.iterator(); while(itr.hasNext()) { report += ((Stock)itr.next()).getSymbol(); if(itr.hasNext()) { report += ", "; } } return report; }

Page 280: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Generalize Type 269

When the refactoring is invoked on this bit of code, it recognizes that the methods invoked are only from the java.util.Collection interface, so the more abstract interface is enabled in the list of choices and is selected by default. This is shown in Figure 9.3.

Figure 9.3: Smart Default Choice

After choosing the interface to retype the variable to, click the OK or Preview button. Of course, if you choose to preview the changes, the Preview window will appear, showing you the changes that Eclipse is going to make to your code. For complex refactorings, it’s wise to preview your changes. However, for simple changes (like this example), it’s fine to just click OK.

Page 281: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Introduce Factory 270

Introduce Factory

This refactoring allows you to interject a level of indirection between the creation of a new object and the code that needs the new instance. This refactoring is used to remove dependence on a constructor from client code. In other words, adding a factory method will introduce a static that will create the object and will make the constructor private. Applying this refactoring will make the client code non-dependent on the constructor. I find this factory particularly useful in moving toward an Abstract Factory pattern implementation. As an example, consider the creation of document objects illustrated below in Figure 9.4.

Figure 9.4: Document Model

In this application, documents in RTF, Word, or plain text format can be opened. Initially, this hierarchy was implemented with public constructors on each class, forcing the client to discover the type of document before creating the object to represent the document. Applying this refactoring will allow the client code to call the introduced factory method instead, relieving the client code from having to discover the document type.

Another example of using this refactoring is converting a class to a singleton. I often discover that a class that I’m instantiating is really a shared instance. When I discover a class like that, applying this refactoring provides a clean way to convert that class to a singleton.

DocumentDocument(InputStream)

RTFDocumentRTDFocument(InputStream)

DOCDocumentDOCDocument(InputStream)

TXTDocumentTXTDocument(InputStream)

Page 282: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Introduce Factory 271

Introduce Factory – AntiPattern Example

As an example, the code described above in Figure 9.4 will be presented as an example of the AntiPattern. Keep in mind that this code is for illustration only and not meant to be the way a real document hierarchy would be coded. Consider the following client code.

private Document openDocument(InputStream input) { int type = discoverType(input); Document doc = null; if (RTF == type) { // decode RTF and use values to initialize // an RTFDocument - several lines of code } else if (DOC == type) { // decode DOC and use values to initialize // a DOCDocument - several lines of code } else if (TXT == type) { // decode TXT and use values to initialize // a TXTDocument - several lines of code } return doc; }

This method is responsible for opening documents; however, the code is too intimate with the details of how documents work. The code discovers the type of the document, but document typing clearly should be part of the document hierarchy instead of kept in some manager class. Given the breakdown of the Document abstraction, it’s likely that this code would be repeated somewhere in the application. Let’s look at a way to fix this code by creating an Abstract Factory to create documents for us.

Page 283: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Introduce Factory 272

Introduce Factory – Apply the Refactoring

The first step is to apply the Introduce Factory refactoring to each of the classes in the hierarchy. To apply the refactoring, select the constructor and choose the Context menu > Refactoring > Introduce Factory, or invoke the Refactoring Context menu and choose Introduce Factory. Figure 9.6 shows the dialog box that’s displayed when the refactoring is invoked.

Figure 9.5: Invoking Introduce Factory

Notice that the factory class has been changed to Document from TXTDocument. With a large source base, it’s probably a good idea to make sure what the refactoring is going to do by previewing the changes before they are applied.

After all the constructors have been made private and a factory method has been introduced for each document type on the Document class, the code in the Manager class can be simplified to simply create a document based on the type.

private Document openDocument(InputStream input) { int type = discoverType(input); Document doc = null; if (RTF == type) { doc = Document.createRTFDocument(input); } else if (DOC == type) { doc = Document.createDOCDocument(input); } else if (TXT == type) { doc = Document.createTXTDocument(input); } return doc; }

Page 284: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Introduce Factory 273

This code still suffers from the disconnected nature of the Document abstraction. It would be better for the discoverType(InputStream) method and even the openDocument(InputStream) method to belong to the Document class instead of the Manager class.

In order to archive the final step in cleaning up this code and making the Document abstraction more coherent, you could use Move refactoring (the Move refactoring is documented in detail in Chapter 7) to move both the discoverType and openDocument methods over to the Document class. Then the Manager class would simply delegate to the Document class.

Page 285: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Introduce Parameter 274

Introduce Parameter

The Introduce Parameter refactoring allows you to add parameters to methods based on a selected expression in the body of the method. You would apply Introduce Parameter in situations where you discover that you need added flexibility in a method that was not known when the method was first written. A typical application would be hardcoding a value in a method either with the value inline, as a local variable, or as a static final variable on the class. If the method needs to have that value as a parameter, applying this refactoring will copy the value out to all the calls. Then, you can change the value in the location that it was discovered to something other than the constant that was introduced in the refactoring.

Introduce Parameter – AntiPattern Example

Needing to apply this refactoring is not necessarly a sign of bad code—it’s more of a reflection of changing requirements. Nevertheless, here’s an example that shows refactoring to add an additional parameter.

public String stocksInPortfolio(Collection portfolio) {String report = "The stocks in this portfolio = ";Iterator itr = portfolio.iterator();while (itr.hasNext()) {

report += ((Stock) itr.next()).getSymbol();if (itr.hasNext()) {

report += ", ";}

}return report;

}

The initial string in this method is fine for the most part, but if you discover later that some reports need this exact string and others need some other string the code would not work as is. To often people copy and paste the code and add a parameter to the copy. This is wasteful and one of the most common AntiPatterns.

Page 286: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Introduce Parameter 275

Introduce Parameter – Applying the Refactoring

The first step to apply this refactoring is to select the value that needs to be turned into a parameter (specifically in this example the text that reads “The stocks in this portfolio = ”. After the value (“The stocks in this portfolio = “) is selected, bring up the Refactoring menu with the Context menu > Refactoring, or use the Refactoring shortcut (Alt-Cmd-T for Macs and Alt-Shift-T for Windows) to bring up the Refactoring Context menu. Figure 9.6 shows the Introduce Parameter dialog box that’s displayed after the refactoring is invoked.

Figure 9.6: Introduce Parameter Invocation

After the refactoring is invoked, the code is changed to look like this.

public String stocksInPortfolio(Collection portfolio, String introText) {

String report = introText;Iterator itr = portfolio.iterator();while (itr.hasNext()) {

report += ((Stock) itr.next()).getSymbol();if (itr.hasNext()) {

report += ", ";}

}return report;

}

Page 287: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Introduce Parameter 276

The transformation is more or less what was expected. The really cool thing is that all the code that calls this method is updated to reflect the value that was inside the stocksInPortfolio method. Here is the updated method in the test code.

public void testStocksInPortfolio() {StockReport reporter = new StockReport();ArrayList stocks = new ArrayList();stocks.add(new Stock("IBM"));stocks.add(new Stock("APPL"));assertEquals("IBM, APPL",

reporter.stocksInPortfolio(stocks, "The stocks in this portfolio = "));}

An astute observer will notice that this test will fail because the return value from stocksInPortfolio will have the string constant prepended to it. To fix this quirk, use the Extract Local Variable refactoring to turn the string value into a local; then, use that local in the assertEquals method call.

Page 288: Eclipse Live: A Guide to Creating Java Applications Using Eclipse

Eclipse 3 Live

Summary 277

Summary

This marks the end of the third and final chapter on refactoring support available in Eclipse. From this list of refactorings, you have gained an understanding of the power of refactoring as well as how you can apply these three refactorings to your existing code. Now that you are fully armed with the list of refactorings available in Eclipse, you can transform your designs safely. I hope you enjoy using the refactoring support in Eclipse as much as I do.