Driving On CruiseControl - Part 2Lasse KoskelaAccenture Technology Solutions In Part 1 of this tutorial, we set up the CruiseControl Continuous Integration server against a Subversion repository. In this second part, we'll continue where we left off by taking our build results online with the CruiseControl reporting web application. Build results onlineAs we discussed in Part 1, there are some very good reasons for wanting to put your Continuous Integration builds' results online for everyone to see. Many don't like filling their inbox with full-blown reports, many projects don't have easy access to a mail server, and many projects would like to integrate the build results to other dashboard-like applications which don't quite fit in with the HTML email publisher solution. In the case of CruiseControl, the web application used for presenting build results needs to work with the XML log files produced by CruiseControl during a build cycle. Not a surprise, the most used application used for exposing CruiseControl's build results over the Web is the reporting application that comes as part of the CruiseControl distribution. Let's dig in. Configuring the CruiseControl web applicationThe first step in getting the CruiseControl reporting application up and running is to build it from the sources and configure a few parameters. Below is the familiar-looking directory structure from Part 1: Assuming you used the above directory structure, this is how you build the cruisecontrol.war web application: C:\CIA\BuildServer\CruiseControl\reporting\jsp> build war lib/ant.jar;lib/optional.jar;lib/junit.jar;lib/xerces.jar Using Javac! Buildfile: build.xml clean: [delete] Deleting directory C:\CIA\BuildServer\CruiseControl\reporting\jsp\dist [delete] Deleting directory C:\CIA\BuildServer\CruiseControl\reporting\jsp\classes [delete] Deleting directory C:\CIA\BuildServer\CruiseControl\reporting\jsp\testresults [delete] Deleting directory C:\CIA\BuildServer\CruiseControl\reporting\jsp\tmp init: [mkdir] Created dir: C:\CIA\BuildServer\CruiseControl\reporting\jsp\dist [mkdir] Created dir: C:\CIA\BuildServer\CruiseControl\reporting\jsp\classes [mkdir] Created dir: C:\CIA\BuildServer\CruiseControl\reporting\jsp\testresults [mkdir] Created dir: C:\CIA\BuildServer\CruiseControl\reporting\jsp\tmp check-duplication: checkstyle: compile: [javac] Compiling 27 source files to C:\CIA\BuildServer\CruiseControl\reporting\jsp\classes test: [javac] Compiling 21 source files to C:\CIA\BuildServer\CruiseControl\reporting\jsp\classes [junit] Running net.sourceforge.cruisecontrol.BuildInfoTest [junit] Tests run: 3, Failures: 0, Errors: 0, Time elapsed: 0,111 sec [junit] Testsuite: net.sourceforge.cruisecontrol.BuildInfoTest [junit] Tests run: 3, Failures: 0, Errors: 0, Time elapsed: 0,111 sec ... -set.log.dir: [input] WARNING! Property user.log.dir not set! Please enter the absolute path to the CruiseControl logs directory: C:\CIA\BuildServer\CruiseControl\main\logs -set.status.file: [input] WARNING! Property user.build.status.file not set! Please enter the absolute path to the current build status file: currentbuild.txt -set.artifacts.dir: [input] WARNING! Property cruise.build.artifacts.dir not set! Please enter the absolute path to the directory where additional build artifacts are stored: / create-web-xml: [copy] Copying 1 file to C:\CIA\BuildServer\CruiseControl\reporting\jsp\tmp war_jdk14: war_pre14: [war] Building war: C:\CIA\BuildServer\CruiseControl\reporting\jsp\dist\cruisecontrol.war war: BUILD SUCCESSFUL Total time: 56 seconds C:\CIA\BuildServer\CruiseControl\reporting\jsp> Notice how the -set.xxx.xxx targets are asking for input? That's the build script noticing that it's missing some essential details about the location of your CruiseControl logs directory, the currentbuild.txt file, and the artifacts directory. The build script inserts the given paths into the web.xml of the resulting cruisecontrol.war archive. After building the reporting web application, we need to deploy it somewhere. I chose to use Jakarta Tomcat as my web container of choice. You could just as easily use another J2EE compliant web container such as Jetty or a full-blown J2EE application server such as JBoss. Tomcat has an auto-deployment directory named "webapps" where you can just drop a .war file and it gets deployed automatically within a few seconds (or whenever you restart Tomcat if it's not running at the time). That's exactly what we're going to do next. With Tomcat installed (unzipped) to C:\CIA\BuildServer\Tomcat, it can be started by double-clicking on bin\startup.bat: 25.9.2004 16:39:57 org.apache.coyote.http11.Http11Protocol init INFO: Initializing Coyote HTTP/1.1 on http-8080 25.9.2004 16:39:57 org.apache.catalina.startup.Catalina load INFO: Initialization processed in 1322 ms 25.9.2004 16:39:57 org.apache.catalina.core.StandardService start INFO: Starting service Catalina 25.9.2004 16:39:57 org.apache.catalina.core.StandardEngine start INFO: Starting Servlet Engine: Apache Tomcat/5.0.28 25.9.2004 16:39:58 org.apache.catalina.core.StandardHost start INFO: XML validation disabled 25.9.2004 16:39:58 org.apache.catalina.core.StandardHost getDeployer INFO: Create Host deployer for direct deployment ( non-jmx ) 25.9.2004 16:39:58 org.apache.catalina.core.StandardHostDeployer install INFO: Processing Context configuration file URL file:C:\CIA\BuildServer\Tomcat\conf\Catalina\localhost\admin.xml 25.9.2004 16:39:58 org.apache.struts.util.PropertyMessageResources <init> INFO: Initializing, config='org.apache.struts.util.LocalStrings', returnNull=true 25.9.2004 16:39:58 org.apache.struts.util.PropertyMessageResources <init> INFO: Initializing, config='org.apache.struts.action.ActionResources', returnNull=true 25.9.2004 16:39:59 org.apache.struts.util.PropertyMessageResources <init> INFO: Initializing, config='org.apache.webapp.admin.ApplicationResources', returnNull=true 25.9.2004 16:40:01 org.apache.catalina.core.StandardHostDeployer install INFO: Processing Context configuration file URL file:C:\CIA\BuildServer\Tomcat\conf\Catalina\localhost\balancer.xml 25.9.2004 16:40:01 org.apache.catalina.core.StandardHostDeployer install INFO: Processing Context configuration file URL file:C:\CIA\BuildServer\Tomcat\conf\Catalina\localhost\manager.xml 25.9.2004 16:40:01 org.apache.catalina.core.StandardHostDeployer install INFO: Installing web application at context path /jsp-examples from URL file:C:\CIA\BuildServer\Tomcat\webapps\jsp-examples 25.9.2004 16:40:01 org.apache.catalina.core.StandardHostDeployer install INFO: Installing web application at context path from URL file:C:\CIA\BuildServer\Tomcat\webapps\ROOT 25.9.2004 16:40:01 org.apache.catalina.core.StandardHostDeployer install INFO: Installing web application at context path /servlets-examples from URL file:C:\CIA\BuildServer\Tomcat\webapps\servlets-examples 25.9.2004 16:40:01 org.apache.catalina.core.StandardHostDeployer install INFO: Installing web application at context path /tomcat-docs from URL file:C:\CIA\BuildServer\Tomcat\webapps\tomcat-docs 25.9.2004 16:40:01 org.apache.catalina.core.StandardHostDeployer install INFO: Installing web application at context path /webdav from URL file:C:\CIA\BuildServer\Tomcat\webapps\webdav 25.9.2004 16:40:01 org.apache.coyote.http11.Http11Protocol start INFO: Starting Coyote HTTP/1.1 on http-8080 25.9.2004 16:40:02 org.apache.jk.common.ChannelSocket init INFO: JK2: ajp13 listening on /0.0.0.0:8009 25.9.2004 16:40:02 org.apache.jk.server.JkMain start INFO: Jk running ID=0 time=0/50 config=C:\CIA\BuildServer\Tomcat\conf\jk2.properties 25.9.2004 16:40:02 org.apache.catalina.startup.Catalina start INFO: Server startup in 4447 ms Dropping the cruisecontrol.war into the webapps directory should result in a bit more activity in the Tomcat window: 25.9.2004 16:41:42 org.apache.catalina.core.StandardHostDeployer install INFO: Installing web application at context path /cruisecontrol from URL file:C:/CIA/BuildServer/Tomcat/webapps/cruisecontrol Looking familiar? Good. You've just put your build results online! Exploring the reporting application UIWith Tomcat running and the reporting application deployed, it's time to verify the installation by pointing a web browser at the front page: As you can see, the front page gives a very brief summary of all projects for which the reporting application can find reports under the logs directory we configured it to look at. In addition to the projects' names, the front page lists the latest status of each project (passed/failed) and when the last build was made. Now try clicking one of the projects listed. Do you see an all-blue page like this? If you see a page like that and your web container's log file has a stack trace saying "org.apache.xml.utils.WrappedRuntimeException: The output format must have a '{http://xml.apache.org/xalan}content-handler' property!", the problem is a version clash between Xerces and Xalan under JDK 1.4. Some solutions you should try to fix this are:
Hopefully, with more or less tweaking, you should be looking at a page like this: First of all, the navigation pane on the left-hand side shows you the project's scheduling status, i.e. tells you when the current build was started or when the next build starts (depending on whether CruiseControl is currently waiting for the next build cycle or doing a build). Below the scheduling status, there's a list of dated builds. Clicking on the date of a build opens up that particular build's results (or whatever tab you had selected when clicking the link). Once you've got more than 10 builds under your belt, only the first 10 will be displayed as links and the rest will be collected into the dropdown box below those links. See those tabs in the top of the main view? The reporting application splits the information in the CruiseControl log file into smaller views so you can look at just the information you want. The first tab, "Build Results", displays a summary of the selected build including date information, a list of failed unit tests, and a very useful list of modifications in the source repository since the previous build (a.k.a. "the suspects" if the build has turned from green to red). A quick peek at this page is most often enough to give you the information you're looking for. Also, the "Build Results" tab includes a link named "Build Artifacts", which leads to a directory listing of the project's artifacts directory -- the place where CruiseControl gathers a project's build output files (such as sample.jar for the sample project we used in Part 1). If you feel like digging deeper, the "Test Results" tab displays a full listing of all tests run and their pass/fail status and the execution time. The "XML Log File" page renders the raw XML log file for you to look at (to date, I have not needed to but it's good to know it's there just in case). The "Metrics" tab displays a couple of graphs showing the green/red stats for your project, which is a more recent and very nice little "extra" for the reporting application. The last tab, "Control Panel", is probably displaying an error page to you. Why is that? Well, if you look at the HTML generated by CruiseControl, you'll see that the error page comes from an embedded IFRAME pointing to port 8000 on the build server. What it's expecting to find from that port is the CruiseControl JMX server (or, more accurately, the HTTP adapter for it). At this point, let's go and restart the CruiseControl process with the JMX features enabled: C:\CIA\BuildServer\CruiseControl\main\bin> cruisecontrol -port 8000 "C:\j2sdk1.4.2_05\bin\java" -cp "..." CruiseControl -port 8000 [cc]syys-25 18:38:24 Main - CruiseControl Version 2.1 Compiled on September 12 2004 1552 [cc]syys-25 18:38:24 trolController- projectName = [SampleCCProject] [cc]syys-25 18:38:24 Project - Project SampleCCProject: reading settings from config file [C:\CIA\BuildServer\CruiseControl\main\bin\config.xml] [cc]syys-25 18:38:24 trolController- projectName = [SampleCCProject2] [cc]syys-25 18:38:24 Project - Project SampleCCProject2: reading settings from config file [C:\CIA\BuildServer\CruiseControl\main\bin\config.xml] [cc]syys-25 18:38:25 ontrollerAgent- Starting HttpAdaptor with CC-Stylesheets [cc]syys-25 18:38:25 ontrollerAgent- starting httpAdaptor [cc]syys-25 18:38:25 Project - Project SampleCCProject starting [cc]syys-25 18:38:25 Project - Project SampleCCProject: idle ... The key here is to give the CruiseControl startup script the port number for the JMX adapter as an argument. We're using port 8000 here as it's the default for the reporting web application. If you need to use a different port, you'll have to start CruiseControl with a different port number and edit controlpanel.jsp under reporting/jsp (yes, I know...). With CruiseControl running JMX enabled, reload the "Control Panel" tab in your browser and you should see the CruiseControl JMX Console: Tip: You might want to customize the reporting web application a little by making the "Control Panel" link open the JMX Console in a new window instead of squeezing it into a tiny IFRAME. The JMX Console is a very handy tool for changing different configurations at runtime without restarting CruiseControl. Try navigating to your project (link named "CruiseControl Project:name=SampleCCProject") to see all the nice things you can change or trigger through the JMX Console. For example, you can change the project's build interval by putting a new value for the "BuildInterval" attribute and clicking the "set" button right next to the text field. You can also force a build by clicking on the "Invoke" button for an operation named "build". Play around with the console and keep the output of the CruiseControl process open to see how your doings affect CruiseControl's. Makes you wonder why more products don't ship with similar JMX utilities, doesn't it. That's all for nowI'm afraid that's all for now. In the next part of my series of CruiseControl tutorials, I might show how to connect your CruiseControl server to the developers' desktops. "I might" means that's what I'm going to do unless I get feedback to tell me otherwise. Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Implicit Conversions, Explicitly by Corey McGlone In this article, I'm going to go over the basics of explicit and implicit conversions and some of the nuances associated with them. In general, the material in this article would be considered "fair game" for the SCJP exam so, if you're planning on taking the exam soon, you'll want to make sure you understand the concepts in this article well. Most of you probably already know what a conversion is. In general terms, a conversion is the process of transforming one thing into another, such as melting ice to turn it into water. In Java, when we talk about conversions, we are generally talking about the process of changing the type of an object from one type to another. One example might be the process of converting an int to a long or a short to a byte. You can also convert objects from one type to another. Often, you'll hear such a conversion called a "cast." For example, to turn an int into a byte, you "cast" the int as a byte. Widening (Safe) Conversions vs. Narrowing (Unsafe) ConversionsThere are two basic types of conversions: widening and narrowing. You might also hear these referred to as safe and unsafe conversions, respectively.So, what is a widening conversion, and why is it safe? Well, when dealing with primitives, a widening conversion occurs when you convert one type, A, to another type, B, when type B has a larger (or wider) range than A. This is the type of cast that occurs when you cast a byte to an int. A byte has a range of just 256 values, from -128 to 127. An int, on the other hand, has a range of 4,294,967,296 values, ranging from -2,147,483,648 to 2,147,483,647. Obviously, any value that fits in a byte will fit in an int. Because of this, there is no chance for data loss and the conversion is considered "safe." With that in mind, can you guess what a narrowing conversion is and why it is considered unsafe? Based on our previous conclusion, that a widening conversion was considered "safe" because there was no chance for data loss, it seems only natural that a narrowing conversion would be considered "unsafe" because there is a chance for data loss. If we turn our previous example around and try to cast an int as a byte, we have a narrowing conversion. This is because the range of a byte is much smaller (or narrower) than the range of an int. What happens if you try to convert an int with a value of 200 to a byte? You're bound to lose some data because the number 200 is not within the range of a byte. Performing such a conversion will change the number 200 into -56 because data is lost. If you'd like to get more details about widening and narrowing conversions, I'd suggest checking out this article in the SCJP Tip Line. Implicit vs. Explicit ConversionsI know, I know - I just stated that there are two types of conversions and now I've gone and come up with two more types. Well, in my defense, implicit and explicit conversions aren't really new conversions, they simply describe how narrowing and widening conversions are performed. An implicit conversion is performed automatically, with no additional input from you (the programmer). An explicit conversion, on the other hand, is not performed automatically and is, instead, dictated by you, the programmer.I'm sure it comes as no shock to you that widening conversions, which are always safe from data loss, generally occur implicitly, while narrowing conversions, which run the risk of data loss, are usually performed explicitly. Let's take a look at an example that performs both:
Take a look at line 1. Notice that we're assigning a byte variable to an int variable. Java is a strongly typed language so you can't just assign one data type to another. Therefore, before this is allowed, some sort of conversion much take place. In this case, we're going from a byte to an int, which is a widening conversion. The compiler realizes that and simply adds a cast to convert the byte to an int prior to assignment. This is called an implicit cast because Java does it for you. Pretty convenient, huh? (See §5.2 Assignment Conversion in the JLS for more details about assignment conversions.) Now let's take a look at line 2. In this case, we're trying to assign an int to a byte variable. We already know from our previous discussion that this is considered a narrowing conversion and we risk losing data if we do so. Because of this, the compiler won't just do the conversion for us. Rather, we need to tell the compiler, explicitly, that we want the conversion to take place. We do that by adding the (byte) in front of the variable. This is your way of telling the compiler, "Yes, I know this is risky, but I'll take the responsibility for it. Just do the conversion." Without that directive, the compiler will give you an error about that line. Implications of Implicit CastingIn general, explicit casts are easy to deal with because you're the one making them. You have to know what type something is, because you're the one telling the compiler which type to make it. However, because the compiler will sometimes cast things for you, you might not realize that it's doing such a thing and the results can be... unexpected. Let's look at a few examples.Binary Numeric PromotionLet's start in the wonderful world of computer hardware. Imagine you're creating a simulator that is designed to test a piece of hardware that your company is designing. In order to test the software, you need to read in two bytes of data from the hardware and xor those bytes together. You'll be storing the result in another byte variable for later use. Here's a little snippet of code that you might be using for such an application:
Looks like a simple program, right? Well, the bad news is that this program doesn't even compile! Why not? Can you see the error? The compiler message helps to give it away - here's what it says: HardwareTest.java:8: possible loss of precision found : int required: byte byte xorTotal = input1 ^ input2; ^So, the question is, why is the compiler complaining about a narrowing conversion at this point? Notice that it says you're trying to convert an int to a byte. Certainly, converting an int to a byte is a narrowing conversion, but where the heck did the int come from? There isn't an int anywhere in that code! Well, the int shows up because an implicit conversion is taking place. In Java, any time you perform a binary operation (an operation requiring two operands), Binary Numeric Promotion is performed. Binary Numeric Promotion casts each operand up to the size of the other or, if neither is larger than an int, both are cast as ints. What type do you think you'll get as a result of xor'ing two int values together? You're going to get an int, of course. That's why you're getting a compiler error - the xor operation is returning an int and we know that we can't assign an int to a byte without a cast because that's a narrowing conversion. Therefore, in order to make the above code work, you must add a cast to the xor statement, like this: byte xorTotal = (byte)(input1 ^ input2); Implicit Explicit CastsNext, let's focus on implicit explicit casts. What?!? Yeah, you heard me - implicit explicit casts. There is a case in which an explicit cast is implied. Anyone know what it is?The answer is a compound assignment operator. Compound assignment operators, such as +=, -=, *= all contain an explicit cast, even though it's not shown. Take the following application as an example:
What does this program print? Even if you don't know what the value of -13 shifted to the right one position is off the top of your head, you can be certain that the result will be positive because we're using the unsigned right shift operator. However, if that's your assumption, I'm afraid to say you need to stop making assumptions. This program prints -7. So what went wrong? Unlike our hardware program, there was no compiler error to tell us that something wasn't right. The compiler was just fine with the code we wrote but, if the code is good, why the failure? Well, the reason for the "failure" is that compound assignment operators contain an explicit cast, implicitly. :) If I rewrite this code snippet and expand the compound assignment operator to what it truly is, the code would look like this:
From that, the problem is probably a bit more obvious. From our previous conversation about binary numeric promotion, we know that b1 is first going to be cast from an 8 bit byte to a 32 bit int. Next, we shift all of the bits 1 position to the right and add a 0 to the left. That makes the resultant value positive, right? Well, yes, it does...for a moment. The very next thing we do, though, is cast our int as a byte and chop off the high order 24 bits, which includes our precious sign bit. What we're left with is -7. Those are just a couple cases in which you can get into trouble from an implicit cast. I'm sure there are plenty more "gotchas" out there waiting for you so be wary. Check out Dan Chisholm's mock exams (referenced below) for some additional practice. ConclusionUnderstanding type conversions is very important with regards to the SCJP exam. You might not see these exact examples on your exam, but the principles are certainly fair game. Make sure you understand this material well. Besides the SCJP exam, understanding conversions and their implications is a fundamental topic that should be well understood by any Java programmer.Always be wary of picky details when you're taking the exam but be especially wary if you start seeing data types like bytes and shorts. If those appear in a question, make sure there aren't any hidden conversions you're missing. Below, I've cited some great resources for more information on this topic if you need some more clarification. Of course, if this material is giving you trouble, feel free to pop into the SCJP Forum. JLS: Conversions and Promotions SCJP Tip Line: Widening and Narrowing Conversions JLS: Compound Assignment Operators Dan Chisholm's Mock Exams, Including Single-Topic Exams on Conversions Until next time, Corey Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Synchronized Multithreading with Swing by Tom Tolman For some people, hearing the word "thread" brings to mind spiders, or else other creeping things which can be seen on dark nights when one is coding alone in the office. However, this should not be the case, for in a Java program threads are your friend, and perhaps unbeknownst to you, they have been aiding your adventures from the first time you used the Swing library. Before we slide down into a possible tangle of multiple threads, remember that one should not begin creating threads without a good purpose, for they are complex and need to be completely thought through before being used. When you are creating threaded code, keep it as simple as possible, for any complexity you introduce will surely lead you into some sticky situations like a moth who gets trapped forever in a deadlocked situation. You may be surprised to find out that Swing already uses multiple threads. "How is this possible?" you might ask. "I have never implemented a Runnable interface nor extended Thread in my years of using Swing." Swing utilizes something called the event dispatch thread which operates behind the scenes. This thread is responsible for handling system events, such as when a user clicks the mouse button or when a Swing timer goes off. Fortunately, event handling code automatically executes in the event dispatch thread, so all of your callbacks are already taking place on this thread. When the user clicks on one of your controls, the event is handled by the event dispatch thread and your code that responds to this event is executed on this separate thread. Try running this example which shows the name of the thread in the label on the left.
The first time the call One of the fortunate things about the fact that events are handled automatically on the event dispatch thread is that Swing is not thread safe and you must modify any realized GUI components from within the event dispatch thread. Thus, the difficult and dangerous task of keeping Swing thread-safe is happening by default for your event handling code, and is already taking place within this context. You could imagine if a separate thread began modifying a combo box at the same moment a user chose that combo box and started scrolling through it. The technical term for such a confluence of events is "uh oh". In the worst case, not only will data integrity be compromised, but the entire application will lock up, and the hours of work the user has spent using your application will vanish into a cloud of smoke coming out of his or her ears. Historically there has been one mighty exception to the rule that you must modify any GUI components from within the event dispatch thread; that was at startup. It had been considered safe to create the GUI in the application's main thread provided no GUI components were visible. This is the way most programs are written, and is likely to be safe, but now as you can see the official way to ensure complete safety is to now also create the GUI itself within the event dispatch thread. This will ensure you have no lockup at startup. There are two methods for invoking code inside the event dispatch
thread when you are not already in that thread: Below is the code to replace the main method coded above with the
creation of the GUI taking place on the event dispatch thread. An
anonymous class is created with the Runnable interface which calls the
Using other threadsIf you have ever used an application and wondered "What the heck is taking so long here?" you have encountered a reason to use multiple threads. The user is in charge of your application, and as soon as you wander off with the event dispatch thread with an extremely slow piece of code, your user has lost control. The application will appear to hang, since no other mouse or keyboard events can be handled as long as the event dispatch thread is busy. For this sample application we will compute the value of P using perhaps the slowest algorithm possible. Since
P is an irrational number, a complete
implementation could take forever to complete. In case the user is not
willing to wait forever, we will utilize multiple threads: one thread to
compute the value of P, and the other default
event dispatch thread to keep the user informed of what we think P is at the moment. For this example we store the
approximated value of As an aside, the way we are computing P here is by throwing random darts that hit a square with a quarter of a circle inscribed in it. The ratio of darts which fall within the circle to the total number of darts thrown gives a way to approximate P. The square is 1 unit across, and the circle has a radius of 1 unit. The complete circle has an area of P * radius * radius so the quarter circle has an area of P / 4. Thus P is approximately equal to 4 * the number of circle quadrant hits divided by the number of throws.
Since this is random, there is no guarantee we will converge on P- all of the darts may well fall into the circle quadrant and it will appear P is very close to 4.0. In reality the most significant digits of PI will be calculated fairly quickly and it will take a very long time to find additional significant digits. This is useful, however, as an example utilizing concurrent threading with Swing.
Notice that the variable At regular intervals, whenever the counter is a multiple of 1000, an
anonymous class implementing Runnable is created that updates the label. It
is passed to The In the main method of the program, the interface is created on the event dispatch thread with its own anonymous instantiation of the Runnable interface. Parting ideasThere are some methods which are thread safe within the Swing component hierarchy. They will be marked in the documentation as "This method is thread safe". In summary, there is no need to create separate threads for the general
Swing application, although you are advised to instantiate your GUI on
the event dispatch thread. All of your event handling code will take place
on the event dispatch thread by default. If you are doing something
advanced that does require multiple threads, be sure to make it thread
safe and manipulate Swing components from within the event dispatch
thread. Java Virtual Machine implementations of threading are not
consistent so code that works in your test environment may fail elsewhere
unless you are careful. Unless the documentation explicitly states that
methods are thread safe, you should assume that they are not. Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
by Rick Chapman The first chapter of "The Product Marketing Handbook for Software" starts with a description of how to position your product for a very simple reason: proper product positioning drives all aspects of your marketing and sales efforts. Improperly position your software and despite the quality of your product, the design of your collaterals (either web-based or paper), the abilities of your sales force, all of your marketing and sales programs will immediately begin to misfire. Improper product positioning can wreak enormous havoc on a company. For example, a fundamental positioning mistake made by my first employer in the industry, MicroPro, publisher of what was once the world's most popular word processor, WordStar, literally destroyed a $67 million dollar company in the span of 18 months. Over the last two decades literally dozens of books and articles and thousands of pages have been dedicated to explaining product positioning to the masses. In the chapter in my book "In Search of Stupidity: Over 20 Years of High-Tech Marketing Disasters" that describes the havoc MicroPro unleashed on itself by releasing two products with the same name, price, audience and functionality at the same time, I describe one of the most popular and widely used explanations. This is marketing guru Regis McKenna's take on product positioning. He believes it is a: " ... psychological location in the consumer's mind, pertaining to the relative qualities a company, product, or service may have with respect to its competitors." The only trouble with this approach is that software is, by its nature, an abstraction, a stream of electrons coursing through your computer or perhaps the Internet. You can never "see" a software application before interacting with it. For this reason, assigning a product a "visceral" or physical identity is very important. Too many times you'll see software products described as being the "best in their class," or the "fastest" or the most "reliable" without anyone having any clear idea of what the software actually does. A classic example of this is Lotus Notes. When the product was first introduced, few potential buyers knew what it did. Lotus was aware of the problem, and in a display of unintended humor, even talked about the issue in their documentation: ?"What Is Notes Anyway? People have been asking that question since the beginning of time (or at least since Notes first came onto the market). It has been hard for people to define Notes because you can use it to do so many things." ?From the Notes 4.0 Beginner's Guide, published in 1996 By the way, the manual never DOES explain what Notes "is." As a result of the confusion, Note's market acceptance was very slow, a fact that allowed Microsoft to catch up in sales to it with Exchange. When Exchange was first introduced, Microsoft positioned the product as a "post office" in your computer, an easy concept for buyers to grasp. On the strength of this positioning, Microsoft was able to limit Lotus' success with Notes. Yes, Notes did eventually become a solid seller, but when Lotus first released the product it had expectations it would become a "killer app," something the product's unclear identity to buyers made impossible. Assigning software a physical identity is not usually very difficult (though it can be tricky with certain products) but despite all the ink spilled on the topic, many companies and marketing "experts" continue to get software positioning wrong. For instance, I attended the recent Software Business magazine conference held in September in San Francisco and sat through a presentation given by a consultant who specializes in high-tech "messaging." At one point, his presentation focused on Microsoft's rollout of Windows 3.X in the early 90s. During his presentation, he showed two slides. The first was filled with a fair amount of jargon and wordy gobbledygook. This, the presenter claimed, was Microsoft's original positioning statement. The second slide, he claimed, represented Microsoft's new, successful positioning strategy. It stated that: "Windows 3.X Will Transform the Way You Use Your Computer" This is wrong. This is not a "positioning" statement?this is a tag line. You can tell by the generic nature of the phrase, as it can be used to describe ANY product. For instance: "Binky 3.X will transform the way you use mapping software." "Binky 3.X will transform the way you manage shipping schedules." "Binky 3.X will transform the way you live your life." And so forth. Now, how exactly did Microsoft position Windows 3.X? It was very simple. They said: "Microsoft Windows (finally) makes your PC work like a Mac (for a lot less money than a Mac)." Now, if you want, you could use the tag line: "And thus transforms the way you will use your PC." In the context of the events surrounding the early 1990s, this was a powerful statement. The Macintosh had been on the market since 1984, everyone who was interested in a desktop computer knew what a Mac could do (and what a PC running DOS couldn't), and IBM had just spent the last several years bungling the release of OS/2 (a complete chapter in "In Search of Stupidity" is dedicated to examining what may be high-tech's biggest fiasco). All that Microsoft had to do was produce a product that was good enough to stand up to the market's scrutiny of its claim about Windows working just about as well as a Mac. Despite the beliefs of Microsoft haters and Macophiles, Windows passed that scrutiny and the rest is history. Later that evening I had a chance to put my product positioning techniques to work. In addition to writing books on high-tech history and marketing, I'm also the managing editor of Soft*letter, a bimonthly newsletter dedicated to examining all aspects of the software business. As I was handling out a free sample of the publication at a show reception, I was approached by a gentleman with some questions on how his company could position their software product. "OK," I said, "can you first tell me what your software product does?" "Sure, " he said. "It Bzzzzz application Zaaaappppiinn integration MOM Bzzzz diagnostics errrrburrr help desk Xxxxx network architecture." I blinked at him. "Uh, again, what does your software do? How would I use it?" "Bzzzzz application Zaaaappppiinn integration?" "No, wait, stop. What does your software product work with?" "Applications." "And how does it work with applications?" "It monitors them." "And where does it monitor them?" "It monitors them on a network." "And what precisely does it monitor about the applications?" "It monitors them for their functionality. If an application crashes, it informs a help desk that the application has crashed." "What else does it tell the help desk? " "It provides diagnostics that describes the network and user environment when the application crashed." "OK, that's better. So, how about this? Your application functions as a sort of virtual fireman on call. He monitors your network for application problems. When a program crashes and burns, the fireman tells you that there's a fire and provides helpful information that will assist you in putting the fire out." He looked at me thoughtfully for a moment. "You know, we paid some consultant six thousand dollars to come out and talk to us about our software. We talked about the fire, but never got to the fireman." In part two of this article, we'll take a look at some contemporary positioning failures (including one by Microsoft) and discuss how to fix a positioning mess. Merrill R. (Rick) Chapman is the publisher and managing editor of Soft*letter and Software Success newsletter. In addition to the aforementioned publications, Soft*letter also publishes the Soft*letter Financial Handbook and the Services Marketing and Metrics Handbook. Rick has worked in the software and high-tech industry since 1978 as a programmer, salesman, support representative, and consultant for many different companies, including MicroPro, Ashton-Tate, IBM, Inso, Bentley Systems, Berlitz, Hewlett-Packard, and Ziff-Davis. He has held senior marketing and sales positions at a variety of high tech companies including AGA, Stromberg, and Miacomet. Rick is also the author of "The Product Marketing Handbook for Software" and "In Search of Stupidity: Over 20 Years of High-Tech Marketing Disasters" and is the co-author of the Software Industry and Information Association's US Software Channel Marketing and Distribution Guide. He is currently at work on a new book on marketing and selling open source software. |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 anyway, so don't be shy! The Topic: Pair Programming is NOT always a choiceOver in the Process forum, Sonny Gill found relief in a recent blog entry from Kathy Sierra:
Sixty-seven replies later, folks are still chimin' in with thoughts and experiences. Now, mosey on o'er, see what folks are saying and chime in with some thoughts of your own. Join this discussion in The Big Moose Saloon! Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Discuss this book review in The Big Moose Saloon! Return to Top |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Following are the scheduled promotions coming in October and November. Be sure to check our promotions page often for changes and new promotions. Participate in a promotion and you might win a copy of a book!
Return to Top |