Jan Tielens presents some ideas on how to do reporting in a SOA world and poses the question whether or not there are any other know solutions or products:
In the "good old days" of pure two-tier Client/Server programming it was quite simple: the reporting engine connected directly to the database and fetches the data by executing SQL statements.
Actually, in a very pragmatic way, "reporting" might just be a service as well. I would just create a service which uses any popular reporting tool to generate PDFs, XLSs, RPTs, and so on. This service would then accept a "ReportRequest" message which contains the parameters and the delivery method (file store, by email, etc.). This backend service can then use high-performance direct T-SQL to access the underlying database to acquire the necessary data. (In a second release, I would even think of combining this with WS-Addressing/WS-Eventing and add another service which sends a message to MSN Messenger to notify the requesting user of the completion of their report. Or maybe the client application is just listening for callbacks ... or similar.)
Normally it's the worst possible idea to create a program which doesn't check its input parameters. No matter if this takes the form of a data file, network buffer, command line parameter, registry setting, or user input: you should never trust it, and instead always verify it! But you already knew this.
But ... there is this nice little tool on IBM notebook called "Access IBM - Presentation Director" which allows you to define various display setting profiles so that you only have to press two keys to switch between thing like
1600x1200 internal
1600x1200 internal plus 1280x1024 on DVI. Dual screen, extended desktop.
1024x768 internal plus 1024x768 on analog beamer. Same desktop.
... define more ...
This tool is as simple as it is perfect, because I usually either use my notebook on the road, or on the docking station with external screens, or at a conference with a beamer and I can now switch the settings without going through 16 menus. This is what I like to call solution-oriented thinking (and IBM has some more of this which make mobile life just a little bit easier.)
However. The program doesn't allow you to set the DVI output to anything higher than 1280x1024. IBM designers also didn't think that somebody might actually want to connect TWO external screens to a notebook, displaying 1600x1200 on DVI and 1024x768 on analog, for example (even though the hardware supports this without any issues). Fortunately, this application stores all its data in registry settings so that you can tweak your display settings to whatever format you like, even if the GUI wouldn't allow you to do so. It took me a while to play with the settings, but I can finally run 1600x1200 on DVI plus 1024x768 on analog.
To the developer of this application: Thank you for not verifying your input parameters, you made my life easier by allowing me to use this application in ways you didn't imagine.
Update: It's a Thinkpad R50p. The DVI port is only available with a docking station/port replicator. The base registry key is HKCU\Software\IBM\NPdirect\Data\Common\Presentation Schemes. (And as always: changing registry might destroy your system, etc.)
The one and only true help in Web Services world has just been released: SoapScope hits version 3.0. Congrats for shipping!
I've been a SoapSope customer since the early days (which in this business admittedly weren't much more than a year ago) and this is one of the tools I keep constantly recommending.
In preparations for a talk about .NET and Java interoperability at OOP 2004, I'm currently running a complete interoperability environment on my laptop. I can - theoretically at least - communicate using .NET 1.1, Indigo (in VM), BEA WebLogic, IBM WebSphere, Glue, Axis/Tomcat, Janeva, JNBridge, and Ja.NET. This communication uses everything from Web Services, .NET Remoting, RMI-via-IIOP down to proprietary protocols. Most of it actually even works.
Of course, running all this with just a gigabyte of RAM isn't too much fun. Add the necessary IDEs for .NET and Java, the two Java app servers and the Longhorn VM, and you are pretty much hitting a 2 GB pagefile quicker than you can say "memory exhaustion". Planning to run these demos on multiple VMs to include machines in different domains didn't help too much in terms of memory conservation either.
No worries, though. I've been thinking about upgrading this notebook to an (albeit quite costly) total of 2 gigs of RAM anyway. Add a nice 2 or 3 GB pagefile and you can go where no notebook has gone before.
Right after thinking along these lines, some "red alert" signs must have lit up, and from the back of my mind, voices were talking to me: "thirty two bits. Don't forget the third two bits." At first I ignored them. 5GB of virtual RAM ought to be enough for everybody. But the voices continued "you ain't gonna get five gigs of virtual RAM with only thirty two bits"
Yours truly - facing the realities of computing on a daily basis.
Additional information: Funnily enough, I've been discussing this very issue with a client of mine just a couple of weeks ago as it's actually a very good idea to consider these limits when building web server clusters. I just didn't think of this boundary in terms of my notebook. I mean, hey, a client machine isn't supposed to hit an architectural limit of your processor, right?
Thanks for the great feedback on my previous article on O/R Mappers. I just wanted to add some more of my 2 cents to the issue of scalability with O/R Mappers (or DataAdapters) compared to using a tasty dose of raw, handcrafted SQL in all its beauty.
Let's assume the following: You have an application which allows you to order stuff. Amongst dozens of other tables, you end up with a table "articles" and a table "inventory". Inventory consists only of two integer columns: articleID and stock. (The reason for splitting the data into two tables is that now, "article" contains only near-static data whereas "inventory" is changed all the time. This allows for better caching, but more importantly, for better transaction locking which in-turn yields higher throughput). One requirement for the application is that it must not allow ordering a greater number of pieces of any article as there are in stock. Let's further simplify the matter and say that every order can only contain a single article.
When writing an application using these tables, you essentially have two different possibilities on how to deal with database access and locking. Let's assume - for the sake of this example - that you are placing an order of 3 pieces of article 42 which originally has 125 pieces in stock:
A) Optimistic concurrency. Your O/R Mapper or DataAdapter [which are, as Frans correctly said, essentially very similar anyway] will load the corresponding entry from "Inventory", check if enough stock is available, update it in-memory and try to write it back using an optimistic concurrency-compatible statement similar to "UPDATE INVENTORY SET STOCK = 123 WHERE ARTICLEID = 42 AND STOCK = 125". This means at least two round trips (one for SELECT, one for UPDATE).
B) Bare Bones SQL the way our fathers did. Your application doesn't even care about loading the inventory table, instead it just sends "UPDATE INVENTORY SET STOCK = STOCK - 3 WHERE STOCK >= 3 AND ARTICLEID = 42" to the database. This automatically ensures that enough stock is available for the given article in just one roundtrip. (Extend this sample as necessary. Either use a stored procedure or create a SQL batch with another SELECT statement returning the available inventory in the same round trip.)
No matter which option you've chosen, you now have to check the return value for your update statement as it will contain the number of affected rows. If this number is zero when using option A (optimistic concurrency), it means that someone else has changed the inventory in the meantime. In the world of O/R Mappers or DataAdapter this means that you have to re-fetch the data, check the inventory level, change the inventory level, and try to update again while hoping that nobody in the meantime changed your data.
With option B however, a simple change in inventory stock level will not result into returning 0 as the count of affected rows. The only time when the affected row counter does not equal 1 is when the "business rule" regarding inventory level has not been satisfied. In addition (when using the stored procedure or the batched SELECT) you will also receive - in the same round trip - the available inventory level to suggest possible corrections to the user. All this is done with fewer round-trips, better performance, less chance to prolong the locking time if other data is changed during the same transaction. Or, more easily, with higher scalability.
So, yes, DataAdapters (and probably O/R Mappers -- even though they still limit the ability to use the best SELECT for any given application) have a place in applications. Whenever you change near-static data, there's few things which make your life easier than using these automated tools. Transaction processing however doesn't really fit that nicely into the strict optimistic concurrency model used with DataSets or the OO model used with O/R Mappers. At least I will keep doing my bare bones SQL the way our fathers did for transactions. No, actually I'll use stored procedures --- but that's a different story.
I'm guilty. I don't believe in O/R Mappers as general purpose solution for all applications. I think that they have some place in this world but wouldn't necessarily see them as the centerpiece of data access in most software architectures.
For me using an O/R Mapper feels like deliberately trying to unify the worst of the object oriented and the worst of the relational world. It seems to me that everyone who wants to convince me to love their O/R Mapper forgot about the real power of SELECT. They believe that SELECT's main purpose in life is to, well, select some data which would nicely map to the object oriented world.
In reality however, SELECT is an easy-to-use means for transformations. In fact, it is so easy-to-use that most people don't even think about transformation when using SELECT. But nevertheless, every SELECT essentially generates new types (let's call them "result sets" or "dynamic views") on the fly [1]. Using on O/R Mapper limits this ability. To correctly support SELECT in the object-oriented world, a statement like "SELECT C.CID, C.NAME, COUNT(O.ORDERID) AS NUMORDERS FROM CUSTOMERS C LEFT JOIN ORDERS O ON C.CID = O.CID GROUP BY C.CID, C.NAME" would have to dynamically create a type similar to this one:
public class DynamicResult_794F3A76_4E8E_4d19_973C_91C88BD95520
{
public int CID;
public String Name;
public int NumOrders;
}
But even if this would be the case, it's usually still too hard to deal with dynamically created types in OO languages. That's why runtime-typed constructs like Datasets, Recordsets, or Resultsets exist.
I also tend to have some issues with the fact that constrained transactional updates like "UPDATE INVENTORY SET AMOUNT=AMOUNT-4 WHERE ARTICLEID=42 AND AMOUNT >= 4 " are not easily portable to OO systems. It's simply not possible to create an object-oriented equivalent of the transactional constraint "AMOUNT >= 4" as long as you don't use pessimistic locking in your O/R Mapper. And we don't really want to even think about this, right?
[1] In fact, you are executing a transformation/new-type-creation if you just run "SELECT * FROM CUSTOMERS". The return type of this instruction is not your "CUSTOMERS"-type/table, but instead another type which might be structurally equivalent. SQL hardly ever cares about anything but structural equivalence, so that the type's names usually don't matter too much. Another fact which doesn't really increase the ease of interoperability with the object oriented world.
Glad that I haven't been travelling today: the pilot of an Austrian Airlines flight from Vienna to Munich had to bring the Fokker 70 down in an emergency landing on a snow-covered field. (Only three injured. Great pilot, by the way!)
Personal sidenote: According to the photos, the plane has been called "Wiener Neustadt" [1]. I've been sitting in this plane (not just the type, but the very airplane involved!) several times already - I guess the last time was just three weeks ago.
[1] Austrian Airlines name their planes after cities in Austria. "Wiener Neustadt", "Innsbruck", and so on.