|
|
|||||||||||||||||||||||||||||||||||||||||||||||
Tim Boudreau's Blog
Nobody Thinks They're An EnterprisePosted by timboudreau on August 18, 2008 at 12:56 AM | Permalink | Comments (8)I wrote this blog nearly two years ago, and was politely asked/advised not to publish it. If I rewrote it today, I'd probably make it a bit shorter. But I think the points are ones our industry needs to learn. The important one is very, very simple: playing to the ego of a non-person is a really lousy marketing strategy. So, today's the day I publish this rather benign rant, and either ruin my career, or more likely, wonder, "what was the big deal about this anyway?". I promise I'll get back to talking about software architecture soon :-) 2006-09-06 22:08:05The first question I was asked in today's NetBeans Day got me thinking about an unsaid thing that needs to be said. I've been thinking it for years. The entire technology industry's marketing organizations are focused around something that is more fantasy than reality. Hoisting myself onto my soapbox... The question was: "Can I use the Enterprise Pack for NetBeans if I'm not an enterprise?" The answer is, of course you can! The fact that the question had to be asked is a symptom of a disease endemic to the technology industry. It can be summed up in one word: Enterprise. My first close-up encounter with the enterprise cargo-cult was in 1999, when NetBeans was first acquired by Sun. Some folks came out to Prague, and kept talking with fear and awe about enterprise customers (best spoken in dramatic basso profondo, ala James Earl Jones or similar). The impression it made was that an enterprise customer is most like an irate two-year-old with teething pains, a baseball bat and the muscles to use it. I'd never heard of these terrifying enterprise customers before - I'd generally just been dealing with people in our beta program who worked for big companies and gave us great feedback. But apparently the greatest horror that could befall you was to talk to your customers - because then one day Scott McNealy might be out playing golf with the president of Smrdistan, and he'd say "I need you to fly that programmer, Jan Pičovak, out to fix my technology problem!" and then you'd lose one of your best programmers for a month. This was such a horror that the best way to avoid it was to make sure programmers never actually talked to the people who use what they make. It would have all been funny if it weren't so very, very weird. I thought to myself, how did these people stay in business? They think our customers aren't people. Enterprise is a market segment. The thing about market segments is that you (as someone marketing something) segment the market. That is, you're the one with the knife carving up the world into segments and naming those segments. The problem happens when you start thinking the rest of the world knows your names for them, or that you are talking to a segment, not to actual people. And the height of silliness is to think that the names you made up for market segments will actually mean something to the people in those segments. What I don't get is how this point is nearly completely lost on the technology sector. Google sometime for enterprise software. Contemplate the amount of money spent on all the web pages and sponsored ads you see. Have you ever been looking for a piece of software or technology and that was what you searched for? Ever wake up one morning and say to yourself "Gee, I think I'll get some enterprise software today! Dunno what it's for, but I'm an enterprise, so it's the software for me!"? I bet you looked for something related to an actual problem you wanted to solve. But what is a poor marketing drone to call these potential customers (bear in mind I've been a marketing drone, but hopefully not too drone-like and never for something I didn't believe in)? Enterprise works. It has heft. It's one word. It sums things up nicely. But it's a segment. It's your name for a bunch of people. Toyota doesn't make a car called a Toyota Soccer Mom. What an unbelievable opportunity they're missing...not. One of the best essays or statements I've ever read is Zed Shaw's Indirection is not Abstraction. That's dead on. The (basso profundo) enterprise is a layer of indirection - it's a thing that makes it harder to talk to people. The confusion is mixing that up with an enterprise being an abstraction for (and thus an okay substitute for) people. Now, in any other industry, you'd go out and figure out how to reach those people and if you need to adjust what you make or how you make it or how and where you communicate to reach them better, you'd do that. In technology, you go up on the rooftop and shout EnterpriseFooFaw and wonder why all the cars in the street didn't just screech to a halt. And if they don't screech to a halt, you go out of business. Or go find a job at the next bit of fresh technology meat that comes along and run it into the ground too.Soccer Mom is a useful market segment. It's also a near-insult. There's a reason we will never see the Toyota SoccerMom or the Ford SoccerMom driving down the street. Java Studio Creator was a good tool. But it was specifically targeted at something called corporate developers (code for people who program in Visual Basic), and so was the marketing campaign. Way to lose your audience. I once asked a room of about 30 tech writers to raise their hands if they were a corporate writer. Big surprise, nobody did. Could somebody find commonalities among those people such that they'd probably really have a use for, or imagine they had a use for, specific products? Sure! Would insulting them to their faces help sales? Probably not (I got in big trouble in that preso for a slide with corporate developer surrounded by a big red circle with a slash through it - there were people who actually thought telling people they were corporate developers was a Good Idea™. While we're at it, why not substitute drone for developer? - but that brings us to a whole other topic, which is how corporate developer plays well to "analysts" [i.e. the people who determine your stock price] even if it makes no sense for your customers - which is its own industry-wide problem - talking to corporate analysts might be one step closer to talking to your customers, but there are a few thousand steps that come next). I've seen quite a few successes and failures in technology marketing over the years. The successes tend to be targetted at people and specific problems; somewhere deep in the Matrix-like enterprise fantasy pod farm is a human being who will decide to use or not use what you make. We're not talking to the Borg Collective here. The spectacular failures tended to be cases where (aside from genuinely lousy technology - there are plenty of ways to blow it) they were promoted to the vendors' fantasy of their customers, rather than to the actual people who actually are trying to solve a problem the technology would solve, and who were capable of making a decision. We can't control stupid marketing campaigns, but we can constrain them. So here's Tim's Rule #1: Any technology whose name contains the words business or enterprise is crap until proven not. And might be crap even when proven not. If enough people take that to heart, maybe we'll see a few less really stupid marketing campaigns. People make technology; people use technology. It's really people all the way down. And that's the "message" of this blog: Nobody thinks they're an enterprise. 2008: It's probably clear here that a lot of my criticisms and comments above have a lot to do with Sun. So I should give credit - since I wrote this, Sun has open-sourced the JDK and quite a few other things - there is a real turn-around happening in terms of connecting with people who use what we make. But there's still a long way to go. Writing Portlets in Wicket using the OpenPortal Portlet ServerPosted by timboudreau on August 16, 2008 at 10:32 AM | Permalink | Comments (0)Addendum, two days later: The solution here almost works, but doesn't. You really need the actual servlet, not something being proxied by WicketFilter - action processing happens before the ThreadLocal for the HTTP request and response have been assigned. Basically, you can get a wicket portlet up in OpenPortal this way, but the first request after that will fail. I have patched OpenPortal to implement the apache portal bridge properly (add a class that assigns the ThreadLocal inside com.sun.portal.portletcontainer.appengine.PortalAppEngineServlet.service() and unassign it on exit; then implement the bridge calling that (I'm sure this is not how the OpenPortal folks would prefer such a thing to be implemented - the Portlet API appears to have been written to take great pains not to assume servlets are part of the equation, and that's probably a Good Thing™, but here it's needed.
The result is almost working - still have to figure out why OpenPortal sometimes sends an ActionResponse where it looks like it should be a RenderResponse; that and completely grokking how org.apache.portals.bridges.common.PortletResourceURLFactory is really supposed to rewrite URLs (looking at how Apache Jetspeed does it didn't help as much as I'd hoped) should do it.
So, sorry for the false alarm, but given that my instructions involved patching a bunch of stuff (now it involves patching OpenPortal, Wicket and Apache Portal Bridges - it's been a long day of reading source code for multiple portal servers - I expect nobody went off and immediately tried to duplicate this.
For anybody concerned about it, the 80/20 rule still stands :-)
I'm helping my friend Jon on a project involving Portlets. There is a nice integration of OpenPortal (the Sun Java System We Made This Name So Long You Can't Say It Portal Server by any other name) with NetBeans, for one-click deployment. But OpenPortal is missing the Apache Portlet Bridges stuff necessary to use the wicket portlet. So I wrote an implementation last night. It's not beautiful, and is currently tied to Wicket, but it will get you going. It's a fun project also because Jon and I grew up a mile from each other and learned programming together. Interesting that we're doing today (sitting in front of a computer coding together) exactly what we were doing 28 years ago (except that we were about 12 and it was Z-80 assembly language). Anyway, he's trying to get some stuff going with Wicket and Portlets. I've got to say I'm not in love with the Portlet spec to say the least - for some light reading check out the WSRP spec (although if a portal server implements it, what it does could be cool). I do like the idea of writing Portlets as simple Wicket Applications - that is sweet. Anyway, here's what you need to do to make it work. It goes through Wicket to get the filter, which is probably not really what you want, but it does make it usable (if someone knows a way to get this stuff from inside the bowels of OpenPortal and implement it right, let me know). First you need to get the sources to the Apache Portals Bridges project. Don't get scared - we're just patching one file to fix a bug in the common subproject, and it's simple. Open org.apache.portals.bridges.util.PortletWindowUtils.getPortletWindowId. If you have NetBeans Maven Support, you can just open the project, look up the type and rebuild it.
getPortletWindowId() does something odd: It calls session.getAttribute(PORTLET_WINDOW_ID) and casts the result as a String. Then it puts a Double under that key. Needless to say, if this method is called twice in the same session, it will throw a ClassCastException, which is what we're fixing. Change two lines and add one so it looks like this:
Object portletWindowId = session.getAttribute(PORTLET_WINDOW_ID);
if ( portletWindowId == null )
{
synchronized (session)
{
Double value = new Double(Math.random());
portletWindowId = value;
session.setAttribute(PORTLET_WINDOW_ID, value);
Enumeration names = session.getAttributeNames(PortletSession.APPLICATION_SCOPE);
while (names.hasMoreElements())
{
String name = (String)names.nextElement();
if (PortletSessionUtil.decodeAttributeName(name).equals(PORTLET_WINDOW_ID) &&
value.equals(session.getAttribute(name,PortletSession.APPLICATION_SCOPE)) )
{
portletWindowId = name.substring("javax.portlet.p.".length(),name.indexOf('?'));
session.setAttribute(PORTLET_WINDOW_ID, portletWindowId);
break;
}
}
}
}
return portletWindowId.toString();
The thing we need now is implementations of org.apache.portals.bridges.common.ServletContextProvider andcom.sun.portal.bridge.PortletResourceURLFactoryImpl for OpenPortal. Not being a portlet expert, this is a bit of a hack, because I'm getting the servlet context from the Wicket Application. Hopefully someone will read this and know where in OpenPortal-source to do this.
Anyway, it's all in the attached project - get that and build it and put it on the classpath of your web application. It contains an interface your Wicket application needs to implement (provides public access to WicketFilter->ServletContext) and a subclass of WicketPortlet [or you can implement IPortletBridge from the attached project on your existing Application if you're already subclassing it - this is the ugly stuff]). Similarly, your WicketApplication subclass needs to implement IWicketApplicationPortletBridge (just makes getWicketFilter() public).
Finally, modify your portlet.xml to point to my subclass of WicketPortlet (I said this was ugly, didn't I?) or your portlet if it now implements my IPortletBridge interface)
<portlet-class>com.sun.portal.bridge.BridgePortlet</portlet-class>Now you need to add two parameters to your portlet.xml to tell it where the implementations of the two Apache Portals Bridges implementation are:
<init-param>
<name>ServletContextProvider</name>
<value>com.sun.portal.bridge.ServletContextProviderImpl</value>
</init-param>
<init-param>
<name>PortletResourceURLFactory</name>
<value>com.sun.portal.bridge.PortletResourceURLFactoryImpl</value>
</init-param>
If you've already got your Portlet working on, say, JetSpeed, you won't need to modify the web.xml file for your web application. If you're just getting going, here's mine for completeness:
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<filter>
<filter-name>WicketPortlet</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>com.jons.project.JonsWicketApplication</param-value>
</init-param>
<init-param>
<param-name>detectPortletContext</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>WicketPortlet</filter-name>
<url-pattern>/wicket/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
</web-app>
Anyway, it would be nice if this stuff would just work with OpenPortal, but this should work in the until then. I've filed a bug with the fix for commons-bridges, so hopefully that will be fixed soon. Also, perhaps I can add a portal template to NetBeans Wicket Support so you can just do New Project > Web > Wicket Portal Project (although it would be nice to get rid of the hacks first).
The implementation of the bridges interfaces can be downloaded here - it's just an Ant-based NetBeans project. Add it to your web app's classpath and implement the two interfaces described above. A zip of the sources is also available (you need portals-bridges-common, Wicket 1.3.x and the servlet API on the classpath to build it).
Objects not Strings - transparent serialization over key/value pairs with proxiesPosted by timboudreau on August 16, 2008 at 12:55 AM | Permalink | Comments (5)I had an interesting thought the other day for a project I'm helping a friend with. Many things we deal with come in key/value pairs (URL parameters for instance). Why not immediately work with objects instead? There's a simple way... Probably someone is already doing this - there are certainly enough serialization libraries out there. But here's the thought - I was thinking of URL parameters when doing this: Say you have some parameters and you are expecting something like "id=128,name=foo,showDetails=true" - in some format like a Preferences object, a Wicket PageParameters or anything else that parses or aggregates key/value pairs. The InvocationHandler for the proxy transparently reads and writes the values based on the name of the method called (i.e. getFoo() means return the value for "foo" for whatever the storage is. It's much nicer to work with objects than this sort of thing directly. So what if you could do something like this:
public interface Params {
public int getId();
public boolean isShowDetails();
public String getName();
}
and then you call
Params params = new MySerializationStrategy().read (params);Behind the scenes, you use dynamic proxies. MySerializationStrategy will simply look at the getters and setters, find the property names from them, and return an implementation that will implement the interface you passed in (you will never implement it yourself). You could even go nuts and serialize and, say, encode serialized objects as strings (not sure that's such a hot idea, but you could). Poof - a bunch of ugly code becomes a nice friendly object. It has a few caveats (for example, an integer returning method with no key in the data needs to return something like -1).
PageParameters to do this.
Semi-checked exceptions?Posted by timboudreau on August 13, 2008 at 09:37 PM | Permalink | Comments (12)I'm out in Seattle visiting my friend Jon - he has bad RSI right now, so I'm helping him out on a project. He told me an interesting idea he was kicking around when he was at Sun in 1997 or so, about how checked and unchecked exceptions should have been done. It needs thinking through, but I think it has some merit. There are problems with some exceptions - both historical ones - things that should have been checked but aren't, and things that aren't but should have been. For example:
Jon's idea is simple and elegant: Allow individual exceptions, by type, to be flagged as checked or unchecked depending on a flag set on the package (if unspecified, you get the default). I.e. the compiler would understand this, and not compile something if an exception could be thrown, but if it had been flagged as unchecked, allow it to compile. Sort of like turning on and off assertions by package, but at compile time. Anything like this would need a lot more thinking through (for example, if you call into another package with a contradictory setting to yours, what happens?). But it's an interesting thing to think about (even if it will probably never happen for Java). What do you think? Simple Dependency Injection with ServiceLoader in JDK 6Posted by timboudreau on August 12, 2008 at 01:57 PM | Permalink | Comments (17)There are a lot of dependency injection/IOC frameworks out there. What you may not know is that there is a very simple yet useful one built into the JDK. And it's type-safe.
JDK 6 introduces
For those familiar with NetBeans Lookup API,
Using
Here is a toy example of using
package serviceloaderdemo;
import java.util.ServiceLoader;
public abstract class HelloProvider {
public static HelloProvider getDefault() {
ServiceLoader<HelloProvider> ldr = ServiceLoader.load(HelloProvider.class);
for (HelloProvider provider : ldr) {
//We are only expecting one
return provider;
}
throw new Error ("No HelloProvider registered");
}
public abstract String getMessage();
public static void main(String[] ignored) {
HelloProvider provider = HelloProvider.getDefault();
System.out.println(provider.getMessage());
}
}
package serviceloaderdemo;
public class HelloImpl extends HelloProvider {
@Override
public String getMessage() {
return "Hello World";
}
}
To register HelloImpl as the injected implementation of
HelloProvider, we just create a folder called
META-INF/services in our source directory,
so it ends up in our JAR file. In that directory we
create a text file named serviceloaderdemo.HelloProvider.
That file contains a single line of text - the name of the
actual implementation of HelloProvider:
serviceloaderdemo.HelloImpl
Now, this is a toy example. But imagine that HelloImpl
were in a completely different JAR file. All you have to do to
change the default implementation of HelloProvider is
to put a different JAR file on the classpath.
Let's look at a more real example.
A couple of blogs ago I
showed a cleaned up design for something like
We are going to need a factory for But there's an added wrinkle here: There are at least two flavors of status UI we will want - one which just displays in the statusbar, and one which blocks the UI with a modal dialog.
That's okay: So, to indicate different kinds of UI, we'll use annotations, and look up an instance using strings (we actually don't want type-safety here - someone might want to provide still another kind of progress monitor UI - but we will define two defaults):
@Retention (value=RUNTIME)
public @interface MonitorKind {
public static final String DEFAULT = "default";
public static final String BLOCKING = "blocking";
String kind();
}
Next we define the SPI class that creates instances of ProgressImpl that
can move a progress bar in the UI:
public abstract class ProgressImplFactory {
public abstract ProgressImpl create (Task task);
public <T> T getCapability (Class<T> type) {
return null;
}
}
The last thing we need is the final API mirror-class
for our SPI class. The important method for lookup via annotations is
getInstance(String):
public final class ProgressMonitorFactory {
private static final Set<String> notFoundKinds = new HashSet<String>();
private final ProgressImplFactory delegate;
private ProgressMonitorFactory(ProgressImplFactory p) {
this.delegate = p;
}
public ProgressMonitor create (Task task) {
return new ProgressMonitor (delegate.create(task));
}
public static ProgressMonitorFactory getInstance(String kind) {
if (kind == null) {
throw new NullPointerException ("Kind null");
}
for (ProgressImplFactory p : ServiceLoader.load(ProgressImplFactory.class)) {
//Would be nicer if ServiceLoader let you get the type
//without creating an instance, ala NetBeans Lookup
Class type = p.getClass();
MonitorKind mk = (MonitorKind) type.getAnnotation(MonitorKind.class);
if (mk != null && kind.equals(mk.kind())) {
return new ProgressMonitorFactory(p);
}
}
if (!notFoundKinds.contains(kind)) {
notFoundKinds.add (kind);
Logger.getLogger(ProgressMonitor.class.getName()).log (Level.FINE,
"Requested ProgressMonitor kind '" + kind + "', but it is" +
" not registered on the classpath.",
new IllegalArgumentException());
}
return null;
}
//implementation omitted...
}
Now we just need a JAR that registers an actual implementation
of
ProgressMonitorFactory factory = ProgressMonitorFactory.getDefault();
Component statusComp = factory.getCapability (Component.class);
if (statusComp != null) {
statusBar.add (statusComp);
}
Then all the application author has to do is implement Task
and send it off to be run in the background. The UI will work
correctly and transparently.
The main point here is that the JDK has a useful built-in dependency injection mechanism, and that you can create APIs and implementations of them that are a joy to use and easy to update by using it. While it's not as rich as some of the dependency injection frameworks out there, it has the benefit of being type-safe, and is more than enough for many purposes. Why You Might Want To Use NetBeans Lookup Instead of ServiceLoader
The differences between
$NB_HOME/platform9/modules/org-openide-util.jar.
The Javadoc
can be found here.
Lookup is not tied to any other NetBeans APIs, nor specifically to
GUI applications. You can use it anywhere you want this sort of
dependency injection.
The Capability Pattern - Future-Proof Your APIsPosted by timboudreau on August 11, 2008 at 01:00 AM | Permalink | Comments (19)Here is a simple pattern which you can use to make your APIs extensible, even by third parties, without sacrificing your ability to keep backward compatibility. It is very frequent to create a library which has two “sides” — an API side and an SPI side. The API is what applications call to use the library. The SPI (Service Provider Interface) is how functionality — for example, access to different kinds of resources, is provided. One example of this is JavaMail: To read/write email messages, you call JavaMail's API. Under the hood, when you ask for a mail store for, say, an IMAP mail server, the JavaMail library looks up all the providers registered (injected) on the classpath, and tries to find one that supports that protocol. The protocol handler is written to JavaMail's SPI. If it finds one, then you can fetch messages from IMAP servers using it. But your client code only ever calls the JavaMail API - it doesn't need to know anything about the IMAP service provider under the hood.
There is one very big problem with the way this is usually
done: API classes
really ought to be I won't go into great detail about why this is true here (my friend Jarda does in his new book and we discuss it somewhat in our book Rich Client Programming). In abbreviated form, the reasons are:
Exception.getCause() in JDK 1.3).
So adding methods to a public, non-final class in an API is
a backward-incompatible change.
Given those constraints, what happens if you mix API and SPI in the same class (which is what JavaMail and most Java standards do)? Well, you can't add methods compatibly because that could break subclasses. And you can't remove them compatibly, because clients could be calling them. You're stuck. You can't compatibly add or remove anything from the existing classes. As I've written elsewhere, it is the height of insanity that an application server vendor is supposed to implement interfaces and classes that its clients directly call — for exactly this reason. It would be much cleaner, and allow Java APIs to evolve much faster, if API and SPI were completely separated. But part of the appeal to vendors, for better or worse, to implement these specifications, is that they can extend them in custom ways that will tie developers who use those extensions to their particular implementation. This behavior not entirely about being evil and locking people in. There is a genuine case for innovation on top of a standard - that's how standards evolve, and some people will need functionality that the standard doesn't yet support. Enter the capability pattern. The capability pattern is very, very simple. It looks like this:
public <T> getCapability (Class<T> type);
That's it! It's incredibly simple!
It has one caveat: Any call to getCapability() must
be followed by a null-check. But this is much cleaner
than either catching UnsupportedOperationExceptions,
or if (foo.isAbleToDoX()) foo.doX() or
if (foo instanceof DoerOfX) ((DoerOfX) foo).doX().
A null-check is nice and simple and clean by comparison. It's
letting the Java type system work for you instead of
getting into a wrestling match with it.
Now, what can you do with it? Here's an example.
In my previous
blog I introduced an alternative design for how you could
do something like
In light of what we just discussed,
public final class TaskStatus {
private final StatusImpl impl;
TaskStatus (StatusImpl impl) {
this.impl = impl;
}
public void setTitle (String title) {
impl.setTitle (title);
}
public void setProgress (String msg, long progress, long min, long max) {
//We could do argument sanity checks here and make life
//simpler for anyone implementing StatusImpl
impl.setProgress (msg, progress, min, max);
}
public void setProgress (String msg) {
//...you get the idea
//...
}
public abstract class StatusImpl {
public abstract void setTitle (String title);
public abstract void setProgress (String msg, long progress, long min, long max);
public abstract void setProgress (String msg); //indeterminate mode
public abstract void done();
public abstract void failed (Exception e);
}
So we have an API that handles basic status display. But people are going to invent new aspects to status display. We can't save the world and solve everybody's task-status problems before they even think of them - and we shouldn't try. We don't want to set things up so that it's up to us to implement everything the world will ever want. Luckily, it doesn't have to be that way. Since we've designed our API so that it can be compatibly added to, we let the rest of the world come up with things they need for displaying task status, and the ones that a lot of people need can be added to our API in the future. The capability pattern lets us do that. We add two methods to our API and SPI classes:
public abstract class StatusImpl {
//...
public <T> T getCapability (Class<T> type);
}
public final class TaskStatus {
//...
public <T> T getCapability (Class<T> type) {
return impl.getCapability (type);
}
}
Let's put that to practical use. Someone might
want to display how much time remains
before the task is done. Our API doesn't handle that.
Through the capability pattern, we can add that. We
(or anyone implementing
public interface StatusTime {
public void setTimeRemaining (long milliseconds);
}
A task that wants to provide this information to the UI, if the
UI supports it, simply does this:
public T runInBackground (TaskStatus status) {
StatusTime time = status.getCapability (StatusTime.class);
for (...) {
//do some slow work...
if (time != null) {
long remaining = //estimate the time remaining
time.setTimeRemaining (remaining);
}
}
}
Even better, our But we know we need a UI - and we know we are targetting Swing right now. How can we really keep this code completely un-tied from UI code and still have it be useful? The capability pattern comes to our rescue again - very very simply. An actual application using this UI simply fetches the default factory for StatusImpls (you need such a thing if you want to run multiple simultaneous background tasks and show status for each — my next blog will explain how this can be injected just by putting a JAR on the classpath) and does something like:
Component statusUi = theFactory.getCapability (Component.class);
if (statusUi != null) {
statusBar.add (statusUi);
}
(or if we want to allow only one background task at a time,
we can forget the factory and put the Component fetching
code directly in our implementation of StatusImpl).
If you are familiar with NetBeans Lookup API, the capability pattern is really a simplification of that (minus collection-based results and listening for changes).
The point here is that the capability pattern lets you have an
API that is composed completely of nice, future-proofed,
evolvable, It also helps one to avoid trying to “save the world” — by allowing for extensibility, it is possible to create an API that is useful without needing to handle every possible thing anyone might ever want to do in that problem domain. Trying to save the world is what leads to scope-creep and never-finished projects. In this tutorial I discuss the don't try to save the world principle in a practical example. Does the mirror-class design seem a bit masochistic? I think it does point up a weakness in the scoping rules of the Java language. It would definitely be nicer to be able to, on the method level, make some methods visible to some kinds of clients, and other methods visible to other kinds of clients. But regardless of this, it's even more masochistic to end up “painted into a corner,”[1] and unable to fix bugs or add features without potentially breaking somebody's code. That's how you end up with ten-year-old unfixed bugs. [1]painted into a corner &mdash An English idiom meaning to leave yourself with no options — you were painting the floor of a room in a pattern such that you end up standing in an unpainted corner of the room, and you can't leave the corner until the paint dries. Where's the state?Posted by timboudreau on August 07, 2008 at 01:04 PM | Permalink | Comments (14)Where's the state? This is a small but useful question when deciding how a problem domain gets carved up into objects: What things have state? What things have values that can change? When and how can they change? Can the changes be observed? Who needs to observe changes in state? These questions make a good start for figuring out how to carve up a problem domain into objects, if you observe the principle keep all related state in one place. I would go so far as to say that the majority of the appeal of Model-View-Controller (MVC) architecture is that it encourages you to keep state in one place. One big reason why statefulness matters is threading. In practice, it is common to see designs where threading was an afterthought. This is to be expected. Concurrency is a not natural way for human beings to think. So commonly a library is designed with no thought about threading; then a problem shows up which multi-threading can solve. Threading ends up being introduced into a codebase that was never designed for it. Retrofitting a threading model on something designed without one is very painful and sometimes impossible. Some threading models can be simple. For example, any time you are writing a Swing application, and you have a method that can do long running I/O, the first line of that method should be assert !EventQueue.isDispatchThread();under all circumstances. But keeping I/O off the event thread is the trivial case. Managing mutations to a model inside an application is a more complicated, less obvious problem. ![]() Any time you find yourself writing a setter (or more generally, mutator) method, it is worth asking yourself Who does this piece of state really belong to? Am I putting state in the right place? Blindly following the beans pattern (add a getter/setter to whatever type you happen to be editing) can result in state being distributed all over objects in an application. The result will be harder to maintain, and if it has any lifespan, less and less maintainable over time. Like entropy, or the no broken windows theory, the more state is distributed all over the place, the more that will masquerade as design and make it okay for a programmer to further diffuse state throughout objects in the application. Human beings can only hold so many things in their minds at a time. The more state is diffused, the more things someone must pay attention to simultaneously to solve a problem in that code. Another question above was Who needs to observe changes in state? There are some corollaries: Where should those changes be published? and Does the thing observing the change need a reference to the thing that changed? Let's return to the concept of having a model for a minute. A model gets all related state in one place quite well. It does nothing for threading issues — these still have to be handled somehow within the model, or appropriate use of the model in a multi-threaded environment must be documented. There is a thing you can do at this juncture that can make life much easier — and it is all about how the code that uses the model is designed. If you're creating an API, there is probably more to it than just a data model. There is also how the model is accessed. Essentially what you can do is this: If you design the rest of your API so that the only way to get a reference to the model is to be passed one (i.e. “don't call us, we'll call you”), then you know the context (particularly, on what thread, while holding which locks) in which your code is going to be called.
I'll pick a class at random as a case in point:
![]() I mentioned mixing concerns: Avoiding mixing concerns is not just an academic issue of idealized architectural purity. APIs are things human beings use. If you avoid mixing concerns in a public class, you can name things more clearly, and the result will be easier to understand and need less documentation (which most people won't read unless they have to anyway). In a perfect world, a thing representing a task should probably be stateless (subclasses can have state if that makes sense for them - but no setters or getters or mutable state in the interface/superclass). If the code that does the work is passed a reference to a model object it can set task state on, that untangles the task-state concern from it.
It also
removes a bit of psychological weirdness: Someone who is writing a
Detangling the other concern is
as simple as returning a
public interface Task<T> {
public T runInBackground (TaskStatus status);
public void runInForeground (TaskStatus status, T backgroundResult);
}
public interface TaskStatus {
public void setTitle (String title);
public void setProgress (String msg, long progress, long min, long max);
public void setProgress (String msg); //indeterminate mode
public void done();
public void failed (Exception e);
}
and then perhaps you would have a concrete TaskRunner class that runs tasks
public final class TaskRunner {
public <T> java.util.concurrent.Future<T> launch (Task<T> task, TaskStatus statusUI) { ... }
}
or, perhaps preferably,
What's the difference between this and
More importantly, by passing in a
One of our questions above was Does the thing observing the
change need a reference to the thing that changed? In this case,
absolutely not. In fact, to make the You'll notice that we've completely removed use of the listener pattern here. The listener pattern is seriously overrated, but is often used by default because developers have seen it in the Java core libraries — we use what we know. The listener pattern has a number of problems. If you've ever had to debug a Swing application where you have a severe bug that only occurs after you switch Windows from Classic to XP appearance or vice versa, then you know this pain — I know I do. What is happening is:
Especially when you have a situation where it is very
unlikely ever to have
more than one listener, the listener pattern is probably
not a good choice. Listeners monitor changes in state.
It is not good design to assume that multiple things will want
to monitor the status of a
public final class ProxyTaskStatus {
private final TaskStatus[] statii;
ProxyTaskStatus (TaskStatus... statii) {
this.statii = statii;
}
public void setTitle (String title) {
for (TaskStatus status : statii) {
status.setTitle (title);
}
}
//...
}
if that's really a need; or leave that up to someone implementing
What we have, instead of the listener pattern, is a situation where we're passing in the model object (in this case, the thing that holds state about status), as opposed to having the state belong to the task itself and having other code listen for changes in the state. Most likely that model object writes directly to a UI showing task progress. I believe I've read of this pattern being called don't call us, we'll call you somewhere (if anyone can point me to where I ran across that usage, let me know so I can give credit where it's due). To get back to threading for a minute: There are three ways I typically see threading + statefulness dealt with in code:
This last item brings me to my main point here:
The combination of keep all related state in one
place plus don't call us, we'll call you
creates the possibility of encouraging and even
actually enforcing a
threading model over a complex data model,
without
(generally) having to document lots of rules about
threading and pray that people obey them. A weakly enforced
example of this from the JDK is ![]() Lastly, for anybody about to scream that I have misrepresented SwingWorker here (it has lots of complexity about batching up interim results and so forth, and the above is a simplification), I'll mention that
API BlogsPosted by timboudreau on August 05, 2008 at 11:38 AM | Permalink | Comments (13)
Manila; Moderation done right; Breadcrumbs for NetBeansPosted by timboudreau on June 24, 2008 at 07:41 PM | Permalink | Comments (0)Here's three blogs in one (I know, I know, you're not supposed to do that): My friend Geertjan and I just did NetBeans Day Manila, in the Philippines, followed by two days of plugin-writing training at the University of the Philippines. The students were wonderful and bright and motivated and it's always a joy to teach to people who are really interested in what you have to say. There's a conversation going on on the mailing list for bloggers on this site - there's been a big issue with people posting comments that consist of 500 porn URLs and such. Back when we were little-bitty NetBeans-the-tiny-Czech-startup we had this solved on our mailing lists - 100% spam-free - a lot better than I can say for how things were after we joined Sun. The system was incredibly simple. My friend, and later, roommate for two years Karel Zatloukal, came up with this brilliant and blindingly obvious system - his becoming a manager was a huge loss to hackers everywhere. It's dead-simple:
majordomo hacked within an inch of its life, but the model is simple and easily repeated on any similar system. I feel like most moderation systems for mailing lists and blog posts are still stuck in the stone-age, and I've experienced vastly better almost ten years ago.
Lastly, at the booth in Manila last week, someone commented to me about a new "breadcrumb" feature in IDEA or Eclipse that they wished NetBeans had. I thought, gee, that would take about 20 minutes to implement, and set about doing it. Well, 20 minutes to get the rough draft. Another few hours to really make it useful. Geertjan already blogged about it (do a text search for "breadcrumb"). It's now available for NetBeans 6.0 and 6.1 on the plugin portal at this URL and should be available in the 6.0 and 6.1 built-in plugin managers soon.
|
September 2008
Search this blog:CategoriesBusinessCommunity Community: Java Enterprise Community: Java Patterns Community: Java Specification Requests Community: Java Tools Community: Java User Groups Community: JavaDesktop Community: NetBeans Community: Portlet J2EE J2ME J2SE JavaOne Jini JSR Linux Open Source Patterns Programming Swing Tools Web Applications Archives
August 2008 Recent EntriesNobody Thinks They're An Enterprise Writing Portlets in Wicket using the OpenPortal Portlet Server Objects not Strings - transparent serialization over key/value pairs with proxies | ||||||||||||||||||||||||||||||||||||||||||||||
|
|