Nach diversem Hinundher bastle ich weiter am "Medienbotenkatalog auf AppEngine". Trotz der guten Apache-Bibliothek POI/HSSF um Excel-Daten zu verarbeiten, kommt man bei den Ausgangsdateien zu so einfachen wie grundsätzlichen Fragen: Wann ist Schwarz eigentlich Schwarz? Wer definiert hier in welcher Palette, dass Font.COLOR_NORMAL Schwarz bedeutet? Und wieso hält sich die Datei nur manchmal daran? Zugegebenermaßen sind die Daten verschieden formatiert, aber immer irgendwie Schwarz - technisch leider nicht :-(
Wenn man dann alles gefärbt hat, kann man sehe, wie fleissig/ineffizient ich Datenbank-Operationen gebaut habe - innerhalb von 5min alles verbraucht - "Your application is near is free resource limit" - also einen Tag warten und bis dahin besser werden, d.h. Batch-Operationen beachten.
Irgendwie hat MF immer die richtigen Dinge zu sagen: Gerade ist bei uns eine DBA vs. Dev-Team-Diskussion entfacht und sie dauert noch an.
Many people treat the relational database "like a crazy aunt who's shut up in an attic and whom nobody wants to talk about"[3]. In this world-view they just want to deal with in-memory data-structures and let the ORM deal with the database.
Unsere Tante heisst Onkel Torsten; und ja, ich bin im Prinzip auch der Ansicht, dass die DB das Objekt-Modell unterstützen soll und nicht umgedreht.
Es wird hoffentlich weniger hässlich als Vietnam, aber dieses Essay hat einige Zustände sehr prägnant zusammen gefasst:
Discussions of inheritance-to-table and association mapping schemes also reveals a basic flaw: At heart, many object-relational mapping tools assume that the schema is something that can be defined according to schemes that help optimize the O/R-M's queries against the relational data. But this belies a basic problem, that often the database schema itself is not under the direct control of developers, but instead is owned by another group within the company, typically the database administration (DBA) group.
In many cases, developers begin a new project with a "clean slate", an empty relational database whose schema is theirs to define as they see fit. But, soon after the project has shipped, it becomes apparent that the developers' ownership of the schema is temporary at best--various departments begin clamoring for reports against the database, DBAs are held accountable to the performance of the database thereby giving them cause to call for "refactoring" and denormalization of the data... Before too long, the schema must be "frozen", thereby potentially creating a barrier to object model refactoring ... In addition, these other teams will expect to see a relational model defined in relational terms, not one which supports an entirely orthogonal form of persistence--for example, the "discriminator" column ...
So, then, the next task is to create a "Query-By-Language"
approach, in which a new language, similar to SQL but "better" somehow,
is written to support the kind of complex and powerful queries normally
supported by SQL
The problem here is that frequently these languages
are a subset of SQL and thus don't offer the full power of SQL.
Was bleibt also: Es gibt viele Wege nach Rom - der gutklingende führt interessanterweise als Beispiel db4o an, ein OO-Datenbanksystem, mit dem wir 2008 mächtig baden gegangen sind.
Summary: Wholehearted acceptance. Developers simply give up on relational storage entirely, and use a storage model that fits the way their languages of choice look at the world. Object-storage systems, such as the db4o project, solve the problem neatly by storing objects directly to disk. ... While many DBAs will faint dead away at the thought, in an increasingly service-oriented world ... it becomes entirely feasible to imagine developers storing data in a form that's much easier for them to use, rather than DBAs.
Andere, eher elementare aber trotzdem gut illustrierte Fakten zum Object-Relational Impedence Mismatch :
Object systems are typically characterized by four basic
components: identity, state, behavior and encapsulation
[Date04] and
[Fussell] define the relational model as characterized by relation,
attribute, tuple, relation value and relation variable.
JOINs are among the most expensive expressions in RDBMS
queries.... As a result, developers typically adopt one of the other two
approaches ...: they either create a table per concrete (most-derived)
class, preferring to adopt denormalization and its costs, or else they
create a single table for the entire hierarchy, often in either case
creating a discriminator column to indicate to which class each row in
the table belongs.
Unfortunately, the denormalization costs are often
significant for a large volume of data, and/or the table(s) will contain
significant amounts of empty columns, which will need NULLability
constraints on all columns, eliminating the powerful integrity
constraints offered by an RDBMS.
Inheritance mapping isn't the end of it; associations between objects, the typical 1:n or m:n cardinality associations so commonly used in both SQL and/or UML, are handled entirely differently: in object systems, association is unidirectional, from the associator to the associatee ..., whereas in relational systems the association is actually reversed, from the associatee to the associator (via foreign key columns).
Allgemeine Regeln oder Grundsätze werden auch gestreift: Law of Diminishing Returns, "the Slippery Slope", "the Drug Trap", "the Last Mile Problem"
Meine Versuche mit GWT/GAE/Roo sind ins Stocken geraten - das unreife Zusammenspiel von roo und GAE, welches die komplizierte Architektur von GWT mit zig Klassen mit echtem Boilerplatecode für ein simples Frontend halbwegs akzeptabel machen könnte, hat den Ausschlag gegeben: Am Ende kommt eine Menge Code heraus, der nicht übersichtlich und erweiterbar ist sondern irgendwie die eigentliche Domäne vernebelt....
Aber ich will an einer Java-Hosting-Lösung festhalten, ohne einen eigenen Server zu mieten oder Amazon-Services zu nutzen (tja, warum eigentlich nicht?) - da kommt bisher als anfangs freie Variante nur GAE in Frage, wenn auch um den Preis eines NoSQL-Datastores. Aber man kann auch mal AWS anschauen:Ein eigener Server und gut, allerdings sind mir die Preismodelle unübersichtlich, dann lieber eine freie Quota bei GAE.
So nebenbei lerne ich, was was ist ("Schlagwörter: Hosting, Cloud Computing, Azure, SaaS, Amazon, 1&1, domainfactory, Online-Speicher, Google App Engine, NIST, IaaS, PaaS"), passend dazu hat die c't eine Artikelserie und kommt darin zu dem Schluss: "Google mit seiner AppEngine als Zwischending zwischen Platform-as-a-service und Software-as-a-service ist von den Dreien {EC2, MS Azure, GAE} noch am leichtesten zu beherrschen..."
Vaadin vs GWT/GAE
Vaadin-Plugin: Kurzes Beispiel mit in Memory DB (https://vaadin.com/springroo); Vaadin-Tutorial: Gute Beispielanwendung AddressBook ohne Roo ohne DB ohne Deployment.
Ach ich weiss es doch auch nicht - warum hab ich damals aufgehört, die GWT 1.x Variante zu verfolgen?
Ach ja: roo 1.2 bedingt -> GAE 1.6.0 mit datanucleus-appengine plugin 2.0.0-RC2 welches datanucleus 3.0.4 welches JPA 2.0 braucht: ätz...
Die GAE-datanucleus-Integration scheint überschattet und aktuell stark limitiert zu sein:
Unowned relations sind plötzlich nicht möglich:
testCountCustomers(de.tixus.mb.roo.gwt.shared.domain.CustomerIntegrationTest): Error in meta-data for field de.tixus.mb.roo.gwt.shared.domain.Staff.id : Cannot have a primary key of type java.lang.Long and be a child object (owning field is "de.tixus.mb.roo.gwt.shared.domain.Customer.servedBy").; nested exception is javax.persistence.PersistenceException: Error in meta-data for field de.tixus.mb.roo.gwt.shared.domain.Staff.id : Cannot have a primary key of type java.lang.Long and be a child object (owning field is "de.tixus.mb.roo.gwt.shared.domain.Customer.servedBy").
testFindCustomer(de.tixus.mb.roo.gwt.shared.domain.CustomerIntegrationTest): java.lang.Long cannot be cast to java.lang.Integer
Datanucleus weiss das und schlägt was vor, aber das hört sich kompliziert an:
By default in GAE/J all relations are owned meaning that any child objects have the parent object Key as part of their Key, and persisted as part of the same entity-group. This is obviously useful in optimising retrieval of data, but there are times when you simply want your model persisting and not have imposition of ownership. In v2 of the plugin you can have unowned relations, where each object is in its own entity-group. To define a relation like this, see the following example
@PersistenceCapable
public class A {
@Persistent(primaryKey="true",
valueStrategy=IdGeneratorStrategy.IDENTITY)
long id;
@Unowned
B
b;
}
@PersistenceCapable
public class B
{
@Persistent(primaryKey="true",
valueStrategy=IdGeneratorStrategy.IDENTITY)
long id;
@Unowned
@Persistent(mappedBy="b")
A
a;
String name;
}
So when we persist an object of type A with related B it will do
the following:
PUT the A, generating its Key, but
without property for B
PUT the B, generating its Key,
and with a property referring to the key of A
PUT the A
with the property referring to the key of B.
Jesus!
DIe JPA 2.0-Integration hat einen bug: "Result class is simple, but field value [Ljava.lang.Object;@135afd61 not convertible into that;"
Darin (verständliches) fehlendes GAE-Commitment der roo-Leute:
Given that GAE and GWT are not high on our priorities due to their restrictions of what you can do, we are expecting GAE to allow JPA 2.0 compliant applications to run like other platforms such as EclipseLink and Oracle for example.
Für eclipse fehlen die GAE, GWT runtimes
<classpathentry kind="con"
path="com.google.gwt.eclipse.core.GWT_CONTAINER"/>
<classpathentry
kind="con" path="com.google.appengine.eclipse.core.GAE_CONTAINER"/>
Kann man per Hand hinzufügen, aber dasroo-plugin
schmeisst sie immer raus:
Roo regenerates gwt-maven-plugin
in pom.xml deleting edits done by the user
Hmm, muss ich mir Sorgen machen, wenn bei unseren (großenteils externen) Entwicklern sowas als Motto "Please keep quit?" im Raum hängt?!
Ansonsten ist es immer gut zu wissen, wer man ist:
Und das M$Office auch lustig sein kann, beweisen diese Meldungen:
roo + SpringMVC: Fuck, ein Standard-Deploy endet so:
tsps-MacBook-Pro:mb-mvc tsp$ mvn gae:deploy -DskipTests=true
Dec
21, 2011 1:51:01 PM org.apache.jasper.compiler.AntCompiler generateClass
SEVERE:
Error compiling file:
/var/folders/rA/rAI3y8hfFQuztePwrkEL5U+++TI/-Tmp-/appcfg6923119542883762797.tmp/WEB-INF/classes/org/apache/jsp/tag/web/util/panel_tagx.java
[javac]
Compiling 1 source file
[javac] error: Bad service
configuration file, or exception thrown while constructing Processor
object: javax.annotation.processing.Processor: Provider
org.datanucleus.enhancer.EnhancerProcessor could not be instantiated:
org.datanucleus.exceptions.NucleusException: Error reading manifest file
"jar:file:/var/folders/rA/rAI3y8hfFQuztePwrkEL5U+++TI/-Tmp-/appcfg6923119542883762797.tmp/WEB-INF/lib/datanucleus-core-1.1.5.jar!/plugin.xml"
Also googlen und frickeln..... Und ja, nach dem Entfernen aller dependencies von datanucleus-enhancer loop et endlich:
tsps-MacBook-Pro:mb-mvc tsp$ mvn clean gae:deploy -DskipTests=true
[INFO]
Scanning for projects...
Beginning server interaction
for medienboten...
0% Created staging directory at:
'/var/folders/rA/rAI3y8hfFQuztePwrkEL5U+++TI/-Tmp-/appcfg3602209683130921361.tmp'
5% Scanning for jsp files.
8%
Compiling jsp files. Dec 21, 2011 2:15:35 PM
com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
INFO:
Successfully processed
/var/folders/rA/rAI3y8hfFQuztePwrkEL5U+++TI/-Tmp-/appcfg3602209683130921361.tmp/WEB-INF/web.xml
20% Scanning files on local disk.
25%
Scanned 250 files.
28% Initiating update.
Email:
xxx@gmail.com
Password for xxx@gmail.com:
77%
Initializing precompilation...
90% Deploying new
version.
95% Will check again in 1 seconds.
98%
Will check again in 2 seconds.
99% Will check again in
4 seconds.
99% Closing update: new version is ready to
start serving.
99% Uploading index definitions.
Update
completed successfully.
Success.
Und Browser auf und schauen - erstmal nix, aber nachdem ich den alten Datenbestand gelöscht habe:
Bis zur nächsten Untiefe:
Ok, ein bekannter Fehler: "These break all list pages. The following exception is generated:JspException: java.lang.NoSuchMethodError: "
Also scope ändern und redeploy, das übt ja...
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>provided</scope><!--
Added -->
</dependency>
Natürlich lasse ich auch die Datastore-Fallstricke nicht aus: Index fehlt, erstmal also Beng:
Caused by:
com.google.appengine.api.datastore.DatastoreNeedIndexException: no
matching index found.
The suggested index for this
query is:
<datastore-index kind="MediaItem"
ancestor="false" source="manual">
<property
name="mediaKind" direction="asc"/>
<property
name="title" direction="asc"/>
</datastore-index>
Aber auch hier sehr gute Hilfe im Netz: datastore-indexes.xml anlegen, obige Fehlermeldung hineinkopieren, re-deploy und los:
Nun muss ich mal klären, wie das mit der freien Quota läuft, also ob eine mässige Benutzung gedeckt ist oder es bald Scheine regnen muss:
Tja, ich fühle mich wie ein Hamster, der ständig den Hype-Zyklus durchrattert: Mein Hauptproblem ist es, die GUI für das Backend GAE auszuwählen; mit roo als Modellierungstool/-sprache bin ich ganz zufrieden. Aber wie weiter
Mal sehen, welches der Modelle ich mal durchgehend zum Laufen und deployed bekomme um gemäß dieser Folie feiern zu können:
Ab hier wirds anstrengend, weil, wie Cengiz sehr richtig bemerkt:
... the trouble lies beyond this point in customizing the Roo-generated scaffold application into a Web application that functions as required. The reason for this is a combination of the highly complex code generated by Roo and the lack of comprehensive documentation on it.
Man muss also was über Acitivities, Places, RequestFactory und modules wissen, um weiterzukommen - tough, Digger, finden auch andere.
Also liest du hier RequestFactory, hier Activities und Places, und hier sowieso IBM.
Aber ich vertraue auf Cengiz und versuche eine Anpassung durch ein neues Modul:
Erstmal neuen Finder dazu bauen - vorerst easy mit einem Like und einem Equals:
~.server.domain.MediaItem roo> finder add --finderName findMediaItemsByTitleLikeAndMediaKind
Die GUI wird erweitert um einen Menu-Eintrag mit der Suchanfrage: (Keine Sorge, kann man noch selbst beschriften...)
No API environment is registered for this thread.
mvn gae:run
JPA query to GAE ersetzt mir roo auch nicht; aber man kann ja die generierten Finder in die Hauptklasse "pushen" und dann anpassen:
Alter, SCARY - in einem Forum auf Dzone hat der irre Norweger gepostet...
Ich will Spring IOC, DI und frameworks. Ich will eine Java-Umgebung als Hoster, möglichst kostengünstig: GAE. Ich will GWT und GAE und CRUD. ohne dafür GUIs bauen zu müssen. Ich will die neuesten GWT-Sachen (Eventbus, Activity, Places). Ich will mich mit einer einfachen DSL auf die Domäne konzentrieren, nicht auf den Request-Zyklus oder Persistenz.
Aber wie immer gilt auch hier: Besser man ist mal zu Fuß den Weg gegangen, eine Web 2.0 App mit GAE, GWT, Spring zu bauen, ansonsten steht man bei den ersten roo-Fehler im Regen. Und man sollte das "roo-Undo" mit git unbedingt nutzen. Sonst ist schnell mal was hingeneriert, was keiner braucht... In jeden Fall ist post-generatem immer mal gut, zu wissen was roo so alles raushaut.
Basic building blocks:
Editor Framework
You might be wondering how the generated application reads and
writes entity objects from and to the view. It is all hidden away inside
the GWT Editor Framework, which provides the functionality implicitly,
using the marker interface pattern.
The RequestFactory
The RequestFactory and JPA provide an easy way to build
data-oriented CRUD applications and together make up the data-access
layer of our Roo-generated application.Built to complement the
service-oriented GWT RPC and not replace it, the data-oritented
RequestFactory is GWT's new mechanism that provides more efficient
client/server data transfer. It allows us to define data-centric Entity
classes on the server side and to define business logic with EntityProxy
on the client side.
Entity
Entity s are server-side Data Access Objects (DAO) in our
RequestFactory mechanism and should be placed in a package that will not
be converted to JavaScript by GWT.
EntityProxy
Objects that implement the EntityProxy interface are Data Transfer
Objects (DTO) and client-side representations of corresponding Entity
objects. The exclusive use of EntityProxy interface on the client side
relieves us from the requirement of writing GWT-compatible code in our
Entity implementations.The BaseProxy interface, along with others that
extend EntityProxy interface, enables RequestFactory to determine fields
that have changed and send only these changes to the server.
Activity Pattern
In the application that Roo has generated for us, the Activity is
the implementation of the Presenter component and IsWidget is the
implementation of the View component of our MVP pattern.
Activity
An Activity is completely isolated from the view and contains no
widgets or UI code. This isolation greatly simplifies the testing of
logic contained within an Activity.
Place
A Place is a bookmarkable state for a Web application. An Activity
needs a corresponding Place in order to be accessible via a URL. It has
an associated PlaceTokenizer that serializes the Place's state to a URL
token.
PlaceController
The PlaceController manages the current Place and navigation
between Places in a Web application and makes the back-button and
bookmarks work as users would expect.
Also ans Werk mit meinem Medienboten-Projekt und diesem Script:
project --topLevelPackage de.tixus.roo.mb
persistence setup --provider DATANUCLEUS --database
GOOGLE_APP_ENGINE
enum type --class ~.shared.domain.Gender
enum constant --name MALE
enum constant --name FEMALE
enum type --class ~.shared.domain.TypeOfPerson
enum constant --name CUSTOMER
enum constant --name STAFF
enum type --class ~.shared.domain.MediaKind
enum constant --name BOOK
enum constant --name BIGFONT
enum constant --name CD
entity --class ~.server.domain.Person --testAutomatically
field string --fieldName displayName --notNull
field string --fieldName userName --sizeMin 3 --sizeMax 30
--notNull
field string --fieldName firstName
field string --fieldName lastName
field reference --type Person staff
field enum --fieldName gender --type ~.shared.domain.Gender
field enum --fieldName type --type ~.shared.domain.TypeOfPerson
field boolean --fieldName admin --notNull
entity --class ~.server.domain.MediaItem --testAutomatically
field string --fieldName mediaNumber --notNull
field string --fieldName title --notNull
field string --fieldName shortDescription
field number --type java.lang.Integer publicationYear
field enum --fieldName mediaKind --type ~.shared.domain.MediaKind
field number --type java.lang.Integer amount
field reference --type ~.server.domain.Person lentTo
web mvc setup
web mvc all --package ~.web
-- web mvc language --code de
security setup
gwt setup
logging setup --level DEBUG
>mvn gwt:run
Probleme:
Plugin execution not covered by lifecycle configuration: org.datanucleus:maven-datanucleus-plugin:1.1.4:enhance (execution: default, phase: compile) pom.xml /mb line 710 Maven Project Build Lifecycle Mapping Problem
->Use quick fix
Project configuration is not up-to-date with pom.xml. Run project configuration update mb line 1 Maven Configuration Problem
->maven->update project configuration
->Add roo project nature
Enables roo shell and allows for Google WebApplication
->Run As ... Google WebApplication -Tataa:
Nachdem ich hier über den Systemwechsel und die damit verlorenen Tools gejammert habe, hat sich alles wieder eingespielt auf der neuen Umgebung:
Prune sieht vernünftig aus als Geosetter-Ersatz für KML/KMZ-Dateien.
Sitecopy macht einen tollen weil unkomplizierten Eindruck: update Kommando raus und Verzeichnisse werden abgeglichen - geht nicht einfacher!
Mann mann mann - choose your filter carefully: Ich brauche einige Versuche und kostbare Zeit um die GWT-SpringSecurity-Verknüpfung hinzukriegen - nun gehts:
Das hier verlangt für den allgemeinen Zugriff "/" (also auch die Anmeldungsseite!) einen anonymen User, sonst einen angemeldeten User und für "/mbopac/admin" eben einen Admin:
<http access-denied-page="/access-denied.html">
<intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY"
/>
<intercept-url pattern="/mbopac/admin/**" access="ROLE_ADMIN" />
<intercept-url pattern="/**" access="ROLE_USER" />
<form-login login-page="/login.html"
default-target-url="/Mbopac.html"
always-use-default-target='true' />
</http>
Das o.g. Tutorial ist ganz nett weil es Spring Security und GWT vereint, aber es läuft nicht auf der AppEngine: Das Problem ist die Spring autogenerierte Login-Seite, eine einfache eigene Seite "login.html" und Konfiguration in applicationContext.xml hilft dann endlich:
<html>
<head>
<meta http-equiv="content-type" content="text/html;
charset=UTF-8">
<link type="text/css" rel="stylesheet" href="Mbopac.css">
<title>OPAC Medienboten Bücherhallen Hamburg</title>
</head>
<body>
<h2>Bitte melden Sie sich an.</h2><br>
<form method="POST" action="j_spring_security_check">
Benutzer: <input type="text" name="j_username"><br>
Passwort: <input type="password" name="j_password"><br>
<input type='checkbox' name='_spring_security_remember_me'/> Auf
diesem Computer eingeloggt bleiben?.<br>
<input type="submit" value="Anmelden >>">
</form>
</body>
</html>
Mit diesem Tipp und einer Ableitungsebene mehr ist dann auch Springkonfiguration mit autowire erhältlich:
public class SpringGwtServlet extends RemoteServiceServlet {
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
WebApplicationContextUtils.
getRequiredWebApplicationContext(getServletContext()).
getAutowireCapableBeanFactory().
autowireBean(this);
}
}
Das hier (Double brace initialization) mag ja nett aussehen ist aber too "cute":
final Set<String> rolesAdmin = new HashSet<String>() {
{
add(CustomAuthenticationProvider.ROLE_USER);
add(CustomAuthenticationProvider.ROLE_ADMIN);
}
};
Es ist hat eben die Implementierungsdetails, dass das erste Klammerpaar eine anonyme Innerclass erzeugt und das zweite Paar einen static initializer deklariert: Somit ist die Klasse unbekannt wird im Serialisierungskosmos von GWT hiermit quittiert:
[ERROR] javax.servlet.ServletContext log: Exception while
dispatching incoming RPC call
com.google.gwt.user.client.rpc.SerializationException: Type
'de.tixus.mb.opac.server.DataImporter$1' was not included in the set of
types which can be serialized by this SerializationPolicy or its Class
object could not be loaded. For security purposes, this type will not be
serialized.: instance = [ROLE_ADMIN, ROLE_USER]
Ich bin immer wieder fasziniert von solchen Beiträgen, wenn jemand kleine, scheinbar simple Wahrheiten so formuliert und kombiniert, dass sie etwas Neues ergeben: Martin Fowlers schon älterer Artikel zur Frage, wann man einen Typ/Klasse erstellen sollte, statt bei Primitiven oder allzu generischen Typen zu bleiben.
When should you make your own type? To begin with, make a type if
it will have some special behavior in its operations that the base type
doesn’t have.
...
Often you’ll find things such as product
codes that are numeric in form. However, even though they look like a
number, they don’t behave like one. Nobody needs to do arithmetic on
product codes— with a special type you can avoid bugs.
...
Even
if a currency code looks like a string, if it doesn’t behave like one,
it should get a different type. Look at the string’s interface and ask
how much of it applies to a currency code? If most of it doesn’t, then
that’s a good argument for a new type.
...
Indeed,
communication is one of the biggest reasons to use a type. If you have a
method that expects to take a cur- rency parameter, you can communi-
cate this more clearly by having a currency type and using it in the
method declaration.
Eine Übersicht aller Perlen von ihm gibts hier:Immer mal lesen!
Dieser Mist hat mich einen schönen ruhigen Vormittag gekostet, ohne dass ich vorangekommen wäre - grrrh:
15:27:00.763 [ERROR] [mbopac] Failed to create an instance of
'de.tixus.mb.opac.client.MbopacMainView' via deferred binding
java.lang.RuntimeException: Deferred binding failed for
'de.tixus.mb.opac.client.PersistenceService' (did you forget to inherit
a required module?)
at
com.google.gwt.dev.shell.GWTBridgeImpl.create(GWTBridgeImpl.java:53)
at com.google.gwt.core.client.GWT.create(GWT.java:98)
at
de.tixus.mb.opac.client.MbopacMainView.<init>(MbopacMainView.java:57)
Und was ist das PROBLEM, gwt?
Erst meinte ich oberschlau, dass man auf die Packages achten muss:
Alles was der Client benötigt und kennen muss, muss auch im client-zugreifbaren package definiert sein; der Pfad dafür wird hier gesetzt.
Application.gwt.xml <!-- Specify the paths for translatable code --> <source path='client' /> <source path='shared' />
Dazu gehört alles, was im (Client-)Interface PersistenceService referenziert wird:
package de.tixus.mb.opac.client; @RemoteServiceRelativePath("greet") public interface PersistenceService extends RemoteService { ... MediaItem lend(String mediaNumber, Person person, Boolean isOverride) throws MediaItemAlreadyLentException; ...
Das war leider doch nicht das Problem, sondern der fehlende Default-Constructor in einer Exception! Den habe ich versehentlich überschrieben, als ich beim Refactoring einen neuen Constructor brauchte:
public class MediaItemAlreadyLentException extends RuntimeException { private Person person = null; private static final long serialVersionUID = 1L; public MediaItemAlreadyLentException() { } public MediaItemAlreadyLentException(final Person person) { this.person = person; } }
Die OPAC-Anwendung nimmt jedoch Formen an: Nun kann man bereits den Katalog durchsuchen, Medien ausleihen und zurückgeben. Es fehlt zum ersten Start noch die Suche nach dem Kunden eines ausgeliehenen Mediums sowie eine Einschränkung der Suche auf noch nie ausgeliehene Titel für einen Kunden - man will schliesslich immer mal was Neues lesen.
Mein zweiter Versuch mit nem MBP ist vieel besser! Mattes Hires-Display, und gleich mal wieder den Hibernate-Modus auf den Windows-bekannten echten Ruhezustand gebracht - dauert beim Aufwachen ca. 20s, der Rechner ist zwischendurch aber wirklich aus und nicht auf Batterie, heisst beim Mac "always safe-sleep with secure virtual memory":
sudo pmset -a hibernatemode 5
Ansonsten schön schnelle *nix-Maschine, git und strings und ls alles da!
Gaaanz wichtig bei Hires-Displays - die Auflösung versaut die Augen, wenn man den Standard-Font von 10-13px belässt. Viele Anwendungen benutzen die System-Vorgaben, welche man sinnigerweise nicht besonders gut mit den System-Tools ändern kann - aber Tinkertool ist das TweakUI fürn Mac:
Alles auf z.B. 16pt ändern und Anwendung neustarten - voila!
Aber man verzweifelt eher an liebgewonnenen Programme für Alltagsaufgaben - Total Commander und das Directory-Sync waren eine super Sache - der für Mac verfügbare Filezilla kann auch Ordner abgleichen aber nicht rekursiv.
Zum Glück erinnere ich mich daran, dass IrfanView ein Windows -Clone von XnView ist - dieses gibts für Mac auch und es funktioniert wie gewohnt - toll!
Ok, ich habe es getan: Ein neues Notebook muss her, also geschaut und verglichen - die Überraschung war, dass ein MBP mit Core i7 und 15 Zoll dann preislich sogar mit Lenovo und Konsorten mithalten kann. Also ein "MACBOOK PRO 15" QUAD-CORE I7 2000 SD DEUTSCH, 4 GB RAM, 500 GB HD" bei Gravis gekauft. Schick, schnell und spiegelnd: Das Foto ist nach dem Aufklappen entstanden - wie kann man sowas herstellen? Ok, nachdem ich so eine Diskussion von Apple-Jüngern gelesen habe, hab ich schon bereut, mein Geld dahin getragen zu haben - wie kann man im wahrsten Sinne des Wortes so oberflächlich sein? Andererseits - kann man einen Hersteller ablehnen, aber seine Waren kaufen? Hier gibts nen guten Eindruck von beiden Modellen... Da ich aber die 150 EUR Aufpreis für die entspiegelte Version sparen wollte, bin ich mit dem o.g. nach Hause. Alles installiert und rumgespielt, aber am Sonntag mit mulmigem Gefühl wieder vorsichtig eingepackt und Montag zu Gravis: "Äh, ich möchte den gern gegen den teureren umtauschen" Gravis in Ehren, man lässt sich darauf ein und nun warte ich auf die neue Lieferung - einen Geldabzug wirds wohl geben weils nun keine Neuware mehr ist - mal sehen.
Es gibt Fixpunkte im erwachsenen Leben, bei uns gehört der Tatort dazu. Und dank Tatort-Fans verpassen wir nie wieder eine Wiederholung, denn so richtig sind wir erst seit ~4 Jahren dabei.
Eine andere schöne Sache ist eine Auflistung unserer DVDs - so könnt ihr sie gerne mal (kostenlos natürlich) ausleihen, wenn ihr nichts mehr zu kucken habt. Aktuell sind nämlich die DVD-Boxen "Loriot", "Kommissarin Lund -Das Verbrechen I" sowie Pedro Almodovar "Die große Edition" im Angbot.
Ich bin weiter begeistert vom webOS und verwirrt von meiner Prägung durch frühere Handy-Betriebssysteme: Wie viel leichter kann man es bitte noch machen einen Screenshot zu erstellen, als es so zu implementieren:(ok, iOS erlaubt ähnliches...)
Screenshots of the Palm WebOS can be taken by simultaneously pressing "Orange Key + Sym + P". Screenshots will be saved to your "Screen captures" folder in the "Photos" app.
Darum kann ich auch gleich mal bebildern, was mir an den angebotenen freien Memo-Apps nicht gefällt:
ClassicNote: Eine äusserst umfangreiche Anwendung, viele Formatierungen und Einfügen verschiedenster Elemente (Text, Links) sind möglich, aber leider unergonomisch nur über die Auswahl des Kontextmenüs erreichbar, also kein Rich-Text-Editor direkt bei der Eingabe. Viel schlimmer ist jedoch, dass man Schlagworte bzw. Kategorien jedesmal umständlich neu anlegen muss und immer nur eine Kategorie auswählen kann - dismissed.
Evernote hat im Prinzip alles was ich brauche: Bookmarking wie Delicious; ein Platz für alle Schnipsel (Mail, Text, Foto, Links); Schlagworte filtern, zusammenstellen - mehr braucht man gar nicht fürs erweiterte Gehirn.
... und erstmal nicht brauche: OCR aus Fotos, Bildern; Online
Synchronisierung zwischen Desktop-Anwendung, Smartphone und gleichzeitig
Online-Backup; read-it-later Offline-Speicher.
Und trotzdem ist die
Bedienung auf Pixie unhandlich (evlt. besser mit der Pro-Version):
Schlagworte muss man erst einblenden; man muss immer online sein, sonst
werden Notizen nur zur Synchronisierung vorgemerkt und erscheinen gar
nicht in der Übersicht.
Sorting Thoughts und textPress! lite mangelt es leider an Verschlagwortung, auch wenn die Reading List sehr gute Bücher enthält!
Also selbst machen:
... oder 5$ ausgeben für Notes - mal sehen.
Es ist immer wieder interessant, den richtigen Cracks hinterher zu
lesen: David Gelernter und Eric Freeman, dieser Eintrag aus "Head
First Design Patterns" hat mich auf die Spur gebracht:
Why
do I have to give a file a name?
Die Idee der Ablage von allen Dingen auf einem PC ohne Dateistruktur nur auf einem Zeitstrahl ist toll, gerade habe ich noch gesehen, dass Cover Flow darauf basiert. In dem Screenshot aus ScopeWare Vision bzw. der Doktorarbeit von Eric Freeman S.86 sieht man schön die namen-lose Anordnung von Informationshappen streng nach Zeit. Tja, evlt. kann man das ja für was Eigenes nutzen.
Ich krieg nen Rappel und muss neu machen! Vorher hab ich mal ein paar alte Layouts rausgekramt - über die Jahre kommt schon was zusammen, aber nicht soviel wie man denken könnte:
Neuerungen: Ich setze mal auf intuitives Design - keine Überschriften auf der Startseite sondern nur noch die Teaser-Bilder. Was ich schon lange im Kopf hatte und nun mit einer geeigneten Vorlage und ein wenig CSS ("thank god its floating") umgesetzt habe, ist das flexible oder auch fluid-Design - die Seite passt sich der Browser-Größe an! Und das mit ganz wenigen CSS-Befehlen, ohne Javascript und sogar im IE siehts ganz passabel aus - toll! Dann habe ich die Kommentare weggelassen, hat eh keiner benutzt, und das etwas gierige Feedjit ist geschrumpft.
Einiges ist mit meinem Blogsystem Thingamablog nur durch Code-Änderung möglich, darum bin ich zum einen noch auf der Version 1.1: Der gute Bob Tantlinger hat für die neue Version 1.5 leider den Sourcecode nicht veröffentlicht.
Ich habe im wesentlichen 2 Anpassungen vorgenommen: Ein weiteres
BlogEntry-Tag ist hinzugekommen: Mit $EntryTeaserImage$ kann das erste
Bild eines Eintrags referenziert werden - es ist genau das, welches ich
auf der Startseite in den Kacheln anzeige.
Beispiel:
<BlogEntry> <EntryTitle> <a title="<$EntryTitle$>" href="<$EntryPermalink$>"> <img src="<$EntryTeaserImage$>"></a> </EntryTitle> </BlogEntry>Die zweite Änderung ist nur eine konsequentere Implementierung des Containers ArchiveYears: Er verarbeitet nun auch alle Attribute des enthaltenen Containers ArchiveList.
<ArchiveYears sort_order="descend" glue=" " format="MMMM" span="1"> <$Year$> <ArchiveYear> <$ArchiveLink$><$ArchiveName$> </ArchiveYear> </ArchiveYears>
Es ist in Bobs Sinne für die "alte" Version, dass ich mein Release und das Template-Set dazu hier wieder zur Verfügung stellen muss (GNU-GPL). Für die neue Version gibt es eine wesentlich restriktivere Lizenz, darum auch keinen Sourcen mehr (er wird seine Gründe haben...):
Restrictions on Use
You may not decompile,
"reverse-engineer", disassemble, or otherwise attempt to derive the
source code for the Software Product.
Restrictions on Alteration
You may not modify the
Software Product or create any derivative work of the Software Product
or its accompanying documentation.
Ich verstehe erst langsam, warum webOS webOS heisst: Ich vermisste eine Java-ME (MIDP) Installation, nun wird mir klar, dass es "nur" HTML+JavaScript gibt - auch gut, Google (-Maps, -GWT) braucht nichts anderes und es ist leichter portierbar auf sowas wie iOS oder Android. Schön ist, dass ich Eclipse weiter benutzen kann.
Die neuste iX 1/2011 hat einen Artikel zum selben Thema unter iOS.(Tutorial: iPhone-Apps mit Webtechniken, Teil II) Wer Webseiten fürs iPhone optimiert hat, wie es der erste Teil dieses Tutorials gezeigt hat, kann gleich den nächsten Schritt gehen und seine Webseite als "Webapp" nativ auf dem iPhone-Desktop starten lassen – ohne Apples AppStore-Prozedur durchlaufen zu müssen.
Wie immer stehen längliche Downloads an: Die offizielle Seite verlangt auch noch eine VirtualBox-Installation (für den Emulator?), seis drum. Die VirtualBox 3.2.12 crashed schon mal WinXP, na herrlich - dann probier ich mal das System Restore - läuft! Version 3.2.10 funktioniert besser.
Mit Eclipse baue ich die Beispiel-App nach, es tippt auf dem Bildschirm. Nun will ich das um GPS-Benutzung und -Logging erweitern; was mir auch sinnvoll erscheint ist eine Art Offline- oder persönliches Qype fürs Taggen von Orten oder Routen: Man kann sich den Spaziergang merken, den Currywurststand, den Fischladen, die Tischtennisplatte oder den Fahrradladen., dazu braucht man keine Empfehlung von anderen - einfach Foto machen, Schlagworte vergeben, Ort bestimmen und beim nächsten Besuch von ausserhalb ist das Programm fertig. Es gibt ja auch immer so Places-Dienste wie Spielplatz, aber eben für jeden Art von Ort nen neuer Dienst, ist doch blöde...
Ah, kurze Webrecherche und dadah: MyPlaces-App is there. Nur Android und iOS, dann kann ich ja webOS machen ;-) Aber genau so isses: Create your own database of places you like, find them back in a few clicks and share with your friends
Witzig, es gibt noch ne App die genauso heisst...
Also new kid on the block - trotz Bedenken ein neues Handy: Kein iPhone, kein Android, Palm/HP-webOs sollte es sein: Auftritt Palm Pixie Plus.
Pro:
Kontra:
Absolut problemlos funktioniert die Einbindung und Synchronisierung mit einem GMail-Konto - alles Kontakte, Emails, Kalender - aber das ist ja auch Teufelswerk. Hab ich dann trotzdem gemacht, weil man dann Outlook-Kontakte exportieren als CSV, GMail-Kontakte importieren von CSV, Palm sync aufs GMail-Konto machen kann. Aber eine ständige synchronisierte Lösung zwischen Firma-PIM und Handy ist das nicht.
Leider gibt es nicht den einen App-Shop, sondern 1000 PalmPreCentralen oder AppCatalog und dt. Seiten und palm.com-Seiten. Aber wenn man auf letzterer etwas findet, dann geht der Download von "Apps" aus dem AppCatalog per WLAN (der Link wird sogar von der Webseite aufs Handy geschickt, s.Abb.)
Lustig lustig die Jungs von DZone: Einen full-fletched Kino-Trailer im Stile von "Brokeback mountain" für Java vs. M$.NET. Aber wird dabei nicht irgendwie auch transportiert: Java==schwul?
Was macht man, wenn man aus einem XSD möglichst viele sinnvolle
XML-Testdaten mit ausreichender Varianz/Kombination automatisiert
erzeugen möchte? Man googlet, aber die Kunst ist eben auch, etwas zu
finden.
Aber man kann auch was lernen, wenn man will: "Combinatorial
test data" , "Partition testing" und "Controllable
combinatorial coverage in grammar-based testing" sind Bereiche, die sich
mit sinnvoller Testdaten-Generierung befassen.
Dabei bin ich auf TAXI gestoßen, welches zumindest eine Varianz von optionalen Attributen und Elementen erzeugen kann und somit Vorlagen liefert. Leider (weils auch schwierig ist) ist nur rudimentäre Unterstützung für RegEx-Pattern: "Currently TAXI can not generate Strings that conform to the pattern grammar. So If there is any element that has the restriction with "pattern", please put the values that conform to that pattern into the database. Otherwise TAXI will put "prefix:string" as the value for the element."
Forschungsarbeiten dazu kann man auch lesen:
Ralf Lämmel and Wolfram Schulte "Controllable combinatorial coverage in grammar-based testing "
Abstract. Given a grammar (or other sorts of meta-data), one can trivially de- rive combinatorially exhaustive test-data sets up to a specified depth. Without further efforts, such test-data sets would be huge at the least and explosive most of the time. Fortunately, scenarios of grammar-based testing tend to admit non- explosive approximations of naive combinatorial coverage. In this paper, we describe the notion of controllable combinatorial coverage and a corresponding algorithm for test-data generation. The approach is based on a suite of control mechanisms to be used for the characterization of test-data sets as well-defined and understandable approximations of full combinatorial coverage. The approach has been implemented in the C#-based test-data generator Geno, which has been successfully used in projects that required differential testing, stress testing and conformance testing of grammar-driven functionality
Leider ist Geno nicht freigegeben oder ich habs nur nicht gefunden...
Man was es nicht alles gibt: Netbooks haben naturgemäß keine optischen Laufwerke, also gibt es bei Neuinstallation des Betriebssystems einige Schwierigkeiten, die ich natürlich nicht zu erst und alleine habe.
WinToFlash ist die Antwort - darauf bin ich aber auch erst nach dieser 7-Punkte-Liste gekommen.
Das wird vom ansonsten tollen WinToFlash leider vergeigt: Die erzeugte boot.ini (die immer noch als Bootmenu benutzt wird) sieht so aus und macht die Partitionseinstellung falsch:
[Boot Loader]
Timeout=30
Default=multi(0)disk(0)rdisk(1)partition(1)\WINDOWS
[Operating Systems]
C:\$WIN_NT$.~BT\BOOTSECT.DAT
= "1st, text mode setup (Boot from flash again after finished)"
multi(0)disk(0)rdisk(1)partition(1)\WINDOWS="2nd,
GUI mode setup, continue setup + 1st start of Windows" /fastdetect
C:\
= "---> DEBUG options. Try if you have hal.dll error <---"
multi(0)disk(0)rdisk(1)partition(2)\WINDOWS="Debug
boot rDisk 1 partition 2" /fastdetect
multi(0)disk(0)rdisk(1)partition(3)\WINDOWS="Debug
boot rDisk 1 partition 3" /fastdetect
multi(0)disk(0)rdisk(1)partition(4)\WINDOWS="Debug
boot rDisk 1 partition 4" /fastdetect
[...]
Das Booten schlägt auf furchterregende Weise fehl, wenn man den 2nd GUI mode benutzt (hal.dll not found)...
... funktioniert aber, wenn man die erste DEBUG-Option auswählt.
(<--Hier hätte man also schonmal drauf kommen können...)
(Schön
auch diese spiegelnden Displays)
(M)eine funktionierende boot.ini sieht dagegen so aus - und so hab ich 2 und 2 zusammengezählt und dann gings auch.
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(2)\WINDOWS
[operating systems]
multi(0)disk(0)rdisk(0)partition(2)\WINDOWS="Microsoft
Windows XP Professional" /noexecute=optin /fastdetect
Meine zweite Computer-Tat diese Woche war die Installation von XP auf
einem Mac. Das zu lösende Problem war jedoch die Installation eines
Dreamweavers auf einem Mac, wenn man nur eine Windowsversion hat. Jetzt
kann man eine Menge über "The
Transition" lesen (Apple setzt nach 10 Jahren PowerPC auf CISC,
weil Intel weniger Energie für gleiche Geschwindigkeit bietet) oder sich
an meinen MacMini-Kauf
erinnern und daran, dass da eine
Parallels-Vollversion dabeilag. Denn die üblichen
Virtualisierungsverdächtigen sind auf dem Mac irgendwie auch nicht
kostenlos:VmWareFusion, CrossOvermac. Anschauen könnte man nochmal
Virtualbox.
Mit frischem Mut und wackeligen Fingern also
Parallels gemacht und darin dann WinXP installiert - 45 min warten und
läuft wie geschmiert! Dann Auftritt Dreamweaver: Installation geht noch,
dann wollen die Serververzeichnisse ja synchronisiert werden und das
ging nicht sofort.
Trotz sichbaren Serververzeichnisse kam immer die Fehlermeldung:
"Dreamweaver kann die Remote-Server-Zeit nicht ermitteln"
Damn. Wieder einmal: Was hat man nur ohne Internet gemacht? (Naja, zugegeben, dann hätte man diesen Fehler auch nicht ...) Die ersten Hinweise haben noch nicht viel gebracht und somit hab ich mal weiter in die Logs des DW geschaut und siehe da: Es gibt eine FTP-Fehlermeldung:
COMMAND: PORT 192,168,1,101,4,170
SERVER-ANTWORT:
500 I won't open a connection to 192.168.1.101 (only to 217.80.184.242)
Damit konnte man sehen, dass es an der NAT-Verbindung der virtuellen Windowsmachine zum Hostrechner scheitert. Und damit spuckt das Internet richtig gute Tipps aus:
The FTP protocol can correct this problem by using PASV (Passive)
mode where the FTP client connects to the server for the data connection
instead of the server connecting to the client.
FTP-Optionen suchen und "Use passive FTP" einstellen - läääääuft! Hätte
man auch mal so draufkommen können...
So richtig flüssig läufts nicht mit dem selbstgesteckten Ziel, jede Woche oder so die c't und iX auszuwerten und zumindest Linksammlungen anzulegen - aber nun ein neuer Versuch.
Executable Specifications: (Vorsicht Binse) Nur ein Teil der Erstellung von Software ist die Programmierung. Viel Zeit und Denkarbeit geht in die Spezifikation bzw. Anforderungsanalyse (Requirements). Das Resultat ist häufig ein unglaublicher Wust an Text, manchmal mit einer Grafik, selten mit Beispielen. Und bei allen besten Absichten der Schreiber ist die Spez voll von mehrdeutigen Formulierungen und leer von "Selbstverständlichkeiten" - in letzter Zeit habe ich es mehrfach erlebt, dass nach der Implementierung bei den ersten Tests absolut grundlegende Missverständnisse bgzl. der Funktion auftraten. Kann das mit Exectuable Specifications/User written Acceptance Tests besser sein?
Concordion - turning
specifications into active specifications.
Plain
English specifications: Rather than forcing product owners to specify
requirements in a specially structured language, Concordion lets you
write them in plain English using paragraphs, tables and proper
punctuation. This makes the specifications much more natural to read and
write, and helps everyone to understand and agree about what a feature
is supposed to do.
Always bang up-to-date: Concordion specifications
are active. Behind the scenes, they are linked to the system under test
and therefore do not go out-of-date. If a change is made to the system's
behaviour then the tests associated with the relevant specification will
fail and let you know.
GreenPepper is an Agile Requirements Definition and Management (RDM) tool.
Causes of inaccuracy (Accurate software development)
Having all your system specifications strictly defined 12 months
earlier and expecting that today you deliver software that fully
satisfies its requirements imply that nothing special happened last year
in your business ecosystem.
Distilled at its essence, the art of
developing valuable systems rely on the successful translation of the
business expert’s knowledge into a working piece of software created by
the developer.
Dazu passt auch BDD (Behavior Driven Development). Stefan Roocks Post hat mich dann noch zu Poppendieck geführt, die viele Essays haben (zuviele fürn armen Entwickler...)
Und das gabs auch noch in dem Umfeld: "Die Sapir-Whorf-Hypothese
besagt, dass die Sprache das Denken formt."
Classicist or Mockist? M.Fowler ist immer wieder für eine Leserunde gut.
Ich war allein bei der JUGHH zum Thema "Clean Code Developer" - meine Kollegen fanden das Thema wichtig, aber irgendwie nicht relevant für sie. Interessant dann auch, dass auf dem Treffen nur 3-4 IT-Architekten und wenige Projektleiter waren: Heisst das, dass die Top-Programmierer, die häufig zu IT-Architekten/Projektleitern werden, das Thema uninteressant finden, weil sie sich sowieso schon als CCDs sehen? Das wäre fatal, denn gerade sie als Leiter sollen die Notwendigkeit von CCD für die Gesamtqualität ihres Teams und damit Systems sehen.
Ich habe im Kuba-Urlaub das Buch "Clean Code" gelesen, es hat zur Initialenzündung des CCDs geführt. Die im Vortrag aufgezählten Prinzipien und Praktiken sind hilfreich, Tag für Tag und auch darüber hinaus. Mir gefällt die tägliche Reflexion und die Pfadfinderregel ("Den Platz ein bisschen sauberer verlassen als du ihn vorgefunden hast") - auch wenn ich mich nicht immer daran halte. Aber innerhalb eines Team können diese Prinzipien im Zusammenspiel mit den angegebenen Tools zu guter Software führen. Das ist sowieso der Hauptgrund für mich, dass Ganze anzuschauen: Da ich eher nicht so der naturbegabte Star-Programmierer bin, brauche ich viele Richtlinien und Praktiken, die meine Ergebnisse einfach gut werden lassen. Ich glaube ich, dass es für eine Firma wichtig ist, dass mit allen im Team eine hohe Qualität erreichnen zu können. Wenn man die CCD-Regeln beherzigt, hat man schon mal ein Korsett, das weniger Fehler zulässt. Joel hat man einen schönen "The Joel Test: 12 Steps to Better Code" aufgestellt:
But, all else being equal, if you get these 12 things right, you'll have a disciplined team that can consistently deliver.
Meine Firma besteht recht gut (8/12) - darum sind wohl auch noch so viele dabei...
Es können einfach nicht alle Firmen nur die 10% Besten eines Jahrgangs bekommen. Auch wenn das den Anschein der frühen Kapitulation auf dem Markt der Talente hat, ist es doch realistisch. Man kann natürlich, wie es Bruce Eckel als Beispiel gehört, ständig Heuern+Feuern wie bei Kayak.com. Aber welche Firma die ausser einer Website echte Kunden, Terminverpflichtungen und ein vertikales Geschäftsfeld hat (mit entsprechender Einarbeitung), kann sich das leisten? Und "Talente" sind auch eher rah gesät, zumal auch bei ihnen gilt: 5% Inspiration 95% Transpiration. Wichtiger ist daher doch eher, innerhalb eines Teams gemeinsam Masstäbe, Regeln und Verbindlichkeiten zu Qualität und Zielen zu finden. Dann kann man immer noch die "poisonous person" identifizieren/entfernen, und vielleicht ist das sogar der Star-Programmer-Loner mit dem schwierigen "Interface". Clean Code kann ein Mittel sein, im Team eine gleich hohe Qualifizierung und Stimmung zu erreichen.
Der Kayak-CTO hat aber einen interessanten Artikel geschrieben (mirror):
We work really hard for 40 to 45 hours a week, but we believe in people having strong personal lives. Over the past six years, there have been maybe five times I've spoken with Steve before 8 a.m., after 5 p.m., or on the weekend.
A lot of companies have the "no assholes" rule. So if the greatest programmer ever is also a jerk, he's fired. Our rule is "no neutrals."
Man lernt ja immer ne Menge Neues, wenn man mal was Altes machen will; heute: Windows XP installieren. Ok, gültige Lizenz und bootfähige CD hab ich noch, klick, ist ne Weile her, aber Radfahren verlernt man ja nicht, booten, schnipp - BSOD. Grrrh, Suse verwünscht und M$ und usw. Dann das Internet gepriesen, ich weiss WIRKLICH nicht was man früher gemacht hat? (Jemanden angerufen? Auf die nächste c't gewartet? Radio gehört? In den Computerclub gegangen? Am Sonntag?)
Ca. 163.000 mal hat jemand was dazu gesagt - die
Lösung ist gaanz einfach: 1) Service Pack 2/3 slipstreamen, 2)
CD brennen, 3) booten - 4) läuft. Naja, slipstreaming? D:\WINDOWSXP-KB936929-SP3-X86-DEU.EXE
/integrate:D:\WXPVOL_DE
Aber es gibt gute Anleitungen, die bis Schritt gut geklappt haben, jedoch hatte ich dann nur eine slipstream-Version meines XPs mit SP3 auf einer nicht bootbaren CD ("Could not find NTLDR" - das haben ca. 263.000 Leute)
Der harte Weg war dann: Installation eines Windows XP, das nach 30 Tagen aktiviert werden muss, dann die o.g. CD eingelegt und XP davon noch einmal installiert, mit Lizenz und so. Nun hat Suse mein Laptop, das Ubuntu da drauf harrt noch seiner Auferweckung und ich bin irgendwie ausgelaugt... GParted und ein Knoppix sind übrigens treue Helfer bei aller Art von Festplatten-Massage...
Leicht ungeplant schaffe ich es doch noch zu Adam in die LOB: "Real World Java EE Patterns - Rethinking Best Practices"
Auf den ersten Blick sieht aber alles ziemlich chaotisch aus. Java Context and Dependency Injection (CDI) (JSR-299), Dependency Injection for Java (JSR-330) und EJB 3.1 definieren Dependency Injection. Managed Beans (aus JSF), CDI-Beans und EJBs verfügen über einen definierten Lebenszyklus.
Es war unterhaltsam, wenn auch etwas atemlos. Adams große Stärke ist die Besinnung auf das Einfache, Undogmatische. Nicht umsonst zeigt er die "Simplest possible EJB", eine Enterprise Bean mit gerade 5 Zeilen.
@Stateless
public class HelloService {
public String
getHello(){
return "Hello from EJB / CDI";
}
}
Er fragt, wozu man ohne Grund einfach immer ein Interface erstellt, selbst wenn es niemals mehr als genau eine Implementierung geben wird. Das verschmutzt nicht nur den Namensraum ("ICustomer" oder "CustomerImpl" wenn "Customer" reichen würde) und es bringt fürs Testen auch keine Vorteile, denn auch Klassen kann man mocken (ok, sie dürfen nur nicht final sein).
Er fragt, warum man die Datenbank (ausser generiertem Mapping) abstrahieren muss, wenn man sie im Griff hat bzw. verwaltet? Niemand hat jemals Oracle ausgetauscht :-)
Er fragt, wozu man 5 Layer hat, wenn bei der neuen Anzeige eines Datenbank-Felds alle 5 geändert werden müssen. Adam benutzt häufig nur 1 Entität in allen Schichten.
Zum Beispiel JSF-to-EJB: Die o.g. EJB und die Verwendung in JSF:
@Stateless
public class HelloService {
@EJB
ClockService clockService;
public String getHello(){
return "Hello from EJB / CDI: " + clockService.currentTime();
}
}
<h:body>
<h:form>
<h:outputLabel
value="#{helloService.hello}"/>
</h:form>
</h:body>
Ganz allgemein schwimmt er gegen den Strom: Setzt EJB ein und progagiert Windows Vista, auch wenn er mit einem MacBook auflief, weil Lenovo ihn versetzt hat. Und es fiel auch die Frage, wer ausser Adam überhaupt noch EJB macht :-)?
Aber die gute Nachricht ist, dass es mit der neue EJB-Spec ein bisschen egal geworden ist, ob Spring oder EJB: Annotations sind z.T. identisch, Konvention over Konfiguration sorgt in beiden Umgebungen für schlanken Code. Konfiguration mit XML ist in EJB tot, eine leere beans.xml ist das jämmerliche Relikt, in Spring kommt man mittlerweile auch mit viel weniger applicationContext.xml aus.
SOA ist bei Managern beliebt, in der Praxis aber nicht so einfach bzw. leichtgewichtig: Datentypen konvertieren zwischen .NET und Java ist hakelig. Wenn man es ROA nennt (Resource oriented architecture) und REST over HTTP macht, kommt man weiter: String/JSON läßt sich gut konvertieren.
Ein vergnüglicher Abend, der Herdentrieb und Hypes kritisch beleuchtet und die scheinbar chaotische und komplexe Welt der Enterprise Anwendung ein wenig heller macht.
Um mich selbst zu disziplinieren, um die Stapel c't, iX und Javamagazin neben meinem Bett zu reduzieren und ohne schlechtes Gewissen ("da könnte noch was Interessantes drin stehen) will ich jede 1-2 Wochen die besten Artikel in einem Blog-Eintrag zusammenfassen. Dann kann man sie mit Stichworten oder Datum wiederfinden.
Und los geht's: c't 02/10
"Um eine Anwendung ins Web zu hieven, muss man nicht unbedingt einen Vertrag mit einem Web-Hoster abschließen. In der Cloud von Google bekommt man CPU-Zeit, Speicher und Bandbreite kostenlos -- ausreichend für satte fünf Millionen Seitenaufrufe pro Monat."
Der Artikel hat in mir die alte Idee wiederbelebt, doch endlich mal einen Hoster zu benutzen, der auch eine Java-Umgebung anbietet: GAE verspricht genau das gratis:
Und das klingt auch vertraut - die gute alte Servlet-API im
WebContainer-Layout:
"App Engine Java applications use the Java
Servlet standard for interacting with the web server environment. An
application's files, including compiled classes, JARs, static files and
configuration files, are arranged in a directory structure using the WAR
standard layout for Java web applications. You can use any development
process you like to develop web servlets and produce a WAR directory."
Und da ich sowieso gerade ein wenig mit Webanwendungen rumbastele scheint es die ideale Ergänzung - mal anschauen.
Um das Profil direkt aufzurufen und parallel zu einem anders konfigurierten Firefox-Fenster zu benutzen, rufen Sie den Browser mit zwei zusätzlichen Parametern auf, nämlich -no-remote und -P „Profilname“. Der erste sorgt dafür, dass ein neues Fenster geöffnet wird, der zweite wählt das darin zu nutzende Profil aus. Ohne -no-remote öffnet ein schon laufender Firefox einfach ein neues Fenster, ohne das Profil zu ändern.
Hotline: GMX-MediaCenter ohne Zusatz-Software?
Befehl „Netzlaufwerk verbinden“ im Menü „Extras“ des Windows Explorer). Starten Sie dort über den Link „Verbindung mit einer Website herstellen, auf der Sie Dokumente und Bilder speichern können“ den Assistenten zum Einrichten einer WebDAV-Verbindung: https://mediacenter.gmx.net
Und ruck-zuck hat man ein Laufwerk G:\ dass im Netz ca. 1GB Speicher kostenfrei zur Verfügung stellt - sehr bequem, wenn man eh schon GMX-Mitglied ist.