|
|
||
Craig Castelaz's BlogProgramming ArchivesNoted Cocoa Programmer Contemplates Switch to JavaPosted by castelaz on December 16, 2004 at 08:50 PM | Permalink | Comments (2)
North Pole - Claus, Inc. has announced they are exploring the cross-platform possibilities of Java. Santa, founder and CEO of Claus, Inc. was an early adopter of Apple computers beginning with VisiCalc on an Apple II. "It's been a natural progression from those early days to now", says Santa. "While it took tens of thousands of diskettes to hold the first Naughty-and-Nice List, and processing was none too speedy because of a 64K memory limitation, we've always enjoyed using and programming Apple computers." "Originally, we were primarily a Basic shop, but as the demands increased, we moved into C. From there, Cocoa was the next step in our development efforts", Santa's Chief Scientist, G. Nome says with a grin, using his fingers to motion quotation marks around the words "next step". When asked about this stranger mannerism, Nome suggested everyone should return to their respective "jobs", and walked away laughing. Contrary to popular understanding, Santa's workshop is state-of-the-art. In fact, it is the recent adoption of wireless handhelds on the shop floor that has precipitated the interest in Java. Santa's web-presence has long been outsourced to a number of externally hosted sites, so Java was not a potentially big player until now. "A JVM is available for nearly every device we have in our production facility", stated Santa. "The big hurdle is gaining the acceptance of our employees. Wireless is nice, but we do know a thing or two about mass production without it" Santa noted with evident pride. The only cloud that darkened the otherwise illuminating visit occurred when the topic of Java 5 and Mac OS X came up. Santa's brows furrowed, and he reached for a rather imposing PDA. It was difficult to see clearly, but it appears he placed coal icons next to several names. Cocoa will continue to play an important role according to Santa. It is clearly his personal favorite. However, he concedes that "Java has its place" and that he isn't "afraid to indulge so to speak". When asked about alternatives, Santa reiterated his long time affinity to Apple, and that Apple believes Cocoa and Java to be the preferred tools for serious development. Java will be slowly rolled into place. An important production scheduling application is already available, but its audience is limited to line supervisors. Depending upon the further acceptance of several key Java-based applications, the shop floor could ultimately become a Java only environment. IT staff is continuing to monitor the situation as each new piece is rolled out. They feel confident they will be able to reach a firm decision within a year. It is hoped that future opportunities to report on progress will be made. The cookies were delicious. Help maximize screen space with a small UI trickPosted by castelaz on October 19, 2004 at 07:32 PM | Permalink | Comments (11)Way back in the primordial ooze stage of our new product, it was decided that we needed a custom control rather than use the standard combobox to handle dropdown lists. So, instead of using a JComboBox, we created our own LookupField control that combined a JTextField with a JButton to simulate the more traditional component. There were several reasons for this decision. Two of the more compelling ones had to do with the ability to control the dropdown button. First, it was felt that the standard button on the JComboBox was too wide. We could easily set a narrower width for the button in our custom control. Second, we could completely hide the custom control’s dropdown button whenever we desired. One of the key features of the product is that users can design their own forms. Frequently, screen space on these forms becomes extremely limited. However, if the user sizes the custom control correctly, they can see the entire text of the item they selected from the dropdown list whenever focus is off the custom control because we hide the dropdown button. The button only becomes visible when the LookupField control gains focus. In other words, the full text is only hidden when the user is on the field, most likely because they wish to set or change the control’s value from the dropdown list. Since the user doesn’t have to account for the extra space of the dropdown button when designing their forms, they can maximize the available screen space. The LookupField served us well for quite some time, but as we migrated to other platforms, and as the look-and-feel of Windows continued to evolve, the custom component began to stick out like the proverbial sore thumb. After collecting samples of what the standard combobox looked like on several target platforms, it was decided that we couldn’t realistically make the LookupField match all them, and that we needed to use the JComboBox. Of course, that meant we would lose our control of the dropdown button. However, as cool as the ability to hide the button was, it wasn’t felt to be critical, and we got the OK to replace the LookupField with the JComboBox control. Now, many of you are going to be able to guess the rest of this story. Development and Testing were fine with the substitution, but Support and Sales nearly stroked. Everyone agreed that the JComboBox looked way better, but Sales and Support were adamant that hiding the dropdown button was extremely important. Fortunately, this story has a happy ending. After a fair amount time fooling around in the debugger, and writing small experimental snippets of code, we were able to hide the dropdown button on a JComboBox. As you can see in the example below, Field 2 is somewhat limited in space. However, the control is able to display the full text of the item selected from the dropdown list since the button associated with the combobox is not visible. Once Field 2 has focus, the dropdown button becomes visible, and the selection list is just a click away. Note, Field 2 did not increase in size to accommodate the button. Finally, once the user has made their selection, and moved to another field, the dropdown button disappears, once again revealing the full text of the item selected from the dropdown list. Like our custom LookupField control, the JComboBox is actually made up of several separate controls. While I haven’t confirmed it across all platforms and look-and-feels, the JComboBox appears to have three underlying components: a rendering pane, a button, and a text field if the combobox has been set to editable. Our small UI trick involves adjusting the widths of the underlying button and text field during the control's focus events. The initialization code in the over-ridden paint method records the original sizes for the button and text field, and then adjusts the widths of the two underlying controls so that the combobox does not have a visible dropdown button when the screen is first painted. In other words, the button doesn't appear until the control gains focus. Usage of the custom control isn't any different than that of a standard JComboBox as shown in the QuickTest code. The setting of the preferred height and width in the QuickTest is to simulate a "tight squeeze" on one of the fields.
//CustomComboBox.java
import javax.swing.*;
import java.awt.*;
public class CustomComboBox extends JComboBox implements FocusListener {
private boolean initialization;
private int textFieldWidth;
private int textFieldHeight;
private int buttonWidth;
private int buttonHeight;
private Component tf;
private Component bt;
public CustomComboBox() {
super();
initialization = true;
setEditable(true);
bt = getComponent(0);
tf = getComponent(2);
tf.addFocusListener(this);
}
public void paint(Graphics g){
super.paint(g);
if (initialization) {
textFieldWidth = tf.getWidth();
textFieldHeight = tf.getHeight();
buttonWidth = bt.getWidth();
buttonHeight = bt.getHeight();
bt.setSize(0, 0);
tf.setSize(textFieldWidth + buttonWidth, textFieldHeight);
initialization = false;
}
}
public void focusGained(FocusEvent e) {
bt.setSize(buttonWidth, buttonHeight);
tf.setSize(textFieldWidth, textFieldHeight);
}
public void focusLost(FocusEvent e) {
bt.setSize(0, 0);
tf.setSize(textFieldWidth + buttonWidth, textFieldHeight);
}
}
// QuickTest.java
import javax.swing.*;
import java.awt.*;
public class QuickTest extends JFrame {
public QuickTest(String title) {
super(title);
setSize(450, 75);
Container pane = getContentPane();
pane.setLayout(new FlowLayout());
JLabel label1 = new JLabel("Field1");
pane.add(label1);
JTextField tf1 = new JTextField(10);
pane.add(tf1);
JLabel label2 = new JLabel("Field2");
pane.add(label2);
CustomComboBox ccb = new CustomComboBox();
ccb.setPreferredSize(new Dimension(50, ccb.getPreferredSize().height));
ccb.addItem("ABCDE");
ccb.addItem("EDCBA");
pane.add(ccb);
JLabel label3 = new JLabel("Field3");
pane.add(label3);
JTextField tf2 = new JTextField(10);
pane.add(tf2);
show();
}
public static void main(String[] args) {
QuickTest qt = new QuickTest("QuickTest");
}
}
As far as UI tricks go, this one is pretty small potatoes. On the other hand, it has redeemed Development in the eyes of Sales and Support, while preserving a nice feature for the users. P.S. In case you're wondering, the opening illustration started with a vague intention of showing the somewhat pliable nature of most requirements. Obviously, it missed the mark. Then again, I was pretty happy with it on its own terms, and it cracked up a friend, so I decided to go with it. AmbiguityPosted by castelaz on August 31, 2004 at 09:13 PM | Permalink | Comments (0)I believe nearly every developer eventually creates their own metaphor for programming. Some are rather tame, such as building bridges between users and systems. Others can be quite aggressive. One of my coworkers will often say hes spent his day mangling bits when things have not gone particularly well. As for me, I frequently picture myself with a large sledgehammer, squashing ambiguities. The gap between users and their systems isnt so much bridged, as it is filled with the remains of ambiguities laid to rest. Like a lot of programmers, I work hard to resolve misunderstandings between what is desired and what is delivered. In my opinion, its the hardest part of the job. At the same time, Im thankful for the standard practices and idioms commonly found throughout the Java community. I wish I could say that the wonderful source level comments we write one another dispel all questions about intent, but the reality is that the idioms are sometimes our only guide to what the code was meant to do. This is especially true in terms of the public interface of a class or component. Even the best JavaDocs in the world will not solve the problems associated with a sloppy and contrary mix of common patterns and practices in a public interface. Ambiguities between developer and user are bad enough. It becomes extremely frustrating when they exist between programmers. For instance, I recently came across a class that I couldnt decide if it was a bean gone bad, or a singleton with an split-personality. Ive changed the actual implementation to shield the guilty, but the sample below retains much of original class' flavor.
public class Foobar {
public static String attribute1;
public String attribute2;
public String attribute3;
private static Fooobar instance;
private Foobar() {
}
public static getInstance() {
if (instance == null) {
instance = new Foobar();
}
return instance;
}
public static String getAttribute1() {
return attribute1;
}
public static void setAttribute1(String attribute) {
attribute1 = attribute;
notifyObservers();
}
public static viod setAttribute1NoNotify(String attribute) {
attribute1 = attribute1;
}
public String getAttribute2() {
return this.attribute2;
}
public void setAttribute2(String attribute) {
this.attribute2 = attribute2;
}
public String getAttribute3() {
return this.attribute3;
}
}
Now, I'm not about to critique the entire class, but even a quick glance shows there's something not quite right with it. The class begins by looking like a singleton with a private constructor and a getInstance method to control the single instance of the class. However, nearly all the other methods of the class are marked public static which makes the getInstance method unnecessary. Furthermore, the public static methods are mostly getters and setters for publicly available attributes which can be accessed from outside the getters and setters. Then, there is the problem of mixing bean-like behaviour with public getters and setters within the singleton. There may be a need for a singleton bean, but then you wouldn't mark the attributes public, nor would you make them instance rather than class variables. Even the experienced developers that I asked to review the code were hard-pressed to know what to make of the class. Is it a bean, a singleton, a bean-gleton? Finally, when you consider the class in the context of a large application, and the fact that the original is both much larger and more complex that the sample shown, you begin to realize that you have just meet the boss ambiguity for this level of the development game.
Standard practices, idioms, and programming conventions are important in that they help to minimize the number of ambiguities that exist between developers. Although we can argue endlessly what the conventions should be, our lives as programmers would be a lot tougher without them. Hesitant AcrobatsPosted by castelaz on July 21, 2004 at 12:15 PM | Permalink | Comments (6)
Last week, Daniel Steinberg wrote that he attended a local JUG meeting to hear Bruce Tate speak about his book Better, Faster, Lighter Java. I was fortunate enough to be at the same meeting. Bruce's presentation was interesting, and sparked a fair amount of interaction between him and the audience. Of course, this made the time fly by all too quickly, but Bruce was still able to squeeze in a little time for questions and comments before he had to leave. As Daniel noted in his blog, one of the comments at the end concerned EJB/J2EEs ability to address complex problems. Light-weight techologies are all well-and-good, but complex problems require complex solutions. And in a follow-up remark, the same person held that technologies like Hibernate simply shift complexity, they don't eliminate it. His belief is that it would take it him as long to feel competent with Hibernate as it took him to feel the same with EJBs. Another set of the comments came from a person who identified himself as a maintenance programmer. When something breaks, hes the guy whos responsible for fixing it. In order to do that, he needs access to the code. His primary concern is that technologies such as Hibernate and Spring may hide too much. Hidden code is a barrier to him. He doesnt want to be held responsible for code he cant touch. While I understand and sympathize with these concerns, I believe both sets of comments reflect an all too common desire by programmers to bury themselves in a morass of details. Of course, Im just as guilty as the rest. As I mentioned in my last blog, the first thing I did when I got an assignment to support digest authentication was to sniff the packets going to and from the secured web site. Now, how detail-centric is that? One of the traits of a good programmer is a love of details. but there is a point where it simply gets in the way. Light-weight technologies like JDO, Hibernate, Pico, and Spring are designed to leverage reflection, code generation, and dependency injection. They're intended to not only introduce flexibility into a system, but to also relieve the programmer of a lot of mundane coding. You'll certainly want to dig a bit under the hood to see how they work, but detailed understanding is not required. If you spend too much time examining the giant's shoe size, you'll never have the time to actually climb up to his shoulders. Buried treasure can be found nearly anywherePosted by castelaz on July 14, 2004 at 04:30 AM | Permalink | Comments (4)
It started as a typical morning. I was three days behind schedule on a one day project, but hoping to finish it by late afternoon. Of course, Id no sooner gotten into the code, trying to recapture yesterdays flow, when the phone rang. The next minute, I was off to my project managers office, no doubt to receive yet another reason to delay my current assignment even more. Now, dont get me wrong. Im not complaining. I happen to have a great project manager. He understands that the to-do list is infinitely expandable, but the workday isnt. He recognizes that if he pulls me from one task to work another, the one left behind isnt going to magically complete itself. His attitude keeps us both sane. Besides, I like variety, and enjoy having several projects to switch to when I grow stale on one. Sure enough, I did indeed get a new assignment. I had to write code to login to a web site protected by digest authentication. Fortunalely, I had worked on a fairly large and reasonably sophisticated authentication/authorization component in the past, and was familiar with the various web login methods. While I had never actually implemented digest authentication before, I had some understanding of how it worked. So, after meeting with one of our network administrators, who helped me setup a secured site, we did a bit of packet sniffing of the digest authenticated site we just created. The packet exchange pretty much matched my general understanding of how authentication process worked, and it didnt look like it would be all that difficult to implement. Fortunately, before writing any code, my innate laziness asserted itself, if laziness can ever assert itself, and I took a little time to look for something I could use as a starting point. Things were looking pretty grim, until I hit upon Jakarta Commons HttpClient. While Simon Brown has already blogged on this same subject, HttpClient - another great Jakarta Commons component, his discussion was limited to the components Post method. Additionally, HttpClient is such a useful gem, it bears mentioning again-and-again. As I just said, HttpClient is part of the Jakarta Commons. The primary goal of the Commons is to create and maintain a repository of reusable Java components which are free and open source. Although I haven't had a wide exposure to all the components the Commons offers, the ones I have used have been a real pleasure. This was particularly true for HttpClient. Unlike a lot of open source, HttpClient has a fairly extensive User Guide, and between it and a few samples, I was able to put together a digest authentication method in relatively short order. Certainly, faster than I would have been able to write a bare-bones, untested implementation myself.
public void access(HttpURL httpURL) throws Exception {
try {
String siteUsername = httpURL.getUser();
String sitePassword = httpURL.getPassword();
HttpClient client = new HttpClient();
if ((siteUsername != null ) && (sitePassword != null)) {
client.getState().setCredentials("Realm", jnlpURL.getHost(),
new UsernamePasswordCredentials(siteUsername, sitePassword));
}
HttpMethod method = new GetMethod(httpURL.getURI());
try {
int statusCode = client.executeMethod(method);
String statusStr = HttpStatus.getStatusText(statusCode);
if (statusStr.equalsIgnoreCase("unauthorized")) {
throw new Exception("Not authorized for realm");
}
if (statusStr.equalsIgnoreCase("not found")) {
throw new Exception("Webapp and/or file not found");
}
doSomethingUseful(method.getResponseBodyAsStream());
method.releaseConnection();
}
catch (HttpRecoverableException hre) {
throw new Exception("Exception '" + hre.toString() + "');
}
catch (UnknownHostException uhe) {
throw new Exception("Exception '" + uhe.toString() + "');
}
}
catch (URIException urie) {
throw new Exception("Exception '" + urie.toString() + "');
}
catch (IOException e) {
throw new Exception("Exception '" + e.toString() + "');
}
}
There really isn't much to say about the code. The HttpURL object passed into the access method can be based upon a URL which has an embedded username and password. If you've never seen the syntax for such a URL, http://craig:foobar@host.com/webapp has an embedded username of craig and password of foobar. If the username and password are available, the access method establishes credentials based upon the username and password pulled from the URL. It then performs a standard HTTP GET. This is where the authentication magic occurs. Without going into a lot of detail, the web server detects the presence of the credentials and attempts to authenticate the client. If all goes well, and the credentials are accepted, the client is silently admitted to the protected site. The various status checks and exception catches throughout the access method are there to give you an indication of what can go wrong.
The only warning I need to give regarding the authentication process is that the password needs to be digested prior to being embedded in the URL. Since the site is protected by digest authentication, it stands to reason that the password needs to be digested before being transmitted to the web server. The encryption of the password is typically based the MD5 Digest. A short article explaining Java 2 Security with sample code using MD5 can be found at Java security evolution and concepts, Part 2. Compared to all that I would have had to write, HttpClient saved me a considerable amount of time and grief. Jakarta Commons offers many other useful components. Check it out ! | ||
|
|