Simple hierarchical popupmenu for Node

I recently got a question how you can organize your Node’s popup menus hierarchically, while keeping registration declarative. Organizing your actions hierachically in the Layer and then using Lookups.forPath().lookupAll(Action.class) doesn’t work, the result is a flat list. So here’s the simplest version I know:

AbstractNode example = new AbstractNode(Children.LEAF) {
            @Override
            public Action[] getActions(boolean context) {
                return findActionsForFolder(FileUtil.getConfigFile("MYPATH"));
            }

            protected Action[] findActionsForFolder(FileObject folder) {
                ArrayList actions = new ArrayList();
                if (folder != null && folder.isFolder()) {

                FileObject[] children = folder.getChildren();
                for (FileObject fileObject : children) {
                    if (fileObject.isFolder()) {
                        Action[] findActionsForFolder = findActionsForFolder(fileObject);
                        Action menu = new MenuAction(fileObject.getName(), findActionsForFolder);
                        actions.add(menu);
                    } else {
                        try {
                            Action action = (Action) DataObject.find(fileObject).getLookup().lookup(InstanceCookie.class).instanceCreate();
                            actions.add(action);
                        } catch (IOException ex) {
                            Exceptions.printStackTrace(ex);
                        } catch (ClassNotFoundException ex) {
                            Exceptions.printStackTrace(ex);
                        }
                    }
                }
}
                return actions.toArray(new Action[actions.size()]);
            }
        };

I’ll leave it as an exercise for you to change the code to use InstanceCookie.Of…
And here’s the MenuNode:

class MenuAction extends AbstractAction implements Presenter.Popup, Presenter.Menu {

Action[] actions;
String name;
public JMenu menu;

public MenuAction(String name, Action[] findActionsForFolder) {
super(name);
this.name = name;
this.actions = findActionsForFolder;
this.menu = new JMenu(name);
for (Action action : actions) {
if (action instanceof Presenter.Popup) {
menu.add(((Presenter.Popup)action).getPopupPresenter());
} else {
menu.add(action);
}

}
}

@Override
public void actionPerformed(ActionEvent e) {
}

@Override
public JMenuItem getPopupPresenter() {

return menu;
}

@Override
public JMenuItem getMenuPresenter() {
return menu;
}
}

That’s it. Now register your actions under MYPATH in hierarchies, e.g.:

@ActionID(
category = "Build",
id = "asd.SomeAction")
@ActionRegistration(
displayName = "#CTL_SomeAction")
@ActionReference(path = "MYPATH/mysubcategory/usd", position = 0)
@Messages("CTL_SomeAction=asd")
public final class SomeAction implements ActionListener {

@Override
public void actionPerformed(ActionEvent e) {
// TODO implement action body
}
}

A star is born

I’ve just implemented the A* algorithm for the TileEngine. Might come handy for calculating the attack path in a “Tower Defense” style game, or for moving to  a location in touch based games like “Emily”.

import de.eppleton.fx2d.tileengine.TileMap;
import de.eppleton.fx2d.tileengine.TileMapLayer;
import java.util.*;

public class AStar {

    public static PathNode getPath(TileMap map, TileMapLayer layer, AStarTile start, AStarTile goal) {

        boolean[][] playField = new boolean[map.getWidth()][map.getHeight()];
        for (int x = 0; x < playField.length; x++) {
            boolean[] bs = playField[x];
            for (int y = 0; y < bs.length; y++) {
                int idx = x + (y * map.getWidth());
                bs[y] = layer.getGid(idx) == 0;
            }
        }

        PriorityQueue<PathNode> openList = new PriorityQueue<>();
        List<PathNode> closedList = new ArrayList<>();
        PathNode stn = new PathNode(null, 0, 0, start.x, start.y);
        openList.add(stn);
        while (!openList.isEmpty()) {
            PathNode toExpand = openList.poll();
            List<PathNode> successors = new ArrayList<>();
            int ex = toExpand.getX();
            int ey = toExpand.getY();
            addSuccessor(playField, ex - 1, ey, toExpand, goal, successors);
            addSuccessor(playField, ex, ey - 1, toExpand, goal, successors);
            addSuccessor(playField, ex + 1, ey, toExpand, goal, successors);
            addSuccessor(playField, ex, ey + 1, toExpand, goal, successors);
            for (PathNode candidate : successors) {
                if (candidate.x == goal.x && candidate.y == goal.y) {
                    return candidate;
                }
                if (alreadyFound(candidate, closedList)) {
                    continue;
                }
                if (alreadyFound(candidate, openList)) {
                    continue;
                }
                openList.add(candidate);

            }
            closedList.add(toExpand);
        }
        return null;
    }

    public static void addSuccessor(boolean[][] playField, int ex, int ey, PathNode toExpand, AStarTile goal, List<PathNode> successors) {
        if (ex < 0 || ey < 0 || ex >= playField.length || ey >= playField[ex].length) {
            return;
        }

        if (playField[ex][ey]) {
            PathNode n = new PathNode(toExpand, ex, ey);
            n.g = g(n);
            n.h = h(n, goal);
            n.f = n.g + n.h;
            successors.add(n);
        }
    }

    private static boolean alreadyFound(PathNode n, Collection<PathNode> l) {
        for (PathNode no : l) {
            if (no.getX() == n.getX() && no.getY() == n.getY() && no.getF() <= n.getF()) {
                return true;
            }
        }
        return false;
    }

    private static float g(PathNode n) {
        PathNode p = n.getParent();
        return p.g + 1;
    }

    private static float h(PathNode act, AStarTile goal) {
        int distX = Math.abs(act.getX() - goal.x);
        int distY = Math.abs(act.getY() - goal.y);
        float ret = (float) Math.sqrt(distX * distX + distY * distY);
        return ret;
    }

    public static class AStarTile {

        int x, y;

        public AStarTile(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    public static class PathNode implements Comparable<PathNode> {

        private PathNode parent;
        private float f, g, h;

        public PathNode getParent() {
            return parent;
        }
        private int x, y;

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        float getF() {
            return f;
        }

        private PathNode(PathNode parent, float g, float h, int x, int y) {
            this.parent = parent;
            this.x = x;
            this.y = y;
            this.g = g;
            this.h = h;
            this.f = g + h;
        }

        private PathNode(PathNode parent, int x, int y) {
            this(parent, 0, 0, x, y);
        }

        @Override
        public int compareTo(PathNode o) {
            return f > o.f ? 1 : f == o.f ? 0 : -1;
        }
    }
}

Here I use it in the tilemap editor:

And here in a game prototype:

Placing obstacles forces recalculation (the guys who get stuck when I place a rock can’t find a path ’cause the start tile is invalid):

 

 

Customizing JavaFX ListView

I’ve started to create a TileMap Editor for my JavaFX game engine. In this editor I’ve got a ListView that displays the Layers. Each Layer is represented by it’s name and a CheckBox is displayed next to it, so you can choose if you want to show or hide the layer:

The Layer List of my TileMapEditor

But simply adding the TileMapLayer objects into a ObservableList and displaying them with a ListView will result in this:

Displaying a TileMapLayer in a ListView with default rendering

In order to display the CheckBox, we need to do this:

layerList = FXCollections.observableArrayList(tileMap.getLayers());
        listView = new ListView<TileMapLayer>();
        listView.setItems(layerList);
        if (!layerList.isEmpty()) {
            listView.getSelectionModel().select(layerList.get(0));
        }
        Callback<TileMapLayer, ObservableValue<Boolean>> getProperty = new Callback<TileMapLayer, ObservableValue<Boolean>>() {
            @Override
            public BooleanProperty call(TileMapLayer layer) {

                return layer.getVisibleProperty();

            }
        };
        Callback<ListView<TileMapLayer>, ListCell<TileMapLayer>> forListView = CheckBoxListCell.forListView(getProperty);

        listView.setCellFactory(forListView);

Resulting in this:

Using a CheckBoxListCell to display the Layers

Now we only need to show some more meaningful text. You can do that by adding a StringConverter. Fortunately the factory method takes a StringConverter as it’s second argument:

layerList = FXCollections.observableArrayList(tileMap.getLayers());
        listView = new ListView<TileMapLayer>();
        listView.setItems(layerList);
        if (!layerList.isEmpty()) {
            listView.getSelectionModel().select(layerList.get(0));
        }
        Callback<TileMapLayer, ObservableValue<Boolean>> getProperty = new Callback<TileMapLayer, ObservableValue<Boolean>>() {
            @Override
            public BooleanProperty call(TileMapLayer layer) {

                return layer.getVisibleProperty();

            }
        };
        StringConverter stringConverter = new StringConverter<TileMapLayer>() {
            @Override
            public String toString(TileMapLayer t) {
                return t.getName();
            }

            @Override
            public TileMapLayer fromString(String string) {
                for (TileMapLayer tileMapLayer : layerList) {
                    if (string.equals(tileMapLayer.getName())) {
                        return tileMapLayer;
                    }
                }
                return null;
            }
        };
        Callback<ListView<TileMapLayer>, ListCell<TileMapLayer>> forListView = CheckBoxListCell.forListView(getProperty, stringConverter);

        listView.setCellFactory(forListView);

That’s it, now the TileMapLayer will be displayed as desired:

 

And here’s a video of the Application in action:

Space Invaders in less than 175 LOC

With the current version of the API I’m at less than 175 LOC for Space Invaders. I’ve got lot’s of “Functional Interfaces” in my APIs that can be converted to Lambda Expressions with JavaFX 8( e.g. SpriteProvider and CollisionHandler). That will make the code nicer and shorter. I could probably also reduce the linecount by bundling the recources (e.g. TileSets) and creating more factories and Builders (SpriteBuilder). But I’m getting closer to what I want…

package de.eppleton.fx2d.samples.spaceinvaders;

import de.eppleton.fx2d.collision.*;
import de.eppleton.fx2d.*;
import de.eppleton.fx2d.action.*;
import de.eppleton.fx2d.tileengine.*;
import java.util.Collection;
import java.util.logging.*;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.canvas.*;
import javafx.scene.input.*;
import javafx.scene.media.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javax.xml.bind.JAXBException;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;

public class SpaceInvaders extends Game {

    Points TEN = new Points(10);
    Points TWENTY = new Points(30);
    Points THIRTY = new Points(40);
    DoubleProperty invaderXVelocity = new SimpleDoubleProperty(0.3);
    AudioClip shootSound = new AudioClip(SpaceInvaders.class.getResource("/assets/sound/shoot.wav").toString());
    AudioClip invaderKilledSound = new AudioClip(SpaceInvaders.class.getResource("/assets/sound/invaderkilled.wav").toString());
    MediaPlayer mediaPlayer = new MediaPlayer(new Media(SpaceInvaders.class.getResource("/assets/sound/invader_loop1.mp3").toString()));
    int score = 0;
    String message;
    int[][] enemies = new int[][]{
        {30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},
        {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20},
        {20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20},
        {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
        {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}
    };

    @Override
    protected void initGame() {
        final GameCanvas canvas = getCanvas();
        try {
            TileSet invaders = TileMapReader.readSet("/assets/graphics/invaders1.tsx");
            TileSet playerTiles = TileMapReader.readSet("/assets/graphics/player.tsx");
            final TileSetAnimation animation30 = new TileSetAnimation(invaders, new int[]{0, 1}, 2);
            final TileSetAnimation animation10 = new TileSetAnimation(invaders, new int[]{4, 5}, 2);
            final TileSetAnimation animation20 = new TileSetAnimation(invaders, new int[]{2, 3}, 2);
            final TileSetAnimation playerAnimation = new TileSetAnimation(playerTiles, new int[]{0}, 100_000);
            for (int i = 0; i < enemies.length; i++) {
                int[] is = enemies[i];
                for (int j = 0; j < is.length; j++) {
                    Points points = is[j] == 30 ? THIRTY : is[j] == 20 ? TWENTY : TEN;
                    Sprite sprite = new Sprite(canvas, "" + ((j * 11) + i), 50 + (40 * j), 140 + (40 * i), 30, 20, Lookups.fixed(points));
                    sprite.setAnimation(is[j] == 30 ? animation30 : is[j] == 20 ? animation20 : animation10);
                    sprite.setVelocityXProperty(invaderXVelocity);
                }
            }
            Sprite player = new Sprite(canvas, playerAnimation, "Player", 350, 620, 40, 30, Lookup.EMPTY);
            player.setAnimation(playerAnimation);
            player.addAction(KeyCode.LEFT, ActionFactory.createMoveAction(playerAnimation, "left", -4, 0, 0, 0));
            player.addAction(KeyCode.RIGHT, ActionFactory.createMoveAction(playerAnimation, "right", 4, 0, 0, 0));
            player.addAction(KeyCode.UP, new ShootAction(playerAnimation, "fire", new BulletProvider(), new HitHandler(), shootSound));
        } catch (JAXBException ex) {
            Logger.getLogger(SpaceInvaders.class.getName()).log(Level.SEVERE, null, ex);
        }
        canvas.addLayer(new Background());
        canvas.addBehaviour(new MoveInvadersBehavior());
        mediaPlayer.setCycleCount(MediaPlayer.INDEFINITE);
        mediaPlayer.play();
        canvas.addLayer(new SpriteLayer());
        canvas.start();
    }

    @Override
    protected double getViewPortWidth() {
        return 700;
    }

    @Override
    protected double getViewPortHeight() {
        return 700;
    }

    public static void main(String[] args) {
        launch(args);
    }

    private class Points {

        int points;

        public Points(int points) {
            this.points = points;
        }

        public int getPoints() {
            return points;
        }
    }

    static class BulletProvider implements SpriteProvider {

        @Override
        public Sprite getSprite(GameCanvas parent, double x, double y) {
            return new Sprite(parent, "bullet", x, y + 10, 10, 20, Lookup.EMPTY);
        }
    }

    class HitHandler implements CollisionHandler {

        @Override
        public void handleCollision(Collision collision) {
            Points points = collision.getSpriteTwo().getLookup().lookup(Points.class);
            if (points != null) {
                score += points.getPoints();
                invaderKilledSound.play();
                collision.getSpriteOne().remove();
                collision.getSpriteTwo().remove();
            }
        }
    }

    class MoveInvadersBehavior extends Behavior {

        @Override
        public boolean perform(GameCanvas canvas, long nanos) {
            Collection<Sprite> sprites = canvas.getSprites();
            boolean stop = false;
            boolean win = true;
            for (Sprite sprite1 : sprites) {
                if (sprite1.getLookup().lookup(Points.class) != null) {
                    win = false;
                    if (sprite1.getX() > 650 || sprite1.getX() < 50) {
                        invaderXVelocity.set(-invaderXVelocity.doubleValue() * (stop ? 0 : 1.3));
                        if (sprite1.getY() >= 600) {
                            message = "Game Over!";
                            stop = true;
                            mediaPlayer.stop();
                        }
                        for (Sprite sprite2 : sprites) {
                            if (sprite2.getLookup().lookup(Points.class) != null) {
                                sprite2.setY(sprite2.getY() + (stop ? 0 : 20));
                            }
                        }
                        break;
                    }
                }
            }
            if (win) {
                message = "You win!";
                canvas.stop();
                mediaPlayer.stop();
            }
            return true;
        }
    }

    class Background extends Layer {        

        @Override
        public void draw(GraphicsContext graphicsContext, double x, double y, double width, double height) {
            graphicsContext.setFill(Color.BLACK);
            graphicsContext.fillRect(0, 0, width, height);
            graphicsContext.setFill(Color.WHITE);
            graphicsContext.setFont(Font.font("OCR A Std", 20));
            graphicsContext.fillText("SCORE<1>    HI-SCORE    SCORE<2>", 30, 30);
            graphicsContext.fillText("" + score + "            9990   ", 30, 60);
            graphicsContext.fillText(message, 300, 400);
            graphicsContext.fillText("" + 3 + "                   CREDIT " + 1, 30, 680);
            graphicsContext.setFill(Color.GREEN);
            graphicsContext.fillRect(30, 650, 640, 4);
        }
    }
}

Here’s a video of the game:

A minimal JavaFX Presentation (in JavaFX)

If you want to do a presentation about JavaFX, it’s very handy to do the presentation itself in JavaFX. This way you can easily show your examples without leaving the presentation. Here’s a very minimal example how to do that.

In NetBeans set up a new JavaFX Project “New Project” -> “JavaFX” -> “JavaFX Application” and name it “FXPresenter”. Now create the Slide class. It’s used to load an FXML file:

package fxpresenter;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.layout.AnchorPane;

public class Slide extends AnchorPane implements Initializable {

    public Slide(String slide) {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(slide));
        fxmlLoader.setRoot(this);
        fxmlLoader.setController(this);
        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
    }
}

Next we need a Presentation that contains the slides and switches between them:

package fxpresenter;

import java.util.ArrayList;
import java.util.List;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.stage.Screen;

public class Presentation extends Group {

    private List<Slide> slides;
    private int index;
    private Slide current;
    public EventHandler<KeyEvent> keyEventHandler;

    public Presentation() {
        this.slides = new ArrayList<>();
        keyEventHandler = new EventHandler<KeyEvent>() {
            public void handle(final KeyEvent keyEvent) {
                if (keyEvent.getCode() == KeyCode.LEFT) {
                    previousSlidePlease();
                } else if (keyEvent.getCode() == KeyCode.RIGHT) {
                    nextSlidePlease();
                }
            }
        };
    }

    public void addSlide(Slide slide) {
        addSlide(slides.size(), slide);
    }

    public void addSlide(int index, Slide slide) {
        slides.add(index, slide);
    }

    public void previousSlidePlease() {
        if (index > 0) {
            index--;
        }
        setSlide(index);
    }

    public void nextSlidePlease() {
        if (index < slides.size() - 1) {
            index++;
        }
        setSlide(index);
    }

    public void setSlide(int index) {
        if (current != null) {
            getChildren().remove(current);
            current.removeEventHandler(KeyEvent.KEY_PRESSED, keyEventHandler);
        }
        current = slides.get(index);
        current.addEventHandler(KeyEvent.KEY_PRESSED, keyEventHandler);

        scaleToFit();
        getChildren().add(slides.get(index));
        current.requestFocus();
    }

    void start() {
        index = -1;
        nextSlidePlease();
    }

    private void scaleToFit() {
        javafx.geometry.Rectangle2D screenBounds = Screen.getPrimary().getBounds();
        double prefWidth = current.getPrefWidth();
        double prefHeight = current.getPrefHeight();
        double scaleX = screenBounds.getWidth() / prefWidth;
        double scaleY = screenBounds.getHeight() / prefHeight;
        double centerX = (screenBounds.getWidth() / 2) - (prefWidth / 2);
        double centerY = (screenBounds.getHeight() / 2) - (prefHeight / 2);
        setTranslateX(centerX);
        setTranslateY(centerY);
        setScaleX(scaleX);
        setScaleY(scaleY);
    }
}

It should be pretty obvious what the code does: If you set a slide it will be scaled to fit the screen, and it will listen to key events. pressing the right arrow moves to the next slide, and the right arrow key set’s the previous slide.
And finally we need an Application to display the whole stuff in full screen:

package fxpresenter;

import java.util.List;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;

public class FXPresenter extends Application {

    @Override
    public void start(Stage stage) throws Exception {

        final Presentation presentation = new Presentation();
        presentation.addSlide(new Slide("Slide1.fxml"));
        presentation.addSlide(new Slide("Slide2.fxml"));

        final Scene scene = new Scene(presentation);
        stage.setScene(scene);
        stage.setFullScreen(true);
        presentation.start();
        List<Screen> screens = Screen.getScreens();

        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

You’re done. Now you can go ahead and create your slides. For the sample create two new FXML files named “Slide1.fxml” and “Slide2.fxml”, and style them using the SceneBuilder, and your ready to go. Here’s a little video showing how you can use SceneBuilder to create the Slides:

Pong! in less than 80 LOC

My idea for the JavaFX game engine is to create an API that makes coding games really easy. So my first goal was to be able to create a simple game like Pong! in less than 100 lines of code. So after creating the API I optimized it a bit and voilà this is Pong in less than 100 LOC (including imports and some empty lines):

import de.eppleton.fx2d.*;
import de.eppleton.fx2d.action.*;
import de.eppleton.fx2d.physics.*;
import de.eppleton.fx2d.physics.action.PhysicsActionFactory;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.text.Font;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.*;
import org.openide.util.Lookup;

public class Pong extends Game {

    int scorePlayer, scoreComputer = 0;

    @Override
    protected void initGame() {
        GameCanvas canvas = getCanvas();
        final PhysicsEngine physicsEngine = new PhysicsEngine(new Vec2(0, 0), new Vec2(0, 6), 100, canvas, true);
        canvas.addLayer(new Layer() {
            @Override
            public void draw(GraphicsContext graphicsContext, double x, double y, double width, double height) {
                graphicsContext.setFont(Font.font("Verdana", 36));
                graphicsContext.fillText("" + scorePlayer + "   " + scoreComputer, 380, 60);
            }
        });
        physicsEngine.createWall(0, 580, 800, 10);
        physicsEngine.createWall(0, 10, 800, 10);
        addBall(canvas, physicsEngine, 400, 300);
        canvas.addBehaviour(new Action() {
            @Override
            public boolean perform(Sprite sprite, GameCanvas playingField) {
                Sprite sprite1 = playingField.getSprite("ball");
                if (sprite1 != null) {
                    Body ball = sprite1.getLookup().lookup(Body.class);
                    if (ball.getPosition().x <= 0) {
                        scoreComputer++;
                        physicsEngine.remove(ball);
                        addBall(playingField, physicsEngine, 400, 300);
                    } else if (ball.getPosition().x >= 8 ) {
                        scorePlayer++;
                        physicsEngine.remove(ball);
                        addBall(playingField, physicsEngine, 400, 300);
                    }
                }
                return true;
            }
        });
        Sprite bat = new Sprite(canvas, "Player", 10, 262, 30, 75, Lookup.EMPTY);
        physicsEngine.createDefaultBody(bat, BodyType.KINEMATIC, 1, .4f, 0);
        bat.addAction(KeyCode.A, PhysicsActionFactory.createLinearMoveAction(null, "up", new Vec2(0, 4), new Vec2(0, 0)));
        bat.addAction(KeyCode.Z, PhysicsActionFactory.createLinearMoveAction(null, "down", new Vec2(0, -4), new Vec2(0, 0)));
        Sprite computer = new Sprite(canvas, "Computer", 750, 262, 30, 75, Lookup.EMPTY);
        physicsEngine.createDefaultBody(computer, BodyType.KINEMATIC, 1, .3f, 0);
        computer.addBehaviour(new Action() {
            @Override
            public boolean perform(Sprite sprite, GameCanvas playingField) {
                Body me = sprite.getLookup().lookup(Body.class);
                Sprite ball = playingField.getSprite("ball");
                if (ball != null) {
                    Body lookup = ball.getLookup().lookup(Body.class);
                    me.setLinearVelocity(lookup.getPosition().y > me.getPosition().y
                            ? new Vec2(0, -1.5f) : new Vec2(0, 1.5f));
                }
                return true;
            }
        });
    }

    private void addBall(GameCanvas canvas, PhysicsEngine physicsEngine, int x, int y) {
        Sprite ball = new Sprite(canvas, "ball", x, y, 20, 20, Lookup.EMPTY);
        Body ballBody = physicsEngine.createDefaultBody(ball, BodyType.DYNAMIC, 1, .4f, .2f, true);
        ballBody.setLinearVelocity(new Vec2(4, 1));
    }

    public static void main(String[] args) {
        launch(args);
    }
}

JavaFX Game Engine now with Physics

I’ve just added a pluggable physics engine to my game engine. So now there are three modules: the core game engine that allows for the creation of simple canvas based games, and an optional tile engine that can read TMX files and render TileBased Worlds and Sprite animations, as well as a pluggable physics Engine that you can use to add realistic physics. Using the NetBeans Lookups API it’s really simple to make it pluggable. Ah, and there’s a fourth module with demos.

The Physics API is still very simple, but you can already do some nice stuff. A nice goodie is the DebugLayer that you can add to the GameCanvas to render the JBox2D bodies. This way you can easily test and debug a game and find bugs with custom Sprite rendering. I’m also trying to provide some helper methods for mapping the Bodies and Shapes to the Sprites, since that’s sometimes confusing…

The first game I created is the “Hello World!” of Games, Pong! Here you can get an impression of the current API:

Custom Code Templates with NetBeans

In the last weeks I’ve shown you some of the NetBeans Code Templates. The existing ones are nice, but what is even nicer is, that you can easily create your own Java templates. We can for example create the “2no” template I used to get the Node delegate from a DataObject as shown here:

Here’s how:

Go to “Settings“-> “Editor” -> “Code Templates” and select “Java” as the Language:

Now click the “New” Button, a dialog will ask for an abbreviation, enter “2no“. The abbreviation will be added to the table, and you can edit the “Expanded Text“. Add this:


${nodeType type="org.openide.nodes.Node" default="Node" editable="false"} ${node newVarName default="n"} = ${dob instanceof="org.openide.loaders.DataObject" default="dob"}.getNodeDelegate();
${cursor}

The type definition in the beginning will automatically add the import for org.openide.nodes.Node. The default defines, that we will get “Node” as the first String, and it’s not editable by the user. Then we define a new variable with a default name of  ”n“. If that name isn’t available in our scope NetBeans will check for the next free name by adding an int to the String (e.g. “n1“).  Next we declare, that we want to call a method called “getNodeDelegate()” on a “org.openide.datasystems.DataObject“, and if there is none in the scope use “dob” instead.

NetBeans Platform specific Code Templates

In my last blog posts I’ve shown you some Code Templates you can use in order to speed up your Java coding. There are also some templates for NetBeans Platform APIs. Here’s an example:

Typing “2fo” converts a java.util.File to a NetBeans FileObject. When pressing the <tab> key, the Code  expands to:

FileObject fileObject = FileUtil.toFileObject(FileUtil.normalizeFile(file));

Typing “2do” converts the FileObject to a DataObject:


try {
 DataObject dataObject = DataObject.find(fileObject);

 } catch (DataObjectNotFoundException dataObjectNotFoundException) {
 Exceptions.printStackTrace(dataObjectNotFoundException);
 }

…and finally “2no” retrieves the corresponding Node.

Node node = dataObject.getNodeDelegate();

The last one is a template I created myself. More about this in another blog entry. And here’s a video how you can use these templates together to display a folder of your file system in the UI:

NetBeans Code Templates: to Array

NetBeans has tons of helpful code templates that expand if you type them and press TAB afterwards. You can also create your own templates. But it really pays to learn some of the existing ones. So I’d like to show some of them here. Today it’s “2ar” which converts an ArrayList to an Array. Typing “2ar” after:

        ArrayList<Integer> integers = new ArrayList<Integer>();

…will expand to:

  ArrayList<Integer> integers = new ArrayList<Integer>();
        class[] integers1 = (class[]) integers.toArray(new class[integers.size()]);

… with the “class” being in “edit mode”, so you can immediately start entering “Integer” at the cursor location and and it will change to:

  ArrayList<Integer> integers = new ArrayList<Integer>();
        Integer[] integers1 = (class[]) integers.toArray(new Integer[integers.size()]);

Here’s a little video:

NetBeans Code Templates: 2al

NetBeans has tons of helpful code templates that expand if you type them and press TAB afterwards. You can also create your own templates. But it really pays to learn some of the existing ones. So I’d like to show some of them here. Today it’s “2al” which converts an Array to an ArrayList. Typing “2al” after:

String [] strings =new String[]{"Hallo", "Welt"};

…will expand to:

String [] strings =new String[]{"Hallo", "Welt"};

List list = new ArrayList(Arrays.asList(strings));

Here’s a little video:

Case-sensitive passwords and @StaticResource

Many years ago I worked for a company providing services for scientists. Sometimes we got trouble tickets regarding authentication, and I’ll never forget this one. The user had really tried everything to log in. We saw his countless tries in the logs after he contacted us for help. We followed up with the user on the phone checking each of the typical errors, caps-lock, keyboard layout, etc, but no success. Fortunately our policy was to auto-generate an initial password, make the user change it on the first log in, and store that encrypted. But we had the initial password, since he hadn’t been able to log in. So we tried it, and it worked without problems. We generated a new password, sent him the registration mail, and asked him to try again. Guess what, no success. But then we realized something, the password hash in his requests didn’t change between his old log in attempts and the new ones. So we asked him if he had entered the same password as before, and he said “yes, it didn’t change”. We double-checked, and the generated password was definitely different from the first one, and we told him, but he said: “No, the website still says: ‘Your password is case-sensitive‘!”.

Variations of this problem is something developers are facing a lot of times. Especially in training situations when you try to keep up with your peers, it’s very easy to simply copy and paste stuff like this and wonder why the icon doesn’t show up:

private static String ICON="path/to/your/icon.png"
Annotations are brilliant for fixing this kind of problem, and they are making a NetBeans Platform developers (and trainers) live so much easier. When I give a NetBeans Platform Workshop we have a lot of tutorials, and way back we spent a *lot* of time fixing Service Registrations in the META-INF/services folder.

Wrong file names, wrong paths inside the file, moved the Service Implementation to another package, even a simple typo will screw up everything, and you have to run the application to find out something is wrong. And it got even worse when it came to examples using layer.xml, where registrations can span multiple screens of XML.

@ServiceProvider and layer-generating Annotations totally fixed that problem. But still we sometimes had these typical String-copy errors:

private static String ICON="path/to/your/icon.png"

But since NB 7.1 there’s also a solution for this. Simply add @StaticResource to your code, and the IDE will clearly point out the problem:

@StaticResource will check for the presence of your resource and warn you if it’s not there. By default it checks for absolute paths in the source path, but there are modifiers to also check relative paths, and analyze the classpath. Really helpful!

Canvas for Bck2Brwsr

Almost two weeks ago I met Jaroslav Tulach in Linz for our annual NetBeans Platform certified Training at the Johanes-Kepler-University in Linz. Every year we take some time to talk about projects and do some coding over a couple of beers, producing “Barcode” if you will. Last year we created a dynamic upload center for NetBeans Platform applications. This year Jarda showed me his new project Bck2Brwsr. The goal of the project is to bring Java back to the Browser. Java has lost a lot of ground in that area, and the advent of smartphones and tablets speeds up this trend, since it’s getting harder and harder to set up the environment for executing Java applications or applets.

In the meantime JavaScript is delivering on the WORA promise Java once started with. And while Java is great as a language especially for larger projects, for structuring and modularizing code in order to create maintainable projects, the platform has lost a lot of ground on the devices. Developers like the language, the superior IDE support as compared to JavaScript, the compile time checks, but for Browser based Projects, Java is no longer a choice as client technology.

The main idea of Bck2Brwsr is to change that and make it dead simple to execute Java Code in the Browser on any device. Since JavaScript is ubiquitous, it’s the obvious target language. GWT solves the problem by providing some APIs and a compiler that generates JavaScript. Bck2Brwsr is different in that it creates a JVM that is written in JavaScript and is capable of executing Java Bytecode directly. So when a Java runtime is available, the code could be executed by that, and benefit from the superior performance of the JVM. But if there’s no JVM, a minimal environment is automatically installed and runs the code in the Bck2Brwsr VM. And if you wonder how such a system will perform, I found Bck2Brwsr to be surprisingly fast already.

Another difference to GWT is, that there’s no standard API for widgets. The idea is, that you as a developer can very easily create your own APIs. For my current projects that would be excellent. I’ve created a GameEngine that is using JavaFX Canvas to render TileMaps and Sprites, and my plan was to create a JavaScript/ HTML5 Canvas version of that as well, so I can target any device. Over a couple of Austrian beers Jarda showed me how to implement such an API, and yesterday I found a little time to code so I gave it a try.

The final plan is to recreate the JavaFX Canvas API to render to HTML5 Canvas. But there are some differences, e.g. how the individual functions like arc and arcTo work. The bigger differences are additional features of JavaFX Canvas like the ability to use SVG paths. All of that should be no big problem, but require some work. So I decide to first implement a 1:1 Java version of the HTML5 Canvas API.

Time to head over to w3schools and have a look at Canvas. There are several subsections. The first one is ”Colors, Styles, and Shadows”. Here we have a couple of properties we can read or set on the GraphicsContect of the Canvas, like fillStyle, strokeStyle, etc.. So what we first need to do is create a representation of the Canvas Element and it’s GraphicsContext:


public class Canvas extends Element {

public Canvas(String id) {
 super(id);
 }

// code omitted

@JavaScriptBody(
 args = {"el"},
 body = "var e = window.document.getElementById(el.fld_id);\n"
 + "return e.getContext('2d');\n")
 private native static Object getContextImpl(Canvas el);

public GraphicsContext getContext() {
 return new GraphicsContext(getContextImpl(this));
 }

We will also need to register our Element with Bck2Brwsr’s PageProcessor. Now when the PageProcessor hits a Canvas Element on the Web page, it will create a Canvas Element for us. The @JavaScriptBody annotation can be used to directly generate the native JavaScript Code for us. In getContextImpl it will find the current Canvas Object and call a method on it to give us the GraphicsContext. We then create a Java Object Wrapper around that JavaScript Object:


public class GraphicsContext {

Object context;

GraphicsContext(Object contextImpl) {
 this.context = contextImpl;
}

@JavaScriptBody(args = {"style"}, body = "this.fld_context.fillStyle=style;")
 public native void setFillStyle(String style);

 @JavaScriptBody(args = {}, body = "return this.fld_context.fillStyle;")
 public native String getFillStyle();

// code omitted

}

So the getter/setter style for simple types is fairly simple to implement. It get’s slightly more complicated when dealing with Objects. HTML5 Canvas also allows us to set Gradients or Patterns as fills. These are JavaScript Objects, so we need some Wrappers to represent them in our Java Code, and we need to provide a reference to be used by the native code:


public void setFillStyle(LinearGradient style) {
 setFillStyleImpl(style.object());
 }

 public void setFillStyle(RadialGradient style) {
 setFillStyleImpl(style.object());
 }

 public void setFillStyle(Pattern style) {
 setFillStyleImpl(style.object());
 }

 @JavaScriptBody(args = {"obj"}, body = "this.fld_context.fillStyle=obj;")
 private native void setFillStyleImpl(Object obj);

The object() method just returns the wrapped JavaScript Object reference:


public class LinearGradient {

private final Object gradient;

LinearGradient(Object linearGradient) {
 this.gradient = linearGradient;
 }

Object object() {
 return gradient;
 }

 public void addColorStop(double position, String color) {
 addColorStopImpl(gradient, position, color);
 }

@JavaScriptBody(args = {"gradient", "position", "color"}, body =
 "gradient.addColorStop(position,color)")
 private native void addColorStopImpl(Object gradient, double position, String color);

}

The rest is applying this pattern to all API methods, so we get a simple 1:1 mapping to Java. And then we can use it in Java Code like this:


/**
 * Edit the index.xhtml file. Use 'id' to name certain HTML elements. Use this
 * class to define behavior of the elements.
 */
@Page(xhtml = "index.xhtml", className = "Index")
public class App {

@On(event = CLICK, id = "hello")
 static void hello() {
 GraphicsContext context = Index.MYCANVAS.getContext();
 // Paths
 context.beginPath();
 context.moveTo(100, 20);
 context.lineTo(200, 20);
 context.arcTo(300, 300, 50, 50, 40);
 context.setFillStyle("#ff0000");
 context.setShadowColor("#000000");
 context.setShadowOffsetX(10);
 context.setShadowOffsetY(10);
 context.setShadowBlur(20);
 context.fill();

 // TextMetrics
 context.fillText("width: " + context.measureText("Hallo").getWidth(), 10, 10);

 // Gradients
 LinearGradient grd = context.createLinearGradient(0, 0, 170, 0);
 grd.addColorStop(0, "black");
 grd.addColorStop(0.3, "magenta");
 grd.addColorStop(0.5, "blue");
 grd.addColorStop(0.6, "green");
 grd.addColorStop(0.8, "yellow");
 grd.addColorStop(1, "red");
 context.setFillStyle(grd);
 context.fillRect(10, 200, 200, 200);

 // Pixel Manipulation
 ImageData imageData = context.getImageData(100, 0, 100, 100);
 context.putImageData(imageData, 200, 200);

 // Image
 context.drawImage(Index.SCREAM, 1, 1, 100, 100);

 // Fonts
 context.setFont(
 "20px Georgia");
 context.strokeText("Hello World!", 500, 50);
 context.setFont(
 "30px Verdana");
 LinearGradient gradient = context.createLinearGradient(0, 0, 800, 0);
 gradient.addColorStop(0, "magenta");
 gradient.addColorStop(0.5, "blue");
 gradient.addColorStop(1.0, "red");
 context.setStrokeStyle(gradient);
 context.strokeText("Big smile!", 500, 90);
 }
}

The code doesn’t look to different to JavaFX Canvas, does it? So here’s the ugly result :-) :

It’s really easy and straightforward to create an API like that, and I’m looking forward to do some more coding with Bck2Brwsr!

Writing a Tile Engine in JavaFX

With the advent of embedded versions of JavaFX, our framework has become more interesting for game development, since we now can target small consumer devices like tablets & smartphones. So I decided to do a little more experimenting with JavaFX for writing Games. This time I wanted to use Canvas to have more control over the rendering in order to be able to optimize the performance on smaller devices. These are my experiences when writing a Tile Engine.

What’s a Tile Engine?

Back in the early days game consoles & computers had very limited resources. So in order to have games with thousands of large screens developers needed to come up with a method to store the screens in a format other than a bitmap per screen. So Tile Engines were invented that can generate large screens from a limited set of re-usable smaller graphics (Tiles). This saves ram and improves rendering performance.

TileMaps

The instructions how to generate the screen are stored in TileMaps. Those maps are typically organized as a 2-dimensional matrix of Tile ids. Usually the tiles are organized in layers to allow for a simple Z-ordering and more flexibility in combining graphics with different backgrounds. Usually TileMaps also support storing of meta data, for example if certain tiles are blocked, or spawn points for enemies.

A TileMap with several layers created with the

TileSets

The tiles referenced in the map are usually stored in TileSets that consist of a single bitmap and meta information about how to divide it into tiles. Here’s an example of such an image from opengameart.com, a site that hosts game assets with Open Source Licences. In my examples I use some of these graphics.

A typical TileSet Image sized 1024 x 1024 (^2 = good for graphics cards)

ObjectGroups

One additional feature of the TMX format are Object Layers. These special layers can be used to define freeform shapes and polylines and assign properties to them. The basic idea behind that is that we can use them to define areas where Sprites are created (spawnpoints), exits, portals, and non-rectangular collision shapes. It’s up to the creator of the TileEngine or the developer who builds games with it to define how to handle the ObjectGroups. I’m planning to use them extensively, and they are a very nice extension point for declaratively defining the gameplay. You can e.g. use them to define animations, skript dialogs, etc..

Spawnpoints defined in the

Workflow, Tools & Formats

The idea of tilemaps also allows for a nice workflow. Graphic designers can create the assets and game designers can import them into a level editor like “Tiled” and design the levels via drag & drop. The maps are stored in a machine readable TileMap format. Tiled for example uses the TMX Map format for storing the TileMap. That’s a very simple XML format, that can then be loaded by the TileEngine. For my implementation I decided to use the TMX Format, so I can use “Tiled” for designing the levels.

Implementation in JavaFX

For the implementation I decided to use JavaFX Canvas immediate mode rendering instead of the retained mode rendering when using individual Nodes. This gives me a bit more control for optimizing the performance on small devices like the Raspberry Pi.

Reading TMX/TSX files

The first thing we need is a way to read the TileMap (TMX) and TileSet (TSX) files. With JAXB it’s pretty simple to create a TileMapReader that can create POJOs from a file. So if you use the Engine you simply call:

TileMap map = TileMapReader.readMap(“path/to/my/map.tmx”);

The Camera

Since in most games the TileMaps will be larger than the screen, only a part of the Map is rendered. Usually the map is centered on the hero. You can do that by simply tracking the map position of upper left corner of the screen. We refer to this as our Camera position. The position is then updated from the hero’s position just before the TileMap is rendered like this:

// the center of the screen is the preferred location of our hero

double centerX = screenWidth / 2;

double centerY = screenHeight / 2;

cameraX = hero.getX() - centerX;

cameraY = hero.getY() - centerY;

We just need to make sure the camera doesn’t leave the tilemap:

// if we get too close to the borders

if (cameraX >= cameraMaxX) {

cameraX = cameraMaxX;

}

if (cameraY >= cameraMaxY) {

cameraY = cameraMaxY;

}

Rendering the TileMap using Canvas

Then it’s really easy to render the tiles. we simply loop through the layers and ask the tilemap to render the correct image at the current position. First we need to find out which tiles are currently visible, and the offset, since our hero moves pixel by pixel, not tile by tile:

// x,y index of first tile to be shown

int startX = (int) (cameraX / tileWidth);

int startY = (int) (cameraY / tileHeight);

// the offset in pixels

int offX = (int) (cameraX % tileWidth);

int offY = (int) (cameraY % tileHeight);

Then we loop through the visible layers and draw the tile:

for (int y = 0; y < screenHeightInTiles; y++) {

for (int x = 0; x < screenWidthInTiles; x++) {

// get the tile id of the tile at this position

int gid = layer.getGid((x + startX) + ((y + startY) * tileMap.getWidth()));

graphicsContext2D.save();

// position the graphicscontext for drawing

graphicsContext2D.translate((x * tileWidth) - offX, (y * tileHeight) - offY);

// ask the tilemap to draw the tile

tileMap.drawTile(graphicsContext2D, gid);

// restore the old state

graphicsContext2D.restore();

}

}

The TileMap will then find out which Tileset the Tile belongs to and ask the TileSet to draw it to the Context. Drawing itself is as simple as finding the correct coordinates in your TileSets Image:

public void drawTile(GraphicsContext graphicsContext2D, int tileIndex) {

int x = tileIndex % cols;

int y = tileIndex / cols;

// TODO support for margin and spacing

graphicsContext2D.drawImage(tileImage, x * tilewidth, y* tileheight, tilewidth, tileheight, 0, 0, tilewidth, tileheight);

}

Game Loop. So we can simplify it to:

The Game Loop is again very simple. I’m using a TimeLine and a KeyFrame to fire a pulse for the game at a certain framerate (FPS):

final Duration oneFrameAmt = Duration.millis(1000 / FPS);

final KeyFrame oneFrame = new KeyFrame(oneFrameAmt,

new EventHandler() {

@Override

public void handle(Event t) {

update();

render();

}

});

TimelineBuilder.create()

.cycleCount(Animation.INDEFINITE)

.keyFrames(oneFrame)

.build()

.play();

Sprites

Every call to update in the TileMapCanvas loops through all Sprites and updates them. Basic Sprites currently contain one TileSet with a walkcycle like this:

Since sprites typically have a lot of transparent space around them, in order to have some extra room for animated behavior like like swinging a sword, I decided to allow to add a MoveBox and a CollisionBox for convenience. The CollisionBox can be used to define an area where our hero can be hurt. The MoveBox should be placed around the legs, so it can pass in front of forbidden tiles while the upper body is overlapping the tile. The blueish area around our “hero” is the sprite boundary:

https://www.youtube.com/watch?v=08H6LZkcqXw

Sprites can also have a timed Behavior. On every update the Sprite loops through it’s behaviors and checks if it’s time to fire. If so it’s “behave” method is called. If we have an enemy, like the skeleton in the sample app, we can add it’s AI here. Our Skeleton has for example a very simple behavior to make it follow our hero. It also checks for collision and causes damage to our hero like that:

monsterSprite.addBehaviour(new Sprite.Behavior() {

@Override

public void behave(Sprite sprite, TileMapCanvas playingField) {

if (sprite.getCollisionBox().intersects(hero.getCollisionBox())) {

hero.hurt(1);

}

}

});

The default interval is a second. If you need other intervals you can set them. Behaviors are reusable, different sprites can share the same Behavior instance. Behaviors are similar to KeyFrames, and I’m currently also using them to time the Animations (increase the tile index for the next render call).

ObjectGroupHandler

As mentioned in the beginning ObjectGroups are handy extension points. In my example game I use them for defining the spawn points of our hero and the monsters. Currently you simply add an ObjectGroupHandler which in turn uses the information in the ObjectGroup to create the Hero and Monster sprites and add Behavior to them:

class MonsterHandler implements ObjectGroupHandler {

Sprite hero;

@Override

public void handle(ObjectGroup group, final TileMapCanvas field) {

if (group.getName().equals("sprites")) {

for (TObject tObject : group.getObjectLIst()) {

if (tObject.getName().equals("MonsterSpawner")) {

try {

double x = tObject.getX();

double y = tObject.getY();

TileSet monster = TileMapReader.readSet("/de/eppleton/tileengine/resources/maps/BODY_skeleton.tsx");

Sprite monsterSprite = new Sprite(monster, 9, x, y, "monster");

monsterSprite.setMoveBox(new Rectangle2D(18, 42, 28, 20));

field.addSprite(monsterSprite);

monsterSprite.addBehaviour(new Sprite.Behavior() {

@Override

public void behave(Sprite sprite, TileMapCanvas playingField) {

if (sprite.getCollisionBox().intersects(hero.getCollisionBox())) {

hero.hurt(1);

}

}

});

}

Putting it all together

To create a sample game all you need to do is create TileMaps, TileSets, one or more ObjectGroupHandler(s) to create the Sprites and add Behavior, and you’re ready to play:

// create the world

TileMap tileMap = TileMapReader.readMap("/de/eppleton/tileengine/resources/maps/sample.tmx");

// initialize the TileMapCanvas

TileMapCanvas playingField = new TileMapCanvas(tileMap, 0, 0, 500, 500);

// add Handlers, can also be done declaratively.

playingField.addObjectGroupHandler(new MonsterHandler());

// display the TileMapCanvas

StackPane root = new StackPane();

root.getChildren().add(playingField);

Scene scene = new Scene(root, 500, 500);

playingField.requestFocus();

primaryStage.setTitle("Tile Engine Sample");

primaryStage.setScene(scene);

primaryStage.show();

That was the starting point of my Tile Engine. In the meantime it has evolved a bit into a more general purpose 2D-engine, so also Sprites that are not using TileSets and Layers that are freely rendered are supported. But it works pretty well so far.

“Auf jeden Fall ist Gerhard jetzt zufrieden” – JMX und die Red hot Chili peppers

[English version] Dr. Roland Huß leitet die Abteilung “Research & Development” bei dem Münchner IT-Beratungs- und Softwarehaus ConSol* und ist Gründer und Hauptentwickler des Open Source Frameworks Jolokia, das es unter anderem ermöglicht mit Tools wie Nagios über JMX Java Virtual Machines zu überwachen. Auf dem JayDay wird Jolokia in dem Talk “Jolokia – JMX on Capsicain” vorgestellt. Wir wollten mehr darüber erfahren:

Das Open Source Framework Jolokia bietet die Möglichkeit zum Beispiel von Perl- oder Javascript -Clients aus über JMX auf entfernte JVMs zuzugreifen. Wie ist das technisch umgesetzt und wo liegen die Unterschiede und Vorteile im Vergleich zu Tools wie VisualVM, die sich ebenfalls auf entfernte JVMs verbinden können?

Die Idee für Jolokia bzw. ihre Vorläuferin Jmx4Perl ist aus der Not heraus geboren. Gerhard Laußer, unser Nagios Guru im Hause, suchte 2009 verzweifelt eine performante und resourcenschonende Lösung um viele Java Server via JMX mit Nagios zu überwachen. Das Problem ist, dass JMX und die JSR-160 Konnektoren für den Remotezugriff, auch auf der Client-Seite, auf die Java Plattform beschränkt sind. Für einen Nagios Server, der 500 Server mit ca. 10 Metriken alle 5 Minuten überwachen muss, bedeutet das, dass er mt den damals gängigen Nagios-JMX-Plugins pro Sekunde 15 mal ein JVM dafür starten muss. Unabhängig davon ist JMX in der Standardkonfiguration mit RMI ein no-go wenn über Firewallgrenzen hinweg überwacht werden soll.

Bei Jolokia wird daher ein Agent auf dem Zielsystem deployed, der REST-ähnliche Aufrufe via JSON über HTTP annimmt und diese auf lokale MBean Aufrufe umsetzt. Das hat mehrere Vorteile: Zum einen ist die Kombination JSON mit HTTP die Lingua franca des Webs, die von allen Programmierplattformen unterstützt wird. Zum anderen gibt es noch viele weitere Vorteile wie Bulk Request oder feingranulare Security, die ich alle im Vortrag vorstellen werde. Die Unterschiede sind sicherlich, dass Jolokia kein offizieller Standard ist, und das ein kleiner (230k) Agent installiert werden muss. Den Agenten gibt es in vier verschiedenen Varianten, so dass letzendlich jede Java  Applikation instrumentiert werden kann. Für die Fälle, bei denen kein Agent installiert werden darf, gibt es schliesslich noch ein agentenloses Proxy Setup.

Für Tools bedeutet dies, dass diese nicht mehr in Java programmiert sein müssen und damit neue Client-Platformen erschlossen werden können. Z.B. existiert ein einfacher nativer iPhone Client “AccessJ”, mit dem man seine JEE Server managen kann.

Auf jeden Fall ist Gerhard jetzt zufrieden.

Du kannst also über Jolokia mit überschaubarem Aufwand alle möglichen Überwachungstools erstellen. Kannst Du uns Beispiele nennen was sich damit in der Praxis alles verwirklichen lässt?

Das Paradebeispiel ist sicherlich das Nagios-Plugin “check_jmx4perl” aus dem Jmx4Perl Paket. Neben dem performanten JMX Zugriff, bietet es eine Fülle weiterer Optionen, die alleine schon eine Votrag füllen würden. Mein Lieblingstool aber ist “j4psh”, eine interaktive Shell, mit Syntax Highlighting und kontextsensitiver Tab-Vervollständigung. Mit Unix ähnlichen Kommandos wie “cd”, “ls” oder “cat” kann man super schnell und komfortabel durch den JMX Namensraum navigieren.

Wirst Du auf dem JayDay Demos dazu zeigen? Welche?

j4psh werde ich auf jeden Fall zeigen, und wenn alles glatt geht auch Ají.

“Aji” ist doch Jolokias kleine Schwester. Wie unterstützt “Aji” Ihren großen Bruder bei der Arbeit?

Ich muss erstmal klar stellen, dass das ein reiner Frauenhaushalt ist. Es sind alles Schwestern, Jolokia, Jmx4Perl und Ají ;-)

Nun, Ají setzt sich auf die Schultern ihrer großen Schwester Jolokia und ist ein JMX User-Interface. Die Idee ist naheliegend: Wenn schon ein Agent installiert werden muss, warum dann nicht auch gleich eine leichte Single-Page-Application (SPA) huckepack dazu ?Aktuell existiert bereits ein MBean-Browser, geplant für die erste Version sind noch konfigurierbare Live-Charts. Leider bin ich noch nicht soweit, wie ich gerne wäre, was einerseits am sehr regem Tagesgeschäft liegt,  andererseits auch daran, dass ich das Projekt dazu nutze, mich auf die JavaScript Bugwelle zu stürzen. curl.js, CoffeeScript, d3.js, Bootstrap, less, backbone.js, handlebars.js, …. und dazu noch ein robuster, automatisierter Build (wahrscheinlich mir Gradle). Viel Zeugs, macht aber Spass und es wird glaube ich richtig gut.

Was gibt es darüber hinaus Neues, und was ist für zukünftige Releases geplant?

Jolokia unterstützt bislang noch keine JMX Notifications. Das steht ganz oben auf der Liste, es gibt verschiedene Realisierungsansätze, angefangen von einem Polling-Mechansismus bis hin zu WebSockets. Mehr Client-Libraries sind auch geplant, wie z.B. Groovy, Scala oder Python. Für weitere Vorschläge bin ich immer offen, Ideen kann man über www.jolokia.org z.B. übers Forum eintüten. Jolokia ist jedoch letztendlich immer noch hauptsächlich ein Freizeit-Open-Source-Projekt mit Unterstützung meines Arbeitgebers ConSol. So, no promises.

Vielen Dank für das Interview!

Gerne, freue mich schon auf den JayDay.

Veröffentlicht in News |

“NetBeans Platform 7″ und andere Java Bücher zu gewinnen…

[English Version] Galileo Press unterstützt den JayDay mit einem “Rundum-Sorglos-Java-Paket”. Registrierte Teilnehmer haben die Möglichkeit eines von diesen Büchern zu gewinnen:

NetBeans Platform 7, Das umfassende Handbuch von Heiko Böck

Java 7, Mehr als eine Insel von Christian Ullenboom

Java ist auch eine Insel von Christian Ullenboom

Einstieg in Eclipse 3.7, Aktuell zu Indigo und Java 7 von Thomas Künneth und Yvonne Wolf

Viel Glück!

Veröffentlicht in News |

Thanks NetBeans!

 The first gift I bought with the Amazon gift card I got along with my “Award of Achievment” from NetBeans has arrived. Today I received this nice recreation of some of the most popular pinball machines of the past decades for the Wii. I’m really looking forward to play Pinbot side by side with the real pin in my office. And I’ll try to get some inspiration for my JavaFX Pinball project that has been sleeping for a while :-) . Thanks NetBeans!