Seiten

Mittwoch, 17. Juli 2013

CDI Commons – Dynamic Service Resolver

In meinem Blog vom 26.6.2013 habe ich über die Möglichkeit geschrieben, wie man zur Laufzeit die
Implementierung auswählen kann die in die entsprechende Stelle injected wird.
Es war immer noch notwendig die Producer anzupassen wie in dem Beispiel des SimpleDateFormatterProducer
image

Gleich mehrere Dinge die nicht angenehm sind.
1) der ContextResolver ist hier hart per Qualifier angegeben.
2) Die Implementierung eines Constructors selbst
3) Lazy-Init des AttributespropertyregistryService
4) fehlende Default Implementierung

Aus Sicht der Entwickler wäre folgendes wünschenswert.
Ein Inject des PropertyregistryService mit folgender Anweisung:
image
Um das zu erreichen wird die Klasse PropertyRegistryServiceProducer implementiert.
Hier wird ein allgemeiner Producer für den PropertyregistryServcie geschrieben.
image

zwei Dinge sind hier von Bedeutung:
1) Der Inject einer Default-Implementierung , in diesem Fall der Inject einer FileBasedRegistry
2) Der Inject einers ContextResolvers mit @Any und @Default
Nun kann zur Laufzeit der ContextResolver ausgewertet werden um die Endscheidung zu treffen welche Implementierung verwendet werden soll.
Der ContextResolver selbst wird wohl immer eine spezialisierte Implementierung in einem Projekt darstellen und muss deshalb selbst Implementiert werden.
Eine Default-Implementierung ist dem Projekt als Beispiel in den test – Foldern beigelegt. (DefaultPropertyContextResolver)
image

Montag, 15. Juli 2013

CDI Transactions tested with Arquillian

How to implement a scope like an transaction? The main goal was the reusability for an SE environment.

After a little bit google- searching I found the ConversationScope. A google start, but not usable in an SE environment.

So I started to write my own Scope. (Source –> Bitbucket) First step will be an annotation CDITransactionScope with the default Annotations.

The only new one is the annotation @NormalScope. With this annotation the container will use this as an Scope-Qualifier.

image

 

The next step is the implementation of an extension. This is done by the class CDITransactionExtension extends Extension.

The interface Extension itself is empty and only a marker.  The task to full fill is the registration of an context.

The AfterBeanDiscovery event is used to add the instance of the context implementation. Important to know is, that at this point the init-phase of the weld-container is not ready.

No @Inject is possible and the only instance that can be injected is the BeanManager. 

image

 

Finally we have to implement the heard of the transaction, the CDITransactionContext implements Context.

This is a class, not managed by the Container and again no @Inject is possible, but the BeanManager

is usable. (see CDITransactionExtension )

Four methods are found to implement from the interface Context.

1) public Class<? extends Annotation> getScope()

2) public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext);

3) public <T> T get(Contextual<T> contextual);

4) public boolean isActive();

 

Nr 2 and 3 are the important methods to implement. This methods are handling the instance-management.

The easiest implementation could be a map like HashMap<Class, Object>.

This implementation will cache all classes implementing the Interface Cacheable. (GenericCache)

If an Class is implementing this Interface it will be cached, otherwise you will always get a new instance.

image

 

To activate this Extension, the class CDITransactionExtension must be registered in the file META-INF/javax.enterprise.inject.spi.Extension

with the full class name.

Now we can start writing transactions.

The Transaction (AbstractCDITransaction) is an Executor-Pattern.

image

 

To Implement a transaction you have to implement an transaction – class with the method doIt().

Here is an excample with references inside and ad the same time outside of the active transaction.

image

 

Inside the transaction are all instances with the Qualifier @CDITransactionScop .

For this jUnitTest I implemented two Producers, one for the Instances inside the transaction and one for the outside one.

image

 

Finally the UML for this small demo.

 

image

Freitag, 12. Juli 2013

Hot Swap in Java with DCEVM

Immer auf der Suche nach neuen Werkzeugen…  Hier DCEVM (http://ssw.jku.at/dcevm/)
Und das beste: Es gibt es schon als intelliJ Plugin..
http://blogs.jetbrains.com/idea/2013/07/get-true-hot-swap-in-java-with-dcevm-and-intellij-idea/
das normale HotSwaping hat enge Grenzen sobald man an der Struktur der Klassen Veränderungen vornimmt. Hier gab es bisher immer noch das kommerzielle Werkzeug jRebel.
(Danke übrigens an das jRebel Team f die OSS Lizenz)
Nun gibt es eine OSS Lösung die genau an dieser Stelle weiterhilft.
Ich werde von meinen Tests berichten… Zwinkerndes Smiley

Donnerstag, 11. Juli 2013

CDI - Commons

Die CDI - Commons Repositories sind nun im Jenkins (http://jenkins.rapidpm.org/) als Job hinterlegt.
Änderungen im develop - Branch werden gebaut und in das Repository (http://nexus.rapidpm.org/nexus/) gelegt.


Sonntag, 7. Juli 2013

Vaadin: Markierte Zeile einer Tabelle editierbar machen

Hier ein kleines Tutorial über den Umgang mit Vaadin-Tabellen und deren Editierbarkeit. Der Anwendungsfall (aus der Praxis) ist simpel:

Eine Tabelle enthält Entitäten des Typs Parameter. Ein Parameter besteht aus dem Parameter-Namen, einer Beschreibung, einem Standard-Wert und einem benutzerdefinierten Wert (welcher den Standard-Wert ersetzt). Die Vaadin-Tabelle enthält einen Container mit Parameter-Beans und besteht somit aus den vier Spalten Name, Beschreibung, Standard-Wert, benutzerdefinierter Wert. Name, Beschreibung und Standard-Wert sind fix, während der benutzerdefinierte Wert editierbar sein soll.

Ziel


Um Vaadin-Tabellen editierbar zu machen existiert die Methode setEditable(boolean b). Wird dieser ein true übergeben wird die gesamte Tabelle editierbar. Sprich: Alle Zellen werden zu Textfeldern. Oftmals ist dies natürlich nicht gewünscht da beispielsweise bestimmte Spalten nicht editierbar sein sollen (wie in unserem Fall). Um das Verhalten der Tabelle den eigenen Wünschen anzupassen muss man die von der Tabelle verwendete TableFieldFactory überschreiben (standardmäßig wird hier eine Instanz der DefaultFieldFactory verwendet, welche sämtliche Zellen der Tabelle editierbar macht). An dieser Stelle möchte ich etwas vorgreifen und schonmal die Codestelle zeigen, in welcher der Tabelle die neu implementierte FieldFactory zugewiesen wird. Die entsprechende Implementierung unserer eigenen TableFieldFactory folgt dann im Anschluss.

parametersTable.addItemClickListener(new ItemClickEvent.ItemClickListener() {
            @Override
            public void itemClick(ItemClickEvent itemClickEvent) {
                final CrawlerParameter crawlerParameter = (CrawlerParameter) itemClickEvent.getItemId();
                if(crawlerParameter == null){
                    buttonLeiste.setVisible(false);
                    parametersTable.setEditable(false);
                } else {
                    buttonLeiste.setVisible(true);
                    parametersTable.setTableFieldFactory(new EditSelectedParamFieldFactory(crawlerParameter));
                    parametersTable.setEditable(true);
                }
            }
        });
Der Tabelle (parametersTable) wird ein ItemClickListener hinzugefügt welcher die itemClick()-Methode ausführt wenn eine Zeile der Tabelle durch den Benutzer markiert wurde. Innerhalb dieser Methode wird zuerst der vom Benutzer ausgewählte Parameter in einer entsprechenden Variable gespeichert. Achtung: Den aktuellen Parameter bekommt man nicht via parametersTable.getValue()! Dies liefert nicht den aktuell angeklickten Parameter, sondern den zuvor ausgewählten (Bei Benutzung dieser Methode ist man also immer genau einen Schritt hinterher). Der aktuell ausgewählte Parameter ist per getValue() erst NACH Durchführung der itemClick()-Methode abrufbar. Anschließend wird überprüft ob überhaupt ein Parameter ausgewählt ist. Ist dies nicht der Fall soll die Tabelle auch nicht editierbar sein, andernfalls bekommt die Tabelle eine neue FieldFactory - nämlich unsere eigene Implementation. Diese bekommt als Parameter den angeklickten Parameter übergeben (die Implementation folgt gleich). Anschließend wird die Tabelle editierbar gemacht (per setEditable(true)). Nun ist die Spalte BenutzerdefWert der ausgewählten Zeile editierbar.

Lösen wir das Rätsel und schauen uns die EditSelectedParamFieldFactory-Implementation an:

public class EditSelectedParamFieldFactory extends AbstractFieldFactory {

    private CrawlerParameter selectedCrawlerParameter;

    public EditSelectedParamFieldFactory(final CrawlerParameter selectedCrawlerParameter){
        this.selectedCrawlerParameter = selectedCrawlerParameter;
    }
    @Override
    public Field<?> createField(Container container, Object aParameter, Object spaltenname, Component uiContext) {
        CrawlerParameter parameter = (CrawlerParameter) aParameter;
        switch(spaltenname.toString()){
            case NAME:
                return null;
            case DESCR:
                return null;
            case DEFAULT_VALUE:
                return null;
            case USER_VALUE:
                if(selectedCrawlerParameter != null){
                    if(selectedCrawlerParameter.equals(parameter)){
                        if(parameter.getDefaultValue().getClass() == Boolean.class) {
                            return new CheckBox();
                        }
                        if(parameter.getDefaultValue().getClass() == Integer.class) {
                            final TextField integerTextField = new TextField();
                            configureField(integerTextField, Integer.class);
                            return integerTextField;
                        }
                        if(parameter.getDefaultValue().getClass() == Double.class) {
                            final TextField doubleTextField = new TextField();
                            configureField(doubleTextField, Double.class);
                            return doubleTextField;
                        }
                        if(parameter.getDefaultValue().getClass() == String.class) {
                            final TextField textField = new TextField();
                            configureField(textField, String.class);
                            return textField;
                        }
                    }
                } else {
                    return null;
                }
            default:
                return null;
        }
    }
}


Wie bereits erwähnt, muss ein selbst implementierte FieldFactory das Interface TableFieldFactory implementieren. Warum erweitert unsere Klasse also AbstractFieldFactory anstatt TableFieldFactory zu implementieren? Ganz einfach: Die Klasse AbstractFieldFactory ist abstrakt und implementiert das TableFieldFactory-Interface (zusätzlich wird in ihr die (für uns nun nicht relevante) Methode configureField() implementiert, welche die zu erstellenden Felder je nach enthaltenem Datentyp mit entsprechenden Validatoren etc. versieht). Wir implementieren also die Methode  public Field<?> createField(Container container, Object itemId, Object propertyId, Component uiContext) des TableFieldFactory-Interfaces. Außerdem besitzt die Klasse ein Attribut, welches den aktuell ausgewählten Parameter speichert. Für jede Zelle der Tabelle wird dann die Methode durchlaufen. Innerhalb der Methode wird bei jedem Durchlauf zunächst geprüft in welcher Zeile (CrawlerParameter parameter) und in welcher Spalte (switch(spaltenname.toString()) wir uns befinden. Befinden wir uns in einer Zelle der Spalten Name, Beschreibung oder StandardWert ist eine Prüfung der Zeile unnötig, da Zellen dieser Spalten eh nicht editierbar sein sollen, egal ob es sich um die ausgewählte Zeile handelt oder nicht. Daher wird in diesen Fällen ein null-Wert zurückgegeben --> Die Zelle ist nicht editierbar. Falls wir uns in der BenutzerdefWert-Spalte befinden wird überprüft ob es sich um die vom Benutzer ausgewählte Zeile handelt. Falls nicht, wird ebenfalls ein null zurückgegeben. Falls doch wird überprüft welchen Datentyp der Parameter besitzt (ist es ein boolescher Parameter, ein Integer-Parameter, ...?). Anschließend wird ein entsprechendes TextFeld (oder eine CheckBox bei einem booleschen Parameter) erstellt und mit entsprechenden Convertern/Validatoren etc. versehen. Anschließend wird das TextFeld bzw. die CheckBox zurückgegeben. Diese Zelle ist also nun editierbar. Damit haben wir das Ziel erreicht: Nur der benutzerdefinierte Wert des aktuell ausgewählten Parameters ist editierbar.

Zusammenfassung
Soll nur die aktuell ausgewählte Zeile (bzw. Teile davon) der Tabelle editierbar sein so muss folgendes getan werden:

  • Tabelle einen ItemClickListener hinzufügen
  • Im ItemClickListener wird der Tabelle eine Instanz einer selbst erstellten, das Interface TableFieldFactory implementierenden, Klasse zugewiesen (setTableFieldFactory(new MyFieldFactory(currentSelectedEntity)) und die Tabelle auf editable gesetzt (setEditable(true)).
  • In der eigenen Implementierung der TableFieldFactory (welche per Konstruktor-Parameter die selektierte Entität übergeben bekommt) wird überprüft ob es sich um eine Zelle handelt welche sich in der selektierten Zeile (und in einer editierbaren Spalte) befindet. Ist dies der Fall wird eine entsprechende Eingabe-Komponente (z.B. ein Textfeld oder eine CheckBox) erstellt und zurückgegeben (andernfalls wird null zurückgegeben und die Zelle ist nicht editierbar).
Hier werden alle Zellen der Spalte BenutzerdefWert editierbar gemacht, unabhängig von der Selektion