|
Articles in this issue :
|
Myths and facts of "Java Everywhere"
By Michael Yuan
The big news from this year's JavaOne conference is "Java Everywhere". What
exactly is "Java Everywhere"? It seems that there are a lot of confusions or
even resistance from existing Java developers. I attempt to clarify some myths
through a series of FAQs in this short article. You can post comments to the J2ME
forum and I will read them. :) Please note that I do not work for Sun
Microsystems. So, the opinions expressed here are only mine.
- Is "Java Everywhere" only about J2ME? No. "Java Everywhere" is
about world domination! The idea is to have a single architecture for all
computing needs from servers to clients, from components to grids. It includes
Java on server (J2EE), desktop (J2SE and Swing/SWT), smart devices (J2ME) as
well as smart cards (JavaCard). They all work together to provide a consistent
platform to make life easier for us developers. The fact that J2ME
seems to be the mostly mentioned these days is because the large un-tapped
market on the client side. With smart devices shipment twice the PC shipment
this year, Windows PCs will no longer be the dominant client platform for
Internet applications. That has finally given us a chance to break the
Microsoft monopoly. (Well, Microsoft is making a major effort to make sure
that the PocketPC/SmartPhone OS and the .NET Compact Framework will dominate
the device space. But that is another story).
- Is "Java Everywhere" about consumer games? Not at all. Mobile
gaming is one of few IT markets that are still fast growing (hence new job
opportunities). Mobile games today are mostly casual games. With more powerful
devices and new APIs such as the J2ME 3D API, action games on handsets are
just around the corner. However, gaming is only one piece of the overall
picture. Adding mobility to enterprise applications is another potentially
explosive growth section. If mobile entertainment allows you to kill time
where waiting in a line in the airport, mobile enterprise will allow you to
avoid the line altogether. This switch from "toys" to "tools" has fueled the
growth of Java itself in the past 8 years.
- How big is the "Everywhere" market, anyway? No one knows, but it
will be bigger than anything we know of. I will just give you one number: the
cell phone ringtone download business in Europe alone is $1.4 billion dollars
for the last year. In contrast, the entire global J2EE server market is $2.25
billion. Ringtone downloading is a very "dumb" service and offers very limited
value to customers. Imagine the market size of hundreds of billions of smart
Java devices accessing J2EE backends and paying for the service on a
monthly basis. J2ME operator Vodafone is the biggest company in
Europe and J2ME applications are already the second largest revenue source
for its content services.
- I am an enterprise developer and I am not interested in client
software. How is "Java Everywhere" relevant to me? Well, it is about your
job security. :) The good news is that you do not have to switch your career
to develop client software. "Java Everywhere" brings growth to the otherwise
saturated J2EE application server market. We need scalable and high
availability servers to connect and drive those smart devices. This new type
of J2EE applications are often different from today's HTML browser based
applications. But with some knowledge of the new end-to-end application
paradigm and common J2EE patterns (e.g. the MVC, facade etc.), you can get
started fairly quickly.
- I am an enterprise developer, do I have to start over to learn
J2ME? No. In fact, the best feature of "Java Everywhere" is that it makes
it easy for existing Java developers to migrate to new application areas. For
example, familiar Java language features and all major IDE tools are available
in J2ME. In addition, many design patterns, best practices and performance
tips can still be applied with little modification. Vendors like Nokia, IBM
and Sun are making major efforts to provide end-to-end Java tools and
educational programs for existing Java developers. However, we have to note
that "Java Everywhere" emphasizes on developer skill transfer rather than
cross-platform runtime environments. Due to the physical constrains of small
devices, many J2SE API libraries are not available in J2ME profiles or are
only available in a lightweight form. We have to investigate and understand
those changes.
- Mobile commerce has already failed. Why bother J2ME? Some people
confuse the "Java Everywhere" push as another "hype" for the WAP-based mobile
commerce, which was widely considered a failed experiment a couple of years
ago. But we do learn from the past. J2ME and smart clients represents a
completely different application paradigm than WAP/WML. It takes in account
the reality of today's wireless network, user requirement and takes advantage
of new device innovations. In short, it is completely different from WAP. For
more information, you can read this
thread in the J2ME forum.
"Java Everywhere" is about world domination and new opportunities for
developers. Interested readers can refer to my new book "Enterprise J2ME" (Coming out this
fall from Prentice Hall) for more information. In the meanwhile, please continue
the conversation in the
Saloon.
Return to Top
|
An Interview with Tim O'Reilly
by Frank Carver
JavaRanch:
I'd like to introduce Tim O'Reilly. Tim O'Reilly is the founder and CEO of
O'Reilly & Associates, thought by many to be the best computer book
publisher in the world. O'Reilly also publishes online through the O'Reilly
Network (www.oreillynet.com) and hosts conferences on technology topics. Tim is
an activist for open source and open standards, and an opponent of software
patents and other incursions of new intellectual property laws into the public
domain.
I've caught up with Tim as he grabs some lunch before hitting the podium to
talk to the geeks and eggheads at British Telecom's sprawling Adastral Park
research site.
Let's start with a question on many of our minds here at the 'Ranch: How's
the Java book market going, and in particular what about Kathy and Bert's "Head
First Java"? Tim O'Reilly:
Over the last few years, many Java books such as "Java in a Nutshell" have
been big sellers for us, but we have seen a drop-off in the current economic
situation. Programming books have been especially hard hit, with a lot of the
book dollars moving to other subjects -- new operating systems like Windows XP
and Mac OS X, and new application areas like digital photography. For example,
Java was the #1 book category for one of our UK distributors last year, but the
#16 area for them for the same period this year. "Head First Java" is an amazing
book, but it's too soon to tell how much of an impact it will have. I'm sure
that if we'd released it a couple years ago it would have been the #1 computer
book bestseller, which would have put it on everyone's radar right away. Now it
will just have to settle for being the #1 Java book :-), which means that it
will take a bit longer for this approach to take over the world.
What's exciting is that the Head First concept is applicable to far more than
just Java. It's a whole new approach to teaching complex subjects in which
mastering key concepts shapes subsequent learning. It can make very difficult
subjects interesting and engaging, and can help even experienced developers to
understand their tools more deeply. The question now, is what other areas can we
give the "Head First" treatment. I'm open to suggestions! We're doing a lot of
brainstorming with Kathy and Bert, because we believe they've come up with
something really brilliant that can be used in so many
ways. JavaRanch:
How about other alternative approaches? The "hacks" series? Tim
O'Reilly:
Oh, the "Hacks" books are doing really well. "Google Hacks" was a number one
seller for something like four months. Linux Server Hacks and Mac OS X Hacks
have also been bestsellers. We have a new wave coming out this fall, including
Amazon Hacks, EBay Hacks, Wireless Hacks, and Windows XP Hacks.
For those of you who don't know them, the "hacks" books are full of short,
fun, things to do, things that are both useful and catch the imagination.
A book about Google might seem strange if you think of Google as just a web
site. But it's not, its part of the new paradigm of software on the internet
platform. You can't buy it or download it, but you probably use it every day,
and there's a big difference between how beginners use it, and how experts use
it. And that spread is what we thrive on. JavaRanch:
Thanks. That leads quite neatly to another of my questions. How are you
tackling the economic slowdown and the challenge of the internet? I've heard
several people say that they don't buy tech books any more, it's all in
Google! Tim O'Reilly:
Of course there's been a change in buying patterns, but I couldn't say how
much of that's due to the economy or the internet. Maybe in the long term we'll
be able to look back and see trends. Reference books are down, but tutorials are
still selling. People still like to have tutorials you can read in the bath. And
people are always looking for succinct collections of expert knowledge -- it
takes a lot of searching to accumulate the kind of depth that we have in our
books.
We are also at the forefront of technical publishing on the Internet, with
both Safari and the O'Reilly Network. The O'Reilly Network is a free
ad-supported network of online technical information sites, including
www.oreillynet.com, xml.com, www.perl.com, and many others. More recently, we've
started doing some "private label" online information sites partnering with
corporate customers. The first of these, and most relevant to your audience, is
java.net, which is sponsored by Sun. O'Reilly is producing the site, and
developing all the content, as well as managing weblogs and other forms of user
interaction. Over time, we may add some subscription-based sites to the O'Reilly
Network as well.
Safari (safari.oreilly.com) is a subscription-based reference library online,
containing well over a thousand books from O'Reilly, Addison-Wesley,
Prentice-Hall, Peachpit, Cisco Press, Macromedia Press, Adobe Press, and many
other leading publishers. Interestingly enough, I got Pearson (the UK company
that's the parent of all those non-O'Reilly imprints listed above) interested in
partnering with us on Safari using an argument based on the ideas of "crossing
the chasm". You start by gaining a small beach-head in one area, dominate that
market, and then grow out to other markets. And the market we originally
targeted to dominate was online publishing for Java. What I saw specifically was
that if you had O'Reilly, Addison Wesley, and Prentice Hall books on one online
site, you'd effectively have almost all the Java books that mattered. So we
hoped to make this a must-have service for Java developers. Of course, it grew
beyond that, and now, between O'Reilly and Pearson, Safari pretty much covers
the computer tech books field. Right now the service mirrors the structure of
existing books, but what we've created is a huge database of high quality
technical content. Over time, it will become possible to offer services based on
that content, services that go way beyond just reproducing the books
online. JavaRanch:
Wow, So what's your opinion on the advantages of the screen vs paper? Can you
see publishing moving on to the internet completely? Tim O'Reilly:
Well, there's no way that paper publishing will go away, at least not till we
have "books" with "e-paper" that can be reloaded electronically with different
texts. The user interface of the book is just too good for many types of task.
That being said, though, what we really need to do is to ignore formats, and
think about functions. A "book" is a form factor, but it covers a lot of
different ground -- reference works, tutorials, entertainment. And translating
those functions to a new medium will mean massive changes to how we
think about electronic publishing. Most of the current eBook efforts are doomed
to failure because all they do is try to reproduce what happens on the printed
page. It's like claiming that the way to make a movie is to point a camera at a
stage play. We know that's crazy. The medium has way more possibilities than
that.
We're already well beyond eBooks for many of the functions formerly performed
by books. MapQuest and sites like it have put a lot of pressure on Atlases and
maps; google and the entirety of the web put encyclopedias out of contention.
Amazon has replaced the specialized "Books in Print" as the definitive source of
bibliographic information. And can you deny that Everquest is successor to the
fantasy novel? One that's presented in an immersive way that makes use of what
the network platform has to offer. It makes the idea of an e-book reader with
the straight text of "Lord of the Rings", or "Harry Potter" seem very weak in
comparison. And I'll note that forward-looking authors like Tom Clancy not only
write books, they produce movies and have companies to build computer games
based on their characters and stories. George Lucas gets novelised versions of
his movies, and has games like Star Wars Galaxies to boot. All of that is why
I'm saying that eBooks per se are too limited a concept, and that what really
interests me about Safari is how much more we can do once the technical
information database it represents gets rich enough. JavaRanch:
I think we're running out of time here, but have you got any general
comments? Tim O'Reilly:
If there's one thing you need to remember it's not to miss the important
things by taking them for granted. A lot of people, even a lot of smart people,
are stuck in the old paradigm of software as something that is delivered on a CD
or on a personal computer. The real "killer apps" of the internet are not like
that. For example, the fundamental paradigm shift represented by the internet is
missed by many people in the open source software community. The killer apps of
the internet are all built on top of Linux and use many other open source tools
but are not themselves open source. A licence like the GPL which lays down what
you must do when you distribute your software is simply never triggered.
The code for Amazon is not open source, but it wouldn't be much use even if it
was. The value in the system is not in the proprietary source code, but in the
data and the network effect created by the large user base contributing to that
data.
The applications that are winning in the internet arena are the ones that
make use of open source software to drive their costs down, but then harness the
power of a collaborative community to help build their product on top of that
open source base. They make money and survive not by selling software but by
selling user benefits and encouraging the users to help their system grow. Every
time you browse from one book to another, or write a review, or any of the
things you can do with Amazon, you increase the quality and usefulness of their
site. Every time you add a link on your web site you add to the power of Google.
And this is a constant process. Microsoft may be on a three-year release cycle,
but Amazon and Google are updated every day. JavaRanch:
Thanks very much, Tim. I'll let you get back to your lunch, now! Tim
O'Reilly:
Thanks.
Return to Top
|
An Interview with Andy Hunt and Dave Thomas, authors of The Pragmatic Programmer
by Julia Reynolds
The following interview was originally published in the Middle Tennessee
Java User's Group (MTJUG) June 2001 newsletter. (Included here with permission.)
In 1999 Andy Hunt and Dave Thomas published their common-sense guide to software development, The Pragmatic Programmer.
MTJUG: What led you to write The Pragmatic Programmer?
DAVE: When Andy and I first started programming together, it was obvious that we had a very compatible way of working. We'd both come up with many of the ideas that later made it into Pragmatic Programmer. During the next couple of years, we refined these into a pretty consistent set of practices. However, they were implicit: we hadn't written anything down--these were just things that we did every day. At the same time, we realized that much of this was new to others, and we
were spending a lot of time explaining what we were doing every time
we met a new project team. All this lead to the idea that maybe we
should write down what we were doing. We thought it would be a pretty
quick process, but in the end it took the two of us over a year.
MTJUG: In your book you talk about the need for Pragmatic Programmers to invest regularly in their knowledge portfolios. You suggest a goal of learning a new language every year. What new languages are you interested in and would suggest others learn more about?
ANDY: Ruby is the latest language that has piqued our interest (see www.ruby-lang.org).
It combines the grace and elegance of Smalltalk with the sheer usefulness of
Perl. I recommend it highly.
DAVE: I'm planning to spend more time playing with Smalltalk this year. In particular, I want to get familiar with Squeak and the morphic
interface.
MTJUG: Which languages would be best to complement the knowledge portfolios of those of us specializing in Java development?
ANDY: That's a tough question. For sheer mind expansion, you might want to look at a dynamically typed language. Ruby comes to mind, of course. Dylan and/or Self might be interesting just to see some other approaches inheritance and such.
DAVE: I'd also suggest Java developers have a look at AspectJ, which is not so much a new language as an add-on to Java, allowing functionality to be spliced in to existing code. Again, it's a different way of looking at things.
In general, look for languages with different philosophies to the ones
you're using. Have a look at Haskell, or Self, or Smalltalk, and see
what good ideas you can bring back to your existing environments.
MTJUG: In Chapter 3, The Basic Tools, you suggest some very specific steps developers should take to master the tools of their trade. Of course, readers can't help but wonder which specific tools you favor in each of the toolsets you discuss. Specifically:
What is your favorite text editor? (Obviously, readers of your book will know it's NOT Notepad!)
ANDY: My favorite editor is vi, hands down. I like some of the features that Emacs has to offer, but I cannot stand the "emacs-pinky" syndrome. I actually use XEmacs in Viper mode, which gives me the best of both worlds -- vi key mappings in a XEmacs environment.
DAVE: Andy of course has a quaint affection for vi, but there is clearly only
one true editor, Emacs. I'm personally using XEmacs 21.4 for just about everything
(e-mail, development, html and xml writing). Before that, I really liked the
simplicity of Brief and Crisp. (Truth be told, I'm also pretty fluent in vi,
but don't tell Andy :) Being able to create and manipulate text is key to our
job: get good enough with whatever editor you use so that it becomes subconscious.
You'll find that this makes things such as refactoring far, far easier.
MTJUG: You emphatically advocate the use of Source Control for everything. What's your favorite Source Control System?
DAVE: We've been using CVS for a while now: it's open source, distributed, multi-platform, and it doesn't enforce locks. It has its warts, but it really works very well in a multi-site, multi-checkin-a-day environment.
MTJUG: What is your opinion of visual IDEs like JBuilder or NetBeans?
ANDY: I don't do a lot with them, personally. For drag-and-drop to create a user interface, they're great, but for integrating into a sophisticated build environment, you may have some troubles. It also seems to be more difficult to integrate code generators and such into that kind of environment.
DAVE: I have mixed opinions about them. On the one hand, I think they are great tools, vastly simplifying the development of complex
applications. I know that half of the software we all use every day
wouldn't exist if developers couldn't rely on the power of IDEs.
At the same time, I find they have troubling aspects. One is that they
dumb-down programming somewhat. If an IDE has a "create table" wizard,
developers are fooled into thinking they are designing a database. If
an IDE provides code to link fields to bean attributes, developers
feel they're doing transactional programming. But behind the scenes,
complex things are going on to support this simplicity. Sometimes you
just _have_ to understand this complexity to make things work
reliably. But the IDEs kind of give you the feeling that they're
taking care of it all. We've seen this on recent projects, where
developers used IDEs to write database access software that lacked any
kind of transactional integrity. When we asked them about it, some of
the developers didn't know what a transaction was. IDEs make things
simple, but sometimes you need complexity. I'd rather see developers
start off their careers by coding things themselves, and then cut
over to using the wizards in an IDE once they understand what's really
going on.
MTJUG: You address unit testing in detail. Do you subscribe to the XP principle of coding the unit test first, before creating the module code?
ANDY: I think it's a great idea, I've even done it on occasion :-). The nice part about this approach is that it forces you to consider the interface right off the bat ? and I mean *really* consider the interface, because you have to actually use the code you are about to write. Being forced to "eat your own dog food" this way suddenly makes you aware of "oh, it would be much handier for users of this
code if I did it *this* way instead..."
DAVE: I personally use a hybrid scheme. When I'm first writing a new class,
I don't bother to do the XP 'write a test that fails' business, I just
write the empty class definition. However, at that point I typically
do drop into something approaching test first. I'll write the test
framework for that class, and check that it all links together.
Typically the first test will be simply constructing an object.
Then I'll start writing tests and supporting code. I don't write
strictly test first, because often in Java that's not possible. In
particular, exception handling can really mess up testing: one line of
code can potentially create three or four different exceptions, and I
typically like to write test cases that check the handling of each. I
find this isn't too dangerous: I can keep the need to write the four
tests in my head while I'm coding. For more complex stuff I'll write
placeholder tests:
public void testMyNewFeature() {
fail("test missing");
}
If I forget to go back and fill them in I'm told the first time they
run.
I definitely feel that good unit testing is a skill that should be promoted
more. Without a complete set of meaningful unit tests, altering code can become
a crap shoot. And designing code to be tested really does load to a better structure.
MTJUG: You mention eXtremeProgramming at several points in your book. Can you describe the development process you follow when working on project teams?
ANDY: Well, it's different every time: sometimes subtly different, sometimes wildly. Alistair Cockburn's notion of "a methodology per project" is apt; to be truly pragmatic we must adapt as necessary to the people, culture and politics of each client individually. In fact, that's a big reason why we don't talk too much about any specific methodology in the book; the practices we advocate are appropriate (or not) independent of the methodology in use. If your team can embrace things like Pair Programming and it works for you, great! If you can't and it won't, then there are other approaches to take to ensure
a common mental-map and community knowledge of the code.
In general, though, we follow an agile approach (see www.agilealliance.org) to ensure that we deliver software of value that is fit for the business purpose, that can built and tested repeatably and reliably, and that honors basic architectural and design principles such as DRY and orthogonality (which gives you things like low coupling and high cohesion for free).
DAVE: As Andy says, it varies, as we try to work with our clients, rather than dictate to them. Many teams (or their organizations) are not ready for
revolutions, so we help introduce practices gradually, and in a way
that addresses their more pressing problems first. Often this means
getting the basics (such as source code control and a build system) in
place first. We then try to introduce unit testing, incremental
development, and other practices. XP has been wonderful at raising the
general awareness for the need for process in software development,
and for telling people that methodologies need not be Draconian.
Return to Top
|
Working with Money in Javaby Thomas Paul
Note: In all of the examples I use standard US notation of
commas representing a numeric group separator and period as a decimal point. The
number 1,000.00 is the number one thousand.
Why can't my computer add?Sooner or later, everyone trying to calculate
money in Java discovers that computers can't add. Take a look at this code:
import java.text.*;
public class CantAdd {
public static void main(String[] args) {
float a = 8250325.12f;
float b = 4321456.31f;
float c = a + b;
System.out.println(NumberFormat.getCurrencyInstance().format(c));
}
} |
This prints out $12,571,782.00, which is incorrect. The correct answer
is $12,571,781.43. So what's the problem? Doesn't Java know how to add
two numbers together? Actually, the problem is much older than Java and can be
found in the IEEE 754 specification that defines how floating-point numbers
work. As at least one software engineer has commented, "floating-point is
designed to give you the wrong answer very quickly." This is not far from the
truth.
What the heck is floating-point anyway?
Floating-point numbers are actually made up of three different parts stored
in a 32-bit field. The first bit is a sign bit. The next eight bits are the
exponent and the remaining twenty-three bits are the significand. The exponent
holds the highest power of 2 that is smaller than the number. The remainder is
stored in the significand. Let's look at the number 1,000. The highest power of
2 less than 1,000 is 2 to the power of 9, which is 512. We add 127 to 9 (that is
so we can store negative exponents without using a sign bit) and store that in
the exponent. The remainder of 488 is stored in the significand. The significand
is divided into two portions. In our example, the first 9 bits (the size of the
exponent) represents the integral portion of the significand and the remaining
14 bits represents the decimal portion. Here is what the bit positions look like
(I inserted a decimal point in the significand to show the two seperate
sections):
0 10001000 111101000.00000000000000 |
The first part is the sign so we know we are dealing with a positive number.
The second portion is the exponent. The bit pattern represents 136. If we
subtract 127 we get 9, which is the exponent we expect. Since the exponent is 9,
the first 9 bits of the significand represents the integral portion of our
number. Converting that to decimal gives us 488. The remainder of the
significand represents the decimal portion, which is zero.
Now lets look at our problem number, 12,571,781.43. Converting that to
float and displaying the bits gives us:
0 10010110 01111111101010010000101 |
The sign is positive. The exponent is 150 which after subtracting 127 leaves
us with 23. When we calculate 2 to the 23rd power we get 8,388,608.
Subtract that from 12,571,781.43 and we are left with 4,183,173.43
that must be stored in the significand. The first 23 bits (because our exponent
is 23) represents the integral portion. But wait! If we use 23 bits for the
integral portion then we are left with nothing for the decimal portion. This
means that the number 12,571,781.43 can only be stored as
12,571,781 in a float. Follow this example for the two values we are
attempting to add in our test program above and you will see how we end up with
the result we get.
What if the exponent is greater than 23? In that case, the decimal number is
calculated and then zeroes are appended to end for the missing digits. In other
words we are losing precision in order to hold a larger number.
The conclusion is that an int variable (31 significant bits) can actually
store a larger whole number more accurately than a float variable (23
significant bits). A float can store a bigger number than an int because of the
exponent but it can only hold about eight significant digits. In our test
program above, we had nine significant digits in the two numbers we were adding
and ten significant digits in the result so we ended up with the wrong answer.
So how do I calculate the CEO's stock options?
We now know that unless we need to work with less than one million dollars
that float is not going to work for us. So what do we do for large monetary
calculations? There are at least two solutions that are commonly used. The first
is to do everything using integral types. You will need to remember that "590"
is really "$5.90" or your payroll system will be giving everyone big raises. You
may, however, run into problems if you need to do percent calculations rounding
to the nearest penny since ints don't support this type of calculation. You may
want to write a class or two to help you out with this.
There is, however, a better solution. Douglas Dunn ("Java Rules", p.235) has
suggested that Java may be the first programming language where using a class
instead of primitives to do monetary calculations may become the norm for
mainstream business applications. That class is what the rest of this article
will discuss.
BigDecimal to the Rescue!
Here is the BigDecimal version of our test program:
import java.text.*;
import java.math.*;
public class CanAdd {
public static void main(String[] args) {
BigDecimal a1 = new BigDecimal("8250325.12");
BigDecimal b1 = new BigDecimal("4321456.31");
BigDecimal c1 = a1.add(b1);
System.out.println(NumberFormat.getCurrencyInstance().format(c1));
}
} |
This program prints out the correct answer, $12,571,781.43. In fact, the
BigDecimal class can handle any number no matter how large it is (within the
limitations of your computer's memory). How does it do it? BigDecimal stores
numbers in an array. Each entry in the array represents a digit. The larger the
number required, the larger the array that BigDecimal creates to hold the
number. The scale (number of digits to the right of the decimal) is stored in an
int.
Running our test program with these statements will still produce the correct
answer:
BigDecimal a1 = new BigDecimal("8273872368712658763457862348566489.7162578164578825032512");
BigDecimal b1 = new BigDecimal("8762347526136571645982560956723521.8374618726457432145631");
BigDecimal c1 = a1.add(b1);
System.out.println(c1); |
You will discover one problem in attempting to print these very large numbers
using the NumberFormat class. The format method of the NumberFormat class
converts BigDecimal to a double before formatting which limits us to only 16
significant digits. It would have been nice if the designers of the NumberFormat
class had added a method to specifically handle BigDecimal.
Constructors of BigDecimal
BigDecimal has four constructors but the one you will want to use most is the
one that takes a String. If we ran our test program using the constructor that
takes a double, we would still get the wrong answer if we used float constants:
BigDecimal a1 = new BigDecimal(8250325.12f);
BigDecimal b1 = new BigDecimal(4321456.31f); |
The reason this doesn't work correctly is that the float constants will
undergo loss of precision before they are sent to the constructor of the
BigDecimal. Using the String constructor will always allow the BigDecimal to
represent exactly the number you want.
Methods of BigDecimal
BigDecimal supports add, subtract, multiply, and divide. The calculated scale
for add and subtract is simply the larger scale of the two numbers you are
working with. The calculated scale for multiply is the sum of the scale of the
two numbers. The scale of the divide method is either the scale you specify in
the method or the scale of the current BigDecimal object depending on which
overloaded version of the BigDecimal you are using. The divide also requires
that you specify a rounding method. ROUND_HALF_UP is the standard arithmetic
rounding method used most often. Lets take a look at this code fragment:
BigDecimal a1 = new BigDecimal("2");
BigDecimal b1 = new BigDecimal("3");
BigDecimal c1 = a1.divide(b1, 9, BigDecimal.ROUND_HALF_UP);
System.out.println(c1); |
This will print: 0.666666667. Since we specified a scale of 9, we get
9 digits after the decimal point. Since we specified "BigDecimal.ROUND_HALF_UP,"
we get the last digit mathematically rounded.
BigDecimal supports the use of the compareTo method for comparing values. In
most cases you will want to use the compareTo even for equals comparisons
because the equals method considers two numbers with the exact same value but
different scales to be not equal. In other words, the equals method considers 2,
2.0, 2.00, and 2.000 to be different numbers. The compareTo will consider all of
them to be equal.
Conclusion
I hope this has removed some of the mystery of the float primitives and has
given you the desire to look into the BigDecimal class as a way to handle
decimal arithmetic. The BigDecimal is the most accurate way to do arbitrary
precision calculations. In most business applications, it is well worth the cost
of the overhead of working with objects for the added accuracy you get with the
BigDecimal class.
References
A lot of the information for this article was derived from the book, "Java Rules"
by Douglas Dunn. This is a great book that should be owned by everyone who
wants to understand the workings of the Java language.
Ilja Preuss pointed out an article
dealing with formatting BigDecimal numbers. As I mentioned above, the
NumberFormat class doesn't do a good job with BigDecimal. The article from
Tech-Tips provides at least one solution.
Jason Menard wrote a nine horseshoe review of a book entitled, "Java
Number Cruncher" by Ronald Mak. The book deals with number issues in Java
including the problem of dealing with the IEEE 754 specification.
Return to Top
|
Untitled Document
Touring the Commons - part 1
by Jason Menard (jason@javaranch.com)
This is the first in a three part series discussing elements of the Jakarta
Commons project. The first part discusses the Commons BeanUtils package, the
second part will discuss the Commons Digester package, and the final part will
demonstrate using these packages together to build a framework for dynamically
generating data transfer objects.
Introduction
The Jakarta Commons project is a warehouse of reusable Java components. Two
very useful components in Commons are BeanUtils and Digester. Digester, which
I will discuss in the second part of this series, is a set of classes that facilitate
the easy mapping of XML to a Java object. BeanUtils, provides easy wrappers
around the Java introspection and reflection APIs, facilitating the simple manipulation
of JavaBeans and the creation of dynamically generated JavaBeans.
JavaBeans
What is a JavaBean? I'll refer you to the Sun JavaBeans API Specification for
all the down-and-dirty details, but for our purposes there are a few important
things to highlight:
- A JavaBean has a constructor that takes no arguments.
- A JavaBean has a set of properties.
- A JavaBean has accessor (getXxx, or isXxx for Boolean properties) methods
and mutator methods (setXxx) that allow access to its underlying properties.
Here is an example of a simple JavaBean representing some business class called
User, presumably the user of some system we are working on:
import java.util.Date;
public class User {
private Date loginDate;
private String name;
private String password;
public User() { }
public Date getLoginDate() {
return loginDate;
}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
public void setLoginDate(Date loginDate) {
this.loginDate = loginDate;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
public void delete() {
// code to delete user from database
}
public void update() {
// code to update user in database
}
public static User getUser(String name) {
// code returning a new User object
// populated from the database
}
} |
If you take a glimpse at the User class you will see it has the common
attributes that we associate with JavaBeans. It has getXxx() and setXxx()
methods for its properties, and it has the default constructor. Additionally
we threw in a couple of business methods that might exist to save the objects
state in the database.
The PropertyUtils Class
The Commons BeanUtils component provides a class called PropertyUtils
that supplies methods for manipulating JavaBeans such as our User class.
Using PropertyUtils to call the accessor and mutator methods would
look something like this:
User user = User.getUser("jason");
String password = (String) PropertyUtils.getSimpleProperty(user, "password");
Date loginDate = new Date();
PropertyUtils.setSimpleProperty(user, "loginDate", loginDate); |
But what's the point? Why not just call the appropriate accessors or mutators?
That's a fair question if we know exactly what type of object we are dealing
with in advance. That may not always be the case however. Often times in applications
we use Data Transfer Objects (DTO) to represent the state of an object in a
compact format suitable for serializing and transmitting over a network. Often
times a DTO will look a lot like a business object in terms of the properties
it exposes, yet it will not possess any business methods. A DTO for the User
class might be something like the following:
import java.util.Date;
public class UserDTO {
private Date loginDate;
private String name;
private String password;
public UserDTO() { }
public Date getLoginDate() {
return loginDate;
}
public String getName() {
return name;
}
public String getPassword() {
return password;
}
public void setLoginDate(Date loginDate) {
this.loginDate = loginDate;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
} |
When we are using DTOs, often we are copying data back and forth between the
DTO and the business object. As this is the case, we might write a method or
two to facilitate this copying. Normally we might have to write multiple methods
depending on the direction we are copying our data - from DTO to business object,
or from business object to DTO. On top of that, we would have to write these
transfer methods for every DTO and business object in our system. Using PropertyUtils,
we can get away with writing just one method.
public void copyProperties(Object dest, Object orig) {
PropertUtils.setSimpleProperty(dest, "loginDate",
PropertyUtils.getSimpleProperty(orig, "loginDate"));
PropertUtils.setSimpleProperty(dest, "name",
PropertyUtils.getSimpleProperty(orig, "name"));
PropertUtils.setSimpleProperty(dest, "password",
PropertyUtils.getSimpleProperty(orig, "password"));
} |
Now using the above method, we could call either copyProperties(user, userDTO)
or copyProperties(userDTO, user) as appropriate to achieve the desired
results. Recognizing how amazingly useful a method such as the above is, there
just so happens to be a method in PropertyUtils already in place for
doing just this. Coincidentally, the method is :
PropertyUtils.copyProperties(Object dest, Object orig) |
To copy the properties from a User object "user", to a
UserDTO object "userDTO", we can use the following single
line of code:
PropertyUtils.copyProperties(userDTO, user); |
That sure beats writing a bunch of code calling several get and set
methods, doesn't it?
One thing you may have noticed is that up to this point we have only referred
to simple properties, via methods such as PropertyUtils.getSimpleProperty().
A simple property is a property with a single value. Java primitives and Strings
are just a couple of examples of simple properties. That may cover you in most
situations, but PropertyUtils also offers methods for handling other
types of properties. Specifically, there are additional methods for dealing
with indexed properties, mapped properties, and nested properties.
Indexed properties maintain an ordered collection of objects that are accessed
individually by an integer index value. Arrays are the obvious example, but
the BeanUtils package also handles indexed properties that are based on an underlying
java.util.List.
If a bean "myBean" has a property:
Then we might access this property in the following manner:
String userName = (String) PropertyUtils.getIndexedProperty(myBean, "userNames", 1); |
Which is analogous to:
String userName = userNames[1]; |
Mapped properties are based on an underlying java.util.Map. A String
key retrieves the value of a mapped property.
If a bean "myBean" has a property:
We could access this property using PropertyUtils like so:
User user = (User) PropertyUtils.getMappedProperty(myBean, "users", "jason"); |
This is somewhat analogous to:
User user = (User) users.get("jason"); |
If a property of a bean is itself a JavaBean, that properties of that JavaBean
are known as nested properties. A nested property may also be simple, indexed,
mapped, or even further nested. Nested properties are accessed using a dotted
notation that should be familiar to us.
If we have a bean "myBean" with the following property:
We could access the simple property "name" of the property "user"
like this:
String userName = (String) PropertyUtils.getNestedProperty(myBean, "user.name"); |
Accessing a nested property of a mapped property:
String userName = (String) PropertyUtils.getNestedProperty(myBean, "users(jason).userName"); |
Accessing a nested property of an indexed property:
String userName = (String) PropertyUtils.getNestedProperty(myBean, "user[1].userName"); |
PropertyUtils also provides generic getProperty() and setProperty()
methods that may be used to manipulate any property, whether it is simple, indexed
or mapped, and regardless of how deeply nested. For example:
PropertyUtils.getProperty(myBean, "mappedProp(key).indexedProp[1].simpleProp"); |
Dynamic Beans
While the methods described above for dealing with existing beans are certainly
very handy, the meat of this package comes in the form of dynamically generated
beans, or "DynaBeans". If you've ever used dynamic action forms in
Struts, then you have used DynaBeans.
The BeanUtils package has two interfaces for handling dynamic beans: the DynaBean
interface, and the DynaClass interface. Concrete implementations of
these interfaces are provided in the form of BasicDynaBean and BasicDynaClass
classes.
Like standard JavaBeans, DynaBeans have a set of properties that may be accessed
or changed. Going back to our earlier example of the User bean, let's say we
have a DynaBean with the same set of properties called "user". This
is how we would go about getting and setting our properties:
String name = (String) user.get("name"); user.set("name","jason"); |
It can't get much easier than that. In addition to the above methods for manipulating
simple properties, an object implementing the DynaBean interface must
also provide the following methods for manipulating indexed and mapped properties:
public Object get(java.lang.String name, int index) public Object get(java.lang.String name, java.lang.String key) public void set(java.lang.String name, int index, java.lang.Object value) public void set(java.lang.String name, java.lang.String key, java.lang.Object value) |
In order to generate our own DynaBean classes, we must first provide
an implementation of DynaClass. A DynaClass provides the same
basic functionality as java.lang.Class for classes that implement the
DynaBean interface. It is in a concrete implementation of DynaClass
where we must specify the properties of associated DynaBeans, and it is this
DynaClass implementation that generates new instances of a DynaBean
with the specified properties.
The set of properties a DynaBean has may be described by an array
of DynaProperty objects. A DynaProperty has name and type
attributes. Instantiating a DynaProperty that is equivalent to the
User name property from our example would be done like this:
DynaProperty nameProperty = new DynaProperty("name", String.class); |
A DynaProperty array describing all the attributes from our User
bean:
DynaProperty[] properties =
{ new DynaProperty("loginDate", Date.class),
new DynaProperty("name", String.class),
new DynaProperty("password", String.class) }; |
Getting back to the DynaClass interface, if we take a look at BasicDynaClass
we see the following constructor:
BasicDynaClass(java.lang.String name, java.lang.Class dynaBeanClass, DynaProperty[] properties) |
The name parameter is the name of this DynaBean class, and
the properties parameter describes the properties our DynaBean
will have. The dynaBeanClass parameter is the class of the implementation
of DynaBean we are using. If we use null
for the dynaBeanClass parameter, this DynaClass will produce
new instances of DynaBeans of type BasicDynaClass. From this information,
and with our previously constructed array of DynaProperty objects,
we can now construct our DynaClass.
DynaClass userDynaClass = new BasicDynaClass("user", null, properties); |
Let's now put it all together to retrieve and use a new instance of our user
DynaBean.
DynaProperty[] properties =
{ new DynaProperty("loginDate", Date.class),
new DynaProperty("name", String.class),
new DynaProperty("password", String.class) };
DynaClass userDynaClass =
new BasicDynaClass("user", null, properties);
DynaBean user = userDynaClass.newInstance();
user.set("name", "jason");
user.set("password", "secret");
user.set("loginDate", new Date()); |
The BeanUtils Class
The BeanUtils class has several static methods for manipulating DynaBeans
in much the same way that the PropertyUtils class manipulates standard
JavaBeans. Methods provided to get and copy properties are the same as in PropertyUtils,
however the only mutator provided is the generic setProperty() method.
With this in mind, we can copy values from our user DynaBean to or
from an instance of the User object we discussed in the first section.
User user = new User();
user.setName("jason");
DynaBean dynaUser = userDynaClass.newInstance();
BeanUtils.copyProperties(dynaUser, user);
System.out.println(dynaUser.get("name")); // displays 'jason' |
One additional method of note in BeanUtils allows the copy of a set
of mapped properties into a DynaBean, using the populate()
method. If the map has a key that corresponds to the name of a DynaBean
property, it will attempt to copy it. Let's take a look at the following example:
Map map = new HashMap();
map.put("name", "jason");
map.put("password", "secret");
map.put("loginDate", new Date());
DynaBean user = userDynaClass.newInstance();
BeanUtils.populate(user, map);
System.out.println(user.get("name")); // displays 'jason'
|
Type conversion is handled behind the scenes using methods defined in a class
called ConvertUtils. You may write your own converter by implementing
the Converter interface and then calling the ConvertUtils.register()
method in order to register your new Converter.
BeanUtils In Action
Let's do something useful with what we've learned. Here's a method that will
take a HttpServletRequest object and return a populated DynaBean
dynamically generated from the names and values of the request parameters.
public DynaBean createFromRequest(String dynaBeanName,
HttpServletRequest req)
throws Exception {
HashMap map = new HashMap();
ArrayList list = new ArrayList();
Enumeration enum = req.getParameterNames();
while (enum.hasMoreElements()) {
String name = (String)enum.nextElement();
String[] values = req.getParameterValues(name);
Class type = null;
if (values.length == 1) {
type = String.class;
}
else {
type = String[].class;
}
DynaProperty prop = new DynaProperty(name, type);
list.add(prop);
map.put(name, values);
}
DynaProperty[] properties =
(DynaProperty[])list.toArray(new DynaProperty[0]);
DynaClass dynaClass =
new BasicDynaClass(dynaBeanName, null, properties);
DynaBean dynaBean = dynaClass.newInstance();
BeanUtils.populate(dynaBean, map);
return dynaBean;
} |
Conclusion
Hopefully I've demonstrated just how handy this package can be. Be sure to
check out the API documentation for more information, particularly the documentation
for the org.apache.commons.beanutils package. This is just one of many
useful components of the Jakarta Commons project, and next month we will take
a look at another.
Resources
Please feel free to email me with
any questions or comments concerning this article.
Return to Top
|
Small and Simple Web Applications - the Friki Way (Part 4)Frank Carver, July 2003
Abstract
This article is the fourth of a series which laments the bloated and
unmaintainable state of so many J2EE web applications and looks at ways to keep
web applications small, simple and flexible. The series uses the author's Friki software as a case study, and
discusses ways to design and build a powerful, flexible and usable web
application in less space than a typical gif image.
This article carries on working through the user requirements. On the way
we'll start to extend our build system to make a deployable web application.
Introduction
If you've read the first,
second
and third
articles, you should be aware that the aim of this project is to develop a
small, simple and understandable "Wiki" (editable web site) application. We've
considered and decided to defer the relatively heavy decision of how to store
and retrieve pages by introducing a Java interface, decided on and implemented a
simple "template" system to display pages, and are building a supporting test
suite and an automated build script as we go along.
First, let's recap what our "customer" wants, and what we have got so far:
- each page may be viewed using it's own unique URL
page content may contain links to other pages by name
DONE
- links to nonexistent pages will be marked with a "?"
- page content may be created and edited using just a browser
- at least 100 different pages can be stored
What do we write next?
In the previous article I introduced the idea of "picking off" the easiest
thing to test. Carrying on with this we now need to make a guess at which
remaining story is the one to go for. Stories one and four still look like they
need a server, and story five needs a repository. Looks like story three is the
next easiest. Interestingly, it looks much easier now that we have got story two
working. So let's get started.
Second task: links to nonexistent pages will be marked with a
'?'
As usual, we start by adding a new test. This time, though, there's no need
to start a whole new test class. We can just add to the existing "LinkTest" and
see how far we get. We want page references to missing pages to be identified by
a '?' which (when clicked) brings up an "edit" page to create the new page.
LinkTest.java
package tests;
import junit.framework.*;
import friki.PageFormatter;
public class LinkTest extends TestCase
{
PageFormatter formatter;
public void setUp()
{
formatter = new PageFormatter();
}
public void testEmpty()
{
assertEquals("", formatter.format(""));
}
public void testNonLink()
{
assertEquals("hello", formatter.format("hello"));
}
public void testEmbeddedLinkSymbol()
{
assertEquals("friki@javaranch.com", formatter.format("friki@javaranch.com"));
}
public void testLink()
{
assertEquals("<a href='view?hello'>hello</a>", formatter.format("@hello"));
}
public void testLinkInPage()
{
assertEquals("You say <a href='view?hello'>hello</a>, I say goodbye.",
formatter.format("You say @hello, I say goodbye."));
}
public void testMissingLink()
{
assertEquals("goodbye<a href='edit?goodbye'>?</a>", formatter.format("@goodbye"));
}
}
Compile and run this by typing "ant". Does it work? Of course not. We have no
code for it.
What we get back is the HTML as if it is a known page. What we need to do, is
to add some code which can tell the difference between known and unknown pages.
Before we hit the keyboard, let's think a little. Even though this is still a
very small project, do we have anything which we can re-use?
In the very first installment of this series, we created an interface:
PageRepository.java
package friki;
import java.util.Iterator;
public interface PageRepository
{
public Page get(String name);
public void put(String name, Page page);
public boolean contains(String name);
public Iterator matchingPageNames(String pattern);
}
That "contains" method looks like just what we need. Let's create the
smallest "cheating" class we can, and see if it helps.
InMemoryPageRepository.java
package friki;
import java.util.Iterator;
public class InMemoryPageRepository implements PageRepository
{
public boolean contains(String name)
{
return !"goodbye".equals(name);
}
public Page get(String name) { return null; }
public void put(String name, Page page) {}
public Iterator matchingPageNames(String pattern) { return null; }
}
It won't compile, we need a Page class. Before we leap to create one, let's
stop and think for a bit again.
Why do we need a Page class at this point? It's not for the method we
want to use, but for some of the others - ones that we've made dummy
implementations for. I can't shake the feeling that there's something wrong with
making an empty class just to satisfy an empty method. Is there a better
way?
Luckily, there is. Remember when I said in part 2 "This interface may
change or even disappear before any code is released"? Well here's just such
a change. Extract the "contains" method to its own interface:
Container.java
package friki;
public interface Container
{
public boolean contains(String name);
}
PageRepository.java
package friki;
import java.util.Iterator;
public interface PageRepository extends Container
{
public Page get(String name);
public void put(String name, Page page);
public Iterator matchingPageNames(String pattern);
}
That's much better. Now we can get rid of those empty methods from our
"in-memory repository", because all it needs to be is an "in-memory
container":
InMemoryContainer.java
package friki;
public class InMemoryContainer implements Container
{
public boolean contains(String name)
{
return !"goodbye".equals(name);
}
}
It now all compiles, and it's neater. That's more progress. But it still
doesn't make that failing test pass. We now need to use our new container in the
application code. If you look back up to the test code you'll see that the
formatter is responsible for generating the links, and so that's the object
which needs to know about presence or absence of pages. For the moment, let's
just pass our container into the formatter constructor:
LinkTest.java
package tests;
import junit.framework.*;
import friki.PageFormatter;
import friki.Container;
public class LinkTest extends TestCase
{
PageFormatter formatter;
public void setUp()
{
InMemoryContainer container = new InMemoryContainer();
formatter = new PageFormatter(container);
}
...
}
Looks good. But we need to update the PageFormatter to use the container:
PageFormatter.java
package friki;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
public class PageFormatter
{
private char symbol = '@';
private Container container;
public PageFormatter(Container container)
{
this.container = container;
}
private String makeLink(String name)
{
if (container.contains(name))
{
return "<a href='view?" + name + "'>" + name + "</a>";
}
else
{
return name + "<a href='edit?" + name + "'>?</a>";
}
}
...
}
Run the tests: [junit] Tests run: 10, Failures: 0, Errors: 0, Time elapsed: 0.078 sec
Excellent. We're still "cheating", though. Let's add another test to point
this out:
public void testMissingLink2()
{
assertEquals("whatever<a href='edit?whatever'>?</a>", formatter.format("@whatever"));
}
It fails, of course. It's time to make that container class do some work.
There's no need to do more work than necessary, though, especially when there
are system classes to help:
InMemoryContainer.java
package friki;
public class InMemoryContainer extends java.util.HashMap
implements Container
{
public boolean contains(String name)
{
return super.containsKey(name);
}
}
LinkTest.java
package tests;
import junit.framework.*;
import friki.PageFormatter;
import friki.Container;
public class LinkTest extends TestCase
{
PageFormatter formatter;
public void setUp()
{
InMemoryContainer container = new InMemoryContainer();
formatter = new PageFormatter(container);
}
...
}
Run the tests: [junit] Tests run: 11, Failures: 1, Errors: 0, Time elapsed: 0.125 sec
The tests still don't work. Can you tell why? The trick is to look in the
test log, and see what failed: Testcase: testLinkInPage took 0.016 sec
FAILED
expected:<...<a href='view?hello'>hello...>
but was: <...hello<a href='edit?hello'>?...>
...
Aha! Our new test now works, but in making it work we have broken one
of the existing tests. Here we begin to see the huge value of keeping our
test cases for old features, and re-running them every time. If we look at the
tests, we see that the old test just assumes that the page "hello" is present.
But our new InMemoryContainer starts off empty, so there is no page "hello". The
code is all doing what it should; we just need to tell the new container that we
have a page "hello".
LinkTest.java
package tests;
import junit.framework.*;
import friki.PageFormatter;
import friki.Container;
public class LinkTest extends TestCase
{
PageFormatter formatter;
public void setUp()
{
InMemoryContainer container = new InMemoryContainer();
container.put("hello", "some stuff");
formatter = new PageFormatter(container);
}
...
}
Run the tests: [junit] Tests run: 11, Failures: 0, Errors: 0, Time elapsed: 0.078 sec
Cool.That's another feature done. Feel free to add more tests if you are not
sure of anything. The gurus say "keep adding tests until fear turns to
boredom"
Let's make war!
If we ever want to have an application we can just "drop in" to a servlet
container, we need to make a deployable "war" file. I won't go in to a lot of
detail on the structure and contents of a war file right now; I suggest you look
around the internet a bit in preparation for the next installment. This time,
however, all we are concerned about is what to do with our freshly baked,
tested, class files.
Before I describe how to do this, I'll take a few moments to describe how
not to do it.
Do you use an IDE (integrated development environment) which has buttons or
menus for this sort of thing? It's a great temptation to use IDE features, but
in this case I feel strongly that it would be a mistake. We want the complete
build-test-package-deploy cycle to be seamless, simple and quick. We already use
Ant for compilation and testing, and I suggest that Ant is good for this too.
When we have eventually finished, I'm expecting to be able to just type "ant" to
compile and completely test the code, build a web application and deploy it to a
running server. Stopping half-way through to click some buttons or menu choices
seems a clumsy way to do it.
If you have been paying attention, you should have guessed that I would
recommend Ant for this. What you may not have been able to predict is that even
though Ant includes a built-in "war" task, I don't recommend that you use
it!
The Ant "war" task allows you to combine physically separated resources into
a single war file. It does this by requiring you to specify where to get each of
the main categories of information in the war file. So you tell it separately
where your classes are, where your "web.xml" file is and so on. While this is a
useful facility if you want to build a war file from files scattered across a
file system, our ant build is not like that. We have control of where our files
go. The final nail is that some versions of the "war" task have problems, such
as creating the important "WEB-INF" directory as the non-standard "web-inf".
For most day-to-day purposes I recommend using the much simpler "jar" task
instead. Use the ant "copy" task to make sure all our resources are laid out
under some directory as they would be in a war file, then scoop the whole lot up
using "jar". Add the following to "build.xml":
build.xml
<project name="friki" default="build" basedir=".">
...
<target name="build-war">
<mkdir dir='build/war/WEB-INF/classes'/>
<copy todir='build/war/WEB-INF/classes'>
<fileset dir='build/delivery/classes'/>
</copy>
<jar jarfile='frikidemo.war'>
<fileset dir="build/war"/>
</jar>
</target>
<target name="build" depends="compile,test,build-war"/>
</project>
Now we can run this and look at the generated "frikidemo.war". It should be
about 4K bytes. If you wish, you can use something like WinZip to see what's
inside, and check that all our class files are in there.
How are We Doing?
We still haven't made a Wiki yet! But we have completed another of our user
goals, and have built on our ant-scripting efforts to automatically produce our
first web application "war" file. We've also seen the benefits of growing our
regression test suite as we go along.
Next session we will attack some more customer goals, and we really will
produce an application which can be deployed to a web server and actually
do something.
Return to Top
|
A Poem on Java 1.5 - Tiger
by Joshua Bloch
Joshua Bloch recited the following poem, which he also wrote, at the conclusion
of the "TS-3072: Forthcoming Java Programming Language Features" 2003
JavaOne converence session. (Included here with permission.)
Tiger, Tiger Burning Bright
Like a geek who works all night
What new-fangled bit or byte
Could ease the hacker's weary plight?
To the most despised cast
We'll bid a fond farewell at last
With generics' burning spear
The need for cast will disappear
While Iterators have their uses
They sometimes strangle us like nooses
With enhanced-for's deadly ray
Iterator's kept at bay
When from collections ints are drawn
Wrapper classes make us mourn
When Tiger comes, we'll shed no tears
We'll autobox them in the ears
The int-enum will soon be gone
like a foe we've known too long
With typesafe-enum's mighty power
Our foe will bother us no more
And from the constant interface
we shall inherit no disgrace
With static import at our side,
our joy will be unqualified
And as for noble metadata
I'll have to sing its praises later
Its uses are so numerous
To give their due, I'd miss the bus
Tiger, Tiger Burning Bright
Like a geek who works all night
What new-fangled bit or byte
Could ease the hacker's weary plight?
(With apologies to William Blake)
Return to Top
|
The Big Moose Saloon Question and Answer of the
Month
Mosey on in and pull up a stool. The
JavaRanch Big Moose Saloon is the place to get your Java questions answered.
Our bartenders keep the peace, and folks are pretty friendly anyways, so don't
be shy!
We pulled one out from the deep past for this inaugural column. Over in The
Java In General (Beginner) Forum, JavaRanch's own Frank Carver did a heck
of a job tellin' folks about making a Singleton in Java. Following is an excerpt
from the
original forum thread.
Question: What's a Singleton?
Answer: A singleton is an object of which there may only ever be a single instance.
(*)
You can make your own class act like a singleton, simply by only creating
a single instance. But this relies on every programmer who uses your code knowing
that there is only supposed to be one instance of the class, which is dangerous.
A better solution is to write the class in a way which prevents a casual programmer
from creating more than one instance. This is where the Singleton pattern and
its associated Java idioms help you.
Lets start with a simple class:
public class Something
{
private String content = "empty";
public void setContent(String content)
{
this.contemt = content;
}
public String getContent()
{
return content;
}
}
The most obvious way for a programmer to try to create an instance of a class
is to call its constructor, so we need to prevent this. If we just fail to provide
a constructor, Java assumes that there is a "default constructor" taking no
arguments which just creates an instance of the class; not what we need! The
Java "trick" for this is to have a constructor, but make it private so no other
classes may call it.
public class Something
{
private String content;
private Something()
{
content = "empty";
}
public void setContent(String content)
{
this.contemt = content;
}
public String getContent()
{
return content;
}
}
Now that we have no constructor, we need to make a way for people to create
a single instance of the class. Typically this is done by provding a static
method which creates an instance, but will create only one. If it is called
again, it just returns the same instance that it created the first time.
public class Something
{
private static Something instance = null;
private String content;
private Something()
{
content = "empty";
}
public static Something getInstance()
{
if (instance == null)
{
instance = new Something();
}
return instance;
}
public void setContent(String content)
{
this.contemt = content;
}
public String getContent()
{
return content;
}
}
Now the code is almost complete. The last thing to remember is that Java is
a multi-threaded language in which many things can be happening "at once". If
two threads were to call getInstance at the same time, then our class might
occasionally create two instances, which would be wrong and very hard to test.
So we make use of Java's built-in synchronization mechanism to make sure that
only one thread can call getInstance at any one time.
So the final singleton class looks like:
public class Something
{
private static Something instance = null;
private String content;
private Something()
{
content = "empty";
}
public synchronized static Something getInstance()
{
if (instance == null)
{
instance = new Something();
}
return instance;
}
public void setContent(String content)
{
this.contemt = content;
}
public String getContent()
{
return content;
}
}
I hope this helps.
(*) The Singleton pattern actually allows for a specified number of instances,
but singletons are harly ever used for any number other than one.
Return to Top
|
Untitled Document
Programming Diversions Puzzle
by Jason Menard
This month's puzzle is courtesy of Jim Yingst.
Maze Solver
Given an input ascii representation of a maze, write a program to find and
display a path from the start point of the maze to the end point of the maze.
The output should be a textual representation of the path through the maze from
start to finish. It is suggested to use a series of '.' (period) symbols to
indicate the path through the maze. You may discuss this puzzle and post your
solutions in the
Programming Diversions forum.
Notes:
- No diagonal moves are allowed.
- For an extra challenge, if there are multiple paths from start to finish,
find the path leading to the shortest solution.
Legend |
O |
Start |
X |
Finish |
+ |
Wall |
Maze #1
+++++++++++++++
+ + + + +
+ + + + + +
+ +++ +++++ + +
O + +++ X
+++ +++++ +++
+ + +++ + +
+ + + +
+++++++++++++++
Maze #2
+++++++++++++++++++++++++
+ + +
+ +++++++ + + +++++++++ +
+ + + + + +
+ +++++ +++ + +++ +++ + +
+ + + + + + + + +
+++ + + + + +++ +++ + + +
+ +O + + + +X + +
+ +++++ + + +++++ +++++++
+ + + + + +
+++++ + +++ + + + + +++ +
+ + + + + + + +
+ +++ + +++++ + +++++ +++
+ + + +
+++++++++++++++++++++++++
Return to Top
|
Movin' them
doggies on the Cattle
Drive
It's where you come to learn Java, and just like the cattle drivers of the
old west, you're expected to pull your weight along the way.
The Cattle Drive forum is where the drivers
get together to complain, uh rather, discuss their assignments and encourage
each other. Thanks to the enthusiastic initiative of Johannes de Jong, you can keep
track of your progress on the drive with the Assignment Log. If you're tough enough
to get through the nitpicking, you'll start collecting moose heads at
the Cattle Drive Hall of Fame.
Fresh riders on the Drive...
Got three new Cattle Drivers signed up on the assignment log: Chuck
Wannall, Nick Revak, and Terry Broman.
Whoa, Nelly! Y'all are in for a ride!
Another moose on the wall for...
Yep, that's right, you make it through, you get yerself a nice little
moose to hang on the wall. Well, OK, so it's a virtual moose on a virtual
wall, but you can be right proud of 'em! Thanks to the efforts of Marilyn
deQueiroz, you can now proudly admire your codin' accomplishments at the
Cattle Drive Hall of Fame. Check it out, pardner, it's worth a wink.
This month, three cowpokes bagged their first moose on the trail. Nicholas
Burgett, Tom Purl, and Amy Phillips
all earned moose badges for completin' the first set of assignments.
Ol' timer Barry Gaunt added another moose to his co-lection
when he finished the last of the current assignments. Somebody buy that
bartender a drink!
Back in the saddle...
Cowpoke Greg Harris is back in the cattle-ropin' saddle
after spendin' a spell out on the range. Look's like he's workin' on the
last of the servlet assignments and will be ready to wrassle JDBC real
soon.
Special mention...
I'm not a-certain what all them letters mean, but folks are a-tellin'
me John Hembree's added "SCWCD" to the list
of varmints he's bagged. It ain't a moose, but I hear it's somethun to
be rightful proud of too.
Cattle driver Donald Cossitt's been hanging out it other
parts of the 'Ranch and he's gone and won himself a copy of that new-fangled
larnin' book "Head First Java." Gonna have himself a liberry
full o' books real soon if he keeps it up.
Nitpicking is hard work
too... We know they're workin' reeeeeally
hard, after all, we've seen what those assignments look
like once the nitpickers have combed through 'em. Hats
off to Marilyn deQueiroz, Pauline McNamara, and Jason Adam for their dedication
and patience with the pesky vermin that always manage to
make their way into those assignments.
Those gentlemen behind the bar...
Notice the fellas wipin' the bar and shinin' up the sarsparila glasses?
Updating that assignment log is tough work too. Big thanks to Michael Matola and Barry Gaunt. Mosey up and place
yer orders. Rumor has it Michael keeps some special stuff under the bar,
if you know what to ask fer.
Joinin' the Drive
You think ya got what it takes? You ready for some work and some good learnin'? Then pull up a stool and read up on Joinin' the Drive. Good luck!
Content and format adapted from Ol' Timer Pauline
McNamara's original column. -Michael Matola.
|
Return to Top
|
Book Review of the Month
Return to Top
|
July
Scheduled Book Promotions :
Return to Top
|
Managing Editor: Dirk Schreckmann
Comments or suggestions for JavaRanch's NewsLetter can be sent to the Newsletter
Staff.
For advertising opportunities contact the Newsletter
Advertising Staff.
|