Mittwoch, 26. Oktober 2011

Der schöne sudo-Dialog mit Ubuntu & GNOME3 - pkexec

Manche Anwendungen möchte man einfach als root starten. Beispielsweise benötige ich einen zweiten Texteditor (Gedit) mit root-Rechten um bequem Systemeinstellungen zu ändern.

Bisher habe ich diese Anwendungen unter Gnome 2 und 3 mit gksudo gestartet. Dazu habe ich die Datei gedit-root.desktop angelegt:

[Desktop Entry]
Name=gedit (root)
GenericName=Text Editor (root)
Comment=Edit text files
Exec=gksodu /usr/bin/gedit
Terminal=false
Type=Application
StartupNotify=true
MimeType=text/plain;
Icon=accessories-text-editor
Categories=GNOME;GTK;Utility;TextEditor;
X-GNOME-DocPath=gedit/gedit.xml
X-GNOME-FullName=Text Editor (root)
X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gedit
X-GNOME-Bugzilla-Component=general
X-GNOME-Bugzilla-Version=3.0.0
X-GNOME-Bugzilla-ExtraInfoScript=/usr/share/gedit-2/gedit-bugreport
X-Ubuntu-Gettext-Domain=gedit

Der Dialog zur Autorisierung sieht dann ähnlich wie dieser aus:


Startet man unter GNOME 3 Anwendungen wie synaptic, fällt sofort der neue Dialog auf um diese Anwendung als root zu auszuführen.


Dieser Dialog wird mit dem Befehl pkexec aufgerufen. In der Datei gedit-root.desktop wird der Aufruf wie folgt geändert:

Exec=pkexec "/usr/bin/gedit"

Das genügt jedoch noch nicht ganz. Es muss noch eine Policy-Datei für den Aufruf angelegt werden (siehe http://hal.freedesktop.org/docs/polkit/pkexec.1.html). Diese Datei sieht dann wie folgt aus:



 
  Run Gedit as root
  Gedit als root starten
  Authentication is required to run Gedit
  Authorisierung ist erforderlich um Gedit zu starten
  gedit
  
  
   auth_admin
   auth_admin
   auth_admin
  

  /usr/bin/gedit
  true
 

Der Name der Datei ergibt sich aus der action id und der Endung policy. Sie wird dann unter "/usr/share/polkit-1/actions/de.aonnet.pkexec.gedit.policy" gespeichert.

Jetzt funktioniert der pkexec Aufruf und es erscheint der gewünschte Dialog:


Mittwoch, 19. Oktober 2011

Epiphany Webanwendungen mit eigenen Icons

Mit dem GNOME Browser Epiphany können Webanwendungen als eigene Anwendungen bereitgestellt werden. Diese können dann wie normale Anwendungen in einem eigenene Fenster gestartet werden (siehe Anmerkungen zur GNOME-Veröffentlichung 3.2). Für mich ist das zum Beispiel bei Google+ interessant, denn ich möchte Google+ die ganze Zeit unabhängig vom Browser (und Abstürzen) laufen lassen.

In Ubuntu 11.10 muss der Browser Epiphany 3.2.0 nachinstalliert werden. Das ist beispielsweise aus dem WebUpd8 GNOME 3 PPA möglich (siehe Things To Tweak After Installing Ubuntu 11.10 Oneiric Ocelot).

sudo add-apt-repository ppa:webupd8team/gnome3
sudo apt-get update
sudo apt-get install epiphany-browser

Dann Epiphany öffnen, gewünschte Webseite aufrufen und als Webanwendung speichern (Datei -> "Als Webanwendung speichern"). Der Name der neuen Anwendung kann jetzt geändert werden, das Icon leider nicht. Dies kann aber nach dem Speichern manuell erfolgen.


Ersteinaml ein passendes Icon besorgen (für Google+ zum Beispiel hier) und speichern. Jetzt genügt es folgendes Icon auszutauschen:


<USER_HOME>/.gnome2/epiphany/app-epiphany-<NAME_DER_ANWENDUNG>-<UUID>/app-icon.png


Das Ergebnis sieht in GNOME 3.2 so aus:


Die "Anwendung" Google+ kann jetzt wie jede andere Anwendung gestartet werden.

Mittwoch, 20. Juli 2011

Merkzettel: @NotNull Check mit aspectj

Möchte man nicht mit assert arbeiten um Parameter auf null zu prüfen, bietet sich aspectj an um einen komfortablen NotNull-Check zu implementieren. Vor allem wenn aspectj schon im Projekt vorhanden ist.

Anforderung: Alle Parameter in Methoden und Konstruktoren, die die Annotation @NotNull besitzen sollen geprüft werden. Wenn ein so markierter Parameter null ist, soll eine IllegalArgumentException ausgelöst werden.

Hier die Beispielimplementierung:

Als erstes die Annotation @NotNull für die zu prüfenden Parameter:

@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {

  String parameterName() default "";
}

Die Annotation @NotNull besitzt das Attribut parameterName. Hier kann der Name des Parameters übergeben werden. Dieser wird für die Fehlermeldung verwendet.



Dann benötigen wir einen Aspect, der alle Methoden und Konstruktoren mit der Annotation @NotNull "trifft".

Pointcut für alle Methoden mit der Annotation @NotNull: execution(* *(..,@NotNull (*),..))

Pointcut für alle Konstruktoren mit der Annotation @NotNull: execution(new(..,@NotNull (*),..))

@Aspect
public class CheckArgumentsAspect {

  /**
   * Der Pointcut trifft auf alle Methoden in allen Packages zu, die
   * mindestens einen Parameter mit der Annotation {@link NotNull} besitzen.
   */
  @Around("execution(* *(..,@NotNull (*),..))")
  public Object checkArgsForMethod(ProceedingJoinPoint joinPoint)
      throws Throwable {

    if (joinPoint.getSignature() instanceof MethodSignature) {

      MethodSignature methodSignature = (MethodSignature) joinPoint
          .getSignature();
      Method method = methodSignature.getMethod();
      Class<?>[] parameterTypes = method.getParameterTypes();
      Annotation[][] parameterAnnotationArray = method
          .getParameterAnnotations();

      System.out.println("Prüfe Parameter der Methode '"
          + methodSignature.toLongString() + "' mit den Parameterwerten '"
          + joinPoint.getArgs() + "'.");

      // Prüft alle Parameter
      checkParameter(joinPoint.getArgs(), parameterTypes,
          parameterAnnotationArray, methodSignature.toLongString());
    }

    return joinPoint.proceed();
  }

  /**
   * Der Pointcut trifft auf alle Konstruktoren zu, die mindestens einen
   * Parameter mit der Annotation {@link NotNull} besitzen.
   */
  @Around("execution(new(..,@NotNull (*),..))")
  public Object checkArgsForConstructor(ProceedingJoinPoint joinPoint)
      throws Throwable {

    if (joinPoint.getSignature() instanceof ConstructorSignature) {

      ConstructorSignature constructorSignature = 
          (ConstructorSignature) joinPoint.getSignature();
      Constructor<?> constructor = constructorSignature.getConstructor();
      Class<?>[] parameterTypes = constructor.getParameterTypes();
      Annotation[][] parameterAnnotationArray = constructor
          .getParameterAnnotations();

      System.out.println("Prüfe Parameter des Konstruktors '"
          + constructorSignature.toLongString()
          + "' mit den Parameterwerten '" + joinPoint.getArgs() + "'.");

      // Prüft alle Parameter
      checkParameter(joinPoint.getArgs(), parameterTypes,
          parameterAnnotationArray, constructorSignature.toLongString());
    }

    return joinPoint.proceed();
  }

  /**
   * Prüft alle Parameter.
   * 
   * @param parameter
   *          Alle Parameter.
   * @param parameterTypes
   *          Die Typen zu den Parametern.
   * @param parameterAnnotationArray
   *          Die Annotations zu den Parametern.
   * @param signature
   *          Die Signatur der Methode oder des Konstruktors.
   */
  private void checkParameter(Object[] parameter, Class<?>[] parameterTypes,
      Annotation[][] parameterAnnotationArray, String signature) {

    for (int i = 0; i < parameterTypes.length; i++) {

      Annotation[] parameterAnnotations = parameterAnnotationArray[i];

      for (Annotation annotation : parameterAnnotations) {

        if (annotation instanceof NotNull) {

          // Hier wird das Argument auf null geprüft
          checkNotNull(parameter[i], parameterTypes[i], i,
              ((NotNull) annotation).parameterName(), signature);
        }
      }
    }
  }

  /**
   * Prüft den Parameter auf null.
   * 
   * @param parameter
   *          Der Parameter.
   * @param parameterType
   *          Der Typ des Parameters.
   * @param parameterIndex
   *          Der Index des Parameters.
   * @param parameterName
   *          Der Name des Parameters.
   * @param signature
   *          Die Signatur der Methode oder des Konstruktors.
   */
  private void checkNotNull(Object parameter, Class<?> parameterType,
      int parameterIndex, String parameterName, String signature) {

    // Hier wird das Argument auf null geprüft
    if (parameter == null) {

      if (StringUtils.isBlank(parameterName)) {
        parameterName = "-";
      }

      String longMsg = MessageFormat.format(
          "Fehler: Der {0}. Parameter (Name: {1}, Typ: {2}) von {3} "
              + "ist null.", parameterIndex + 1, parameterName,
          parameterType.getName(), signature);
      throw new IllegalArgumentException(longMsg);
    }
  }
}


Eine Klasse mit zu prüfenden Parametern könnte zum Beispiel so aussehen:

public class TestClass {

  public TestClass() {
    super();
  }

  public TestClass(@NotNull(parameterName = "kNotNull") Integer kNotNull) {
    super();
    System.out.println("    kNotNull: " + kNotNull);
  }

  public void methodWithOneNotNullArg(
      @NotNull(parameterName = "aNotNull") Integer aNotNull) {
    System.out.println("    aNotNull: " + aNotNull);
  }

  public void methodWithOneNotNullArg(Long b,
      @NotNull(parameterName = "cNotNull") Integer cNotNull) {
    System.out.println("    b       : " + b);
    System.out.println("    cNotNull: " + cNotNull);
  }

  public void methodWithTwoNotNullArg(
      @NotNull(parameterName = "dNotNull") Integer dNotNull,
      @NotNull Integer eNotNull) {
    System.out.println("    dNotNull: " + dNotNull);
    System.out.println("    eNotNull: " + eNotNull);
  }

  public void methodWithoutNotNullArg(Long f) {
    System.out.println("    f       : " + f);
  }
}


Jetzt sollten alle Parameter mit der Annotation @NotNull geprüft werden. Hier noch der passende JUnit-Test dazu:

public class TestClassTest {

  private TestClass testClass = new TestClass();

  @Test
  public void testMethodWithOneNotNullArgInteger() {
    testClass.methodWithOneNotNullArg(1);
  }

  @Test
  public void testMethodWithOneNotNullArgIntegerNull() {
    try {
      testClass.methodWithOneNotNullArg(null);
      Assert.fail("Hier wurde eine IllegalArgumentException erwartet.");
    } catch (IllegalArgumentException e) {
      Assert.assertNotNull(e);
      System.err.println("    " + e.getMessage());
    }
  }

  @Test
  public void testMethodWithOneNotNullArgIntegerInteger() {
    testClass.methodWithOneNotNullArg(11l, 11);
  }

  @Test
  public void testMethodWithOneNotNullArgIntegerIntegerNull1() {
    testClass.methodWithOneNotNullArg(null, 11);
  }

  @Test
  public void testMethodWithOneNotNullArgIntegerIntegerNull2() {
    try {
      testClass.methodWithOneNotNullArg(11l, null);
      Assert.fail("Hier wurde eine IllegalArgumentException erwartet.");
    } catch (IllegalArgumentException e) {
      Assert.assertNotNull(e);
      System.err.println("    " + e.getMessage());
    }
  }

  @Test
  public void testMethodWithOneNotNullArgIntegerIntegerNullNull() {
    try {
      testClass.methodWithOneNotNullArg(null, null);
      Assert.fail("Hier wurde eine IllegalArgumentException erwartet.");
    } catch (IllegalArgumentException e) {
      Assert.assertNotNull(e);
      System.err.println("    " + e.getMessage());
    }
  }

  @Test
  public void testMethodWithTwoNotNullArg() {
    testClass.methodWithTwoNotNullArg(111, 111);
  }

  @Test
  public void testMethodWithTwoNotNullArgNull1() {
    try {
      testClass.methodWithTwoNotNullArg(null, 222);
      Assert.fail("Hier wurde eine IllegalArgumentException erwartet.");
    } catch (IllegalArgumentException e) {
      Assert.assertNotNull(e);
      System.err.println("    " + e.getMessage());
    }
  }

  @Test
  public void testMethodWithTwoNotNullArgNull2() {
    try {
      testClass.methodWithTwoNotNullArg(222, null);
      Assert.fail("Hier wurde eine IllegalArgumentException erwartet.");
    } catch (IllegalArgumentException e) {
      Assert.assertNotNull(e);
      System.err.println("    " + e.getMessage());
    }
  }

  @Test
  public void testMethodWithTwoNotNullArgNullNull() {
    try {
      testClass.methodWithTwoNotNullArg(null, null);
      Assert.fail("Hier wurde eine IllegalArgumentException erwartet.");
    } catch (IllegalArgumentException e) {
      Assert.assertNotNull(e);
      System.err.println("    " + e.getMessage());
    }
  }

  @Test
  public void testMethodWithoutNotNullArg() {
    testClass.methodWithoutNotNullArg(null);
  }

  @Test
  public void testMethodWithoutNotNullArgNull() {
    testClass.methodWithoutNotNullArg(null);
  }

  @Test
  public void testConstructorWithNotNullArg() {
    new TestClass(999);
  }

  @Test
  public void testConstructorWithNotNullArgNull() {
    try {
      new TestClass(null);
      Assert.fail("Hier wurde eine IllegalArgumentException erwartet.");
    } catch (IllegalArgumentException e) {
      Assert.assertNotNull(e);
      System.err.println("    " + e.getMessage());
    }
  }
}

Und jetzt oft @NotNull verwenden und weitere Annotations zum Prüfen von Argumenten erstellen!

Das Projekt befindet sich hier.

Mittwoch, 6. Juli 2011

Merkzettel: Java Prozesse beenden

Auch Java-Programme müssen manchmal mit Systemmitteln beendet werden. Dafür benötigt man die Prozess-ID PID. Diese kann beispielsweise auf einem UNIX oder LINUX-System mit dem Befehl ps angezeigt werden. Aber der Befehl führt oft nicht zum gewünschten Ergebnis:
> ps -A |grep java
  2016 ?           2:41 java
  5875 pts/2       0:05 java
 29278 ?           2:42 java
 26519 ?           0:23 java
 26623 ?           0:28 java
 26480 ?           0:24 java
 26146 ?           2:36 java
 26568 ?           0:24 java
 26668 ?           0:34 java
 26667 ?           1:10 java
  5588 pts/2       4:07 java
 26145 ?           0:10 java
  
Um herauszufinden, welcher Java-Prozess beendet werden soll bietet sich der Befehl <JAVA_HOME>/bin/jps an:
> /opt/java/jdk1.5/bin/jps
5996 Jps
26667 jar
5588 start.jar
5875 Main
29278 start.jar
5995 Main
  
Mehr Informationen erhält man mit den Optionen -v für die Argumente und -l für den Package Namen der Main-Klasse.
> /opt/java/jdk1.5/bin/jps -lv
6160 sun.tools.jps.Jps -Dapplication.home=/opt/regr/java/jdk1.5 -Xms8m
26667 XyzServer.jar
29278 /opt/jetty7.1.6/start.jar -Xmx1024M -Xms512M -Djetty.port=8080 -DSTOP.PORT=8079
  
Jetzt weiß ich das zu meinen Jetty-Server (mit Port 8080) die PID 29278 gehört!
> kill -9 29278
Und weg ist er! 

Dienstag, 21. Juni 2011

Grails Cloud Foundry Integration

Trotz kleiner Fallen geht es sehr einfach, eine Gails-App auf Cloud Foundry laufen zu lassen, denn es gibt ja für alles ein Grails Plugin. Hier zum Beispiel das Plugin Cloud Foundry Integration von Burt Beckwith.

Ich habe das ganze mal mit Eclipse (STS) + Grails-Plugin ausprobiert, geht aber genauso auf der Kommandozeile. Hier die notwendigen Schritte:
  1. Bei Cloud Foundry anmelden
  2. Neues Grails-Projekt cftwest
  3. Grails Command Prompt: install-plugin cloud-foundry
  4. Anmeldedaten von Cloud Foundry in der Datei /cftwest/grails-app/conf/Config.groovy eintragen:

    grails.plugin.cloudfoundry.username = 'xyz@xyz.de'
    grails.plugin.cloudfoundry.password = 'xyz'
  5. Ein kleiner Test:
    Grails Command Prompt: cf-info

    VMware's Cloud Application Platform
    For support visit support@cloudfoundry.com

    Target:   http://api.cloudfoundry.com (v0.999)

    User:     xyz@xyz.de
    Usage:    Memory   (512,0M of 2,0G total)
              Services (1 of 16 total)
              Apps     (1 of 20 total)
  6. Und deployen:
    • Grails Command Prompt: prod cf-push
    • Auf die Frage nicht mit y sondern mit der URL antworten:
      Application Deployed URL: 'cftwest.cloudfoundry.com'?
      cftwest.cloudfoundry.com
    • Service wird noch nicht benötigt:
      Would you like to bind the 'mysql-68c6b11' service? ([y], n)
      n
  7. Jetzt unter http://cftwest.cloudfoundry.com/ aufrufen!
  8. Update der Anwendung mit:
    Grails Command Prompt: prod cf-update

Weitere Infos zum Plugin findet man unter Cloud Foundry Plugin - Reference Documentation und weitere Links zum Thema in Graeme Rocher's Blog.

Als nächstes werde ich das mal zusammen mit CouchDB und Cloudant ausprobieren...