Pages

.

Common User-Agent Strings

Today's post is more of a note-to-self than anything else. I'm always trying to remember how the various browsers identify themselves to servers. The attendant user-agent strings are impossible to carry around in one's head, so I'm setting them down here for future reference.

To get the user-agent strings for five popular browsers (plus Acrobat), I created a script (an EcmaScript server page) in my Sling repository that contains the line:

<%= sling.getRequest().getHeader("User-agent") %>

This line simply spits back the user-agent string for the requesting browser (obviously). The results for the browsers I happen to have on my local machine are as follows:

Firefox 3.6:
Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6

Chrome 5.0.375:
Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.38 Safari/533.4

IE7.0.6:
Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506)

Safari 5.0.1:
Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/533.17.8 (KHTML, like Gecko) Version/5.0.1 Safari/533.17.8

Opera 10.61:
Opera/9.80 (Windows NT 6.0; U; en) Presto/2.6.30 Version/10.61

Acrobat 9.0.0 Pro Extended:
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/523.15 (KHTML, like Gecko) Version/3.0 Safari/523.15

Interestingly, Acrobat seems to spoof a Safari signature.

If you want to perform this test yourself right now, using your present browser, simply aim your browser at http://whatsmyuseragent.com/, and you'll get a complete report on the full header sent by your browser to the server.
reade more... Résuméabuiyad

When will China pass America?
















Much is being made over the fact that China just passed Japan to claim the title of "world's second-biggest economy." Although this has provoked a spate of hand-wringing over Japan's decline (see
here and here and here, for example), more level-headed commentators point out that China has eleven times as many people as Japan; for 1.34 billion people to have remained permanently poorer than 1.27 million people would have been (and for a century, was) a human disaster of epic proportions. Sure, Japan has made a lot of mistakes, but those are most certainly not the reason for China's ascendancy.

The next question on everyone's lips, naturally, is: When will China pass the U.S. to become #1? The consensus in the media seems to be that China had better not get too confident; like Japan in the 80s, they argue, China's rapid growth has come at the expense of inefficient overinvestment, misallocation of capital, suppressed consumption, and the entrenchment of special interests that will not be easily dislodged when the boom finally runs its course.

And I agree: China does share many of Japan's weaknesses. Some of these, fact, seem far more severe in China than they did in Japan - the corruption, the pollution, the suppression of consumption, the state control of banks. These are the kind of things that raise a country's growth during the "extensive" phase, when all you have to do to grow is save, save, save, and invest, invest, and invest. But because the same people who received the cheap capital during the boom will continue to hold the strings of the state after the economy gets saturated with buildings and roads and machines, this growth model bakes inefficiencies into the economy. Growth goes faster, but crashes more abruptly and at a lower level.

That said, the sheer numbers involve make it HIGHLY unlikely that China will NOT overtake America in the near future. If China were twice as big as the U.S., I'd say there was some chance they'd never be richer than us. But they are 4.3 times as big.

To see how certain a thing this is, let's do some rough calculations in our heads. This is pretty easy, with the Rule of 70; to find out how fast something doubles, divide 70 by its percentage growth rate. Thus, if China is growing at 10% and America at 2%, China's economy will double in size relative to America's in 70/8 = 8.75 years.

Now, let's do some math. Japan's growth slowed when it hit about half of U.S. per capita GDP (in the late 70s). Same for South Korea in the late 90s. Assume for the moment that China's slows at around the same level. China's GDP per capita is now about 16% o...r 17% of America's. At a relative growth rate of 8% (the average for recent years), China will hit half of America's per capita GDP (and more than twice America's total GDP) in a little more than 15 years.

But perhaps China has more limitations than Japan and South Korea did; suppose that corruption, resource constraints, or China's size relative to its trading partners acts as a check on its growth. Suppose for this reason, China's growth slows when it hits 35% of America's per capita GDP (and 1.5 times America's total GDP). That's still 10 years of hyper-charged growth.

Now suppose that China's growth slows a little bit, so that the growth rate relative to the U.S. is only 6% (i.e. about how fast India is growing now). And assume China reaches only 35% of America's per-capita GDP before its growth slows even more. That's 14 more years of pretty fast growth!

Put this another way: for China to fail to overtake U.S. total GDP, they will have to stall out at 23% of U.S. per capita GDP - the average Chinese worker will have to less productive than an entry-level employee at Wal-Mart. This would mean that their growth would have to stall within 5 years (if we measure GDP in PPP terms, or if they allow their currency to appreciate) or 7 years (if we measure GDP in nominal terms, and they keep their currency undervalued forever and ever).

So, basically, unless China's growth crashes spectacularly and semi-permanently by the midpoint of this decade, their economy will be larger than that of the U.S. by the mid-2020s. That would be an unprecedented slowdown, MUCH more severe than what Japan experienced in the 90s, and at a far lower level of development. I doubt any reasonable China critic would predict such a Biblical disaster.

Thus, the inescapable answer is: China will soon be the world's largest economy, no matter how you measure things. This will happen in less than two decades. And GDP is a direct measure of national power. We must therefore prepare for a world in which the leading geopolitical hegemon will be a non-democratic country, a country with little stake in the existing system of international norms and institutions, a country with hundreds of millions of citizens still living in poverty.

How much of the world we knew in the 20th Century was dependent on the hegemony of free-trading democratic countries? How much of the seemingly unstoppable technological progress, the respect for international boundaries, the slow advance of human rights, etc. were dependent on the lucky fact that the U.S.-Britain-France alliance (and later, the U.S.-Britain-France-Japan alliance) had the most guns?

We're about to find out.
reade more... Résuméabuiyad

Looking back on the Aldus-Adobe deal

There's a terrific interview with Paul Brainerd about the history of Aldus Corporation over at computerhistory.org. In it, Brainerd comments on the why and how of Aldus's eventual acquisition by Adobe (a subject of considerable interest to me, since the company I work for -- Day Software -- has just been acquired by Adobe). Of the acquisition, Brainerd says:
At a 30,000 foot level, we had similar approaches to running a company. But at a working level, there were some very definite philosophical differences.

There was a definite difference in the customer orientation. We spent a lot more time talking to customers. Adobe's philosophy was more of an engineering-based one: if we make a great product, like PostScript, sooner or later people will want it.

But the reason I even considered Adobe was their underlying ethical standard of running a high-quality company that was fair to their customers and their employees. Unfortunately, that couldn't be said of all the companies in the industry.

A lot of thought went into the merger, and I think it was one of the best.
Hopefully, we'll all be saying much the same thing about the Day-Adobe deal years from now.
reade more... Résuméabuiyad

Day Software Developer Training: Days Two and Three

I made it through Days Two and Three of developer training here at Day's Boston office. Under the expert tutelage of Kathy Nelson, the eight of us in the class got a solid grounding in:
  • Apache Sling and how it carries out script resolution. (For this, we used my August 16 blog post as a handout.)
  • Modularizing components and allowing for their reuse.
  • Enabling various WCM content management tools such as CQ's Sidekick, which help web authors to create and edit web pages.
  • Creating a Designer to provide a consistent look and feel to a website, and using a common CSS file.
  • Creating a navigation component that to provide dynamic navigation to all pages as they are added or removed by authors.
  • Adding log messages to .jsp scripts, and using the CRXDE debugger.
That was Day Two. On Day Three we focused on:
  • Creating components to display a customizable page title, logo, breadcrumbs, and configurable paragraph.
  • Creating and adding a complex component (containing text and images), to implement bespoke functionality.
  • Adding a Search component. (We saw 3 different ways to do this.)
  • Internationalization, so that dialogs displayed to web authors can be displayed in one of the 7 languages supported out-of-the-box by Day Communiqué.
By the end of the third day, we had written hundreds of lines of JSP and manually created scores upon scores of custom nodes and properties in the repository.

Still to come: Creating and consuming custom OSGi bundles; workflow; and performance optimization tools.

I can't wait!
reade more... Résuméabuiyad

Day Software Developer Training: Day One

Yesterday, I made it through Day One of developer training at Day Software's Boston office. It was an interesting experience.

There are eight of us in the class. Interestingly, two of the eight enrollees have little or no Java experience (one is not a developer); most of the rest have varied J2EE backgrounds. All are (as you'd expect) relatively new Day customers. One is from an organization that is trying to migrate away from Serena Collage. The organization in question chose Day over Ektron partly on the basis of the flexibility afforded by Day's Java Content Repository architecture, which is relatively forgiving when it comes to making ad hoc changes to the content model over time. (We spent a fair amount of time discussing David Nüscheler's Seven Rules for Content Modeling.)

We spent much of the morning talking about architecture, standards, and the Day technology stack, which is built on OSGi, JCR (JSR-283), Apache Jackrabbit, and Apache Sling. Surprisingly (to me), OSGi was an unfamiliar topic to a number of people. The fact that bundles could be started and stopped without taking the server down was, for example, a new concept for some.

All of us were given USB memory sticks containing the Day Communiqué distribution (and a training license), and we were asked to install the product locally from the flash drive. A couple of people had trouble getting the product to launch (they received the dreaded "Server not ready, browser not launched" message). In one case, it was a firewall issue that was easily resolved. In another case, someone was using Java 1.3 (the product requires 1.5, minimum). A third person had trouble getting WebDAV to work on Windows 7. I noticed, in general, that the people with the fewest problems (all the way through the class) were using Macs.

We were shown how to access the CQ Servlet Engine administration console, the CRX launchpad UI and Content Explorer, and the Apache Felix (OSGi) console, as well as the CRXDE Lite integrated development environment -- a very nice browser-based IDE for doing repository administration and JSP development, among other tasks.

We were also shown how to (and in fact we did) set up author and publish instances of CQ on our local drives, and replicate content back and forth between them.

In the afternoon, we did a variety of hands-on exercises designed to show how to create and manipulate nodes and properties in the repository; how to create folder structures; how to create templates; and finally, how to create components and Pages. (At last, we got our hands dirty with JSPs.)

Some students had trouble getting used to the fact that in JCR, everything is either a node or a property. "Folders" in the repository, for example, are actually nodes of type nt:folder. If you use WebDAV to drag and drop a file into a folder, the file becomes a node of type nt:file and the content of the file is now under a jcr:content node with a jcr:data property holding the actual content. It requires a new way of thinking. But once you get the hang of it, it's not hard at all.

Day Two promises to be interesting as we take a closer look at Sling, URL decomposition and script resolution, and component hierarchies. Hopefully, we'll get even more JSP under our fingernails!
reade more... Résuméabuiyad

Understanding how script URLs are resolved in Sling

One of the things that gives Apache Sling a great deal of power and flexibility is the way it resolves script URLs. Consider a request for the URL

/content/corporate/jobs/developer.html

First, Sling will look in the repository for a file at exactly this location. If such a file is found, it will be streamed out as is. But if there is no file to be found Sling will look for a repository node located at:

/content/corporate/jobs/developer

(and will return 404 if no such node exists). If the node is found, Sling then looks for a special property on that node named "sling:resourceType," which (if present) determines the resource type for that node. Sling will look under /apps (then /lib) to find a script that applies to the resource type. Let's consider a very simple example. Suppose that the resource type for the above node is "hr/job." In that case, Sling will look for a script called /apps/hr/job/job.jsp or /apps/hr/job/job.esp. (The .esp extension is for ECMAScript server pages.) However, if such a script doesn't exist, Sling will then look for /apps/hr/job/GET.jsp (or .esp) to service the GET request. Sling will also count apps/hr/job/html.jsp (or .esp) as a match, if it finds it.

Where things get interesting is when selectors are used in the target path. In content-centric applications, the same content (the same JCR nodes, in Sling) must often be displayed in different variants (e.g., as a teaser view versus a detail view). This can be accomplished through extra name steps called "selectors." For example:

/content/corporate/jobs/developer.detail.html

In this case, .detail is a selector. Sling will look for a script at /apps/hr/job/job.detail.esp. But /apps/hr/job/job.detail.html.esp will also work.

It's possible to use multiple selectors in a resource URL. For example, consider:

/content/corporate/jobs/developer.print.a4.html

In this case, there are two selectors (.print and .a4) as well as a file extension (html). How does Sling know where to start looking for a matching script? Well, it turns out that if a file called a4.html.jsp exists under a path of /apps/hr/jobs/print/, it will be chosen before any other scripts that might match. If such a file doesn't exist but there happens to be a file, html.jsp, under /apps/hr/jobs/print/a4/, that file would be chosen next.

Assuming all of the following scripts exist in the proper locations, they would be accessed in the order of preference shown:

/apps/hr/jobs/print/a4.html.jsp
/apps/hr/jobs/print/a4/html.jsp
/apps/hr/jobs/print/a4.jsp
/apps/hr/jobs/print.html.jsp
/apps/hr/jobs/print.jsp
/apps/hr/jobs/html.jsp
/apps/hr/jobs/jobs.jsp
/apps/hr/jobs/GET.jsp
This precedence order is somewhat at odds with the example given in SLING-387. In particular, a script named print.a4.GET.html.jsp never gets chosen (nor does print.a4.html.jsp). Whether this is by design or constitutes a bug has yet to be determined. But in any case, the above precedence behavior has been verified.

For more information on Sling script resolution, be sure to consult the (excellent) Sling Cheat Sheet as well as Michael Marth's previous post on this topic. (Many thanks to Robin Bussell at Day Software for pointing out the correct script precedence order.)



reade more... Résuméabuiyad

JSOP: An idea whose time has come

The w3c-dist-auth@w3.org list today received an interesting proposal for a new protocol, tentatively dubbed JSOP by its authors (David Nüscheler and Julian Reschke of Day Software). As the name hints, JSOP would be based on JSON and would be a RESTful protocol designed to facilitate the exchange of fine-grained information between browsers and (repository-based) server apps. As such, it's one of the first proposals (maybe the first?) to make extensive use of HTTP's new PATCH verb.

Why does the world need JSOP? "For the past number of years I always found myself in the situations where I wanted to exchange fine-grained information between a typical current browser and a server that persists the information," explains David Nüscheler. "In most cases for me the server obviously was a Content Repository, but I think the problem set is more general and applies to any web application that manages and displays data or information. It seemed that every developer would come up with an ad-hoc solution to that very same problem of reading or writing fine-grained data at a more granular level than a resource."

For example, what if you want to modify not just a resource but certain properties of the resource? WebDAV is often an answer in such situations (or you might be thinking AtomPub in the case of CMIS), but the fact is, it can take a lot of effort -- too much effort, some would say -- to achieve your goals using WebDAV, and in the end, HTML forms have no native understanding of property-based operations. As Nüscheler puts it, WebDAV and AtomPub "are not very browser-friendly, meaning that it takes a modern browser and a lot of patience with JavaScript to get to a point where one can interact with a server using either of the two."

So in other words, something as simple as setting or getting attributes on a folder shouldn't take a lot of hoop-jumping. You should be able to do things like:

Request:
GET /myfolder.json HTTP/1.1

Response:
{
"createdBy" : "uncled",
"name" : "myfolder",
"id" : "50d9317a-3a95-401a-9638-333a0dbf04bb"
"type" : "folder"
}

or:

Request:
GET /myfolder.4.json HTTP/1.1

Response:
{
"createdBy" : "uncled",
"name" : "myfolder",
"id" : "50d9317a-3a95-401a-9638-333a0dbf04bb"
"type" : "folder"
"child1" :
{
"grandchild11" :
{
"depth3" :
{
"depth4 : { ... }
}
}
}
}
In the above example (with nested folders), notice that the GET is on a URL of /myfolder.4.json. Notice the '.4.json', indicating that the server should return folders 4 levels deep.

Suppose you want to create a new document under /myfolder, delete an old document, move a doc, and update an attribute on the folder -- all in one operation. With JSOP, you could do something like:

PATCH /myfolder HTTP/1.1

+newdoc : { "type" : "document", "createdBy" : "me" }
-olddoc
>movingdoc : /otherfolder/mydocument
^lastModifiedBy : "me"

where + means to create a node/property/resource, - means delete, > means move, and ^ means update.

JSOP proposes not only to be JavaScript-friendly but forms-friendly. So for example, imagine that you want to upload a .gif image and update its metadata at the same time, using an HTML form. Under the Reschke/Nüscheler proposal, you could accomplish this with a form POST:

POST /myfolder/my.gif HTTP/1.1
Content-Type: multipart/form-data;
boundary=---------21447684891610979728262467120
Content-Length: 123
---------21447684891610979728262467120
Content-Disposition: form-data; name="data"
Content-Type: image/gif
GIF89a...................!.......,............s...f.;
---------21447684891610979728262467120
Content-Disposition: form-data; name="jsop:diff"
Content-Type: text/plain
^lastModifiedBy : "me"
+exif { cameraMake : "Apple", cameraModel : "Apple" }
---------21447684891610979728262467120--

Bottom line, JSOP promises to provide an easy, RESTful, forms-friendly, JavaScript-friendly way to do things that are possible (but not necessarily easy) right now with WebDAV or AtomPub. It should make working with repositories a snap for mere mortals who don't have time to master the vagaries of things like CMIS or WebDAV. In my opinion, it's a much-needed proposal. Here's hoping it becomes a full-fledged IETF RFC soon.
reade more... Résuméabuiyad