blog van Paul Hildebrand
Java
KeyPressedEventHandler with Java FX 2.0 and multiple keys
Sep 26th
Today I was trying out JavaFX 2.0 beta which is soon to be released as final. I tried to make a small game where you can fly around a small plane. This is not to hard, you just take the image of a plane taken from the top and make it rotate by listening to key events for left and right and of course a key event to make it go forward.
But when I made a KeyPressedEventHandler I noticed that it would only handle one key at a time. But for a game you need multiple keys handled simultaneously. For instance turning a plane to the left (Key.LEFT) while throttling (Key.UP). When multiple keys are pressed at the same time the KeyPressedEvent is triggered for all keys but only an event for the last pressed key will be triggered continuously. Luckily I found out that the KeyReleasedEvent is always triggered. This gave me the idea to buffer all the pressed keys, and remove each key from the buffer when a release of the given key was done.
The code below creates a new event handler which buffers the pressed keys in a EnumSet.
– A key is added to the set when the KeyPressedEvent is triggered.
– A key is removed from the set when the KeyReleasedEvent is triggered.
import java.util.EnumSet; import java.util.Set; import javafx.event.EventHandler; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; /** * * @author Paul */ public class MultiplePressedKeysEventHandler implements EventHandler<KeyEvent> { private final Set<KeyCode> buffer = EnumSet.noneOf(KeyCode.class); private final MultiKeyEvent multiKeyEvent = new MultiKeyEvent(); private final MultiKeyEventHandler multiKeyEventHandler; public MultiplePressedKeysEventHandler(final MultiKeyEventHandler handler) { this.multiKeyEventHandler = handler; } public void handle(final KeyEvent event) { final KeyCode code = event.getCode(); if (KeyEvent.KEY_PRESSED.equals(event.getEventType())) { buffer.add(code); multiKeyEventHandler.handle(multiKeyEvent); } else if (KeyEvent.KEY_RELEASED.equals(event.getEventType())) { buffer.remove(code); } event.consume(); } public interface MultiKeyEventHandler { void handle(final MultiKeyEvent event); } public class MultiKeyEvent { public boolean isPressed(final KeyCode key) { return buffer.contains(key); } } }
This is an example how to use the new MultiplePressedKeysEventHandler. Notice that you will have to add this event handler to both the “setOnKeyPressed” and “setOnKeyReleased” method.
The MultiKeyEvent gives you access to a method “isPressed” which you can use to check if the given key is pressed.
private void initKeyEventHandler() { final MultiplePressedKeysEventHandler keyHandler = new MultiplePressedKeysEventHandler(new MultiKeyEventHandler() { public void handle(MultiKeyEvent ke) { if (ke.isPressed(KeyCode.POWER) || ke.isPressed(KeyCode.X)) { Platform.exit(); } if (ke.isPressed(KeyCode.LEFT) || ke.isPressed(KeyCode.A)) { rotation -= 2d; } if (ke.isPressed(KeyCode.RIGHT) || ke.isPressed(KeyCode.D)) { rotation += 2d; } if (ke.isPressed(KeyCode.UP) || ke.isPressed(KeyCode.W)) { speed += 0.1d; } if (ke.isPressed(KeyCode.DOWN) || ke.isPressed(KeyCode.S)) { speed -= 0.1d; } } }); imageView.setFocusTraversable(true); imageView.setOnKeyPressed(keyHandler); imageView.setOnKeyReleased(keyHandler); }
This piece of code has some rough edges and can be improved in many ways. I hope it gave you an idea or direction how to solve the given problem.
Improve code coverage on private constructors with cobertura.
May 23rd
If you use cobertura for checking your jUnit code coverage, you have probably also encountered the problem that private constructors cannot be covered. All your classes have 100% coverage but your utillity classes with private constructors stick at 90 or 95% (or even less). Those red sections in the cobertura overview annoyed the hell out of me so I did some research and found a solution using Java reflection.
@Test public void testPrivateConstructor() throws Exception { Constructor<?>[] cons = MyUtil.class.getDeclaredConstructors(); cons[0].setAccessible(true); cons[0].newInstance((Object[])null); }
Just add this test to one of your unit tests and replace “MyUtil” with your class and tada!! your private constructor is covered.
Of course, code coverage is not a measure of quality but you get my point.
MBeans using spring and annotations
Feb 24th
This article is about how to create MBeans using annotations and spring. It assumes that you already have a MBean server available like the one in tomcat. Just follow the steps below.
First we want to create a Mbean, using the @Managed annotations you can convert any class you want into a MBean. The class MyMBean below is a very simple one which will only expose one method (i.e. getSomething). The @ManagedResource annotation will mark the class as a MBean. The @ManagedAttribute and @ManagedOperation annotations can be used to expose any attributes or methods. Also notice the @Component annotation, this annotation will make the MBean eligible for the spring context scanner.
package my.cool.app.mbean; import org.springframework.jmx.export.annotation.ManagedOperation; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.stereotype.Component; @Component @ManagedResource(objectName = "MyApp:name=MyMbean") public class MyMBean { @ManagedOperation(description = "Returns something.") public String getSomething() { return "Something"; } }
Next, you will have to create a MBeanExporter in your spring application context. Normally just adding the <context:mbean-export/> element will do the trick but not in this case. You need a JmxAttributeSource which is capable of reading the @ManagedResouce annotations. The downside is that we have to make a more elaborate configuration which is not as beautiful as the short <context:mbean-export/> element.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <!-- this bean must not be lazily initialized if the exporting is to happen --> <bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"> <property name="assembler" ref="assembler" /> <property name="namingStrategy" ref="namingStrategy" /> <property name="autodetect" value="true" /> </bean> <bean id="jmxAttributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource" /> <!-- will create management interface using annotation metadata --> <bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler"> <property name="attributeSource" ref="jmxAttributeSource" /> </bean> <!-- will pick up the ObjectName from the annotation --> <bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy"> <property name="attributeSource" ref="jmxAttributeSource" /> </bean> </beans>
But now spring still will not find any of your MBeans. Remember the @Component annotation (or @Named annotation if you are using jsr 330) at the class declaration of your MBean? Adding the component-scan element to your spring application context configuration will make sure all classes annotated with @Component, @Service or @Repository will be added to the spring context.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="my.cool.app.mbean" /> </beans>
That’s it. Good look with it.