Planet

October 22, 2013

Stefan Tilkov

On Monoliths

A while ago, I gave a talk at QCon about breaking up monoliths (there’s a video up on InfoQ), repeated it in a slightly improved version at JavaZone (see slides and video), and the topic continues to come up in almost every consulting engagement and client workshop I’ve been involved in since then. Like many of the topics I talk about, it’s somewhat unfair that I get the positive feedback and people assume I came up with the ideas all on my own: Most of stuff like this is the result of collaboration, with my colleagues at innoQ (see for example an article I wrote with Phillip Ghadir for ObjektSpektrum if you read German), as well as customer staff. But wherever it originated, I found that it strikes a nerve with many developers and architects, not only in big companies that conduct million-Euro development projects, but also in smaller e-commerce companies and even startups that have started to become successful.

The main idea is this (no surprise for almost everyone, I guess): Nobody wants monoliths, i.e. big systems composed of hundreds of thousands or millions of lines of code (in a language like Java) or tens of thousands (e.g. in Ruby), yet everyone ends up having them. And once you have one, you’re basically stuck with them: They’re incredibly hard to maintain, extend, and modernize; yet they provide value and can’t simply be replaced (something that many organizations attempt but fail at, because it’s awfully hard to create something new that is not only great in terms of architecture, but also can actually function as a full replacement for all of the old system’s features.

So what’s the proposed remedy? To talk about that, we need to take a step back and find out how we actually end up systems that are too big in the first place. My theory is that the number one reason is project scope.

When a project is started, there is an assumption that it’s the goal of a project to create a single system. This typically goes unquestioned, even though the people or person coming up with the project boundaries often don’t decide this consciously. This is most obvious if they’re non-technical people who make decisions on a budget basis.

So the very first thing we should be doing as architects (or lead developers if you don’t like the term) is to find out what it actually is we should be building. Is it really a single system? If our task is to replace an existing system with a new one, it’s very tempting to just accept existing boundaries and go with them. If we’re consolidating two systems, it’s equally tempting to view our scope as the union of the predecessor systems’ scope. In the rare cases where our task is to actually modularize something existing, it’s because of business reasons (such as deregulation). Again, while it might seem like a good idea to just accept the boundaries being suggested to us, it’s not at all clear why this should be a good idea. After all, if whoever came up with those boundaries is not an architect or developer, what makes us think they made a good choice?

In my view, the most important thing to do, then, is to find out how many systems we should be building in the first place. It may be a single one, but it may also be two, five or a dozen (though probably not more) – clearly, the decision should be made very consciously, because whatever system boundaries you pick, you will likely be stuck with them for a very long time.

As “system” is a term that can mean almost anything, I need to define what I mean by it in this context. A system is an independent unit that is developed according to its own rules, and only connected to other systems in an unobstrusive fashion. A system, according to this model, has its own database, business logic, and user interface; it’s deployed separately. It’s likely developed by a different team than other systems. It has its own life cycle, in terms of development as well as deployment. It’s operated autonomously. It has its own test suite. In basically every regard, it’s as different from all the other systems as a piece of commercial off-the-shelf software would be. (In fact, one of the systems may end up being a piece of standard software.) Is that the same as the “Micro Services” idea? If you watch James Lewis’s great talk (here’s a recording, also done at JavaZone; in fact his was scheduled directly after mine), you’ll find a lot of similarities, but the major difference is probably the size of each individual unit. But to me, seeing similar concepts appear in different contexts is a very good sign.

It doesn’t really matter that much whether you get the number and distribution right with the first attempt – in fact, you can reasonably consider that to be highly improbable. But it’s one thing to find out you should have built six or eight systems instead of seven, i.e. get it wrong in one or two places, and a completely different one to notice it should have been seven instead of one.

I’ve rambled on for long enough for a single post, so here’s a preliminary conclusion: How many systems you build should be a very conscious decision. It will affect the life of those tasked with evolving and maintaining it for years to come.

October 22, 2013 08:47 AM

October 07, 2013

Stefan Tilkov

Harassed by Getty Images

Ah, the joys of “Intellectual Property”. I’ve been a long-time fan of the wonderful demotivational posters offered by despair.com. From time to time, I point people to them, e.g. by tweeting about it. In the past, when there was no Twitter (yes, that time existed), I used this blog to do so – on one occasion not only using a plain <a>, but an <img> element as well, embedding one of their images on this site (this is the post minus the image). After all, why not send a little traffic to these fine folks?

But obviously Despair uses stock photos – a perfect use case if there ever was one –, and the rights to the particularly cheesy one used in this poster apparently belong to Getty images (see? I did it again. Sue me.). Now I’ve received a letter, first from their internal legal department, and – after explaining the misunderstanding on their part – now from their lawyer. In both cases, they insist that we need to license the image to use it.

As I didn’t copy the image of the poster, but only link to it, this seems entirely absurd to me – particularly if Despair properly licensed the image, which I’m quite sure of. (If at all, Despair might have more reason, but I can’t believe they’d be that unreasonable, purely out of their own interest.) But my guess is the legal trolls at Getty believe it won’t be worth the hassle to me. They’re wrong – I don’t believe they deserve a single cent of my (or the company’s) money. If you have any advice, or want to share some of your own experience with these people, please leave a comment.

October 07, 2013 09:52 AM

June 30, 2013

Stefan Tilkov

Keyboard Optimization

For the last few months, I’ve been continuously tweaking my laptop settings to optimize my typing speed and efficiency. Here are some of the things I’ve learned, and some of the tools and approaches I’m currently using.

  • I finally took up “real” touch typing, i.e. using 10 fingers and a classical system. For years I’d been quite satisfied, even a little proud, of my awesome typing skillz, only to be shot down when I took the first real speed typing test and found myself scoring a ridiculous 30-40 words per minute, the reason being mostly mistakes and the “occasional” peeking if some not-so-common character came along. There are a ton of tools for learning touch typing. The one I spent the most time with is the excellent (and free) Tipp10, which is available in both offline and online versions. There’s also the very nicely done Keys if you’re on a Mac and have a US keyboard. If you like to have some social interaction, both 10 Fast Fingers and Typeracer are also quite nice. Finally, for a programmer, typing.io is a fantastic resource.

  • I switched to a US keyboard layout a while ago. I don’t know why I’d put this off for so long; while I originally started out using a German keyboard layout (QWERTZ instead of QWERTY), I easily write half of what I produce in English, so it wouldn’t have hurt to do this earlier. The most important benefits are that almost all of the characters required in programming languages are far easier to type using a US keyboard and all of the keyboard shortcuts, particularly in editors such as my favorite, Emacs, suddenly make a lot more sense. Because I still type a lot of German texts and hated the default input method for umlauts and the German “ß”, I installed the great “USGerman” keyboard layout, which allows me to use Option+a, u, o and s to get the appropriate characters with a single combination. This has turned out to be a very workable solution.

  • It also made me use Cmd for Meta in Emacs, which is both good and bad: It’s good because when you use Emacs, you use Meta a lot; it’s bad, because some of the keyboard shortcuts for moving around now need to be done with Cmd (in Emacs) and Option (everywhere else), which can be a bit annoying. Also, I ordered my new laptop with a German keyboard layout by accident, which ended up being great because it means I now have no chance to actually look at the keyboard for those special characters anymore. (It also allowed me to turn the keyboard lighting all the way down.)

  • Speaking of Emacs, I wanted to make sure I have the Emacs short cuts available everywhere. This is actually the case to a large degree by default on a Mac, but I wanted to be able to rely on combinations such as Ctrl-M, Ctrl-H, Ctrl-I, etc. Enter the slightly ugly, stupidly named, but incredibly powerful KeyRemap4MacBook, which allowed me to ensure Emacs keys work everywhere. (I hear this is possible for vi users, too.)

Now I find myself not taking my fingers off home row very much, and I’m comfortably moving along at 60-80 wpm. There’s a weird satisfaction in this – I noticed that in a recent company-internal discussion, many of my co-workers did not seem to see much of a point in taking up touch-typing because they don’t see typing speed as the limiting factor. One reason I might see this differenly is because I (sadly) don’t program much these days, but produce a lot of prose instead. And there, at least, I’m absolutely confident that not having your typing get in the way is a huge asset.

June 30, 2013 07:19 PM

January 17, 2013

Phillip Ghadir

Software Architecture Camp - Reloaded

Softwarearchitektur

Systeme zu konstruieren, die funktionieren und elegant sind, ist das Größte. Leute, die das tun, können ihren Erfolg im Stillen genießen und brauchen eigentlich niemanden, der ihnen auf die Schulter klopft. Das ist aber auch gut so, denn das passiert sowieso in den aller seltensten Fällen.

Der Weg zu einem tollen System ist häufig …

… versperrt mit Wegelagerern, die hoffen, ihre eigenen Anforderungen ohne viel zutun erfüllt zu bekommen.
… länger als er sein muss, weil zu Beginn die Ausgangslage oder das Ziel nicht genau bekannt sind.
… anstrengend, weil nicht alle mit gleichem Eifer in die gleiche Richtung wollen.
… versteckt in einer Menge von technischen, fachlichen oder organisatorischen Möglichkeiten und Unwägbarkeiten.

Und trotzdem ist das Bauen von Software eines meiner größten Hobbys. Am liebsten mit dem besten verfügbaren Team.

Ich rede auch gern darüber, wie man Software-Systeme ordentlich baut. Deshalb habe ich damals den iSAQB e.V. mitgegründet und deshalb gebe ich auch heute noch gern Architektur-Trainings.

Software Architecture Camp

In diesem Jahr gibt es aber erstmalig Trainings in einem neuen Format: Das Software Architecture Camp geleitet von zwei Trainern!

Dr. Gernot Starke und ich geben gemeinsam ein Training zu Software-Architektur, das konform zum Foundation Level-Lehrplan des iSAQB ist und die besten Ideen enthält, die Gernot und mir für so ein Training eingefallen sind.

Zudem übernimmt die Entwickler Akademie die Schulungsorganisation und spendiert jedem Teilnehmer ein riesiges Paket an Büchern. Gernot und ich durften wünschen. Die Teilnehmer bekommen. Großartig!

Zudem verlangt die Entwickler-Akademie für die Trainings einen Preis, der in meinen Augen ein echtes Schnäppchen ist.

Wer sich für Softwarearchitektur interessiert, dem verspreche ich, dass er nicht nur von dem großen Bücher-Paket sondern auch von den unterschiedlichen Sichtweisen von Gernot und mir sehr profitieren wird.

Ich freue mich auf die Schulungen, in denen der Techniker auf den Methodiker trifft - in denen ich, Chaot, auf Gernot, den Meister der Strukturen, treffe.

Auf der folgenden Seite können Sie sich anmelden, wenn Sie dabei sein wollen: Software-Architecture-Camp bei der Entwickler-Akademie.

by Phillip Ghadir at January 17, 2013 09:36 PM

January 07, 2013

Stefan Tilkov

Why I like Schemaless Data Structures

Martin Fowler has written an infodeck (which is actually a nice format for these kinds of things) on schemaless databases and in-memory structures. I agree with most of it, at least as far as database design is concerned. But I believe there are two important concerns missing, and that’s the reason why I don’t agree with the conclusion. Note that my concern is not really with the database part, but with the extrapolation to in-memory data structures, so you should read the following with this in mind:

First, a major effect of using “schemaless”, simple data structures is loose(r) coupling between pieces of logic (functions) operating on them. If I pass a map to a piece of code, that code will extract what it’s interested in, possibly transforming the data in the process (ideally into a new data structure using efficient copy strategies). It will thus only depend on what it actually uses. Take the following Clojure code as an example (this would be doable similarly in Python, Ruby, Perl, Scala and even Java, although with way more boilerplate in it).

;; "projects" is a Clojure set containing three maps
(def projects #{
                {:id "1",
                 :kind :time-material,
                 :description "Consulting for BigCo",
                 :budget 25000,
                 :team [:joe, :chuck, :james]}
                {:id "2",
                 :kind :fixed-price,
                 :description "Development for Startup",
                 :budget 100000,
                 :team [:john, :chuck, :james, :bill]}
                {:id "3",
                 :kind :fixed-price,
                 :description "Clojure Training",
                 :budget 3000,
                 :team [:joe, :john]}})

;; all-members returns all team members in all projects
(defn all-members
  [projects]
  (reduce conj #{} (flatten (map :team projects))))

;; yields #{:chuck :joe :james :john :bill}

The code and the data structure are coupled just by one map key (:team in the example); I can add other data elements without problems as long as I maintain that contract. In languages such as Clojure, a huge library of useful functions to manipulate data rely on this fact (conj, flatten, map and reduce in this case.)

More importantly, it’s possible (and actually quite common) to use generic data structures at boundaries, such as between modules/namespaces. At their interfaces, these modules accept simple data structures, not complex object graphs. In fact this makes it a lot easier to evolve a single-process program into a distributed one: The kinds of interfaces you use internally resemble what you’d be using remotely (in fact there’s a very nice mapping between a nested Clojure, Ruby or Python data structure and e.g. JSON.)

Some languages, such as Clojure, take this to an extreme: Almost everywhere you’d create a new class in an OO language, you’d just use a simple data structure, such as map, a vector, set or list. In a statically typed limited OO language such as Java, you will almost always end up creating new classes. Of course you can use Map in Java, too, but it would not be idiomatic (and the reverse is true for Clojure as well, which enables you to create “normal” Java classes, too.) Some languages are somewhere in the middle, as shown by the Ruby code in Martin’s example. But I find it not very useful to favor one style over the other by default, unless you consider the language you’re using.

Secondly, the use of custom-built data structures – and that’s what a schema boils down to if viewed from a programming language standpoint – always means additional code. You might consider this irrelevant, but it’s nicely shown in interfaces such as those of WSGI, Rack or Ring when you compare them to their equivalent in Java: A simple map containing a method or response code, headers and a body vs. different concrete classes for requests with tons of specific attributes (and bonus getters and setters). Restricting the use of schemaless design to the cases where you actually need dynamic variability misses this advantage.

In summary, I think of Martin’s points are valid, and he definitely spent more time on articulating his conclusion than I did in writing this comment. But I think those two aspects – coupling and verbosity – are worth considering when you need to decide which approach to use.

January 07, 2013 10:00 PM

December 22, 2012

Stefan Tilkov

New languages

Being a programming language geek, I typically try to use the Christmas vacation to learn (or rather, play with) a programming language I don’t know. This year, I find this very hard, as there are so many interesting languages one could spend time with. Some I have considered are:

  • Go: I was thoroughly unimpressed with this language when it came out, and I still fail to see a lot of interesting stuff in it. But I’ve heard many people I respect say only good things about their experience with it, so maybe I should reconsider.
  • Rust: At first glance, this seems to be a very nicely designed language (and it has a really excellent tutorial). Even though its language features are very advanced, it seems to be intended for low-level use cases (that I mostly don’t have).
  • Fantom: Seems to be interesting, too; I remember I looked at it a long time ago, but never in depth.

What do you think? What else is worth a look?

December 22, 2012 02:00 PM

November 06, 2012

Stefan Tilkov

Some Thoughts on SPDY

What follows is a number of claims about SPDY, none of which I can back up in any reasonable way. My hope is that readers who know more will educate me. SPDY, in case you don’t know it, is a replacement for the HTTP wire protocol. It originated with Google and aims to preserve HTTP’s application semantics while improving its efficiency and performance.

  • Supporting true multiplexing on top of a single TCP connection is great. There is no way anybody can prefer the HTTP 1.0 model, which forces a separate TCP connection per request, nor the HTTP 1.1 model, which allows for persistent connections but still requires serialized request/response interactions (never mind HTTP pipelining as it doesn’t work in practice). Browsers having to open separate connections to the same server to achieve parallelism is not a satisfactory solution.
  • Reducing header overhead is also an excellent idea. I’ve heard some criticism about the way this is actually done in SPDY, but it very clearly serves no purpose to have a browser send e.g. the same ridiculously long user agent string with each and every request.
  • I used to not care much for the push support, i.e. the opportunity for the server to actively send stuff to the client, for the same reason I’m not a fan of Websockets: I don’t think you actually need this in practice on the application level. But in a session done by Jetty developer Thomas Becker today, I learned about a quite intriguing usage of this in Jetty’s SPDY implementation: On the first request of a page and the subsequent request for the resources that are referenced in that page, Jetty will build a map based on the Referer header – it essentially remembers which secondary resources a page references. When the next request comes along, it can actively send the referenced resources before the client actually asks for them.
  • I think the fact that SPDY requires TLS is a mistake. While I totally concede that most traffic on the Net is (and should be) encrypted, there are many use cases e.g. within an organization or for public information where this does not make sense. Besides, it prevents the usage of intermediaries, even though I admit these will be much harder to build for SPDY than for plain HTTP anyway.
  • While SPDY proponents point to impressive performance improvements, they are the more impressive the worse the website is implemented. For sites that are already optimized in terms of front end performance, e.g. minimize and compress content, minimize the number of requests, usage proper caching, the effect is going to be much less. That said, some of the things we do in terms of optimization, e.g. combining multiple CSS or JS files into a single one, are not exactly milestones of architectural purity.
  • For machine-to-machine communication – i.e. typical RESTful web services – I don’t think SPDY will have the same kind of effect as for Web sites, but I’m willing to let myself be convinced otherwise.
  • One of the sad but inevitable things when introducing a binary protocol as opposed to a text-based one is reduced transparency for humans. If SPDY becomes successful – and I have small doubts it will – being able to telnet to a servers port 80 is going to be what I miss most.

SPDY has a very good chance of essentially becoming the new HTTP 2.0, and I’m happy about it: I’m pretty confident the HTTP WG with the formidable Mark Nottingham taking care of things will produce something that will be usable for a long time to come.

November 06, 2012 12:02 PM

September 04, 2012

Stefan Tilkov

innoQ Company Events

Since the beginning of innoQ’s existence, 13 years ago, we’ve maintained a regular schedule of so-called “company events”. In my opinion, this is one of the really, really great things about innoQ, and it’s also quite different from what others do. Which is a sufficient excuse for me to write this …

So what’s an innoQ event? All of innoQ’s consultants meet at some more or less remote venue and spend two or three days there, discussing projects, technology, methods, in general: anything of interest to our work. Most of the time we use the classical conference format (an innoQ guy presents something, followed by sometimes controversial discussion), but we use other approaches, such as open spaces, pecha kuchas, and lightning talks, too. We occasionally invite guests (we were lucky to have e.g. Steve Vinoski, Michael Hunger, Markus Voelter, Michael Nygard pay us a visit). While the location is mostly somewhere in Germany, we go to Switzerland sometimes, and one event per year is reserved for a trip “far far away” (in the past years we went to e.g. Prague, Barcelona, Paris, Rome, Budapest, and Strasbourg; these are the only events where we actually spend a day just sightseeing). Some of the events focus on project reports, others are programming events, one event per year is dedicated to company strategy.

What is amazing to most people I talk to about this is the frequency we do this with, and the resulting amount of time, effort and money we invest. We do 6-8 events per year, 2 of them three days long, the rest two days. Events are always scheduled during regular workdays, typically Thursdays and Fridays; attendance is mandatory. This adds up to 15-18 days per person per year, with the most significant cost factor being the “lost” revenue. Of course there’s also a lot of effort involved in organizing the whole thing: The colleague who does this essentially organizes 6-8 small conferences (we’re regularly about 50 people these days) per year (no small feat; thanks, Thomas).

It’s worth every single cent.

Company events are among the very best things about innoQ. They serve to help us to spend some quality time discussing various topics, whether it’s company strategy, a new programming language, library, framework or product, a new approach to project management, or some very project-specific problem. We’re also able to invite candidate employees to a setting where they have a great chance to get to know how innoQ works.

Most importantly of all, they’re fun. We spend a lot of time doing geek things, but there’s always time for great food, occasional drinks, and socializing and talking about other important things in life.

So if you see me tweet from Barcelona during the next three days (where I plan to spend some time with the works of one of my favorite artists tomorrow), you know why I’m there.

September 04, 2012 09:00 AM

June 22, 2012

Stefan Tilkov

Hypermedia Benefits for M2M Communication

Hypermedia is the topic that generates the most intensive discussions in any REST training, workshop, or project. While the other aspects of RESTful HTTP usage are pretty straightforward to grasp and hard to argue with, the use of hypermedia in machine-to-machine communication is a bit tougher to explain.

If you are already convinced that REST is the right architectural style for you, you can probably stop reading and switch over to Mike Amundsen’s excellent “H Factor” discussions. That may be a bit tough to start with, though, so I thought it might make sense to explain some of the ways I use to “pitch” hypermedia. I’ve arrived at a number of explanations and examples of meaningful hypermedia usage, explicitly targeted at people who are not deep into REST yet:

  • “Service Document” and “Registries”: Especially for people with an SOA/SOAP/WSDL/WS-* background, the idea of putting at least one level of indirection between a client (consumer) and server (provider) is well established. A link in a server response is a way for a client to not couple itself to a particular server address, and a server response including multiple links to “entry point” resources is somewhat similar to a small registry. If providing links to actual entry point resources is the only purpose of a resource, I’ve become used to calling it a “service document”. Of course these documents themselves can be linked to each other, allowing for hierarchies or other relationships; they can support queries that return a subset of services; they can be provided by different servers themselves; and they are way more dynamic than a typical registry setup. In other words, the very simple approach included in HTTP is far more powerful than what most crazily expensive registries provide.
  • Extensible contracts: The merits of being able to link to resources can be exploited to add functionality to existing systems without breaking their contracts. This is most visible in server responses that have one or more places where you can put additional links. As your server or service evolves, you can add links that will be ignored by existing clients, but can be used by those that rely on them. The concept of “a link to a resource” is both sufficiently generic and specific to be meaningful enough, especially if you include a way to specify what they actually mean via a link rel=… approach (but more on that in a separate post).
  • Co-Location Independence: What I mean by this slightly weird term is the fact that while resources that are exposed as part of a service interface are sometimes (or maybe even often) designed in a way that requires them to be part of the same implementation, they very often are not, i. e. they could at least in theory be part of a different system. (In fact you can reasonably argue that there should be no assumption about this at all, neither on the server nor the client side for something to be rightfully called “RESTful”, but I simply haven’t found that to be doable in practice.) In those cases where resource don’t need to be hosted by the same implementation, you can and should couple them via links and have clients navigate them instead of relying on common URI paths.

There are quite a few more examples to talk about, but I won’t do that now as I promised to publish something today and don’t want to get into the habit of keeping drafts lying around for too long again. (I know, lenghty this is probably not. Sue me.) So please let me know in the comments what you think of these three if you’re just starting to pick up REST, and what additional ways of explanations you use if you already have done so.

June 22, 2012 01:00 PM

April 04, 2012

Stefan Tilkov

Waterfall sucks, always. Duh.

Today, I had an interesting discussion over Twitter related to project organization in restricted environments. (Update: I’ve removed all references to the actual discussion because the person I interacted with felt mis-quoted, and I don’t think that it was actually that important with regards to what I actually wanted to get across.) This prompted me to take a break from my usual topics and elaborate a bit on my thoughts with more than 140 characters. All this assumes you’re in the role of not only having to actually deliver a piece of software, but also to get the necessary funding – regardless of whether you’re part of an internal organization that requires approval from top management or you’re an external partner that needs to sell to its customer. That said, I’ll focus on the second scenario as that is my primary focus at innoQ.

First of all, in an ideal world, the customer understands all of the reasons that led to the Agile movement, e.g. accepts that an upfront specification that’s 100% correct is an unattainable pipe dream, agrees to participate in the actual development process, and most importantly, understands that what needs to be built will only become clear during the actual development project. We do have some customers who understand this very clearly, and they agree to our favorite pricing model: We offer a sprint/iteration for a fixed price or on a T&M basis, and after each sprint the customer can decide to continue or to stop (which will require one final short wrap-up phase). This reduces the customer’s risk, which is often seen as a benefit big enough to outweigh the perceived disadvantage of not knowing what the overall cost will be. It’s great to be able to work in an environment where everybody’s goals are perfectly aligned, and this is the case in this model.

Unfortunately, this ideal model is not always an option. Of course one way for a development organization to ensure that all projects are done this way is to simply refuse doing it in any other fashion. That’s a good option, but whether it’s actually doable strongly depends on internal power distribution or external market forces.

But what do you do when you have to accept a different set of restrictions? For example, the customer/stakeholder might require a fixed-scope, fixed-time, fixed-price offer. My guess is we can all agree that this is bad idea for everyone involved. But how do you approach things if you just have to do things this way? What do you do if, as an additional downside, the developers assigned to the project are not as skilled as you’d like the to be?

As possible answer might be to use a classical waterfall approach, but I think this is never a good choice. At the very least, go with an iterative approach, even if that means you have to game the system to do that.

Of course you have to put up some effort into an initial up-front analysis. You’ll be aware that much of what you find out may actually turn out to be wrong, but it’s still better to make a slightly more informed estimate up front as opposed to a totally bogus one, especially if you’re an external partner that’s supposed to provide a fixed-price quote. Then, make sure that you grow the system in increments – i.e., build a first working system, using a number of interesting use cases; then add functionality in the next iteration, and continue until done.

Typically, this will resemble something like an agile process – but with slightly larger iterations (e.g. maybe 6 weeks instead of two), and with the added amount of documentation required to fulfill the typical waterfall requirements. (If this reminds you of a Unified Process kind of thing, that’s no coincidence.)

In the end, you’ll have created all of the documents and other artefacts required, but simply not in the order they were supposed to be generated (first analysis, then design, then implementation, then test), but with the trimmed-down focus of each iteration.

Is this perfect? Not even remotely. But in my experience, you have a far greater chance to meet your goals than with actually following the waterfall approach, and even more importantly, management is likely to accept it (partially because it’s obvious, partially because you don’t tell them about it).

If you can’t get away with that, you’re really out of luck, and it’s as they say: You need to change the company, and if you can’t, change companies.

April 04, 2012 12:17 PM

March 06, 2012

Stefan Tilkov

Announcing "ROCA"

In the past few days, we finally managed to write down a few ideas on Web frontend design, basically a set of rules to apply if the goal is to come up with a Web app that is actually on the Web as opposed to be tunnelled through the Web. We tried to come up with a catchy name, and finally arrived at ”ROCA”, a conveniently pronouncable acronym for “Resource-oriented client architecture”.

I am aware that for many folks, specifically those who are interested in REST and thus likely to read this, a common reaction might be “Duh”. And one of the main things I’d like to stress is that we have not invented a single thing, but rather collected a certain set of rules that we found many people liked, but couldn’t name.

Since we started discussing this, we’ve found strong support, as well as violent opposition. Which is exactly what we were looking for, because in only very very few cases, people didn’t understand what we described, and that’s the whole point of the effort: Give a name to a certain cohesive set of practices so that they may used as a reference both when you agree with them, want to build a system that adheres to them or criticize them because you disagree.

I’m looking forward to comments over at the discussion page. If you believe we should make changes, please fork the GitHub repo and create a pull request.

March 06, 2012 06:46 PM

March 05, 2012

Martin Eigenbrodt

Semistatic JSON Binding

One of the design goals of RESTful services is loose coupling. When using JSON as data format one implication is that both a REST Server and Client should not fail on additional fields. This way smaller changes can happen to the API without breaking existing services and clients. Implementing this happens automatically in dynamic languages where a JSON Object is just some type of map. But it is easy to get wrong using Java.

A naive databinding approach would not accept additional data. However the other extrem - unmarshalling json to maps and lists - does not fit Javas static typed world. Luckily there is an approach in the middle: semistatic Binding. Bind those attributes you expect to static java types and put everything else in a Map. Here es an example how to achieve this with Jackson1 and Svenson2.

Example for Jackson:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SomeEntity {

    private String staticProperty;

    @JsonIgnore
    private Map<String,Object> dynamicProperties = new HashMap<String, Object>();

    @JsonAnyGetter
    public Map<String,Object> getDynamicProperties() {
        return dynamicProperties;
    }

    // Boilerplate getter and setter for staticProperty go here...
}

This Class can be mapped to from arbitrary JSON Objects. e.g:

1
2
3
4
5
{
    "foo" : "bar",
    "staticProperty" : "A Value",
    "TheAnswer" : 42
}

Attributes matching a (bean-)property (“staticProperty” here) go into that property. Everything else goes into the map.

The very same can be done in svenson by implementing DynamicProperties (or subclassing AbstractDynamicProperties, but that would hide the fun of it…):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class SomeEntity implements DynamicProperties

{
   private String staticProperty;
   private Map<String,Object> dynamicProperties = new HashMap<String, Object>();

   public Object getProperty(String name)
   {
       return dynamicProperties.get(name);
   }

   public void setProperty(String name, Object value)
   {
       dynamicProperties.put(name,value);
   }

   public Set<String> propertyNames()
   {
       return dynamicProperties.keySet();
   }

    // Boilerplate getter and setter for staticProperty go here...
}

March 05, 2012 02:50 PM

February 28, 2012

Stefan Tilkov

REST vs. Websockets, oh my

There is an entirely absurd discussion going on about “REST vs. Websockets”, predictably claiming that in the long term, REST will be succeeded by Websockets. This is stupid on many levels. I’ll try to be brief:

  • To be pedantic, REST vs. … almost never makes sense, as people are rarely talking about REST (the architectural style) in comparison to another architectural style. But never mind, let’s assume that what was meant was actually “RESTful HTTP” vs. “Websockets”, then …
  • Websockets is not something “more”, it doesn’t add something, it’s not dynamic, or interactive, or in any way “good” – unless you make the same claim about TCP. Websockets essentially allows you build your own proprietary protocols that may or may not be great, with all the typical advantages and disadvantages these end up having: possibly better performance, possibly better suited to the specific task at hand, less standardized, not widely implemented, etc. It’s not a case of one being better than the other, it’s about being different.
  • In the long run, HTTP (used in a way aligned with its architectural goals) will continue to have benefits for loosely coupling systems. If that’s what you want, it makes the most sense. If you’re after the most efficient communication possible, and are willing to sacrifice some of the loose coupling – fine, go ahead, use Websockets. But it’s not as if one will supersede the other.
  • Does this mean I claim that HTTP is perfect? Of course not, it most definitely could be improved. But if this improvement comes, it’s definitely going to introduce more, not less constraints.

February 28, 2012 06:31 PM

February 27, 2012

Martin Eigenbrodt

Jersey CDI and Tomcat

The other day I tried to run Jersey (Version 1.11) and Weld (The CDI Implementation) in Tomcat. It didn’t work out of the box. Some googling revealed the problem: To support CDI Jersey needs access to the BeanManager, the main interface for frameworks that want to interact with CDI. According to the JAX-RS spec the BeanManager should be bound to java:comp/BeanManager in JNDI where Jersey tries to look it up.

Unfortunately Tomcat does not allow putting it there. However it can be obtained via JNDI from java:comp/env/BeanManager. This Problem is mentioned by the WELD Documentation. So how can you make Jersey find it here? There is a thread on the jersey Mailinglist which provides a solution: Subclass Jerseys Servlet/Filter implementation and lookup the BeanManager from java:comp/env/BeanManager. I tried this and it works fine for me.

However while this blogpost was siting in my draft folder there are even better news: Within a Servlet Container Weld also binds the BeanManager into the ServletContext. Starting from 1.12 (Which has been released on Feb. 22) Jersey will lookup the BeanManager from ServletContext and everything works out of the box. If you need to backport the change you can have a look at the patch attached to JERSEY-883.

February 27, 2012 06:02 PM

February 26, 2012

Stefan Tilkov

PATCHing Rails

As mentioned on the Ruby on Rails weblog, Rails 4.0 will include (optional) support for partial updates via PATCH, a change included to better comply to the HTTP spec and the REST architectural style. As you can guess, I really like this motivation, even though I think it’s insufficient to justify major changes – being “RESTful” should not be the goal, building a better system should be. It seems the Rails team has found a good way to do this, as the change is made in a backwards-compatible fashion (so if you don’t care, you can simply ignore it). But it highlights one of the things I really, really like about Rails: It tries to make it a lot easier to build something that’s RESTful than something that isn’t, and its reach means many more people will be exposed to this as the way it’s being done.

So what about the change itself? When should you use PUT, POST, PATCH? First of all, these are the truths I base my views on:

  • POST can mean anything; its most common use is to create something under a location determined by the server; it’s neither safe nor idempotent nor cachable; it should be used whenever using any of the other methods violates one of their guarantees.
  • PUT can mean creation or update; it affects the resource to which it is applied; it’s idempotent; it contains a full representation of the resource (as far as the client is responsible for it). Most importantly, by using PUT the client asks the server to store (in the widest possible sense) the representation under the location provided.
  • PATCH, a relatively new verb (at least in it’s standardized form) is intended to address partial updates, i.e. it updates only parts of the resource it’s being applied to; it’s not idempotent; the client asks the server to change parts of a resource.

If you’re a server developer and want to enable your clients to update only parts of a resource – say, a customer’s address –, you basically have three options:

  • POST the new address to the resource and have the server decide what to do – in this case, process the address change only – based on the content
  • Expose each part you want to be changeable individually as a resource in its own right and use PUT, i.e. make address a resource http://…/customer/:id/address that you can PUT to
  • Use PATCH to put information about the intended change to the resource itself, using an appropriate format understood by the server

Using POST is OK, but only because it essentially means nothing. The PUT option is perfectly fine, but requires you to explicitly create resources for this purpose. This is actually the best option in many cases, especially if the resources you create in the process turn out to be meaningful in their own right, support other methods (GET in particular). It often ends up feeling a bit contrived, though, so it’s nice to have the third option: Using PATCH means you are being very clear about the purpose of the request, and don’t need to create new and possibly otherwise unnecessary resources. It’s still fully RESTful because PATCH is an extremely generic method.

Note that while using POST for partial updates is OK, using PUT (as Rails does) is not, because it violates the behavior as defined by the spec. So changing it is a very good idea, and the only two options are POST and PATCH.

Even though PATCH is clearly useful, and has the ultimate REST authority’s blessing, my recommendation in the past has been to avoid it because you can’t count on anyone (or anything) supporting it, and go with PUT or POST instead. With Rails’ influence, I see this changing – and I very much look forward to being able to include PATCH in my RESTful designs in the future. Add PATCH (in addition to PUT and DELETE) to HTML 5, and I’ll be more than happy …

February 26, 2012 10:09 AM

February 05, 2012

Stefan Tilkov

Blog update

I took the opportunity of a 10-hour flight to finally re-import all of my blog’s old entries into Octopress, fiddling around with a semi-automated process of Ruby and shell scripts and some creative Emacs macros (check out the archives if you care, even though I have no idea why you would). So if this worked, all of the old stuff should be preserved, even though most of it now only has historic value (and prevents Google searches to hit a 404). I’ve also taken the risk of moving the whole stuff back to its old location, and will redirect the temporary one back once I’m sure everything works. One less excuse to not take up blogging again …

February 05, 2012 07:41 PM

January 14, 2012

Stefan Tilkov

XPath and XML

Aristotle Pagaltzis has written a very nice and concise XPath intro. Of all the various standards in the XML ecosystem, I like XPath best, most of all because it enables interaction with an XML document or message in a way that matches Postel’s law: It’s a great way to implement code that doesn’t break each time a minor change is made to an XML format. In fact I’d say it’s one the few very good reasons to stick with XML instead of adopting JSON – even though there are things like JSONPath, they don’t have the tool support and standardization you get from XPath.

January 14, 2012 04:45 PM

December 03, 2011

Stefan Tilkov

Media Types in RESTful HTTP

A topic that comes up again and again in discussions about RESTful design is what kinds of media type to use. I’ve changed my mind about this a few times, often enough so that I believe it makes sense to write a bit about my current thinking on this. (A “media type”, should you happen to wonder what I’m talking about, is a specific format with a well-defined name, in this context represented in the form of a media type identifier such as application/xml or text/html or application/json. [That’s not 100% correct, but that doesn’t really matter here.] A “hypermedia format” is one that includes links or other hypermedia controls.)

There are a number of different ways to deal with media types when designing a RESTful HTTP system. One school of thought advises to stick with hypermedia formats/media types that are well-defined and widely understood, such as HTML or Atom. In other words: Whatever it is you’re trying to send around as part of an HTTP message, use an existing format, such as HTML, the main reason being that there are many processors that are able to understand it. Use the appropriate MIME identifier (such as text/html) in Content-type headers. One can make a strong case for this option: Hypermedia formats are hard to design, so you should avoid inventing your own.

But let’s assume you’ve decided to define your own hypermedia format, mike amundsen-style, whether by designing a completely new XML vocabulary, your own JSON structure, or some other approach: What MIME type do you use?

You can send content labeling it with the generic identifier, say application/xml. In this case, the MIME identifier will signal the technical format being used, while the semantics are only known to clients who either have some out-of-band knowledge or interpret the content itself. The rationale for this approach is that unless your home-grown hypermedia format is likely to be widely adopted, you’d better stick with a media type that is well-known, even though it doesn’t convey specific semantics. Duncan Cragg wrote a nice post on this a while back.

The second option is to invent your own MIME type, say application/vnd.mycompany-myformat, the argument being that you need to convey the semantics of the content to ensure a client, server or intermediary can actually know whether it’s able to process it.

This begs the question of how many different MIME types you’ll end up with. Instead of creating a new identifier for each type of content, (e.g. a customer, a list of customers, a list of orders), I’ve found that a good approach is to think of a specific context – a domain, if you prefer – that your format covers. I like the similarity of this approach to other hypermedia formats, e.g. HTML or Atom/AtomPub, where you actually end up describing something that applies to a set of collaborating participants, instead of some server-side interface. In my favorite example domain (order processing), you might end up with a MIME type of application/vnd.mycompany-ordermanagement, relate this to a particular XML namespace and define a few different XML root elements (order, order confirmation, cancellation, etc.). The assumption would be that processors can be reasonably expected to able to understand all of the elements in this context, not just one of them. (Of course the same reasoning applies when using JSON or something else, minus the namespace benefits/troubles, depending on your view of XML.)

One final approach that I find very interesting was mentioned by Jan Algermissen a while ago: If your format is based on an existing one, e.g. HTML or XML, your server can actually send the same content with different MIME types, depending on the client’s capabilities. A client that only included application/json in its Accept header would then get the content labeled application/json, while one that includes the specific MIME type application/vnd.whatever would get the same content with this label applied.

December 03, 2011 10:15 AM

December 02, 2011

Stefan Tilkov

A Fresh Start ... Again

Google Reader’s extremely weird behavior after a first attempt to move my blog to a new location has made me try yet another different blog setup. This time, there should be nothing at all left from the old installation, no redirects, different URIs for everything, a new blog system – clearly, there’s no way for Google Reader to mess things up this time. Let that be the last thing said about this mess; even though it’s very sad to see all of those blog followers go who only occasionally drop by whenever something new appears in their Google Reader subscription, many of them may not have been following anymore, anyway. Who knows. It’s been a long time since I spent some serious time blogging.

Which doesn’t mean one can’t restart, right? We’ll see. For now, this is the one and only blog I’ll maintain. It has zero moving parts, i.e. it’s a static site, (currently) generated using Octopress. If everything works as expected, you’ll be able to subcribe to the an Atom feed that should actually be working for a change.

I’m somewhat undecided with regards to comments and the Twitter integration (both in the sidebar as well as in the bottom of each post). I’ve toyed with the idea of doing away with comments altogether, but I have to say that Disqus seems quite smart.

December 02, 2011 05:54 PM

January 23, 2011

Philipp Haußleiter

Using UIAutomation for Multilanguage iOS Applications

With the appearance of iOS 4.0 Apple introduced a new Test-Framework for automatically UI Testing: UI Automation. Based on Javascript and build-in into Instruments, UI Automation is a very useful tool during the Developing of iOS Application. A very good introduction in UIAutomation is here and here. During the development of a iOS Application, we decided to port it to iOS 4.0 and therefor use also UIAutomation for regression testing (before that we used GHUnit Tests for Component Testing - but thats another story). As we are primarily a company dealing with web-based application, we had almost zero afford to deal with the Javascript syntax of UI Automation. But we had to deal with the fact, that we developing a dual language Application (de and en), and therefore need a possibility to test the whole UI in both languages. If you are familiar with UI Automation, you probably know that the Framework uses the accessibility labels of your UI and also often Button Labels. So you have to deal with the actual language of the current UI Setting. But wait. There is already a valid mapping of different language to a given key. If you internationalize your application you will use so called Localizable.strings to do your language Mapping (more here). So we just need a way to move our already existing Mapping into our UI Automation world. UI Automation supports the import of separate JavaScript Files to use your own Libraries and Settings. So i build a conversation script to translate your different Localizable.strings to JavaScript and moving all languages into one big collection. So for example a String like this:
	"Library" = "Bibliothek";
	"Shop" = "Kiosk";
Will be converted to:
	UIA.Localizables = {
	"de":{
	...
	"Library" : "Bibliothek",
	"Shop" : "Kiosk",
	...
	},
	"English":{
	}
	...
	}
The next step is to determine during your UIAutomation Test which language Setting you need to Load from your Localization File. It is possible to readout some System Settings during an UIAutomation Test. The basic functions to find your current language and to read the correct language Array look like this:
	UIA.getCurrentLang = function(){
		if(application.preferencesValueForKey("AppleLanguages")[0]  == "en")
			return "English";
		else
			return application.preferencesValueForKey("AppleLanguages")[0];
	}
	UIA.getCurrentLocalizables = function(){
		return UIA.Localizables[UIA.getCurrentLang()];
	}

	var Localizable = UIA.getCurrentLocalizables();
The first function is necessary to capture a quirk of the recent Xcode Versions (some people calling it a bug :-) ). So now we can just use our String within our Test-Cases.
#import "lib/Localizables.js"

function	delay(seconds){
	UIATarget.localTarget().delay(seconds);
}

function tapTab(name){
	var window = UIATarget.localTarget().frontMostApp().mainWindow();
	window.tabBar().buttons()[name].tap();
}

var window = UIATarget.localTarget().frontMostApp().mainWindow();
tapTab(Localizable['Library']);
delay(1);
tapTab(Localizable['Shop']);
delay(7);

I attached the conversion script to this post. You just need to alter the source and destination folders of your i18n files and the UIAutomation-Tests directory. Download file

by Philipp Haussleiter at January 23, 2011 01:59 PM

Show Build-Information in your iOS App About Panel

Sometimes it might be useful to have an exact piece of information about what version of an app you have currently running. Especially if you have a decent Testing-Group, it is important to track the versions in which a bug appears. The goal of this post is to achieve a info panel like this in your application. You get the Application version (from the Application Bundle), the Repository Revision and the Date of the last Commit.

BuildInfo.png

Picture 1: Example Application About Dialog


We are using here the build-in functions of subversion to update given keywords with the repository information. More about this topic here. There is also a way to use this method with git, but i did not test it yet. You may find out more about this here The first step is to create a File-Template you can import in your code, with which you can access all the necessary details:

	#define APP_VERSION   \ 
	[[[NSBundle mainBundle] infoDictionary]   \  
	objectForKey:@"CFBundleVersion"]
	#define APP_EXECUTABLE   \ 
	[[[NSBundle mainBundle] infoDictionary]   \  
	objectForKey:@"CFBundleExecutable"]
	#define APP_NAME   \ 
	[[[NSBundle mainBundle] infoDictionary]   \  
	objectForKey:@"CFBundleName"]
	#define APP_BUILD_REVISION @"$Rev$"
	#define APP_BUILD_DATE @"$Date$"
	#define APP_LAST_AUTHOR @"$Author$" 
	
Code 1: version.h template


The next step is to tell Subversion to replace the placeholder with the subversion values. You can do this with setting the subversion keyword for that file. After that, with every commit of the file "version.h" the values will be updated.

	svn propset svn:keywords 'Revision Author Date' version.h

Code 2: version.h template


The very last step is to make sure, that "version.h" will be updated each time you make a change to your application. Assuming you build your app every time you made a change, you can use the functions, build into Xcode to force an update on "version.h". We use the trick, that every change on the propsets of "version.h" is equal to a file modification itself. So we create a small bash script, setting the propset "build" to a new value. After that, "version.h" needs to be commited as a new version.

	#!/bin/sh

	DATE=`date`
	HOST=`hostname`

	svn propset build "$HOST $DATE" Version.h

Code 3: buildUpdate.sh


Now we need to add the run of "buildUpdate.sh" to our Build-Cycle. (Picture 2 & Picture 3).

TargetSettings.png

Picture 2: Project Target Settings


RunScriptSetting.png

Picture 3: Insert Script Call


After a successful commit, the file "version.h" will look something like this:
	#define APP_VERSION   \ 
	[[[NSBundle mainBundle] infoDictionary]   \  
	objectForKey:@"CFBundleVersion"]
	#define APP_EXECUTABLE   \ 
	[[[NSBundle mainBundle] infoDictionary]   \  
	objectForKey:@"CFBundleExecutable"]
	#define APP_NAME   \
	[[[NSBundle mainBundle] infoDictionary]   \  
	objectForKey:@"CFBundleName"]
	#define APP_BUILD_REVISION @"$Rev: 1047 $"
	#define APP_BUILD_DATE @"$Date: 2011-01-21 18:53:38 +0100 (Fri, 21 Jan 2011) $"
	#define APP_LAST_AUTHOR @"$Author: phaus $"

Code 4: updated version.h


You might modify the output (e.g. filter out the $s or reformat the date) to get a more stylish output.

by Philipp Haussleiter at January 23, 2011 01:59 PM

January 17, 2011

Phillip Ghadir

Das TAO des Konfigurationsmanagement

Kommt es zur Technik gelten in der Softwaretechnik vermutlich zwei Maxime:

Es gibt nichts Wichtigeres als die Auswahl einer stabilen technologischen Basis für die Entwicklung. Der Einsatz neuer Technologien steht dem entgegen. Gleichzeitig gilt jedoch: Es gibt nichts Wichtigeres als den Erhalt von Anpassbarkeit und Erweiterbarkeit eines Systems. Dies beinhaltet die Erneuerung oder den Austausch von eingesetzten Technologien und destabilisiert jede stabile technologische Basis.

Jeder Techniker trägt diesen Kampf in sich aus. Ständig suchen wir aufs Neue die Balance zwischen dem Wunsch nach stabiler, vorhersehbarer Softwareentwicklung und der Veränderung von Umgebung, Anforderungen und Rahmenbedingungen.

by Phillip Ghadir at January 17, 2011 10:21 PM

August 17, 2010

Philipp Haußleiter

Philipps 5 mins: Graph-Fun with AJAX and Canvas

I always searched for an efficient way add dynamic diagrams to a web-project without using flash or other plugin-based magic. With the support of the canvas tag element in almost all mainstream browser, i thought it would be a good time for creating a short demo how things workout. You will need at least two Parts for this demo. First of all you will need a Source JSON feed. For this demo i just hacked together a very basis PHP script:
<?php
header('Content-type: application/json');
echo'{';
echo '"value":"' . rand(0, 60) . '"';
echo '}';
?>
The result is something like:
{"value":"34"}
Secondly you need a Webpage, where you want to insert your canvas element, load the data from the json feed and draw the changing values to the canvas element. For a better performance, we will implementing pulling the data and drawing the data within two parallel cycles. The Common data Storage will be an array of 300 value (for our diagram with a width of 300px). We are using two additional JS Files. The first we need for creating our XHTTPRequest Object and handling the response within a callback method. The second script is for parsing the JSON Feed as a Javascript Object in a safe way (an ordinary eval works, but is to unsecury). Our main-script works in several steps: First we initialize an array with empty elements:
	function init(){
		for(var i=0; i < 300; i++){
			randomValues[i] = 0;
		}
	}

This step is optional, but then you have a nice "zero line" at the beginning.

Secondly we have a method, that pushes a new value to the existing array, and drops the first entry, if the length of the array is greater than 300.

	function addValue(arr, value){
		if(arr.push(value) > 300){
			arr.shift();
		}
	}

The next two methods are necessary for sending our ajax-request and for handling the response in a callback method. Basically the callback method just calls the addValue method.

The timeout variable is set to 200 ms. So the script calls our backend periodically every 200 ms and then adds a new value to our array.

	function pullValue(){
		sendRequest('random.php',handleRandomRequest);
		setTimeout(pullValue, timeout);
	}

	function handleRandomRequest(req) {
		var text = JSON.parse(req.responseText);
		addValue(randomValues, text.value);
	}

The last method is for the drawing functionality:

	function draw(){
		ctx.clearRect(0, 0, 300, 60);
		ctx.fillStyle = "rgba(101,101,101, 0.5)";
		ctx.fillRect (0, 0, 300, 60);
		ctx.lineWidth = 1;
		ctx.strokeStyle = 'blue';
		ctx.beginPath();
		ctx.moveTo(1, 60-parseInt(randomValues[0]));
		for (var i=1; i<randomValues.length; i++){
			value = 60-parseInt(randomValues[i]);
			ctx.lineTo(i,value);
		}
		ctx.stroke();
		setTimeout(draw, timeout);
	}

ctx is a 2d context of the canvas element. On every call of the draw method, all elements of the array are painted. The first element is always the start point. Because the canvas coordinate system has the point 0,0 in the upper left corner but the 0,0 point of our diagram should be in the lower left corner, you have to subtract the array-values from 60 to get the right drawing coordinate. This method also runs periodically every 200 ms. But it also works for two times for pulling the data an drawing it.

Here you can see the script in action

by Philipp Haussleiter at August 17, 2010 12:39 PM

July 19, 2010

Jörg Plewe

50.000 times too slow?

Recently I was in urged to do a web project with the latest and coolest web framework Ruby on Rails.

That's just one line of code grabbed from somewhere:

      next if entry =~ /^\./

Ruby is said to read like a natural language, but for my eyes this is just a cryptic sequence. Maybe I have the wrong natural language. So to clarify what it means I translated it to some assembly language:

      cmp (entryptr),'.'
      beq next

Yes: if the entry begins with '.', skip it.

The latter code might take 2 CPU cycles to execute.

How much will the Ruby code take? There is the line to be interpreted (syntax check, AST creation,... no idea), than a regexp engine is started which starts parsing the expression before it tries to find a match. All this is accompanied by lots of heap activity. My guess: 100.000 cycles. At least.

So it's 50.000 times slower than it needs to be to get the work done. Who needs a cluster of 50.000 machines to serve his application? Well, all others should be well of with a single server, even a poor one, if it only was programmed with care.

I know it's unfair and untrue, so please don't comment on that. But ... to some respect it is true.

by herkules at July 19, 2010 11:44 AM

April 04, 2010

Willem van Kerkhof

20% Rabatt für die Frozen Rails-Konferenz in Helsinki

Wenn sonst noch jemand vor hat, die Frozen Rails-Konferenz in Helsinki am 7. Mai 2010 zu besuchen, kann hier 20% Preisnachlass bei der Ticketbuchung absahnen: http://www.amiando.com/frozenrails?discountCode=M3QGQMMR Ich selbst werde zusammen mit Tim vom 6. bis 9. da sein. Spread the word!

by Willem van Kerkhof at April 04, 2010 01:11 PM

March 06, 2010

Willem van Kerkhof

Wenn MySQL mal gar nicht installieren will...

...oder Apache nicht auf localhost horchen möchte... ...oder Ejabberd sich nicht nach localhost connecten mag... ...oder Dienst XYZ nicht auf localhost ... will... dann schau mal nach ob das loopback-device überhaupt "up" ist! und wenn nicht: `ifup lo` und dann mal nachsehen ob in /etc/network/interfaces vielleicht aus Versehen die Zeile mit `auto lo` auskommentiert ist. Notitz an mich selbst: Netzwerkdienste setzen eine korrekte Netzwerkkonfiguration voraus. Wer diese Einstellungen zuerst überprüft, erspart sich womöglich einen Arbeitstag mit sinnloser Frustration.

by Willem van Kerkhof at March 06, 2010 10:17 PM

March 04, 2010

Jörg Plewe

fully concentrate on business logic...

How often have I read about cool new technologies where, after all, the programmer can now 'fully concentrate on the business logic'.

This meanwhile happens since at least 20 years so I wonder why still anybody does something else than concentrating on the 'business logic'?

One reason might be that it always has been a lie. Using any web framework still ends with tracking and analyzing HTTP requests or reading server logs, using persistence layers ends with monitoring the database, network traffic and such and so on. The promised abstraction just doesn't hold.

But - the main question is: Who - as a software developer - really wants to deal with business logic? Isn't business logic the most boring thing after all? What is the business logic of a webshop e.g.? Displaying a list, maybe filtered, putting things of that list onto another list (aka 'shopping cart'), sending an email, telling the backend. +1 here, -1 there. Nobody studied computer science for that. Business is all about lists and simple calculations. Let's face it: business logic is boring. Period.

I cannot imagine that our brightest minds cannot come up with a suitable solution for a shop in 20 years. Each year we have at least dozens of new-and-cool frameworks to solve that list-thing. +1. -1. Once again. How stupid.

Nevertheless programming computers can be exciting. That's why people try to get rid of doing business logic and do frameworks and technology. That's by far more appealing. Just to get that funded, they claim that later some poor guy can 'fully concentrate on the business logic'.

by herkules at March 04, 2010 09:41 PM

February 26, 2010

Jörg Plewe

Eclipse, COM ... nothing new...

When reading http://www.eclipsezone.com/articles/what-is-iadaptable/ I got the impression that certain solutions always come back, are quite universal and independant of domain, language or environment.
The question here is about getting certain aspects of an object that are not statically declared in a yet typesafe manner. The Eclipse world came up with IAdaptable, as it seems.

public interface IAdaptable {
  Object getAdapter(Class clazz);
}

public class HashMap implements IAdaptable {
  public Object getAdapter(Class clazz) {
    if (clazz == java.util.List.class) {
      List list = new ArrayList(this.size());
      list.addAll(this.values());
      return list;
    }
    return null;
  }
// ...
}

To be used like that:

IAdaptable adaptable = new HashMap();
List list = (List)adaptable.getAdapter(java.util.List.class);

Well, this immediately brought back good-old COM, Microsofts Component Object Model back to my mind. I alway felt comfortable with COM because COM objects are just as plain C++ objects and thus very fast.
The basics are the same. Every object is just a IUnknown (which transforms into an IAdaptable)

[
  object,
  uuid(00000000-0000-0000-C000-000000000046)
]
interface IUnknown {
  [restricted]
  HRESULT _stdcall QueryInterface([in] GUID* rrid, [out] void** ppvObj);
  [restricted]
  unsigned long _stdcall AddRef();
  [restricted]
  unsigned long _stdcall Release();
}

For this is nothing useful, derived interfaces can be defined.

class IStandardMathFunctions : public IUnknown
{
  public:
  STDMETHOD(Add)(long, long, long*)
  STDMETHOD(Sub)(long, long, long*)
};

class IAdvancedMathFunctions : public IUnknown
{
  public:
  STDMETHOD(Fibonacci)(short, long*)
}

But despite static inheritance, every COM object can deliver all of its capabilities (which is, all the interfaces it implements) at runtime from the object using QueryInterface() which is pretty much the same approach as with IAdaptable.

Interesting enough, when I designed an abstraction layer for my pet project http://www.flyingguns.com describing the static 3D world, my solution was similar again.

public interface Feature
{
}

public interface SceneObject extends Disposable
{
  /**
   * Provide a certain feature if available.
   */
  <T extends Feature> T getFeature( Class<T> clazz );
}

public interface Movable extends Feature
{
  void set( Vector3f position, Quat4f orientation );
}

public interface Damageable extends Feature
{
  void setDamage( float damage );
  void destroy();
}

public interface Aircraft extends Feature
{
  SceneObject[] getEngines();

  void setRudder();
  void setAileron();
  void setElevator();
}

The approach has a lot of advantages.

  • 100% typesafe, no casts due to generics
  • features can be combined deliberately per object
  • features may even change over time
  • makes delegation easy, good for code reuse
  • no runtime overhead
  • small footprint: resources for unsused features don't need to be loaded e.g. no fire and smoke if nobody wants to damage an object.

Sample code looks like that:

SceneObject fokkerobject = scene.create( DefaultDatabaseSetup.FOKKER_DR1 );

Movable fokkermover = fokkerobject.getFeature( Movable.class );
fokkermover.set( new Vector3f( 0f, 700, 0f ), new Quat4f( 0,0,0,1 ) );

WeaponSystem fokkerweapons = fokkerobject.getFeature( WeaponSystem.class );
SustainedFire machinegun = fokkerweapons.getWeapons()[0].getFeature(SustainedFire.class);

machinegun.startFire();

SceneObject bullet = machinegun.createProjectile();
Movable bulletmove = bullet.getFeature( Movable.class );
bulletmove.set( machinegun.getNuzzlePosition(), new Quat4f( 0,0,0,1 ) );

Projectile hitter = bullet.getFeature( Projectile.class );
Projectile.Hit hit = hitter.findHit( 50 );
if( null != hit )
   System.out.println( hit.getTarget() + " at " + hit.getLocation() );

 

 

by herkules at February 26, 2010 02:22 PM

February 22, 2010

Willem van Kerkhof

Javascript-Bibliothek für den Umgang mit Währungsangaben

Lange Zeit fehlte mir eine Javascript-Bibliothek, die mir den Clientseitigen Umgang mit Währungsangaben vereinfacht. Die typischen Use-Cases: * Parsen von Angaben wie "25.35 €", "100.5 $", "42,42" und Überführen in ein einheitliches internes Format * einfache formatierte Ausgabe solcher Werte, mit und ohne Währungssymbol * für die ganzen deutschsprachigen Komma-Fetischisten: Transparentes Handling von Dezimal-"," \*graus\* Aus dieser Not ward' currency.js geboren, eine kleine auf prototype.js aufbauende (und also davon abhängende) Lib. Anwendungsbeispiele: --- Input-Parsing: var value_1 = $pM('42.23'); var value_2 = $pM($F('my_input')); Formatierte Ausgabe mit Währungssymbol: alert(value_1.to_s()); Formatierte Ausgabe ohne Währungssymbol: alert(value_1.to_val()); Für Berechnungen muss leider die interne cent-Repräsentation verwendet werden, da Javascript das Überladen arithmetischer Operatoren leider nicht erlaubt: var sum = $M(value_1.cents + value_2.cents) Für weitere Dokumentation siehe (currency.js auf Github)

by Willem van Kerkhof at February 22, 2010 10:24 AM

February 17, 2010

Jörg Plewe

Simple things just work .... Ant 1.8

I recently got pointed to that link: http://java.dzone.com/news/ant-18-scanning-leaves-171

I read the news with some pleasure reminding me that I still like Ant based builds very much over Maven in many cases.

Of course there are a lot of well maintained projects on the web that work very well with Maven. You never know how many enthusiasts-hours have been spent to make that happen. However, in smaller business projects I experienced the situation to be slightly different. Not a single one I came across ran out-of-the-box. Some actions had to be taken upfront, jars needed to be downloaded seperately, some repository got closed or moved away … and so on.

For the latest project of that kind I created an Ant based build which was really fun to do. It was so simple, fast, predictable, transparent. Together with the NetBeans' Ant environment including Ant debugger - fun!

Looking at the result, the build was fully canned, way faster (even with Ant 1.7 which is known to be slow now :)), the artifacts smaller. Everything is built right in place, no redundant copying of files which feels good from an esthetic point of view. The build.xml files are just a few lines and so much smaller than any pom.xml could ever be.

Interesting enough when monitoring the process: creating the Ant build from scratch took approximately the same time as bringing the Maven stuff to work.

I like the simple things.

by herkules at February 17, 2010 01:57 PM

January 28, 2010

Jörg Plewe

Bundle-Bee - transparent, grid-distributed OSGi computing

In my famous company innoQ I currently have the opportunity to work on a real cool tool: Bundle-Bee. It claims to be able to take any OSGi bundle and distribute the computational load to an ad-hoc grid (e.g. all machines in an office) without special setup or configuration.

We just released version 0.5.3 which is still very restricted and far from feature complete - we don't even know what feature completeness might mean - but is already quite useful when it comes to doing computationally intensive things. Like fractals ... for example. Fractal computing is known to be computationally intensive and it may take long times to render the fascinating images. On the other hand, the nature of the problem gives a straight forward way to compute the 2D area in separated tiles and thus - in parallel. 

Bundle-Bee is about taking any OSGi bundle an distributing parts of that bundles computational code over a grid with (close-to) zero configuration. We did so e.g. on one of our company's meeting, where we ran the fractal computation on the attendees notebooks in parallel without any hassle. Just let them all start the Bundle-Bee equipped OSGi container (not a big deal) and they immediately make up an ad-hoc grid. Even one iPhone has been part of the game. The fractals jar is automagically flooded to the grid and execution load is split among all nodes.

Hence Bundle-Bee does the grid distribution transparently, there is no special notion of grid computing in the code at all. The brute-force Mandelbrot implementation is just like that:

public class MandelbrotAlgorithm {

   private static int iterate(Complex z0, int maxdepth) {
      Complex z = z0;
      for (int t = 0; t <= maxdepth; t++) {
         if (z.absSquared() >= 4.0)   return t;
         z = z.times(z).plus(z0);
      }
      return maxdepth;
   }

   public int[][] computeArea( Complex z0, Complex dz, int width, int height, int maxdepth ) {
      int[][] res = new int[width][height];
     
      for( int i=0; i
         for( int j=0; j
            Complex z = new Complex( z0.re()+i*dz.re(), z0.im()+j*dz.im() );
            res[i][j] = iterate(z, maxdepth);
         }
      }
      return res;
   }

Just the main loop needs to go multithreaded which is always a bit tricky when dealing with an UI, but the pseudocode looks easy like that:

MandelbrotAlgorithm algo = new MandelbrotAlgorithm();

onUpdateRequest(area) {
  Collection tiles = divideToTiles(area);
  foreach(tile : tiles) {
    runAsThread() {
      int[][] result = algo.computeArea(tile);
      tile.paint(result);
    }
  }
}

 

Letting Java2D fly a bit, a kind-of-nice UI is not far away:

screenshot

 

Now, by just packing that code into an OSGi bundle (which basically means to augment it with a handful of entries in the MANIFEST.MF), it is ready to be run on a Bundle-Bee grid with nearly unlimited computing power. 

 

Sounds too easy, eh? But it actually is....

 

 

 

AttachmentSize
fractalscreenshot.png153.09 KB

by herkules at January 28, 2010 12:43 PM

January 27, 2010

Philipp Haußleiter

creating JNI with Swig

I am currently playing around with JNI and Java due the colleagues question to make the connect features of jack-audio (http://jackaudio.org) accessible to java. There is already a javalib (http://jjack.berlios.de) with some features, there seems still some needes ones missing. So i started today to have a look into SWIG (http://swig.org). "SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages." After some hours of research i ended up with some facts: To created yourself a Java binding to a given c/c++ Program or Library you need one or more Interface files (*.I) and swig file with all the necessary swig module descriptions. There is an example on the swig homepage ( http://www.swig.org/Doc1.3/SWIGDocumentation.html#Introduction) to explain the workflow of SWIG. There is a c file exmple.c: /* File : example.c */ double My_variable = 3.0; /* Compute factorial of n */ int fact(int n) { if (n <= 1) return 1; else return n*fact(n-1); } /* Compute n mod m */ int my_mod(int n, int m) { return(n % m); } The mapping example.i files looks as the following: /* File : example.i */ %module example %{ /* Put headers and other declarations here */ extern double My_variable; extern int fact(int); extern int my_mod(int n, int m); %} extern double My_variable; extern int fact(int); extern int my_mod(int n, int m); As you can see, the Interface file has a similar syntax with some additional meta information. You can now create your JNI bindings: swig -java example.i There are also flags for different other languages: -allegrocl - Generate ALLEGROCL wrappers -chicken - Generate CHICKEN wrappers -clisp - Generate CLISP wrappers -cffi - Generate CFFI wrappers -csharp - Generate C# wrappers -guile - Generate Guile wrappers -java - Generate Java wrappers -lua - Generate Lua wrappers -modula3 - Generate Modula 3 wrappers -mzscheme - Generate Mzscheme wrappers -ocaml - Generate Ocaml wrappers -octave - Generate Octave wrappers -perl - Generate Perl wrappers -php - Generate PHP wrappers -pike - Generate Pike wrappers -python - Generate Python wrappers -r - Generate R (aka GNU S) wrappers -ruby - Generate Ruby wrappers -sexp - Generate Lisp S-Expressions wrappers -tcl - Generate Tcl wrappers -uffi - Generate Common Lisp / UFFI wrappers -xml - Generate XML wrappers As a result you get three new files: example.java exampleJNI.java example_wrap.c The example_wrap.c can be used to compile the needed library file for your JNI access. The two java Files are the basic JNI implementation: class exampleJNI { public final static native void My_variable_set(double jarg1); public final static native double My_variable_get(); public final static native int fact(int jarg1); public final static native int my_mod(int jarg1, int jarg2); } And a basic java example how to access these functions: public class example { public static void setMy_variable(double value) { exampleJNI.My_variable_set(value); } public static double getMy_variable() { return exampleJNI.My_variable_get(); } public static int fact(int arg0) { return exampleJNI.fact(arg0); } public static int my_mod(int n, int m) { return exampleJNI.my_mod(n, m); } } To get into working with SWIG i can advise the sources of the G4Java Project (http://java.freehep.org/sandbox/G4Java). There is also a maven plugin to use SWIG from within your maven build: http://java.freehep.org/freehep-swig-plugin. I am currently trying to create the necessary Interface files from the jack-audio sources to use them for a first run of SWIG. For python and tck you can use cmake to create these files.

by Philipp Haussleiter at January 27, 2010 11:48 PM

Willem van Kerkhof

MyTether für das Palm Pre

Heute installierte ich die neueste Version von MyTether (http://mytether.net/) auf dem Palm Pre und siehe da, diesmal funktioniert die Anwendung wie sie soll. Nun sitze ich im RE4 von Düsseldorf nach Witten und habe endlich vernünftigen Internetzugang auf dem Notebook über das Pre -- Wahlweise via USB oder WiFi, bevorzugt natürlich ersteres. Die Installation ist beliebig einfach: quantino:~# novaterm quarkino:/# wget -qO- http://mytether.net/install.php | /bin/sh Um Tethering über USB zu verwenden, muss USBnet aktiviert sein. MyTether kann dies über einen Schiebeschalter, aber wenn man schonmal auf der Konsole ist, kann man das auch gleich dort erledigen: quarkino / # usbnet enable Usbnet will be enabled after reboot. Type "reboot" now. quarkino / # reboot Und lossurfen!

by Willem van Kerkhof at January 27, 2010 06:12 PM

JRuby Errno::ECONNREFUSED: Connection refused

Nun endlich war es so weit: ich versuchte es auch einmal mit JRuby. Beim Versuch, erste Gems zu installieren, bekam ich aber den hier: quantino:~# jruby -S gem update --system Updating RubyGems ERROR: http://gems.rubyforge.org/ does not appear to be a repository ERROR: While executing gem ... (Gem::RemoteFetcher::FetchError) Errno::ECONNREFUSED: Connection refused - Connection refused (http://gems.rubyforge.org/yaml) Ich ließ es links liegen und wandte mich anderen Dingen zu. Später am selben Tag versuchte ich das WebOS-Docor-JAR zum Verarzten meines Palm Pre zu starten, welches erst einmal eine Verbindung ins Welt Weite Netz suchte -- Verbindung fehlgeschlagen. Da musste es einen Zusammenhang geben! Und den gibt es: es sind *nicht* die Proxy-Einstellungen, wie lange vermutet, sondern seit einem nicht besonders lange zurück liegenden System-Upgrade (Debian unstable/experimental) muss folgender Parameter der JVM mit übergeben werden, sofern man sein System nicht für IPv6 eingerichtet hat: -Djava.net.preferIPv4Stack=true JRuby reicht Kommandozeilen-Parameter hinter -J an die JVM weiter, aus obigem Kommando wird demnach: quantino:~# jruby -J-Djava.net.preferIPv4Stack=true -S gem update --system Updating RubyGems Nothing to update Um die -J Option nicht ständig angeben zu müssen, tut es auch ein export JAVA_OPTS="$JAVA_OPTS -Djava.net.preferIPv4Stack=true" in ~/.bashc Und damit läuft die Sache dann auch.

by Willem van Kerkhof at January 27, 2010 02:55 PM

January 01, 2010

Jörg Plewe

RMI .... cool somehow

Happy New Year folks!

In 2002, I wrote a highly specialized, very small Q&D tool for my brother to support him in his oncological doctoral's practice. They are three physicians sharing a common room with some unique ultrasonic device. They needed something showing them at their desk wether the room is available or is currently occupied by somebody else. The name 'dokma' is a German pun.

screenshot

Admittedly not very pretty and I definately need to give it an UI overhaul.

Those days, I used RMI (still with 'rmic' to generate the stubs, supported by a NetBeans RMI plugin). The tool worked flawlessly since then. Yesterday, I did a small extension (allow for more physicians) and thereby upgraded to the new, stub-less RMI scheme.

While doing so, I remembered how cool RMI is. Today people tend to think of web technologies for everything that sounds like networked operation, starting with a tomcat as a bare minimum. But in this case, RMI is way easier and way cooler, for it allows to transparantly use callbacks for 'room-occupied' notifications and a tiny, integrated chat function.

String serverurl = "rmi://" + servername + "/DokmaServerInterfaceImpl";
mServer = (DokmaServerInterface)Naming.lookup( serverurl );
mServer.registerCallback( mCallback );

Very hard and cumbersome to achieve with web techniques. And RMI comes with the JRE, so the whole application still fits into a 50kb jar file and zero installation and configuration effort.

A point worth discussing: is RMI still the way to go today? What do the experts say?

 

 

 

by herkules at January 01, 2010 02:31 PM

November 28, 2009

Jörg Plewe

iRant

Do you know that feeling? Something annoys you and you have to tell somebody? This is the bare reason for this blog entry. Don't read it. It is just for me to make me feel better.

When I joined my new company in January, they let my choose my weapons freely. For I've heard so much about these Apple machines and how cool they are and so far ahead of the Windows world, I was curious and ordered one for myself. MacBook Pro 17". Very new, very expensive. Must be great.

As my grandma used to say (or was it Forrest Gump?): if you say something, try to say something kind.

The display, the battery and the touchpad are great. Outstanding, excellent.

The rest was quite disappointing. First, besides the lack of some keys (PgUp/Dn....), many very important key labels are just missing (at least on the German keyboard). []{}~|\ ... try to find them. Very annoying on a coders machine. And why? My old, cheap discounter notebook had them.

The aluminium case looks very stylish but unfortunately is quite thin-skinned and soon had a first bump which no longer looks so cool. But worse, the bottom edge is very sharp and tries to shave my wrist. Which might be part of the plan, for it often gets quite hot on the lower left side which might be easier to bear with if your skin is properly shaved.

Software ... well. The darkest part maybe (I'm an UI guy, so maybe I'm too sensitive here). Try to find a svn client. You have to buy it or go back to the commandline which always gives me that nice feeling of the 80th ... spooky. The Finder needs to go back to usability lab. Even Mac-enthusiasts do say that. Explorer is far ahead! The basic desktop handling is clearty motivated by technical reasoning instead of by a users perspective. Use cmd-tab to switch process, than use cmd-shift-' (German keyboard) to switch applications windows ... what the f**k? Users deal with windows, they don't care for processes.
What is the difference between closing, hiding and minimizing an applications window? Very subtle. I never found out....
And so on and so on ...

Anyway, often it is very hard to get that far. Instead, I admire the spinning beach ball of death. This always seems to happen if MacOS runs out of physical memory. Which is ... always. Or the camera doesn't work any more. For that, Apple very frequently publishes system updates that require a reboot. I assume this is a trick...

Finally, Java applications are somewhat different than they used to be on windows. They feel sluggish and less responsive. I use VirtualBox with Windows if I want to work with NetBeans e.g. (not a joke!).

I don't say its a bad thing overall, it's ok, some things are really nice. The blog just emphasizes on the annoying parts. But next time I'll save my companies money, and will buy a common notebook for less than half. I have no idea where Macs reputation comes from but I suspect it is kind of reality distortion and wishful thinking. It's a linux for the rich.

Did I mention that I also ordered an iPhone? :)

by herkules at November 28, 2009 06:59 PM

November 08, 2009

Philipp Haußleiter

Wiederherstellen eines MacOS Festplatten-Backups mit Hilfe von DD

Das Festplattendienstprogramm von MacOS bietet unter einer übersichtlichen Oberfläche ein umfangreiches Tool um mit Festplatten zu arbeiten. Allerdings gibt es hier einige Probleme, welche oftmals einen Umweg über das Terminal benötigen. Ich habe das Tool dazu benutzt um eine Festplatte aus einem neu erworbenen Netbook zu sichern, bevor ich mit verschiedenen Linux Distributionen spiele :-). Das war notwendig, weil diese Festplatte eine Recovery-Partition enthält, von der man dann ggf. das Windows-System wiederherstellen kann. Das Festplattendienstprogramm ermöglicht es sehr einfach, ein Image von einem kompletten Device (einer Festplatte) zu ziehen. Hierbei werden auch gleich nicht gefüllte Bereiche ausgespart, sodass von der 160 GB Platte ein knapp 8GB großes Image übrig bleibt. Bis zu diesem Zeitpunkt befand ich mich noch in dem Glauben, dass ich das Image zu einfach wieder zurückspielen könnte. Achja: Für das Backup habe ich die 2,5" SATA Platte ausgebaut und mit Hilfe eines USB-SATA Adapters meinem MacBook Pro zur Verfügung gestellt. Die Struktur der Festplatte sieht wie folgt aus: bash-3.2# diskutil list ... /dev/disk1 #: TYPE NAME SIZE IDENTIFIER 0: FDisk_partition_scheme *160.0 GB disk1 1: Windows_NTFS System 85.9 GB disk1s1 2: DOS_FAT_32 69.6 GB disk1s2 3: 0xDE 4.5 GB disk1s4 ... Dem unbedarften Leser scheint hier nichts besonderes aufzufallen, allerdings ist die letzte Partition vom Typ EISA-Konfiguration und kann von MacOS nicht gemountet werden. Interessanterweise ist es dem Festplattendienstprogramm aber möglich, die Partition mit in ein Gesamt-Image zu sichern, wenn man das komplette Device sichert. Dummerweise ist eine Wiederherstellung auf Device-Ebene nicht vorgesehen :-). D.h. es ist möglich die Partition mit der (aktuellen) Windows Partition(NTFS), sowie eine weitere Partition mit Update-Daten(FAT32) wiederherzustellen, aber die eigentlich Revocery-Partition bleibt im Nirvana verschollen :-/. Weiterhin ist es hierzu notwendig, das sowohl Zielfestplatte, als auch Backup-Image die identische Partition-Struktur haben - d.h. legt ein Linux-Installer ein eigenes Partition-Schema an, so ist es nicht mehr so einfach möglich, das Backup wieder einzuspielen. Was uns bei beiden Problemen hilft ist das Unix-Tool "dd". Als allererstes ist es wichtig, herauszufinden, wie die beiden Devicenamen lauten. Hierzu mounten wir das Backup-Image und schließen die Festplatte wieder an den Mac an. Danach lassen wir uns die Disk-Device auflisten: bash-3.2# diskutil list /dev/disk0 #: TYPE NAME SIZE IDENTIFIER 0: GUID_partition_scheme *200.0 GB disk0 1: EFI 209.7 MB disk0s1 2: Apple_HFS Imotep HD 199.7 GB disk0s2 /dev/disk1 #: TYPE NAME SIZE IDENTIFIER 0: FDisk_partition_scheme *160.0 GB disk1 1: DOS_FAT_32 DISK1S1 84.9 GB disk1s1 2: Linux_Swap 970.6 MB disk1s3 3: DOS_FAT_32 69.6 GB disk1s2 4: 0xDE 4.5 GB disk1s4 /dev/disk2 #: TYPE NAME SIZE IDENTIFIER 0: FDisk_partition_scheme *160.0 GB disk2 1: Windows_NTFS System 85.9 GB disk2s1 2: DOS_FAT_32 69.6 GB disk2s2 3: 0xDE 4.5 GB disk2s4 Unsere Quelle ist /dev/disk2, unser Ziel /dev/disk1. Als allererstes kopieren wir den MBR vom Image auf die Festplatte (hier ist auch die Partition-Tabelle gespeichert - man erspart sich das aufwendige Neu-Partitionieren). Der MBR befindet sich innerhalb der ersten 512k einer Festplatte. bash-3.2# sudo dd if=/dev/disk2s1 of=/dev/disk1s1 bs=512 count=1 Nun sind wir in der Lage die beiden sichtbaren Partitionen über das Festplattendienstprogramm wiederherzustellen. Hierzu wählen wir unser Ziel an. Unter dem Tab "Wiederherstellen" ziehen wir einmal unsere Zielpartition in das Input-Feld "Ziel" und aus dem gemounteten Image die Quell-Partition in das Input-Feld "Quelle". Sollte das Programm eine Fehlermedung ausgeben, so ist es ggf. notwendig, die Partitionen erst zu deaktivieren (Partition anwählen und über die Toolbar oben deaktivieren). Nach einigen Minuten sollte das Backup eingespielt sein. Dies ist ein großer Vorteil gegenüber von "dd", weil dd die Daten sektorweise wiederherstellt (also auch Nullsektoren 1:1 überträgt), während das Festplattendienstprogramm nur die reinen Daten überträgt und Nullsektoren ausspart. Was bleibt ist die letzte nicht-sichtbare Partition. Dies kopieren wir nun abermals per "dd". Um nicht kB-Weise zu kopieren wählen wir hier 512MB Slices: dd if=/dev/disk2s4 of=/dev/disk1s4 bs=512m Obwohl es nur knapp 5GB sind, nimmt der Kopiervorgang einiges an Zeit in Anspruch, sodass sich abschätzen lässt, wie zeitaufwendig ein Wiederherstellen der kompletten 160GB per "dd" wäre. Mich hat diese Erkenntnis eine halbe Nacht gekostet :-). Vielleicht steht irgendjemand einmal vor dem gleichen Problem (z.B. sichern/wiederherstellen von reinen Linux Partitionen).
Blogged with the Flock Browser

by Philipp Haussleiter at November 08, 2009 05:20 PM

September 27, 2009

Willem van Kerkhof

LaTeX und Regexen

Bei der Vorbereitung zu meinem nächsten Vortrag zum Thema Regular Expressions musste ich schmerzerfüllt feststellen, dass LaTeX beim Versuch, Regexen vernünftig zu formatieren, ein ganz schön widerwärtiges Biest sein kann. Um diese Aufgabe etwas zu erleichtern und außerdem den Aufbau der Regexen dem Publikum etwas strukturierter vorstellen zu können, bediene ich mich nun folgender LaTeX-Kommandos: \definecolor{White}{rgb}{1,1,1} \definecolor{Grey}{rgb}{0.5,0.5,0.5} \definecolor{Brown}{rgb}{0.3,0.3,0} \definecolor{Red}{rgb}{0.7,0,0} \definecolor{Green}{rgb}{0,0.7,0} \definecolor{Darkblue}{rgb}{0,0,0.5} \definecolor{Violet}{rgb}{0.8,0,0.9} \definecolor{Orange}{rgb}{0.8,0.4,0} \newcommand{\RegEx}[1]{/#1/} \newcommand{\LargeRegEx}[1]{{\large /#1/}} \newcommand{\Or}{\textcolor{Brown}{\textbar}} \newcommand{\Kleene}{\textcolor{Violet}{*}} \newcommand{\OneOrMore}{\textcolor{Violet}{+}} \newcommand{\ZeroOrOne}{\textcolor{Violet}{?}} \newcommand{\Begin}{\textcolor{Brown}{\^{}}} \newcommand{\End}{\textcolor{Brown}{\textdollar}} \newcommand{\MLBegin}{\textcolor{Brown}{\Escape{A}}} \newcommand{\MLEnd}{\textcolor{Brown}{\Escape{Z}}} \newcommand{\Escape}[1]{\textbackslash{}#1} \newcommand{\Wordchar}{\textcolor{Darkblue}{\Escape{w}}} \newcommand{\Space}{\textcolor{Darkblue}{\Escape{s}}} \newcommand{\Nonwordchar}{\textcolor{Darkblue}{\Escape{W}}} \newcommand{\Digit}{\textcolor{Darkblue}{\Escape{d}}} \newcommand{\Match}[1]{$($#1$)$} \newcommand{\Matches}[1]{\textcolor{Darkblue}{\textbackslash{}#1}} \newcommand{\Repeat}[1]{\textcolor{Violet}{$\{$#1$\}$}} \newcommand{\AnyOf}[1]{\textcolor{Green}{$[$#1$]$}} \newcommand{\NoneOf}[1]{\textcolor{Red}{$[$\^{}#1$]$}} \newcommand{\AnyNumberOf}[1]{#1\Kleene} \newcommand{\OneOrMoreOf}[1]{#1\OneOrMore} \newcommand{\ZeroOrOneOf}[1]{#1\ZeroOrOne} \newcommand{\Modifier}[1]{\textcolor{Orange}{#1}} Diese Anweisungen müssen for dem `\begin{document}`-Stanza stehen, damit man innerhalb des Dokuments dann z.B. folgende Regex inklusive Syntaxhighlighting darstellen kann: /^[\w\d._%+-]+@[\w\d.-]+\.\w{2,4}$/ wird in LaTeX zu: \RegEx{\Begin\OneOrMoreOf{\AnyOf{\Wordchar\Digit.\\_\%+-}}@\OneOrMoreOf{\AnyOf{\Wordchar\Digit.-}}\Escape{.}\Wordchar\Repeat{2,4}\End} Gerendert wird das dann in etwa so: regex1.png Bei der Darstellung der Sonderzeichen in LaTeX hat mir dieses Dokument sehr geholfen: Everybody Loves Regular Expressions!

by Willem van Kerkhof at September 27, 2009 10:53 AM

July 01, 2009

Philipp Haußleiter

ja,

ich lebe noch. Sobald mir mehr einfällt, schreibe ich mal wieder was :-).

by Philipp Haussleiter at July 01, 2009 11:11 PM

June 26, 2009

Willem van Kerkhof

Good old Legacy Systems (tm)

Da will man einfach nur eine Wegbeschreibung ausdrucken. Doch da stellt man fest, dass der Drucker ja nur einen Parallelport hat, das Notebook aber nicht. Zu neu. Der Arbeitsrechner... dito, zu neu. Aber ganz unten im Schrank steht ja noch ein alter Rechner, der hat sowas. Ist ja auch eigentlich der Printserver. Aber dieser wiederum hat ein Netzwerkproblem, will heißen: Hänger und Segfaults bei Netzwerkoperationen, keine Ahnung warum. Kernelupdate? ohne Netzwerk schwierig. Live-CD? Gute Idee, hätte die Kiste denn ein Laufwerk eingebaut -- lang lebe LVM über 5 eingebaute Festplatten, einfach eine herausnehmen ist eher als Experiment anzusehen. USB-Stick + Live-System + .....? Ach vergiss es einfach. Good old Legacy System (tm): Papier und Stift. Funktioniert immer. Wer braucht schon Mainframes, SOA und RoR ;)

by Willem van Kerkhof at June 26, 2009 09:50 PM

June 14, 2009

Willem van Kerkhof

Ruby + Qt4

Ich habe heute wieder einmal ein Wenig mit Qt4 gespielt, diesmal jedoch nicht mehr mit C++, sondern Ruby. Die Voraussetzungen dazu beschränken sich auf die zu installierende Bibliothek libqt4-ruby und natürlich die Qt4 API-Referenz. Fazit: Sehr cool, die Eleganz von Ruby gepaart mit der mächtigen Qt-Bibliothek macht richtig Lust auf schöne Multiplatform-Desktop-Applikationen. Die von QtRuby (`libqt4-ruby`) zur Verfügung gestellten Qt-Bindings orientieren sich zu sagen wir mal 98% an den Original-APIs, mit der Ausnahme dass das "Q" in denn C++-Klasssennamen durch ein "Qt::"-Prefix ersetz wurde. Was ich allerdings als weniger hübsch empfinde, ist die konsequente Weiterverwendung von `camelCase` auch bei Methodennamen -- es steht keine Rubyesquere `under_score`-Variante zur Verfügung. Anders sieht das aus bei den Qt-/KDE-Ruby-Bindings in der Korundum-Bibliothek. `korundum4` ist eine m.E. etwas weiter fortgeschrittene Qt-Schnittstelle für Ruby, die zusätzlich die umfangreichen KDE-Erweiterungen der Qt-Bibliotheken zur Verfügung stellt. Außerdem wird Camelcase und Underscore-Schreibweise bei allen Methodennamen gleichermaßen unterstützt und "Setter" werden wie gewohnt über "value = " (und zusätzlich über "setValue()") angeboten. Während QtRuby konsequent auf Ruby-Datentypen setzt, verwendet Korundum (noch) eigene Wrapper-Datentypen an Stellen, wo die C++ API einen Pointer erwartet. So würde ein Aufruf von `Qt::FontDialog.getFont(my_boolean, Qt::Font.new("Helvetica", 10), self)` in Korundum so aussehen: `Qt::FontDialog.getFont(Qt::Boolean.new, Qt::Font.new("Helvetica", 10), self)`. Dies soll sich laut Dokumentation aber in Zukunft ändern. Bis auf diese Dinge lassen sich Programme, die für QtRuby geschrieben wurden, auch mit Korundum weitestgehend ohne Änderungen auch mit Korundum betreiben. Ein paar hilfreiche HowTos: * Einführung mit QtRuby: http://developer.kde.org/language-bindings/ruby/tutorial/tutorial.html * Eigene Widgets mit QtRuby oder Korundum: http://usefulfor.com/ruby/2007/06/04/ruby-qt-custom-widget-example/ * Qt-Anwendungen mit Ruby und .ui- (Qt Designer-) Dateien: http://www.arachnoid.com/ruby/RubyGUIProject/index.html Happy Hacking!

by Willem van Kerkhof at June 14, 2009 10:27 PM

June 03, 2009

Willem van Kerkhof

Buttons oder Links?

vs. Delete
Wenn wir alle vorgeben, das Prinzip von REST verstanden zu haben, warum taucht dann immer wieder die Frage auf, warum (besonders zu Zeiten von Javascript, Ajax und Web 2.0) Links als Verweise auf Aktionen ("lösche Blogeintrag", "sende Formular ab"...) so schlimm seien. Dabei ist es doch nur eine Hilfe für den Benutzer UND den Entwickler: * *Links* kann man (noch!) ohne Gefahr zu laufen, unerwünschte Nebeneffekte zu erzeugen, folgen. In REST-Sprech: Links verweisen auf Repräsentationen von Ressourcen. * *Buttons* verweisen auf Aktionen, die per definitionem (?) Nebeneffekte aufweisen. In REST-Sprech: Ihre Betätigung leitet Veränderungen an Ressourcen ein. Will ich einem Benutzer also zeigen, was er sich alles ansehen kann, so biete ich ihm eine Liste von Links an. Will ich ihm hingegen die Möglichkeiten zur Veränderung der dargestellten Ressource(n) anbieten, so biete ich ihm eine Sammlung an Buttons. Dieses Prinzip konsequent angewendet nimmt dem Entwickler m.E. sowohl im UI-Design als auch bei der Umsetzung einer RESTful Webanwendung (Denk-)Arbeit ab. Dem Benutzer der Anwendung macht es die Interpretation der UI ebenfalls erheblich leichter. Was die Implementierung selber angeht, also ob Buttons nun Formulare abschicken oder ob die Aktionen via JavaScript eingeleitet werden, ist bei dieser Betrachtung erst einmal unerheblich. Ersteres ist der Accessibility von Webanweniungen natürlich bedeutend zuträglicher, zweiteres ist für "flashy" Web 2.0-Anwendungen wohl das Mittel der Wahl. Den Königsweg zu beschreiten und die Formulare per "unobtrusive Javascript" nach dem Laden der HTML-Seite zu verändern, um sie durch Ajax-getriebenes Verhalten zu ersetzen, ist aber auch kein bedeutender Mehraufwand (der sich aber auszahlt). Leider wird o.g. Verhalten durch gewisse Standardmethoden in Ruby on Rails nicht unbedingt gefördert: `link_to_remote` setzt per default einen POST-Request ab, `link_to_function` existiert und `button_to_remote` existiert erst seit Rails 2.0 oder 2.1. Sieht das jemand anders? Was sind dafür die Gründe? Diskussionsbeiträge sind willkommen! Anmerkungen zu Michaels Kommentar (Danke dafür!): Ich gebe dier zu 100% recht, dass einen die richtige technische Lösung nicht davon entbindet, das Interface-Design derart zu gestalten, dass der Benutzer klar sieht wo er ohne Nachzudenken hinklicken darf und wo Vorsicht angebracht ist. Jedoch führt meines Erachtens die o.g. Regel, konsequent umgesetzt, automatisch zu besserem Interfacedesign: einfach nur deswegen, weil man eine einfache Regel umsetzt, an die sich der Benutzer gewöhnen kann. Siehe dazu auch die immer wieder geführte Diskussion, ob in einem Dialog der mit einem [OK] und einem [Abbrechen]-Button [OK] links oder rechts von [Abbrechen] angebracht werden sollte: es spielt keine Rolle, Hauptsache man wendet ein einziges Schema konsequent an. Ich behaupte jedenfalls, dass es keinem Entwickler weh tut, konsequent Buttons für Aktionen und Links für "Ansichten" zu verwenden. Spätestens seit CSS 2.0 lassen sich beide ohnehin beliebig stylen. Und dann gibt's ja noch die Textbrowser... (nein, die sind beileibe nicht tot ;-))

by Willem van Kerkhof at June 03, 2009 09:11 PM

Ruby + Subversion

Wer sich wie ich dieses Wochenende im großen weiten WWW vergebens auf die Suche nach Dokumentation für die äußerst praktischen Ruby-Subversion-Bindings (libruby-svn) macht, dem sei an dieser Stelle gesagt: die Bibliothekt *ist nicht dokumentiert*. Das Einzige, was mir weitergeholfen hat, sind folgende Blogs: * http://icepick.info/2008/09/25/using-rubys-svn-bindings/ (das einfachste beispiel) * http://www.oneofthewolves.com/2007/03/06/ruby-subversion-bindings-finally-some-documentation/ (schöne Einführung) * http://www.oneofthewolves.com/2007/12/22/ruby-subversion-bindings-better-documentation/ (Code und Unit Tests) Das und ein Wenig Subversion-Anwenderwissen reicht bei Weitem aus, um z.B. Datenbankeinträge mit einem Subversion-Repository zu synchronisieren.

by Willem van Kerkhof at June 03, 2009 08:58 PM

May 29, 2009

Phillip Ghadir

Welche Bücher sollte ein Informatiker gelesen haben?

Hiermit komme ich der Bitte nach, ein paar Buchempfehlungen zu geben, die man als praktischer Informatiker gelesen haben sollte. Es gibt tolle theoretische Bücher oder Grundlagenwerke. Es gibt Bücher, die sich eher auf Projektmanagement beziehen. Und es gibt Bücher, die sich auf das Selbstverständnis und das Handwerkszeug beziehen.

Es fällt mir schwer, meine persönliche Top 10 zusammenzustellen. Bemerkenswert ist, wie alt die wertvollen Bücher im Schnitt sind… Hier ist die Liste der nicht spezifischen Bücher, die sich eher mit zeitlosen Themen befassen:

* Code Complete
* Pragmatic Programmer
* Mythos des Mann-Monats
* Objektorientierte Softwareentwicklung Analyse und Design mit UML 2.0
* Refactoring
* Extreme Programming
* Business Components Factory
* Analysis Patterns
* Programming Pearls
* Release It

Eigentlich müsste man natürlich noch The Art of Computer Programming aufführen, allerdings kenne ich nur einen der das Buch lesen und durcharbeiten kann. Die anderen Menschen, die ich kenne, sind nicht bereit dazu, sich mit der hohen Informationsdichte und Komplexität auseinander zu setzen.

Darüber hinaus könnte man noch ein paar hervorragende Programmiersprachen-spezifische Bücher aufführen, die sicherlich einen Platz in der Top 10 einnehmen könnten. Meine Liste der tollen Management-Bücher habe ich vor gut zwei Jahren vorgestellt: Empfehlung zu Management-Büchern

Preisfrage: Welches der obigen Bücher sollte man mit ganz großer Vorsicht lesen und besser noch doppelt und dreifach darüber sinnieren?

by Phillip Ghadir at May 29, 2009 01:46 PM

May 26, 2009

Willem van Kerkhof

Live von der RailsWayCon Berlin

Nach einem frühen aber entspannten Flug nach Berlin gibt's jetzt im Laufe des Tages immer mal das eine oder andere Bild von der RailsWayCon im BCC am Alexanderplatz. Natürlich ist die innoQ mit einigen Rails-Spezialisten vor Ort vertreten: Raum und Zeit für Diskussionen mit den "üblichen Verdächtigen": Einen sowohl abgedrehten als auch außerordentlich kompetenten Vortrag über Ola Binis neueste Programmiersprachenkreation namens "Ioke" gab's vom Meister persönlich: Mehr gibt's später!

by Willem van Kerkhof at May 26, 2009 01:04 PM

May 17, 2009

Tim Keller

Rails, JRuby, Oracle und Leopard

Oracle 10g läuft ja nun auch offiziell unter Leopard in Verbindung mit einem Intel Mac (siehe Oracle-Webseite). Also habe ich das Ganze mal ausprobiert. Wer keine Lust hat die wirklich sehr ausführliche Oracle-Anleitung zu verwenden, dem kann ich die Anleitung von Raimonds Simanovskis empfehlen. Diese hat bei mir wunderbar funktioniert. Wenn Oracle wie gewünscht läuft, besorgt Ihr euch - wenn nicht sowieso schon vorhanden - eine aktuelle JRuby-Version. Bei mir ist das Version 1.1.6. Ein Blick in den "Getting Started" - Artikel kann nicht schaden. Anschließend wird Rails, Rake, der jdbc-adapter usw. über JRuby installiert: jruby -S gem install rails rake activerecord-jdbc-adapter Da wir die Oracle-Datenbank über den Oracle-jdbc-Treiber ansprechen, wird dieser nun in das lib-Verzeichnis von JRuby kopiert. Der Oracle-Treiber sollte sich im Verzeichnis $ORACLE_HOME/oracle/product/10.2.0/db_1/jdbc/lib befinden. Nun wird noch die database.yml angepasst: development: adapter: jdbc driver: oracle.jdbc.driver.OracleDriver url: jdbc:oracle:thin:@localhost:1521:ORCL username: mein_user password: geheim Danach sollte eine Verbindung mit der ORCL-Instanz möglich sein.

by Tim Keller at May 17, 2009 03:25 PM

Philipp Haußleiter

VirtualBox error: fixing VDI already registered

Oftmals ist es zweckmäßig, eine Art Template-Image für eine virtuelle Maschine (VM) zu erstellen, mit welchem man eine saubere Basis erhält, auf der man Software installieren kann, speziell für die einzelne VM. Das Problem ist, dass VirtualBox in jedes VDI (virtual disk image) eine eindeutige ID schreibt, welche es verhindert, dass eine identische Copy eines Images mehrmals eingebunden wird. Constantin Gonzalez hat dazu in seinem Blog eine interessante Lösung beschrieben. Ich habe das zum Anlass genommen, diesen Befehl in ein bash-script zu gießen ;-). Hier als das Script: #!/bin/sh # Copy VDI with unique identifier if [ $# -ne 2 ]; then     echo "Usage: ./copyVDI.sh <VID-source> <VID-target>"     exit 1 else     if [ $1 == $2 ]; then         echo "VID-source has to be not equal to VID-target!"         exit 1     fi     cp $1 $2     dd if=/dev/random of=$2 bs=1 count=6 seek=402 conv=notrunc     exit 0 fi Das Script sollte selbsterklärend sein. Faktisch kann man hiermit eine Copy eines VDIs anlegen, welche gleichzeitig eine eindeutige ID erhält.
Blogged with the Flock Browser

by Philipp Haussleiter at May 17, 2009 01:04 PM

May 15, 2009

Willem van Kerkhof

Neue USV

Langsam füllt sich der Serverschrank: Heute um 11:20 MESZ wurde die vorgestern bestellte neue USV geliefert. Es handelt sich dabei um ein 2U-1500VA-Modell von APC (SC1500I) mit RS232-Anschluss zur Überwachung, 4 Last-Ausgängen sowie je einen RJ11- und RJ42-Ein- und Ausgang für Netzwerk-Überspannungsschutz. Wider meiner schlimmsten Befürchung ist der eingebaute Lüfter nur dann in Betrieb, wenn die Kiste wirklich warm wird -- bislang also noch nicht. Mit der vierfachen Kapazität und dreifachen Leistung der bisherigen USV können nun endlich alle angeschlossenen Rechner gleichzeitig betrieben werden, die Überbrückungszeit dürfte dabei rund 30 Minuten betragen. Das beigelegte Zubehör kann sich sehen lassen: neben zweier Anschlusskabel lag ein RS232/USB-Konverter, Montagewinkel für alle möglichen Rackmonunt-Variationen, Käfigmuttern und (unerwartet und sehr sympathisch) ein loser C14-Stecker. Die mitgelieferte Software ist zwar nur für SuSE und RH- Linuxe ausgelegt, aber dafür hat Debian ja seinen eigenen apcupsd. So, jetzt dreht mir den Saft ab! ;) **Nachtrag**: Wie zu erwarten war, funktionierte die standard-Konfiguration für apcupsd nicht auf Anhieb, was auf den USB-nach-RS232-Konverter zurückzuführen war. Hier meine Einstellungen, die den erhofften Erfolg brachten: modprobe usbserial pl2303 in /etc/apcupsd/apcupsd.conf: UPSCABLE smart # NICHT usb UPSTYPE apcsmart # ebenfalls NICHT usb DEVICE /dev/ttyUSB0 Nach einem Neustart von apcupsd erschien keine Fehlermeldung mehr und beim Ziehen des Netzsteckers erschien ganz selbstverständlich ein 'Power Failure' und 'Running on UPS Batteries' in /var/log/apcupsd.events. Damit einher gehend wurde eine entsprechende Broadcast-Nachricht übers Netz geschickt. */me wartet nun auf ein schweres Unwetter*

by Willem van Kerkhof at May 15, 2009 06:40 PM

May 05, 2009

Willem van Kerkhof

Petition gegen Stoppschilder im Internet

Da surft man unschuldig im WWW und jede drite Seite, die man besucht, wurd durch ein großes STOPP-Schild gesperrt. Die IP-Adresse und diverse weitere Daten landen automatisch auf einer schwarzen Liste beim BKA und man muss im ständigen glauben leben, als Kinderschänder oder politischer Störer durchzugehen. Fiktion? Bald nicht mehr. "Da oben" im Bundestag überlegt man sich eine gesetzliche Grundlage für solche Zensurmaßnahmen. Jeder sei hiermit aufgerufen, die entsprechenden Petition hiergegen mitzuzeichnen: https://epetitionen.bundestag.de/index.php?action=petition;sa=details;petition=3860. Die eigene Freiheit wird es einem danken. Nachtrag: man sieht wie kompetend die Onlineplattform des Bundestags an der Stelle implementiert ist: bei der unglaublichen Zahl von 2 Zeichnungen pro Sekunde arbeiten die Server am Anschlag, so dass noch nicht einmal Captchas angezeigt werden. Zwei Reloads waren nötig um mich anmelden zu können:

by Willem van Kerkhof at May 05, 2009 02:26 PM

April 27, 2009

Willem van Kerkhof

Rails I18n heiß diskutiert

Die halbherzig implementierte I18n-API des Rails 2.3-Frameworks bedarf dringend einer grundlegenden Überarbeitung, wenn Rails es einmal ernsthaft in Großunternehmen schaffen soll. Ich hoffe, dass am Ende die Vernunft siegen wird und Gettext es in den Rails-Kern schafft. Ein Versuch der Evangelisierung und eine angeregte Debatte zu dem Thema findet zur Zeit auf der Rails Core-Googlegroup statt. Kommentare und Einsichten sind willkommen!

by Willem van Kerkhof at April 27, 2009 07:59 PM

April 18, 2009

Willem van Kerkhof

Der Serverschrank ist da!

Nach längerem Gehabe mit der Spedition Maier in Hagen (\*hmpf\*) ist er nun endlich angekommen und aufgebaut: der lange ersehnte Rittal-Serverschrank! Mit einem Liefergewicht von 75 kg inklusive Palette frage ich mich zwar, wie das Paket es bis zur ersten Zwischenetage im Treppenhaus geschafft hat, aber solcherlei Fragen sollte man besser nicht stellen. Aufgebaut umfasst das 21U-Gehäuse jedenfalls 700x700x1025mm3 und bietet eine abschließbare Fronttür sowie zwei abnehmmare Seitenwände. Ich bin begeistert von der Fertigungsqualität, die Montage konnte komplett ohne Werkzeug erfolgen und dauerte keine 20 Minuten. Natürlich lagen keine Käfigmuttern bei, ebenso muss ich mich noch um Geräuschdämm-Matten kümmern. Die Server und Switche konnten heute nach Lieferung der Käfigmuttern aber schon erfolgen, wie die Bilder bezeugen. Die nächste geplante Anschaffung ist nun ein KVM-Switch und eine 1.5kVA-USV, da 500VA leider nicht mehr ausreichen, um beide Rechner und die Switche zu betreiben.

by Willem van Kerkhof at April 18, 2009 12:03 AM

April 01, 2009

Phillip Ghadir

Die Architektur des Webs ist gescheitert

Das Internet, das wir alle kennen und täglich nutzen, durchdringt nicht nur den Alltag immer stärker sondern findet durch eine Bewegung anerkannter IT-Experten immer größere Beachtung. Bis vor kurzem wurde stark bezweifelt, dass die für das Internet verwendeten Dienste und Anwendungen ausreichen könnten, um ernsthafte Geschäftsanwendungen zu realisieren. Selbstverständlich sah man lange Zeit in Amazon, Google und Co. die obligatorischen Ausnahmen, die für einige aus diesem Zweifel eine Regel machten.

Dank technisch versierter Experten wie Fielding, Tilkov oder Vinoski wird mittlerweile auch den Anhängern der klassischen Unternehmens-IT-Disziplinen klar, welche Vorzüge in dem Architektur-Stil des weltweiten Datennetzes (des World Wide Webs) stecken. Rhethorischen Fragen a la “Warum findet man mit einer Internet-Recherche mehr Informationen über einen bestehenden Vertrag mit einem Vertragspartner als in Eurer Bestandsführung?” treffen einen Nerv und decken architekturelle Schwächen gängiger Unternehmensanwendungen auf.

Dennoch: Gerade in einer Zeit, die das Zusammenbrechen technischer Barrieren verspricht, gefährdet eine aktuelle Entdeckung den Fortbestand des Internets, wie wir es kennen. Die IP-Adressen - sozusagen die Telefonnummern des Internets - sind aus.

Ohne eine flächendeckende Umstellung auf das neue Protokoll IPv6 wird das Internet nicht mehr lang funktionieren. Der selbst ernannte IT-Experte und System-Analytiker, Max Mustermann, prognostiziert einen Zusammenbruch innerhalb der nächsten 2 hoch 128 Tage. Es bleibt also weniger Zeit als gedacht.

Darüber hinaus führt eine kürzlich entdeckte Lücke in der Spezifikation des Internets (nachzulesen unter ietf.org) in letzter Konsequenz dazu, dass jeder Nutzer eines herkömmlichen Web-Browsers sich angreifbar macht und was noch schlimmer ist, selbst unbeabsichtigt zu einem Angreifer für Internet-Angebote werden kann. Die Angriffsform ist auf Neu-Deutsch unter dem Akronym CSRF (wie sea-surf) bekannt. ZDNet sagt dazu unter Anderem:

In either case, the browser executes a malicious transaction such as a wire transfer to the cybercrook’s bank.

Frei übersetzt bedeutet dies: Man kann hier von einem immer schnelleren Verbrauch der jetzt schon kaum mehr verfügbaren IP-Adressen ausgehen. Schuld daran ist die Architektur des Webs - genannt REpresentational State Transfer - oder kurz: REST.

by Phillip Ghadir at April 01, 2009 07:01 PM

March 28, 2009

Philipp Haußleiter

advanced XML-Parser

Innerhalb unseres Projektes ist die Notwendigkeit entstanden, XML-Dokumente, die etwas umfangreicher als die Standard-Java-Deskriptoren sind, auf Gleichheit hin zu untersuchen. Folgende XML-Strings sind gegeben: A) <items>     <item name="a">         <value>1</value>     </item>     <item name="b">         <value>2</value>     </item>     <item name="c">         <value>3</value>     </item> </items> B) <items>     <item name="a">         <value>1</value>     </item>     <item name="c">         <value>3</value>     </item>     <item name="b">         <value>2</value>     </item> </items> Diese Untersuchung soll eine Aussagen über: - Gleichheit: (2 Dateien enthalten das gleiche XML-Modell - sowohl in der gleichen Reihenfolgen A) , als auch in einer anderen Reihenfolge B) ). - Veränderungen: welche Stellen sind verändert worden. Während Forderung zwei sich noch mit einem einfachen String-Vergleich lösen lässt, ist Forderung eins nur durch das erkennen eines Modells lösbar. Hierbei ist es notwendig, die einzelnen Knoten zu erkennen. Zudem sind die zu-untersuchenden XML-Dateien > 5 MBb sodass viele - professionelle XML-Tools hier streiken müssen und mit Speicherfehlern aufgeben. Der Ansatz der hier vorgestellt wird, setzt sich aus drei Stufen zusammen:
  1. SAX-basiertes Parsen der Datei und einlesen in eine Datenbank (aus Performance-Gründen wird hier H2 als inMemory Datenbank genutzt).
  2. Um schnelle Vergleiche zu ermöglichen wird ein Modell benutzt, welches u.a. auch in ZFS angewendet wird: Erkennen von Veränderungen anhand von Prüfsummen. Was bei ZFS dazu benutzt wird, um Änderungen innerhalb des Dateisystems zu erkennen, soll hier dazu dienen, Unterschieder zwischen zwei XML-Modellen schnell und zuverlässig zu erkennen. Hierzu wird für jeden Knoten eine Prüfsumme berechnet. Diese leitet sich jeweils aus den Prüfsummen seiner Kindsknoten, dem Inhalt seiner Attribute und dem Wert des Knotens ab. Momentan wird über diesen Gesamt-String ein SHA1-Hash gebildet. Eine weitere Prüfsumme wird benötigt, um den Knoten innerhalb des Modells zu lokalisieren (wir verwenden hier den XML-Path+Knotennummer):
    1. <--                   Hash des Pfades                   --> 3a52ce780950d4d969792a2559cd519d7ee8c727  ./items/item/value           
    2. <--             Hash des Knotens item              --> 481e6ff69a8402231a3b9c6e46a7fef4f09adbb3 hash von: "item", attribute "name=b", hash von "value"
  3. Da sowohl eine Aussage über Unterschied im sortierten Zustand - die Reihenfolge der (Kinder-)Knoten ist wichtig - als auch im unsortierten Zustand (Die Reihenfolge der Kinder-)Knoten ist egal, wird vor dem Berechnen des Hashes des Kindes eines Knotens, die Kinder einmal unsortiert und einmal sortiert als Basis für den SHA1-Hash genommen.
Momentan ist das Datenmodell soweit vollständig, die Knotenwerden beim Parsen in die Datenbank eingelesen. Dieser Vorgang wird momentan noch hinsichtlicher Dauer und Speicherverbrauch optimiert. Auch eine aussagekräftige Fortschrittanzeige sollte eingebaut werden. Danach muss der Algorithmus zum Erkennen der unterschiedlichen Stellen implementiert werden. Als letztes sollen diese Unterschiede in einer übersichtlichen und - für große Dokumente - gut navigierbaren GUI angezeigt werden.
Blogged with the Flock Browser

by Philipp Haussleiter at March 28, 2009 08:28 PM

GnuPG Java Wrapper API

Yaniv Yemini wrote a small GnuPG Java Wrapper API. Just had a small look over it. So to get it your version from here: http://www.macnews.co.il/mageworks/java/gnupg Here is just a small demo:
import javax.swing.JOptionPane;

import org.gpg.java.GnuPG;

public class Loader {

public static void main (String args[]){
	GnuPG pgp = new GnuPG ();
		
	String toolChain[] = {"sign", "clearsign", "signAndEncrypt", "encrypt", "decrypt"};
	String message = JOptionPane.showInputDialog(null, "Message you want to encrypt?", "Enter your message", JOptionPane.QUESTION_MESSAGE);
	String keyID = "0x56B69D6B";
	System.out.println("using message: "+message);
	System.out.println("using key ID: "+keyID);
	for(String tool : toolChain){
		System.out.println("running: "+tool);
		if(tool.equals("sign")){
			String passPhrase = enterPassPhrase(tool);
			pgp.sign (message, passPhrase);				
		}

		if(tool.equals("clearsign")){
			String passPhrase = enterPassPhrase(tool);
			pgp.clearSign (message, passPhrase);				
		}			
		if(tool.equals("signAndEncrypt")){
			String passPhrase = enterPassPhrase(tool);
			pgp.signAndEncrypt (message, keyID, passPhrase);				
		}
		if(tool.equals("encrypt")){
			pgp.encrypt (message, keyID);				
		}	
		if(tool.equals("decrypt")){
			String passPhrase = enterPassPhrase(tool);
			pgp.decrypt (message, passPhrase);				
		}				
		System.out.println("result: " + pgp.getGpg_result() + "\n\n");
		System.out.println("error: " + pgp.getGpg_err() + "\n\n");
		System.out.println("exit: " + pgp.getGpg_exitCode() + "\n\n");
	}
}
    
    public static String enterPassPhrase(String usage){
    	return JOptionPane.showInputDialog(null, "Please enter the Passphrase of your private Key for "+usage, "Passphrase", JOptionPane.QUESTION_MESSAGE);
    }

}
Unforntunetally there is a Problem with decrypting a message. It is possible to decrypt the String with the gpg CI Version, but within Java it does not work. So maybe the error is on my site :-).
Blogged with the Flock Browser

by Philipp Haussleiter at March 28, 2009 06:25 PM

SortedProperties

Angenommen, man braucht für ein Java Property Set ein geordnete Ausgabe - zum Beispiel um einem Übersetzer eine sortierte Liste mit zu übersetzenden String zu liefern. Man erstellt eine Klasse (zum Beispiel SortedProperties) und lässt diese von Properties erben. Bedingt durch die Kapselung ist es notwendig, dass die Methoden private static char toHex(int nibble) ; private String saveConvert(String theString, boolean escapeSpace); private static void writeln(BufferedWriter bw, String s); und Attribute private static final char[] hexDigit; in die neue Klasse kopiert werden müssen. Wir überschreiben die Methode public synchronized void store(OutputStream out, String comments) Diese Methode ist für das eigentliche Speichern in eine Datei verantwortlich. Der neue Inhalt entspricht bist auf eine zusätzliche Sortierung dem alten:

	public synchronized void store(OutputStream out, String comments)
			throws IOException {
		
		TreeMap propTree = new TreeMap();
		
		for (Enumeration e = keys(); e.hasMoreElements();) {
			String key = (String) e.nextElement();
			String value = (String) get(key);
			key = saveConvert(key, true);
			value = saveConvert(value, false);
			propTree.put(key, value);
		}
		BufferedWriter awriter;
		awriter = new BufferedWriter(new OutputStreamWriter(out, "8859_1"));
		if (comments != null)
			writeln(awriter, "#" + comments);
		writeln(awriter, "#" + new Date().toString());		
		Set keys = propTree.keySet();
		for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
			String key = (String) iterator.next();
			writeln(awriter, key + "=" + propTree.get(key));			
		}	
		awriter.flush();
	}
Dies ist tatsächlich ein sehr einfacher Weg, um vorhandene Java-Methoden für eigene Zwecke anzupassen.
Blogged with the Flock Browser

by Philipp Haussleiter at March 28, 2009 05:34 PM

February 22, 2009

Philipp Haußleiter

sophisticated Backups mit Rsync Part II

Version 3.1 Features:
  • Logs werden nun gzip komprimiert
  • wöchentliche Backups aus der vorherigen Woche werden tar.gz komprimiert
Download startbackup-3.1.sh
Blogged with the Flock Browser

by Philipp Haussleiter at February 22, 2009 11:39 PM

February 17, 2009

Philipp Haußleiter

Das Problem mit der Abstraktion der Arbeit

Aus aktuellem Anlass möchte ich mich hier einem Thema widmen, welches möglicherweise jeden betrifft. Es geht um die Tatsache, dass man um sich herum Projekte und Arbeit sucht, welche sich über die Zeit ansammelt und mit der Zeit liegen bleibt. Es gibt viele Theorien und Methoden, wie man dieser Situation begegnen soll. Meistens geht es um - mehr oder weniger - hippe Methoden mit noch trendigeren Namen (z.B. GettingThingsDone). Sucht man nach dem Thema Arbeitsmethoden oder Zeitmanagement, finden sich ganze Bücherregale mit Titeln, unterschiedlicher Ausrichtung. Dennoch habe ich mir persönlich die Frage gestellt, wieso man viele Dinge anfängt, nach einiger Zeit zurückblickt und zu dem Ergebnis kommt, dass man doch zum aktuellen Zeitpunkt weit von einem (dem) gesteckten Ziel entfernt ist, welches man eigentlich hat erreichen wollen. Ich spreche hier nicht von den typischen Kandidaten wie "Ich sollte mal wieder den Keller aufräumen", oder auch "Irgendwie sollte ich mal wieder die Steuern machen :-(". Es geht mir hier um die vielen kleineren und größeren Projekte, die man sich (und hin und wieder auch zusammen anderen Mitmenschen) vor langer Zeit gestellt hat und die trotz vieler Möglichkeiten daniederliegen und meist über das erste Planungsstadium nicht hinweg gekommen sind. Ich möchte nachfolgend ein paar Punkte nenne, die ich an mir selber (als quasi Betroffener), hin und wieder aber auch bei anderen (als unbeteiligter Beobachter), bemerkt habe: 1. Titelgebend und meiner Meinung nach einer der seltsamsten Gründe - Abstrahierung des Probemes: Ich habe lange Zeit überlegt, ob Abstrahierung überhaupt der richtige Begriff ist. Letztendlich ist damit gemeint, dass man ab einem gewissen Punkt erkennt, dass man das gesetzte Ziel rein theoretisch sicher und zu 100% erreichen kann. Dies mag zum einen Daraus resultieren, dass man in dem Arbeitsumfeld schon einiges an Erfahrung gesammelt hat, zum anderen auch daran, dass man im Beruf oder in anderen Situationen schon schwierigere/komplexere Dinge gelöst hat. So werden viele Aufgaben vor sich her geschoben und im Team werden simple, unerfreuliche, langweilige, aber notwendige Aufgaben an Unerfahrende Helfer abgeschoben, weil das eigene Genie sich für höheres berufen fühlt. Während man selber also nur mit dem Kopf die Dinge löst und selber fest der Meinung ist, dass die Verwirklichung der eigenen Ideen nur noch Detailfragen aufwerfen wird, wird man hinterher von dem langsamen Fortschritt und einer fehlerhaften Basis überrascht werden. 2. Das "Ich mache lieber alles selbst, weil es sonst falsch ist, oder zu lange dauert" - Problem. 3. "Keine Zeit - das mache ich dann mal, wenn ich (Frei-)Zeit habe" / "sobald X eintritt, mache ich das mal" 4. "Lasst uns die Wikipedia nachbauen" 5. "Das sieht so simpel aus - das kann ich auch"

by Philipp Haussleiter at February 17, 2009 11:13 AM

instant jruby & derby enviroment für eine RoR Anwendung

Als angestammter Java-Entwickler geht es mir oftmals schwer von der Hand, einer Ruby on Rails (RoR) Anwendung mit relativ wenig Auffand eine brauchbare Laufzeit-Umgebung zu bieten. Normalerweise sollte das OS (MacOS 10.5.6) alles Brauchbare bieten. So ist oftmals ein Rails installiert und auch das (standardmäßig genutzte) SQlite 3 ist vorhanden. Dennoch sind es oftmals Plugins (spezielle Rails Version / spezielle gems), welche einen zusätzlichen Aufwand benötigen. Nicht zu vergessen, dass RoR nicht auf allen Systemen vorinstalliert ist und dementsprechend ein interessierter Entwicklung von einem Out-of-the-Box Erlebnis weit entfernt ist. Sehen wir den Tatsachen ins Auge... die Wahrscheinlichkeit eine installierte JVM vorzufinden ist (noch?) deutlich höher, als eine Lauffähige Ruby-Installation. Was liegt also näher, als die benötigte Umgebung auf Java fußen zu lassen. Hierzu werden verwendet: * jRuby in Version 1.1.5 (http://jruby.codehaus.org) * Derby-DB in Version 10.4.2.0 (http://db.apache.org/derby) * weiterhin wird eine installierte JVM (>1.5) vorrausgesetzt Alles weitere wird mit Hilfe von shell-Scripten bewerkstelligt. Wobei momentan nur Unix-Scripte benutzt werden. Eine Portierung auf Windows sollte aber nur eine Sache von Minuten sein. Es liegt eine RoR-Anwendung in einem Entwicklungs-Status vor. Diese wurde bisher in einem Netbeans-Enviroment mit einer SQlite-DB betrieben. Das Verzeichnis ist folgendermaßen aufgebaut: ROOT | |- microblog (dies ist unsere RoR-Anwendung) | |- derby (derby-installtion - es werden jeweils das bin und lib Verzeichnis benötigt) | |-bin | |-lib | |- jruby (jruby-installtion - es werden jeweils das bin und lib Verzeichnis benötigt) | |-bin | |-lib | Das Hauptproblem besteht darin, dass alle benötigten gems in das entsprechende Unterverzeichnis installiert werden müssen. Weiterhin muss die Derby-DB mit dem entsprechenden Rake-Task auf mit der aktuellen Schema-Datei instanziiert werden. Zuletzt sollen die vorhandenen User-Daten in die Derby-DB eingefügt werden. Punkt 1) Anpassen der database.yml Wir nutzen weiterhin eine jdbc-Connection. Allerdings ändert sich der Treiber auf den der Derby-DB: database.yml
    development:
      adapter: jdbc
      driver: org.apache.derby.jdbc.ClientDriver
      url: jdbc:derby://localhost/microblog_development;create=true
      encoding: utf8
      pool: 5
      username: microblog
      password: microblog
      host: localhost
Punkt 2) Export der alten DB-Daten: Wir benutzen hierzu das Tool sqlitebrowser (http://sqlitebrowser.sourceforge.net) und erzeugen uns so einen SQL-Dump der alten SQLite-DB. Wir benutzen hierbei nur die SQL-Inserts für den User-Import. Diese speichern wir in die Datei: microblog/db/microblog.sql Punkt 3) Für den Import erstellen wir einen Rake-Task: microblog/lib/tasks/sql-import.rake
namespace :microblog do
  desc 'Import old SQL Data'
  task :sqlimport => :environment do
  dbConn = ActiveRecord::Base.establish_connection :development
  sql = File.open("db/microblog.sql").read
  sql.split(';').each do |sql_statement|
    dbConn.connection.execute(sql_statement)
  end
  puts "imported user data '#{Time.now}' "  
  end
end
Punkt 4) Erstellen des Setup-Scriptes: Folgende Schritte sind notwendig: 1. setzen aller benötiger Verzeichnisse 2. installieren aller benötigter gems 3. starten des Derby-DB-Servers 4. Rake db:migrate 5. import der alten Daten 6. beenden des Derby-DB-Servers Das Script sieht wie folgt aus: jruby-setup.sh
#!/bin/sh

BASE_DIR=`pwd`
CP=".:$BASE_DIR/jruby/lib/*:$BASE_DIR/derby/lib/derbyclient.jar"
JAVA_OPTS="-Djdbc.drivers=org.apache.derby.jdbc.EmbeddedDriver"
JRUBY="$BASE_DIR/jruby/bin/jruby"

DERBY_HOME=`cd derby && pwd`
export DERBY_HOME=$DERBY_HOME
cd $BASE_DIR

echo "setting up jgems..."
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem update --system
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install jruby-openssl --no-rdoc --no-ri
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install -v=2.2.2 rails --no-rdoc --no-ri
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install activerecord-jdbc-adapter activerecord-jdbcderby-adapter --no-rdoc --no-ri

echo "starting derby..."
$BASE_DIR/derby/bin/startNetworkServer &

echo "setting up derby..."
cd microblog
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake db:migrate
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake microblog:sqlimport
cd $BASE_DIR

echo "stopping derby..."
$BASE_DIR/derby/bin/stopNetworkServer &
Es ist zu erwähnen, dass es notwendig ist, jeweils auf die entsprechende jRuby-Installation zu verweisen. Weiterhin benötigt jRuby den entsprechenden derbyClientDriver, welcher in die (von jRuby später verwendete) JAVA_OPTS-Variabel eingetragen wird. Ebenfalls musst der Classpath soweit angepasst werden, dass sowohl jRuby, als auch Derby über die notwendigen Bibliotheken verfügen. Als letztes ist noch erwähnenswert, dass die beiden Rake-Tasks jeweils aus dem App-Verzeichnis ausgeführt werden. Punkt 5) Das Start-Script. Letzendlich sind auch zum eigentlichen Betrieb des Servers Anpassungen notwendig, da auch hier die jRuby-Instanz mit den verwendeten gems benutzt werden sollen. Das Script sieht wie folgt aus: run.sh
#!/bin/sh
BASE_DIR=`pwd`
CP=".:$BASE_DIR/jruby/lib/*:$BASE_DIR/derby/lib/derbyclient.jar"
JAVA_OPTS="-Djdbc.drivers=org.apache.derby.jdbc.EmbeddedDriver"
JRUBY="$BASE_DIR/jruby/bin/jruby"
export BASE_DIR=$BASE_DIR
export JRUBY=$JRUBY

DERBY_HOME=`cd derby && pwd`
export DERBY_HOME=$DERBY_HOME
cd $BASE_DIR


echo "starting derby..."
$BASE_DIR/derby/bin/startNetworkServer &

echo "setting up derby..."
cd microblog
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake db:migrate

echo "starting microblog"
$BASE_DIR/jruby/bin/jruby $BASE_DIR/microblog/script/server

echo "stopping derby..."
$BASE_DIR/derby/bin/stopNetworkServer &
Es entspricht also einer abgespeckten Variante des Setup-Scriptes. Hierbei wird auch immer ein db:migrate aufgerufen, für den Fall, dass sich die DB-Struktur in der Zwischenzeit geändert haben sollte. Punkt 6) Auslieferung ;-). Derby und jRuby belegen knapp 80 MB sodass es notwendig ist, die Dateigröße für den Transport zu verringern. Zuallererst sollten die benötigten gems am besten immer online bezogen werden, sodass man hier ein paar MB sparen kann. Weiterhin benutzen wir Jar um die vorhandenen Dateien auf ein 13 MB-Archiv zu packen. Die veränderten Scripte sehen wie folgt aus: Zuerst das Script, welches die vorhandenen Dateien packt: pack.sh
#!/bin/sh
find . -name '*.DS_Store' -type f -delete
jar -cvf statusQ-runtime.jar derby/ jruby/ run.sh pack.sh microblog/db/microblog.sql microblog/lib/tasks/sql-import.rake
rm -R jruby
rm -R derby
rm run.sh
rm microblog/db/microblog.sql
rm microblog/lib/tasks/sql-import.rake
rm pack.sh
Und nun das geänderte jruby-setup.sh Script, welches vor dem eigentlichen Setup noch für das Entpacken aller Dateien verantwortlich ist: jruby-setup.sh
#!/bin/sh
jar -xvf statusQ-runtime.jar
rm -R META-INF
chmod +x run.sh
chmod +x setup.sh
chmod +x pack.sh
chmod +x jruby/bin/jruby
chmod +x derby/bin/startNetworkServer
chmod +x derby/bin/stopNetworkServer

BASE_DIR=`pwd`
CP=".:$BASE_DIR/jruby/lib/*:$BASE_DIR/derby/lib/derbyclient.jar"
JAVA_OPTS="-Djdbc.drivers=org.apache.derby.jdbc.EmbeddedDriver"
JRUBY="$BASE_DIR/jruby/bin/jruby"

DERBY_HOME=`cd derby && pwd`
export DERBY_HOME=$DERBY_HOME
cd $BASE_DIR

echo "setting up jgems..."
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem update --system
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install jruby-openssl --no-rdoc --no-ri
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install -v=2.2.2 rails --no-rdoc --no-ri
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/jgem install activerecord-jdbc-adapter activerecord-jdbcderby-adapter --no-rdoc --no-ri

echo "starting derby..."
$BASE_DIR/derby/bin/startNetworkServer &

echo "setting up derby..."
cd microblog
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake db:migrate
$BASE_DIR/jruby/bin/jruby $BASE_DIR/jruby/bin/rake microblog:sqlimport
cd $BASE_DIR

echo "stopping derby..."
$BASE_DIR/derby/bin/stopNetworkServer &
Als nächstes sollte die Scripte auf Windows portiert werden. Weiterhin wäre es interessant, die Derby/jRuby Binaries jeweils direkt online zu beziehen.

by Philipp Haussleiter at February 17, 2009 11:11 AM

February 14, 2009

Phillip Ghadir

Wenn ich dort wäre…

… dann wäre ich wohl ein 3D-Avatar.

Seen-Landschaft, umgeben von Bergen

Denn dieses Bild wurde mit Terragen gerendert.

by Phillip Ghadir at February 14, 2009 12:16 PM

January 31, 2009

Phillip Ghadir

Informationsdichte in Texten

Da habe ich am Mittwoch auf der OOP-Konferenz Tom DeMarco gehört. Er hat allen Ernstes gesagt, dass er die Texte eines Kollegen dadurch verbessert, indem er sinnlose Füllsätze einfügt. Dessen Texte seien sonst immer so dicht mit Informationen gepackt, dass man beim Lesen zu viele davon überliest. Durch die Füllsätze bekomme das Gehirn Zeit, alle Informationen zu verdauen.

Das Prinzip der PyramideMeiner Ansicht nach sollte man auf Füllsätze verzichten und Informationen kurz und präzise darstellen. Die Form eines Textes so anzupassen, dass auch einem unaufmerksamen Leser darin enthaltene Kernpunkte auffallen, sollte meiner Meinung nach durch sinnvolle Übergänge und Erläuterungen erfolgen. Sinnlose Füllsätze haben da nichts zu suchen. Frau Minto beschreibt in ihrem Buch Das Prinzip der Pyramide, wie man seine Informationen klar strukturieren kann.

by Phillip Ghadir at January 31, 2009 09:50 PM