Synchronized Sets mit Scala

Ein Snippet zum Frühstück:

Bei mir hat sich gerade die Notwendigkeit ergeben, in Scala mit einem mutable HashSet in einem Multithreading-Context zu arbeiten.

Um dieses Threadsafe zu machen kann man das HashSet mit dem Trait SynchronizedSet instanzieren:

val mutableThreadsafeSet = new HashSet[Long] with SynchronizedSet[Long]

Ich liebe diese Sprache <3

Mit Spring den Classpath durchsuchen

Für eine Plugin-Engine musste ich heute den Classpath nach Klassen durchsuchen, die von einer abstrakten Plugin-Klasse erben. Von den gefundenen Klassen sollte aber keine Instanz erzeugt werden, eine Erweiterung des <context:component-scan> kam daher nicht in Frage. Die Lösung für das Problem ist die Klasse ClassPathScanningCandidateComponentProvider:

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

    @SuppressWarnings("unchecked")
    public static Set<Class<? extends AbstractPlugin>> getAvailablePlugins() {

        ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(false);
        componentProvider.addIncludeFilter(new AssignableTypeFilter(AbstractPlugin.class));

        Collection beanDefinitions = componentProvider.findCandidateComponents("my.plugins.package");

        Set<Class<? extends AbstractPlugin>> set = new HashSet<Class<? extends AbstractPlugin>>();

        for(BeanDefinition beanDefinition : beanDefinitions){
            try {
                set.add((Class<? extends AbstractPlugin>) Class.forName(beanDefinition.getBeanClassName()));
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }

        return set;
    }

}

Diese Lösung benötigt keinen Spring-Context. Oliver Gierke hat mir den entscheidenen Hinweis auf den ClassPathScanningCandidateComponentProvider gegeben, vielen Dank!

Thread.isInterrupted() gibt immer false zurück

Ich bin beim Implementieren von Lasttests über einen sehr fiesen Java-Bug im Multithreadingbereich gestoßen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Thread t = new Thread(new Runnable(){

    @Override
    public void run() {
        while(Thread.currentThread().isInterrupted()){
            System.out.println("running into take");
            try {
                System.out.println("received: " + blockingQueue.take());
            } catch (InterruptedException e) {
                System.out.println("caught interrupted exception.");
            }
        }
    }

});

t.start();

blockingQueue.add("foo");

t.interrupt();

Bei diesem Code hätte ich erwartet, dass das t.interrupt() dafür sorgt, dass ein blockendes blockingQueue.take() in den catch-Block springt und danach der Thread terminiert, da Thread.currentThread().isInterrupted() true zurückgibt. Bei mir springt, bei installiertem Java 1.6 32 / 64 Bit, der Thread nach dem catch sofort wieder zurück zu blockingQueue.take() - weil Thread.currentThread.isInterrupted() immer false zurückgibt.

Über Stackoverflow bin ich auf folgenden JVM-Bug gestoßen:

http://bugs.sun.com/view_bug.do?bug_id=6772683

Um einen Thread sicher über ein Thread.interrupt() zu beenden und dabei den Bug zu umgehen verwendet man am besten eine Shutdown-Flag:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Thread t = new Thread(new Runnable(){

    private boolean shutdown = false;

    @Override
    public void run() {
        while(!shutdown){
            System.out.println("running into take");
            try {
                System.out.println("received: " + blockingQueue.take());
            } catch (InterruptedException e) {
                System.out.println("caught interrupted exception.");
                shutdown = true;
            }
        }
    }

});

t.start();

blockingQueue.add("foo");

t.interrupt();

Damit wird der Thread sauber beendet. Warum der Bug im aktuellen JDK 1.6 immer noch nicht behoben ist, ist mir ein Rätsel. Wie es mit Java 7 / Windows aussieht konnte ich noch nicht testen.

TinyMCE Mixin in Tapestry 5 - Version 2

In der ersten Version des TinyMCE-Mixins wurde der der Javascript-Code der zum laden benötigt wird noch über den MarkupWriter in der @BeginRender-Methode in den Code geschrieben. Tapestry 5 stellt für das Ausführen von Javascript-Code in einer Component den “JavaScriptSupport” (injectbares Interface) zur verfügung. Über diesen Javascript-Support kann man JS-Code in die Seite schreiben der dann an der richtigen Stelle geladen wird. Man braucht so keine <script>-Tags mehr und Tapestry stellt sicher, dass das Javascript an der richtigen Stelle aufgerufen wird.

In der ersten Version wurde auch der gesamte Init-Javascript-Code (Mehrzeiler) in den Markup-Writer geschrieben. Es bietet sich an, diesen Code noch mal durch eine einfache Funktion zu ersetzen, die man in einer externen Javascript-Datei definiert die dann von Tapestry geladen (und komprimiert etc.) werden kann.

JS-Datei:

1
2
3
4
5
6
7
function enableMCEAsMixin(element){
    tinyMCE.init({
        mode : "exact",
        theme_advanced_resizing : true,
        elements: element
    });
}

TinyMCE-Mixin:

 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
package de.metacoder.blog.mixins;

import org.apache.tapestry5.annotations.BeginRender;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.InjectContainer;
import org.apache.tapestry5.corelib.components.TextArea;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;

@Import(library = {
        "context:scripts/tiny_mce/tiny_mce.js",
        "context:scripts/tiny_mce/tiny_mce_mixin_init.js"
        })
public class TinyMCE {

    @InjectContainer
    private TextArea textArea;

    @Environmental
    private JavaScriptSupport javaScriptSupport;

    @BeginRender
    public void enableTinyMCE() {
        javaScriptSupport.addScript("enableMCEAsMixin('%s');", textArea.getClientId());
    }
}

TinyMCE Mixin in Tapestry 5

Um das Schreiben von Blog-Einträgen komfortabler zu gestalten haben wir TinyMCE in unser Blog eingebunden. Für TinyMCE müssen die TinyMCE-Javascripts in der Seite geladen und dann ein tinyMCE.init aufgerufen werden.

Bei Tapestry bietet sich hierfür ein Mixin an um nicht auf jeder Seite den Import der JS-Lib und den Init-Call einbauen zu müssen.

Das Mixin wird in Tapestry-Manier im Package “mixins” erstellt und heißt bei uns TinyMCE.

 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
package de.metacoder.blog.mixins;

import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.annotations.BeginRender;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.InjectContainer;
import org.apache.tapestry5.corelib.components.TextArea;

@Import(library = { "context:scripts/tiny_mce/tiny_mce.js" })
public class TinyMCE {

        @InjectContainer
        private TextArea textArea;

        @BeginRender
        public void enableTinyMCE(final MarkupWriter markupWriter) {
                markupWriter.writeRaw(
                        "<script type=\"text/javascript\">\n" +
                        "       tinyMCE.init({\n" +
                        "               mode : \"exact\",\n" +
                        "               elements: \"" + textArea.getClientId() + "\"\n" +
                        "       });\n" +
                        "</script>"
                );
        }
}

Sobald man dieses Mixin erstellt hat, kann man es im Template bei einer Textarea als Mixin angeben:

<t:textarea t:mixins="tinyMCE" t:id="content" t:value="blogEntry.content" />

Das sorgt dann automatisch dafür, dass die TinyMCE-Scripts in der Seite richtig geladen und der Editor initialisiert wird.