Über mich und meine Erfahrungen
Showing all posts by malime
Triggerentwicklung für ClearCase

Bei einem Kunden habe ich mir Gedanken gemacht, wie man die Entwicklung von Triggern geeignet umsetzen kann. Die Umsetzung ist eigentlich recht einfach. Alles was notwendig ist, besteht darin in eclipse E-P-I-C zu verwenden, und mit Perl auch den PadWalker zu installieren. Anschließend beginnt es damit, sich zunächst zu überlegen, wann der ( Pre- oder Posttrigger ) Trigger aktiv werden soll, und welche Umgebungsvariablen ClearCase hierbei setzt. Hierzu wird das folgende Perlskript mit der gewünschten Triggeraktion ausgeführt und erzeugt hierdurch eine entsprechende Ausgabe:

1
2
3
4
5
6
7
8
9
10
use strict;
use warnings;
foreach my $key ( sort keys(%ENV) ) {
   if( $key =~ "CLEARCASE" ) {
      print "$key = ($ENV{$key})\n";
   }
}
# Rückgabewert von 1 bedeutet der Trigger wird nicht ausgeführt (bei einer Preop)
# Rückgabewert von 0 bedeutet der Trigger wird ausgeführt
1;

Danach erstellt man eine Launchkonfiguration unter eclipse, in der unter dem Reiter Environment genau diese Variablen mit ihren Werten gesetzt werden. Damit hätte man eine Testumgebung, die seiteneffektfrei zur Entwicklung und Testen des Trigger verwendet werden kann. Neben den Debugoptionen, die durch PadWalker möglich sind, sollte auch Profiling mittels NYTProfiler und die Codeabdeckung durch Devel-Cover beachtet werden.

Remote Apache Ant debugging unter eclipse

Da ich es bereits mehrfach immer wieder selber herausfinden mußte ein kleiner Tipp zum Remote debuggen von ( gerade auch für selbstgeschriebene ) Apache Ant Tasks. Eigentlich ist es recht einfach, man muß nur bei Apache Ant entweder über die ANT_OPS Umgebungsvariable oder mittels VM Argumenten beim Aufruf von Apache Ant
starten, und kann dann anschließend durch das Remote Java Application debugging

die Ausführung eines Apache Ant Tasks explizit debuggen.

Erweiterung von RTC um Fokusierung

Nachdem ich seit einiger Zeit bei einem Kunden das IBM Rational Team Concert einsetze, privat aber auch mit Subversion und Mylyn gearbeitet habe, habe ich mich gefragt, warum es zu dem „Add SVN Revision…“ bei der Erstellung von Links zu Work Items, kein Pendant für das CCM existiert.

Es ist zwar möglich mit „Add Related Artefakt…“ auf eine entsprechende Artefakt — dieses könnte eine Datei oder etwas ähnliches sein — zu verweisen, aber diese Navigation ist manuell einzugeben.

Insbesondere existiert diese Beziehung zwischen Work Item und Resource oder Artefakt sobald

ein Changeset erzeugt wird. Dieses Changeset kann dann auch genutzt werden, um eine entsprechende Navigation vom Work Item zu dem Artefakt zu erhalten.

Für mich geht es hierbei um die Frage, ob man das entwicklerfokusierte Arbeiten, welches mit Mylyn in eclipse einzug gehalten hat, nicht auch auf den RTC übertragen kann. Sofern jemand Mylyn kennt, weiß er, dass Mylyn aus der Disseration von Mik Kersten stammt, der mit IBM RTC Mylyn Connector bei seiner Firma Tasktop eine entsprechende Implementierung umgesetzt hat. Soweit so gut, denkt man sich, das Problem ist somit gelöst.

Es ist möglich diese Integration zu erwerben, um damit den RTC effizienter einzusetzen. Allerdings verstehe ich es als meine Aufgabe, mich mit den Tools auseinander zu setzen, und nicht nur mit Ihnen zu arbeiten. Warum also nicht eine spezifische Ergänzung entwicklen, die es mit wenigen Klicks möglich macht, das in bezug stehende Artefakt automatisch hinzufügen zu können? Ein schöner Gedanke, ich lasse diese Idee einmal reifen, und lasse mich überraschen, ob sie Früchte trägt.

Einbetten von JavaScript in Java

Die Einbettung von JavaScript oder Skriptsprachen ist mit Java 6 recht einfach geworden:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package de.malime.scripting;
 
import java.util.ArrayList;
import java.util.List;
 
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
 
 
public class ScriptingTest {
 
      /**
       * @param args
       */
      public static void main(String[] args) {
            ScriptEngineManager mgr = new ScriptEngineManager();
            List<ScriptEngineFactory> factories = mgr.getEngineFactories();
            for (ScriptEngineFactory factory : factories) {
                  System.out.println("ScriptEngineFactory Info");
                  String engName = factory.getEngineName();
                  String engVersion = factory.getEngineVersion();
                  String langName = factory.getLanguageName();
                  String langVersion = factory.getLanguageVersion();
                  System.out.printf("\tScript Engine: %s (%s)\n", engName,
engVersion);
                  List<String> engNames = factory.getNames();
                  for (String name : engNames) {
                        System.out.printf("\tEngine Alias: %s\n", name);
                  }
                  System.out.printf("\tLanguage: %s (%s)\n", langName,
langVersion);
            }
            ScriptEngine jsEngine = mgr.getEngineByName("ECMAScript");
            try {
                  List<String> namesList = new ArrayList<String>();
                  namesList.add("Jill");
                  namesList.add("Bob");
                  namesList.add("Laureen");
                  namesList.add("Ed");
                  jsEngine.put("namesListKey", namesList);
                  System.out.println("Executing in script environment...");
                  jsEngine.eval("var x;" + "var names =
namesListKey.toArray();"
                              + "for(x in names) {" + "
println(names[x]);" + "}"
                              + "namesListKey.add(\"Dana\");");
                  System.out.println("Executing in Java environment...");
                  for (String name : namesList) {
                        System.out.println(name);
                  }
            } catch (ScriptException ex) {
                  ex.printStackTrace();
            }
      }
 
}
Erstellen von JSPWiki Plugins

Das JSPWiki läßt sich recht einfach auch durch eigene Plugins erweitern. Hierzu ist es ausreichend eine jar zu Erstellen, diese und WEB.INF/lib abzulegen und unter den jspwiki.properties den Suchpath entsprechend zu erweitern. Anschließend kann mittels

[{ContextAndParameterPlugin parameter='true'}]

dass Plugin aufgerufen werden:

[{ContextAndParameterPlugin parameter='true'}]

Hier ist der Code zu dem Plugin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package de.malime.jspwiki;
 
import java.util.Iterator;
import java.util.Map;
 
import com.ecyrd.jspwiki.WikiContext;
import com.ecyrd.jspwiki.WikiEngine;
import com.ecyrd.jspwiki.WikiPage;
import com.ecyrd.jspwiki.plugin.PluginException;
import com.ecyrd.jspwiki.plugin.WikiPlugin;
 
public class ContextAndParameterPlugin implements WikiPlugin {
	@SuppressWarnings("rawtypes")
	public String execute (WikiContext Context, Map ParameterMap) throws PluginException {
		String Result = "<h4>ContextAndParamterPlugin - examines the actual \"WikiContext\" and lists the given plugin parameters</h4>\n\n";
		Result += "<table width=100%>";
		/**
		 * examine the actual wiki engine 
		 **/
		WikiEngine Engine = Context.getEngine();
		Result += "  <tr valign=top>\n";
		Result += "    <td><b>Wiki Engine:</b></td>\n";
		Result += "    <td>" + Engine.getApplicationName() + " (" + Engine.getPageCount() + " pages, up since " + Engine.getStartTime() + ")</td>\n";
		Result += "  </tr>\n";
		/**
		 * examine the actual wiki page 
		 **/
		WikiPage Page   = Context.getPage();
		String Author = Page.getAuthor();
		Result += "  <tr valign=top>\n";
		Result += "    <td><b>Wiki Page:</b></td>\n";
		Result += "    <td>" + Page.getName() + " (version " + Page.getVersion() + ", modified on " + Page.getLastModified() + ( Author == null ? "" : " by " + Author) + ")</td>\n";
		Result += "  </tr>\n";
		/** 
		 * finally show the request context 
		 **/
		Result += "  <tr valign=top>\n";
		Result += "    <td><b>Request Context:</b></td>\n";
		Result += "    <td>" + Context.getRequestContext() + "</td>\n";
		Result += "  </tr>\n + </table>\n";
 
		if (ParameterMap == null || ParameterMap.isEmpty()) {
			return Result;
		}
	    Result += "<dl>";
	    for (Iterator ParameterInterator = ParameterMap.entrySet().iterator(); ParameterInterator.hasNext(); ) {
	    	Map.Entry Entry = (Map.Entry)ParameterInterator.next();
	    	Object Key = Entry.getKey();
	    	Result += "<dt>" + Key + (Key != null ? " (" + Key.getClass().getName() + ")" : "") + "\n";
	    	Object Value = Entry.getValue();
	    	if (Value == null) {
	    		Result += "<dd>(null)";
	    	} else {
	    		if (Value instanceof String) {
	    			Result += "<dd>\"" + Value + "\"";
	    		} else {
	    			Result += "<dd>(" + Value.getClass().getName() + ") " + Value + "\n";
	    		}
	    	}
	    }
	    return Result + "</dl>";	
	};
}
Erweiterungen von Acceleo

Nachdem die Generierung mit Acceleo gelungen ist, und der Inhalt der Webpräsenz durch eine Acceleo Generierung wiederhergestellt werden kann, kommen die nächsten ToDos auf die Liste. Zum einen möchte ich die Erweiterung von Acceleo beim Post-Generation-Schritt verbessern, indem nach der Generierung HTMLTidy aufgerufen wird. Es geht mir hierbei nicht darum, HTMLTidy als nachgelagerten Schritt durchzuführen, sondern die Generierungsoptionen von Acceleo zu erweitern. Hierzu soll dementsprechend der Generíerungsservice von Acceleo erweitert werden, damit der HTML-Code gleich „beautified“ wird. Das nächste was mir fehlt ist die Verwendung/Einbindung von Logging Mechanismen. Sprich eine Implementierung von Java Sevice Wrappern, die eine einfach aber auch effiziente Möglichkeit bieten während der Generierung Log-Ausgaben anzusetzen. Einfach, nun ja, da nicht klar ist, in welcher Umgebung Acceleo laufen wird ( eclipse, headless eclipse oder Ant-Task ) ist das mehr als eine einfache Überlegung.

Auf der Suche nach dem Fehler bei der Verarbeitung von xml

Ich habe vor einiger Zeit den Fehler eingestellt, dass die Verarbeitung von emtl Datei nicht ganz korrekt ist. Da der Fehler auch in dem Release Candidaten 1 noch vorhanden ist, mache ich mich einmal auf die Suche nach einer Lösung. Nachdem ich den Bug geschlossen habe, da ich einen Fehler in meiner Umsetzung gefunden habe, konnte ich auch mit dieser Korrektur ein identisches zumindest aber ähnliches Verhalten ab Milestone 7 reproduzieren. Aus diesem Grunde habe ich stattdessen den eclipse Bug eröffnet.

Nachtrag: Es scheint als habe ich die Lösung gefunden. Ich werde gleich Feierabend machen und die Lösung gegenchecken, mal sehen ob meine Annahme korrekt ist, und es sich hierbei um ein „Layer 8 Problem“ handelt. Auch in dem zweiten Projekt ließ sich das Verhalten reproduzieren. Würde mich interessieren, ob die Zeile

[comment @main/]

erst mit dem Acceleo Milestone 3.1.0 M7 zur Steuerung von Templates aktiviert worden ist. Da diese Zeile vor Acceleo 3.10 M7 keine Aufwirkungen zeigte, ist es zu vermuten, dass diese Änderung mit diesem Milestone eingeführt wurde.

Entwicklen und Debuggen für RTC

Basierend auf den vorhandenen Beschreibungen zum RTC 2.0 für das Entwickeln und Debuggen habe ich eine virtuelle Maschine aufgesetzt, in der ich für und unter dem RTC 3.0 entwickeln kann. Das Umsetzen eines OperationAdvisors war recht einfach, und dauert eigentlich nur recht kurz. Nachdem nun einige Zeit vergangen ist, habe ich das Vorgehen für den RTC 5.0 angepasst. Auch hier konnte ich einen Ansatzpunkt finden, aber auch dieser bezieht sich auf die veraltete Version 4.0 und ist unter Rational Team Concert Extensions Workshop zu finden, daneben existiert auch eine entsprechende Aktualisierung auf Running The RTC 4.x Extensions Workshop With RTC 5.0.x.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package de.malime.rtc.operationadvisor;
 
import java.util.List;
 
import org.eclipse.core.runtime.IProgressMonitor;
 
import com.ibm.team.links.common.IItemReference;
import com.ibm.team.links.common.IReference;
import com.ibm.team.process.common.IProcessConfigurationElement;
import com.ibm.team.process.common.advice.AdvisableOperation;
import com.ibm.team.process.common.advice.IAdvisorInfo;
import com.ibm.team.process.common.advice.IAdvisorInfoCollector;
import com.ibm.team.process.common.advice.runtime.IOperationAdvisor;
import com.ibm.team.repository.common.IItemHandle;
import com.ibm.team.repository.common.TeamRepositoryException;
import com.ibm.team.workitem.common.IAuditableCommon;
import com.ibm.team.workitem.common.ISaveParameter;
import com.ibm.team.workitem.common.model.IApprovalDescriptor;
import com.ibm.team.workitem.common.model.IApprovals;
import com.ibm.team.workitem.common.model.IWorkItem;
import com.ibm.team.workitem.common.model.IWorkItemHandle;
import com.ibm.team.workitem.common.model.IWorkItemReferences;
import com.ibm.team.workitem.common.model.WorkItemEndPoints;
 
public class ParentApprovalRequired implements IOperationAdvisor {
 
  public void run( AdvisableOperation operation, IProcessConfigurationElement advisorConfiguration, IAdvisorInfoCollector collector, IProgressMonitor monitor ) throws TeamRepositoryException {
    Object data= operation.getOperationData();
    if (data instanceof ISaveParameter) {
      if ( ( ( ISaveParameter ) data).getNewState() instanceof IWorkItem ) {
        IWorkItem workitem = ( IWorkItem )( ( ISaveParameter ) data).getNewState();
        parentIsApproved( ( IWorkItem )( ( ISaveParameter ) data).getNewState() );
        IAuditableCommon auditableCommon = ( ( ISaveParameter ) data ).getSaveOperationParameter().getAuditableCommon();
        IWorkItem parent = getParentWorkItem( ( ISaveParameter ) data, auditableCommon );
        if( parent != null ) {
          if( parentIsApproved( parent ) == false ) {
            IAdvisorInfo info = collector.createProblemInfo("Parental Approval", "The parent of the work item is not approved", "error");
            collector.addInfo( info );
          }
        }
      }
    }
  }
 
  private IWorkItem getParentWorkItem( ISaveParameter param, IAuditableCommon auditableCommon ) throws TeamRepositoryException {
    List<IReference> parentRefs= null;
    IWorkItemReferences refs= param.getNewReferences();
    if (refs.hasReferences(WorkItemEndPoints.PARENT_WORK_ITEM)) {                        
      parentRefs= refs.getReferences(WorkItemEndPoints.PARENT_WORK_ITEM);
    }
    if (parentRefs != null && !parentRefs.isEmpty()) {
      IReference parent= parentRefs.get(0);
      if (parent.isItemReference()) {
         IItemHandle refItem= ((IItemReference)parent).getReferencedItem();
         if (refItem instanceof IWorkItemHandle) {
           return (IWorkItem) auditableCommon.resolveAuditable((IWorkItemHandle) refItem, IWorkItem.FULL_PROFILE, null);
         }
      }
    }
    return null;
  }
 
  private boolean parentIsApproved( IWorkItem workitem ) {
    IApprovals approvals = workitem.getApprovals();
    if( approvals != null ){
      for (IApprovalDescriptor descriptor : approvals.getDescriptors()) {
        if( !"com.ibm.team.workitem.approvalState.approved".equals( descriptor.getCumulativeStateIdentifier() ) ) return false;
      } 
    }
    return true;
  }
 
}

Bei dem Entwickeln und Debuggen ist eigentlich nur zu Beachten, dass es jetzt nicht mehr einen jazz-Server, sondern stattdessen einen RTC- und einen CCM-Server gibt. Der OperationAdvisor gehört in den letzteren. Auch beim Entwicklen eines HTTPS Zugriffes, um auf Workitems zuzugreifen, ist dieses zu beachten. Ich war erfolgreich, nachdem ich zuerst den RTC-Server abgefragt habe, und dann mit den gefundenen Informationen auf den CCM-Server zugegriffen habe.

IBM Rational Team Concert 3.0

Endlich habe ich die Zeit gefunden mir eine virtuelle Maschine aufsetzen um meine Erfahrungen und Erkenntnisse zum Customizing des RTC zu erweitern. Viele Dinge, die auf jazz.net beschrieben sind, lassen sich auf den RTC 3.0 übertragen, auch wenn es kleinere Abweichungen gibt, bei denen man etwas aufpassen muss. Das nächste was ich sicherlich umsetzen werde, ist eine Erweiterung zum Workflow. Die Idee ist, wenn eine Story mehrere Tasks hat, und gleichzeitig wenigstens einen Approver besitzt, dass die Tasks nur beginnen dürfen, sofern alle Approver zugestimmt haben. Die Ergänzung zu dieser Idee ist, dass eine Story, sofern diese Tasks und Reviewer hat, die Reviewer nur dann ihr Review absegnen können, wenn alle Tasks abgeschlossen sind. Sicherlich eine kleinere Erweiterung, aber es hat ausreichende API Zugriffe, und wird weitere Erkenntnisse zum Debuggen und Profilen liefern.