Html5 Web Sockets



字数:0 关键词: HTML5 前端技术

The New Old JayView! OpenGL ES on Android Microlog, Logging in Java ME An in-depth look at the Bluetooth features Introducing Akka Simpler scalability, fault-tolerance, concurrency & remoting through actors Swing Rocks The last dance? Agile Testing – a new profile for testers Cop and Qi4j Reviving object-orientation Sneaky Throw HTML5 Web Sockets: A quantum leap in scalability for the web 2010 No. 1 Volume 21 Passion for IT on paper. For us, by all of us. Jayview home u Details page 2 2 We’ve been hard at work on JayView since 2001. Our first efforts were, well, humbling. Though JayView has always been the place where Jayway – the founding company – put its passion for sharing knowledge on cellu- lose. It was, and still is, for the fun of it all. So pretty print wasn’t a high priority at first. We never wrote about ourselves or, for that matter, how beautiful our customers are The New Old JayView! (which they happen to be :-). Not only can you find our own words in the magazine, but also that of interesting people from other companies. We just write about things we like – or not. Subjects that hopefully affects us all. It used to be that JayView was all about Java development and agile methodologies. However, we matured and finally felt ready for a reboot and to cover more ground. In your hand, you hold the first issue of the new JayView – new subject sections, new format. What hasn’t changed is the attitude or our passion for knowledge. I hope you will enjoy the new old JayView. Björn Granvik Chief Editor, JayView Publication Officer: Thomas Dagsberg / CEO Jayway | Editors: Björn Granvik, Darius Katz, Patrik Nilsson, Katarina Wiberg Contact: | Jayway: Addresses and telephone numbers on the back | Cover Picture: 33981225 Layout and Graphic Design: Peter Kleine / Jayway, Holmbergs i Malmö AB | Print: Holmbergs i Malmö AB, 2010 Free subscription of JayView – enter your information at: Finally a way to get rid of checking those an- noying checked exceptions: public class Sneak { public static RuntimeException sneakyThrow(Throwable t) { if ( t == null ) throw new NullPointerException(“t”); Sneak.sneakyThrow0(t); return null; } @SuppressWarnings(“unchecked”) private static void sneakyThrow0(Throwable t) throws T { throw (T)t; } } Credit goes to Reinier Zwitserloot and his mail on Java Posse ( Sneaky Throw Jan Kronquist/Jawyay 3 HTML5 Web Sockets: A quantum leap in scalability for the web Peter Lubbers & Frank Greco/Kaazing Corporation Lately there has been a lot of buzz around HTML5 Web Sockets, which defines a full- duplex communication channel that operates through a single socket over the Web. HTML5 Web Sockets is not just another incremental en- hancement to conventional HTTP communica- tions; it represents a colossal advance, especially for real-time, event-driven web applications. HTML5 Web Sockets provides such a dra- matic improvement from the old, convoluted “hacks” that are used to simulate a full-duplex connection in a browser that it prompted Google’s Ian Hickson – the HTML5 specifica- tion lead – to say: Let’s take a look at how HTML5 Web Sockets can offer such an incredibly dramatic reduc- tion of unnecessary network traffic and laten- cy by comparing it to conventional solutions. Polling, Long-Polling, and Streaming – Headache 2.0 Normally when a browser visits a web page, an HTTP request is sent to the web server that hosts that page. The web server ac- knowledges this request and sends back the response.  In many cases – for example, for stock prices, news reports, ticket sales, traffic patterns, medical device readings, and so on – the response could be stale by the time the browser renders the page. If you want to get the most up-to-date “real-time” information, you can constantly refresh that page manu- ally, but that’s obviously not a great solution. Current attempts to provide real-time web applications largely revolve around polling and other server-side push technologies, the most notable of which is Comet, which de- lays the completion of an HTTP response to deliver messages to the client. Comet-based push is generally implemented in JavaScript and uses connection strategies such as long- polling or streaming. With polling, the browser sends HTTP requests at regular intervals and immediately receives a response.  This technique was the first attempt for the browser to deliver real- time information. Obviously, this is a good solution if the exact interval of message de- livery is known, because you can synchronize the client request to occur only when infor- mation is available on the server. However, real-time data is often not that predictable, making unnecessary requests inevitable and as a result, many connections are opened and closed needlessly in low-message-rate situa- tions. With long-polling, the browser sends a re- quest to the server and the server keeps the request open for a set period. If a notifica- tion is received within that period, a response containing the message is sent to the client. If a notification is not received within the set time period, the server sends a response to terminate the open request. It is important to understand, however, that when you have a high message volume, long-polling does not provide any substantial performance im- provements over traditional polling. In fact, it could be worse, because the long-polling might spin out of control into an unthrottled, continuous loop of immediate polls. With streaming, the browser sends a com- plete request, but the server sends and main- tains an open response that is continuously updated and kept open indefinitely (or for a set period of time). The response is then up- dated whenever a message is ready to be sent, but the server never signals to complete the response, thus keeping the connection open to deliver future messages. However, since streaming is still encapsulated in HTTP, in- tervening firewalls and proxy servers may choose to buffer the response, increasing the latency of the message delivery. Therefore, many streaming Comet solutions fall back to long-polling in case a buffering proxy server is detected. Alternatively, TLS (SSL) connec- tions can be used to shield the response from being buffered, but in that case the setup and tear down of each connection taxes the avail- able server resources more heavily. Ultimately, all of these methods for pro- “Reducing kilobytes of data to 2 bytes… and reducing latency from 150ms to 50ms is far more than marginal. In fact, these two factors alone are enough to make Web Sockets seriously interesting to Google.” ARCHITECTURE 4 viding real-time data involve HTTP request and response headers, which contain lots of additional, unnecessary header data and in- troduce latency. On top of that, full-duplex connectivity requires more than just the downstream connection from server to cli- ent. In an effort to simulate full-duplex com- munication over half-duplex HTTP, many of today’s solutions use two connections: one for the downstream and one for the upstream. The maintenance and coordination of these two connections introduces significant over- head in terms of resource consumption and adds lots of complexity. Simply put, HTTP wasn’t designed for real-time, full-duplex communication as you can see in the follow- ing figure, which shows the complexities asso- ciated with building a Comet web application that displays real-time data from a back-end data source using a publish/subscribe model over half-duplex HTTP. Figure 1 – The complexity of Comet applications It gets even worse when you try to scale out those Comet solutions to the masses. Simu- lating bi-directional browser communica- tion over HTTP is error-prone and complex and all that complexity does not scale. Even though your end users might be enjoying something that looks like a real-time web ap- plication, this “real-time” experience has an outrageously high price tag. It’s a price that you will pay in additional latency, unneces- sary network traffic and a drag on CPU per- formance. HTML5 Web Sockets to the Rescue! Defined in the Communications section of the HTML5 specification, HTML5 Web Sockets represents the next evolution of web communications – a full-duplex, bidirec- tional communications channel that operates through a single socket over the Web. HTML5 Web Sockets provides a true standard that you can use to build scalable, real-time web applications. In addition, since it provides a socket that is native to the browser, it elimi- nates many of the problems Comet solutions are prone to. Web Sockets removes the over- head and dramatically reduces complexity. To establish a WebSocket connection, the client and server upgrade from the HTTP protocol to the WebSocket protocol during their initial handshake, as shown in the fol- lowing example: Example 1 – The WebSocket handshake (browser request and server response) GET /text HTTP/1.1\r\n Upgrade: WebSocket\r\n Connection: Upgrade\r\n Host:\r\n …\r\n HTTP/1.1 101 WebSocket Protocol Handshake\r\n Upgrade: WebSocket\r\n Connection: Upgrade\r\n …\r\n Once established, WebSocket data frames can be sent back and forth between the client and the server in full-duplex mode. Both text and binary frames can be sent full-duplex, in ei- ther direction at the same time. The data is minimally framed with just two bytes. In the case of text frames, each frame starts with a 0x00 byte, ends with a 0xFF byte, and con- tains UTF-8 data in between. WebSocket text frames use a terminator, while binary frames use a length prefix. Note: although the Web Sockets protocol is ready to support a diverse set of clients, it cannot deliver raw binary data to JavaScript, because JavaScript does not support a byte type. Therefore, binary data is ignored if the client is JavaScript – but it can be delivered to other clients that support it. The Showdown: Comet vs. HTML5 Web Sockets So how dramatic is that reduction in unnec- essary network traffic and latency? Let’s com- pare a polling application and a WebSocket application side by side. For the polling example, I created a simple web application in which a web page requests real-time stock data from a RabbitMQ mes- sage broker using a traditional publish/sub- scribe model. It does this by polling a Java Servlet that is hosted on a web server. The RabbitMQ message broker receives data from a fictitious stock price feed with continuously updating prices. The web page connects and subscribes to a specific stock channel (a topic on the message broker) and uses an XML- HttpRequest to poll for updates once per second. When updates are received, some calculations are performed and the stock data is shown in a table as shown in the following image. Figure 2 – A JavaScript stock ticker application Note: The back-end stock feed actually pro- duces a lot of stock price updates per second, so using polling at one-second intervals is actually more prudent than using a Comet long-polling solution, which would result in a series of continuous polls. Polling effectively throttles the incoming updates here. It all looks great, but a look under the hood reveals there are some serious issues with this application. For example, in Mozilla Firefox Internet LAN Your RIA client application Silverlight or Flash plug-in Browser Lots to build Costly server resources devoted to translating LAN protocol to HTTP Can’t manage the actual client — end user and data source aren’t really connected Messy, slow, error prone HTTP - Long polling, etc.) Messaging Broker $ datasource App Server HTTP Server AMQP Client Custom code to simulate a realtime 2-way connection C O M P A N Y SYMBOL PRICE CHANGE SPARKLINE OPEN LOW HIGH THE WALT DISNEY COMPANY DIS 27.65 0.56 27.09 24.39 29.80 GARMIN LTD. GRMN 35.14 0.35 34.79 31.31 38.27 SANDISK CORPORATION SNDK 20.11 -0.13 20.24 18.22 22.26 GOODRICH CORPORATION GR 49.99 -2.35 52.34 47.11 57.57 NVIDIA CORPORATION NVDA 13.92 0.07 13.85 12.47 15.23 CHEVRON CORPORATION CVX 67.77 -0.53 68.30 61.49 75.11 THE ALLSTATE CORPORATION ALL 30.88 -0.14 31.02 27.92 34.12 EXXON MOBIL CORPORATION XOM 65.66 -0.86 66.52 59.87 73.17 METLIFE INC. MET 35.58 -0.15 35.73 32.16 39.30 Figure 1 Figure 2 ARCHITECTURE 5 with Firebug (a Firefox add-on that al- lows you to debug web pages and monitor the time it takes to load pages and execute scripts), you can see that GET requests hammer the server at one-second intervals. Turning on Live HTTP Headers (another Firefox add-on that shows live HTTP header traffic) reveals the shocking amount of head- er overhead that is associated with each re- quest. The following two examples show the HTTP header data for just a single request and response. Example 2 – HTTP request header GET /PollingStock//PollingStock HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv: Gecko/20091102 Firefox/3.5.5 Accept: text/html,application/xhtml+xml,application/ xml;q=0.9,*/*;q=0.8 Accept-Language: en-us Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive Referer: Cookie: showInheritedConstant=false; showInheritedProtectedConst ant=false; showInheritedProperty=false; showInheritedProtectedPr operty=false; showInheritedMethod=false; showInheritedProtectedM ethod=false; showInheritedEvent=false; showInheritedStyle=false; showInheritedEffect=false Example 3 – HTTP response header HTTP/1.x 200 OK X-Powered-By: Servlet/2.5 Server: Sun Java System Application Server 9.1_02 Content-Type: text/html;charset=UTF-8 Content-Length: 21 Date: Sat, 07 Nov 2009 00:32:46 GMT Just for fun, I counted all the characters. The total HTTP request and response header information overhead contains 871 bytes and that does not even include any data! Of course, this is just an example and you can have less than 871 bytes of header data, but I have also seen cases where the header data exceeded 2000 bytes. In this example applica- tion, the data for a typical stock topic message is only about 20 characters long. As you can see, it is effectively drowned out by the exces- sive header information, which was not even required in the first place! So, what happens when you deploy this application to a large number of users? Let’s take a look at the network throughput for just the HTTP request and response header data associated with this polling application in three different use cases. • Use case A: 1,000 clients polling every second: Network throughput is (871 x 1,000) = 871,000 bytes = 6,968,000 bits per second (6.6 Mbps) • Use case B: 10,000 clients polling every second: Network throughput is (871 x 10,000) = 8,710,000 bytes = 69,680,000 bits per sec- ond (66 Mbps) • Use case C: 100,000 clients polling every 1 second: Network throughput is (871 x 100,000) = 87,100,000 bytes = 696,800,000 bits per second (665 Mbps) That’s an enormous amount of unnecessary network throughput! If only we could just get the essential data over the wire. Well, guess what? You can with HTML5 Web Sock- ets! I rebuilt the application to use HTML5 Web Sockets, adding an event handler to the web page to asynchronously listen for stock update messages from the message broker (check out the many how-tos and tutorials on for more information on how to build a Web- Socket application). Each of these messages is a WebSocket frame that has just two bytes of overhead (instead of 871). Take a look at how that affects the network throughput overhead in our three use cases. • Use case A: 1,000 clients receive 1 message per second: Network throughput is (2 x 1,000) = 2,000 bytes = 16,000 bits per second (0.015 Mbps) • Use case B: 10,000 clients receive 1 mes- sage per second: Network throughput is (2 x 10,000) = 20,000 bytes = 160,000 bits per second (0.153 Kbps) • Use case C: 100,000 clients receive 1 mes- sage per second: Network throughput is (2 x 100,000) = 200,000 bytes = 1,600,000 bits per second (1.526 Kbps) As you can see in the following figure, HTML5 Web Sockets provide a dramatic reduction of unnecessary network traffic compared to the polling solution. Figure 3 – Comparison of the unnecessary network throughput overhead between the polling and the WebSocket applications And what about the reduction in latency? Take a look at the following figure. In the top half, you can see the latency of the half-duplex polling solution. If we assume, for this exam- ple, that it takes 50 milliseconds for a message to travel from the server to the browser, then the polling application introduces a lot of ex- tra latency, because a new request has to be sent to the server when the response is com- plete. This new request takes another 50ms and during this time the server cannot send any messages to the browser, resulting in ad- ditional server memory consumption. In the bottom half of the figure, you see the reduction in latency provided by the WebSocket solution. Once the connection is upgraded to WebSocket, messages can flow from the server to the browser the moment they arrive. It still takes 50 ms for messages to travel from the server to the browser, but the WebSocket connection remains open so there is no need to send another request to the server. ARCHITECTURE 6 HTML5 Web Sockets and the Kaazing WebSocket Gateway Today, only Google’s Chrome browser sup- ports HTML5 Web Sockets natively, but oth- er browsers will soon follow. To work around that limitation, however, Kaazing WebSocket Gateway provides complete WebSocket emu- lation for all the older browsers (I.E. 5.5+, Firefox 1.5+, Safari 3.0+, and Opera 9.5+), so you can start using the HTML5 WebSocket APIs today. WebSocket is great, but what you can do once you have a full-duplex socket connec- tion available in your browser is even greater. To leverage the full power of HTML5 Web Sockets, Kaazing provides a ByteSocket li- brary for binary communication and high- er-level libraries for protocols like Stomp, AMQP, XMPP, IRC and more, built on top of WebSocket. For example, if you use a high-level library for protocols such as Stomp or AMQP (sup- plied with the Kaazing Gateway), you can talk directly to backend message brokers like the RabbitMQ broker described in the exam- ple. By having a direct connection to services, there is no need for additional application server logic to translate those bi-directional, full-duplex TCP backend protocols to uni- directional, half-duplex HTTP connections; the browser can simply understand these pro- tocols natively. Summary HTML5 Web Sockets provides an enormous step forward in the scalability of the real-time web. As you have seen in this article, HTML5 Web Sockets can provide a 500:1 or – depend- ing on the size of the HTTP headers – even a 1000:1 reduction in unnecessary HTTP head- er traffic and 3:1 reduction in latency. That is not just an incremental improvement; that is a revolutionary jump – a quantum leap! Kaazing WebSocket Gateway makes HTML5 WebSocket code work in all the browsers today, while providing additional protocol libraries that allow you to harness the full power of the full-duplex socket con- nection that HTML5 Web Sockets provides and communicate directly to back-end ser- vices. For more information about Kaazing WebSocket Gateway, visit and the Kaazing technology network at 700,000,000 600,000,000 500,000,000 400,000,000 300,000,000 200,000,000 100,000,000 0 Bits per second Polling Web Sockets Use Case A Use Case B Use Case C 6,968,000 16,000 69,680,000 160,000 696,800,000 1,600,000 Request 1 Response 1 Request 2 Response 2 Request n Response n WebSocket Upgrade WebSocket Frame 1 WebSocket Frame 2 WebSocket Frame 3 WebSocket Frame 4 WebSocket Frame n Server Message MessageMessage Web Sockets Polling Browser Server Browser MessageMessageMessageMessageMessage Time 50ms 100ms 150ms 200ms 250ms Time 50ms 100ms 150ms 200ms 250ms Messaging Service Kaazing WebSocket Gateway Internet Trading Gateway News Feed Payment System Database Storage Application Logic Web Service ERP/CRM System Desktop Solution Figure 4 – Latency comparison between the polling and WebSocket applications Figure 5 – Kaazing WebSocket Gateway extends TCP-based messaging to the browser with ultra high performance Figure 3 Figure 4 Figure 5 ARCHITECTURE 7 About the Authors Peter Lubbers is the Director of Documen- tation and Training at Kaazing where he oversees all aspects of documentation and training. Peter is the co-author of the Apress book Pro HTML5 Programming and teaches HTML5 training courses. An HTML5 and WebSocket enthusiast, Peter frequently speaks at international events. Prior to joining Kaazing, Peter worked as an information architect at Oracle, where he wrote many books, such as the award-win- ning Oracle Application Server Portal Con- figuration Guide and the Oracle Application Server Developer’s Guide for Microsoft Of- fice. Peter also develops documentation au- tomation solutions and two of his inventions are patented. Before joining Oracle, Peter architected and developed the internationalized Micro- soft Office User Specialist Testing Frame- work. Peter was also a technical reviewer for the book “Pro JSF and Ajax: Building Rich Internet Components” (Apress, 2006). A native of the Netherlands, Peter served as a Special Forces commando in the Royal Dutch Green Berets. In his spare time (ha!) Peter likes to run ultra-marathons. He is the 2007 and 2009 series champi- on and three-time winner of the Tahoe Super Triple marathon. Peter lives on the edge of the Tahoe National Forest and loves to run in the Sierra Nevada foothills and around Lake Ta- hoe (preferably in one go!). Frank Greco is the founder of the New York Java Special Interest Group (NYJava- SIG), one of the largest Java Users Groups (JUGs) on the planet with almost 5,000 active members in the local Java community. The References • HTML5 Web Sockets Specification: • • Kaazing corporation: • Download the Kaazing WebSocket Gateway: • Skills Matter HTML5 Communication Training Courses: • Book: Pro HTML5 Programming: Powerful APIs for Richer Internet Application Development (Link) Frank Greco NYJavaSIG has had some of the most famous Java luminaries speak at their meetings; in- cluding Java Champions: Rod Johnson, Brian Goetz, Doug Lea and Josh Bloch. Their mem- bers are very enthusiastic and, like most New Yorkers, don’t hesitate to ask tough questions of their monthly speakers. Frank has a long history as a “Champion” of the Java Platform; he taught a developer track session at the very first Java Day back in September 1995 in New York and started the NYJavaSIG that afternoon. Frank has been involved with software development for over 10 years and has worked on sophisticated ar- chitectures, innovative user interfaces, mobile computing and next-generation collaborative financial systems. Frank is both a Java com- munity leader as well as a luminary technolo- gist working for Kaazing. Peter Lubbers ARCHITECTURE 8 Setting up an OpenGL ES View Setting up an OpenGL view has never been hard, and on Android it is still very easy. There are only two things you need to get started. The surface to draw on and the class that draws. GLSurfaceView GLSurfaceView is a API class in Android 1.5 and later, that helps you write OpenGL ES applications. It’s a really good start because it connects the OpenGL ES to the view system. It is also constructed to fit in with the Activity life-cycle. If you want to get going fast with your OpenGL ES application this is where you should start. The only thing you need to do when you create a GLSurfaceView is to tell what render- er to use. It’s done with this function: public void setRenderer( GLSurfaceView.Renderer renderer) GLSurfaceView.Renderer GLSurfaceView.Renderer is a generic ren- der interface. In your implementation of this renderer you should put all your calls to the OpenGL ES system. There are three functions to implement in this interface: onSurfaceCreated onSurfaceCreated is called when the surface is created or recreated. Here you should put the things you only want to call on once or OpenGL ES on Android Open Graphics Library Embedded Systems or OpenGL ES is the most obvious way to go if you want 3D on your device. As a former game developer I am thrilled about being able to write 3D graphics on my device. This article is an attempt to get you going with your own 3D creations. Per-Erik Bergman/Jayway EMBEDDED 9 things that you don’t need to change within the rendering cycle. Turning on and off the z-buffer or setting the color that OpenGL ES will use to clear the screen with are typical things to have here. public void onSurfaceCreated( GL10 gl, EGLConfig config) onDrawFrame onDrawFrame is called every time the cur- rent frame should be drawn. You put every- thing that you want to be drawn here. public void onDrawFrame(GL10 gl) onSurfaceChanged onSurfaceChanged is called when the sur- face changes size. If you flip the device and it changes orientation this function is called. A good thing to put here is the ratio calcula- tions. Since OpenGL ES by default have the ratio 1:1 it will look weird unless you have a square display. public void onSurfaceChanged( GL10 gl, int width, int height) Putting it together First thing that we need to do is to create our Activity. Most times this is done when you create the project. I call this Activity Open- GLExample. GLSurfaceView package se.jayway.opengl.tutorial; import; import android.opengl.GLSurfaceView; import android.os.Bundle; public class OpenGLExample extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GLSurfaceView view = new GLSurfaceView(this); view.setRenderer(new OpenGLRenderer()); setContentView(view); } } GLSurfaceView.Renderer The renderer takes a little bit more work to setup. EMBEDDED 10 package se.jayway.opengl.tutorial; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLU; import android.opengl.GLSurfaceView.Renderer; public class OpenGLRenderer implements Renderer { /* * (non-Javadoc) * * @see * android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(javax. * microedition.khronos.opengles.GL10, javax.microedition.khronos. * egl.EGLConfig) */ public void onSurfaceCreated(GL10 gl, EGLConfig config) { // Set the background color to black ( rgba ). gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // OpenGL docs. // Enable Smooth Shading, default not really needed. gl.glShadeModel(GL10.GL_SMOOTH);// OpenGL docs. // Depth buffer setup. gl.glClearDepthf(1.0f);// OpenGL docs. // Enables depth testing. gl.glEnable(GL10.GL_DEPTH_TEST);// OpenGL docs. // The type of depth testing to do. gl.glDepthFunc(GL10.GL_LEQUAL);// OpenGL docs. // Really nice perspective calculations. gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, // OpenGL docs. GL10.GL_NICEST); } /* * (non-Javadoc) * * @see * android.opengl.GLSurfaceView.Renderer#onDrawFrame(javax. * microedition.khronos.opengles.GL10) */ public void onDrawFrame(GL10 gl) { // Clears the screen and depth buffer. gl.glClear(GL10.GL_COLOR_BUFFER_BIT | // OpenGL docs. GL10.GL_DEPTH_BUFFER_BIT); } /* * (non-Javadoc) * * @see * android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(javax. * microedition.khronos.opengles.GL10, int, int) */ public void onSurfaceChanged(GL10 gl, int width, int height) { // Sets the current view port to the new size. gl.glViewport(0, 0, width, height);// OpenGL docs. // Select the projection matrix gl.glMatrixMode(GL10.GL_PROJECTION);// OpenGL docs. // Reset the projection matrix gl.glLoadIdentity();// OpenGL docs. // Calculate the aspect ratio of the window EMBEDDED 11 GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f, 100.0f); // Select the modelview matrix gl.glMatrixMode(GL10.GL_MODELVIEW);// OpenGL docs. // Reset the modelview matrix gl.glLoadIdentity();// OpenGL docs. } } Fullscreen Just add these lines in the OpenGLExample class and you will get fullscreen. public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestWindowFeature(Window.FEATURE_NO_TITLE); // (NEW) getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // (NEW) ... // Previous code. } This is pretty much all you need to get your view up and running. If you compile and run it you will see a nice black screen. Building a polygon 3D models are built up with smaller elements (vertices, edges, faces, and polygons) which can be manipulated individually. Vertex A vertex (vertices in plural) is the smallest building block of a 3D model. A vertex is a point where two or more edges meet. In a 3D model a vertex can be shared between all con- nected edges, faces and polygons. A vertex can also represent the position of a camera or a light source. You can see a vertex in the im- age to the right marked in yellow. To define the vertices on android we de- fine them as a float array that we put into a byte buffer to gain better performance. Look at the image and code to the right and match the vertices marked on the image to the code. private float vertices[] = { // 0, Top Left -1.0f, 1.0f, 0.0f, // 1, Bottom Left -1.0f, -1.0f, 0.0f, // 2, Bottom Right 1.0f, -1.0f, 0.0f, // 3, Top Right 1.0f, 1.0f, 0.0f, }; Vertex EMBEDDED Don’t forget that a float is 4 bytes and to multi- ply it by the number of vertices to get the right size on the allocated buffer. // a float is 4 bytes, therefore we // multiply the number of // vertices by 4. ByteBuffer vbb = ByteBuffer.allocateDirect( vertices.length * 4); vbb.order( ByteOrder.nativeOrder()); FloatBuffer vertexBuffer = vbb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); 12 OpenGL ES has a pipeline with functions to apply when you tell it to render. Most of these functions are not enabled by default so you have to remember to turn on the ones you like to use. You might also need to tell these functions what to work with. So in the case of our vertices we need to tell OpenGL ES that it’s okay to work with the vertex buffer we cre- ated and we also need to tell where it is. // Enabled the vertex buffer for writing and to be used during rendering. gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); // Specifies the location and data format of an array of vertex // coordinates to use when rendering. gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); When you are done with the buffer don’t for- get to disable it. // Disable the vertices buffer. gl.glDisableClientState( GL10.GL_VERTEX_ARRAY); Edge An edge is a line between two vertices. They are border lines of faces and polygons. In a 3D model an edge can be shared between two adjacent faces or polygons. Transforming an edge affects all connected vertices, faces and polygons. In OpenGL ES you don’t define the edges, you rather define the face by giv- ing them the vertices that would build up the three edges. If you would like modify an edge you change the two vertices that makes the edge. You can see an edge in the image to the right marked in yellow. Face A face is a triangle, a surface between three corner vertices and three surrounding edges. Transforming a face affects all connected ver- tices, edges and polygons. The order does matter When winding up the faces it’s important to do it in the right direction, because the direc- tion defines what side will be the front face and what side will be the back face. Why this is important is because to gain performance we don’t want to draw both sides so we turn off the back face. So it’s a good idea to use the same winding all over your project. It’s pos- sible to change what direction that defines the front face with glFrontFace. gl.glFrontFace(GL10.GL_CCW); To make OpenGL skip the faces that are turned into the screen, you can use something called back-face culling. This determines whether a polygon of a graphical object is visible by check- ing if the face is wound up in the right order. Edge Face EMBEDDED 13 gl.glEnable(GL10.GL_CULL_FACE); It’s of cource possible to change what face side should be drawn or not. gl.glCullFace(GL10.GL_BACK); Polygon Time to wind the faces. Remember, we have decided to go with the default winding, meaning counter-clockwise. Look at the im- age and the code to the right to see how to wind up this square. private short[] indices = { 0, 1, 2, 0, 2, 3 }; To gain some performance we also put these ones in a byte buffer. // short is 2 bytes, therefore we multiply the number of vertices by 2. ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2); ibb.order(ByteOrder.nativeOrder()); ShortBuffer indicesBuffer = ibb.asShortBuffer(); indicesBuffer.put(indices); indicesBuffer.position(0); Don’t forget that a short is 2 bytes and to mul- tiply it by the number of indices to get the right size on the allocated buffer. Render Time to get something on the screen, there are two functions used to draw and we have to decide which one to use. The two functions are: public abstract void glDrawArrays(int mode, int first, int count) glDrawArrays draws the vertices in the order they are specified in the construction of our verticesBuffer. public abstract void glDrawElements( int mode, int count, int type, Buffer indices) glDrawElements need a little bit more infor- mation to be able to draw. It needs to know the order in which to draw the vertices, it needs the indicesBuffer. Since we already created the indicesBuffer I’m guessing that you’ve figured out that’s the way we are going. What’s common with these functions is that they both need to know what it is they should draw, what primitives to render. Since there are various ways to render the indices and some of them are good to know about for debugging reasons, I’ll go through them all. Polygon EMBEDDED 14 What primitives to render GL_POINTS Draws individual points on the screen. GL_LINE_STRIP Series of connected line segments. GL_LINE_LOOP Same as above, with a segment added be- tween last and first vertices. GL_LINES Pairs of vertices interpreted as individual line segments. GL_TRIANGLES Triples of vertices interpreted as triangles. GL_TRIANGLE_STRIP Draws a series of triangles (three-sided poly- gons) using vertices v0, v1, v2, then v2, v1, v3 (note the order), then v2, v3, v4, and so on. The ordering is to ensure that the triangles are all drawn with the same orientation so that the strip can correctly form part of a surface. GL_TRIANGLE_FAN Same as GL_TRIANGLE_STRIP, except that the vertices are drawn v0, v1, v2, then v0, v2, v3, then v0, v3, v4, and so on. I think that the GL_TRIANGLES is the easiest to use so we go with that one for now. v1 v2 v3v4 v7 v5v6 v0 v1 v2 v3v4 v7 v5v6 v0 v1 v2 v3v4 v7 v5v6 v0 v1 v2 v3v4 v7 v5v6 v0 v1 v2 v3 v4 v5 v0 v1 v2 v3 v4 v5 v0 v1 v2 v3 v4 v5 v0 EMBEDDED 15 Putting it all together So let’s put our square together in a class. package se.jayway.opengl.tutorial; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.opengles.GL10; public class Square { // Our vertices. private float vertices[] = { -1.0f, 1.0f, 0.0f, // 0, Top Left -1.0f, -1.0f, 0.0f, // 1, Bottom Left 1.0f, -1.0f, 0.0f, // 2, Bottom Right 1.0f, 1.0f, 0.0f, // 3, Top Right }; // The order we like to connect them. private short[] indices = { 0, 1, 2, 0, 2, 3 }; // Our vertex buffer. private FloatBuffer vertexBuffer; // Our indice buffer. private ShortBuffer indicesBuffer; public Square() { // a float is 4 bytes, therefore we multiply the number of // vertices by 4. ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); vertexBuffer = vbb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); // short is 2 bytes, therefore we multiply the number of // vertices by 2. ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2); ibb.order(ByteOrder.nativeOrder()); indicesBuffer = ibb.asShortBuffer(); indicesBuffer.put(indices); indicesBuffer.position(0); } /** * This function draws our square on screen. * @param gl */ public void draw(GL10 gl) { // Counter-clockwise winding. gl.glFrontFace(GL10.GL_CCW); // Enable face culling. gl.glEnable(GL10.GL_CULL_FACE); // What faces to remove with the face culling. gl.glCullFace(GL10.GL_BACK); // Enable the vertices buffer for writing and to be used during // rendering. gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); // Specifies the location and data format of an array of vertex EMBEDDED 16 // coordinates to use when rendering. gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_SHORT, indicesBuffer); // Disable the vertices buffer. gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); // Disable face culling. gl.glDisable(GL10.GL_CULL_FACE); } } We have to initialize our square in the OpenGLRenderer class. // Initialize our square. Square square = new Square(); And in the draw function call on the square to draw. public void onDrawFrame(GL10 gl) { // Clears the screen and depth buffer. gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // Draw our square. square.draw(gl); // ( NEW ) } If you run the application now the screen is still black. Why? Because OpenGL ES renders from where the current position is, that by default is at point: 0, 0, 0 the same position that the view port is located. And OpenGL ES doesn’t render the things that are too close to the view port. The solution to this is to move the draw position a few steps into the screen before rendering the square: // Translates 4 units into the // screen. gl.glTranslatef(0, 0, -4); I will talk about the different transformations in the next tutorial. Run the application again and you will see that the square is drawn but quickly moves further and further into the screen. OpenGL ES doesn’t reset the drawing point between the frames. That you will have to do yourself: // Replace the current matrix with // the identity matrix gl. glLoadIdentity(); Now if you run the application you will see the square on a fixed position. References The info used in this tutorial is collected from: • Android Developers | • OpenGL ES 1.1 Reference Pages | If you are interested in more information, go to our team blog at: EMBEDDED 17 Microlog, Logging in Java ME – An in-depth look at the Bluetooth features Microlog is an open-source logging library that consists of a large number of features. It will feel familiar to anyone who has used Log4J before since it provides very similar APIs and configuration options. Built and designed for small and resource constrained devices, it is specifically created to provide a lightweight and flexible logging solution for Java ME. The project is licensed under the Apache Software License V2 and is therefore considered safe to use in both open-source and commercial proprietary software. Jarle Hansen/Brunel University & EDB Business Partner EMBEDDED 18 This article will start with a quick introduc- tion on how you use Microlog and the basic features. Then, after the basics a more in- depth look at the Bluetooth features is pre- sented. All examples in this article was cre- ated with Microlog version 2.2.4. The Basics Microlog can be configured in several ways. You have the option to hard code the append- ers and patterns you want to use directly into the code. This is obviously not recommended as it becomes hard to change. For all updates you would have to re-compile the code. Also, mixing configuration parameters and appli- cation logic is often not a good practice in general. The best ways to configure Microlog is to either use a properties file or add the con- figuration values to the JAD-file of the proj- ect. The examples shown in this article only use a properties-file for the configuration. If you want to use the JAD-file you simply add the MidletPropertyConfigurator instead of the PropertyConfigurator. There are a large number of appenders available in Microlog. This article gives a de- tailed description of the BluetoothSerialAp- pender, but the table to the right shows the different options that are available. Let’s start by looking at a very simple ex- ample that consists of a MIDlet and a micro- file. A simple microlog.proper- ties example: microlog.level=DEBUG microlog.appender=ConsoleAppender microlog.formatter=SimpleFormatter A very simple logger MIDlet: Appender Description ConsoleAppender Use the emulator and log in the output win- dow. CanvasAppender View the log on the device. FormAppender View the log on the device. RecordStoreAppender View the log on the device. FileAppender To have a log-file to view on your computer. MemoryBufferAppender View the log on the device. BluetoothSerialAppender Log from the device to your computer and view the log in real time. HttpAppender Log to a HTTP server. SMSBufferAppender Send an SMS when something goes wrong. MMSBufferAppender Send an MMS or e-mail when something goes wrong. SocketAppender Log to a socket server. SyslogAppender Log to a syslog daemon on a remote server. A syslog daemon is a standard logging server that is available on most common operating systems. S3BufferAppender/S3FileAppender Log to the cloud, such as an Amazon S3 ac- count. Any S3 browser can view the file. EMBEDDED public class LoggingMidlet extends MIDlet implements CommandListener { private static final Logger logger = LoggerFactory.getLogger(LoggingMidlet.class); private final Form form = new Form(“LoggingMidlet”); private final Command logCommand = new Command(“Log”, Command.OK, 1); private final Command exitCommand = new Command(“Exit”, Command.EXIT, 1); public LoggingMidlet() { // Configure Microlog PropertyConfigurator.configure(); } protected void startApp() throws MIDletStateChangeException { form.addCommand(logCommand); form.addCommand(exitCommand); form.setCommandListener(this); Display.getDisplay(this).setCurrent(form);“MIDlet started”); } 19 In the constructor of the MIDlet the microlog. properties file is loaded. By using the MIDlet constructor you are certain that it is only ex- ecuted once. The application will simply log a text when it starts and then the user can ei- ther push the “Log”-button to create new log statements or the “Exit”-button to shutdown the application. In the destroyApp() method LoggerFactory.shutDown() is called. This will shutdown Microlog gracefully by closing all open resources before exiting. The Logger variable is added as a static fi- nal constant with the class name as input. By simply calling logger.fatal/error/warn/info/ debug/trace you are able to log a message. It is also possible to log an exception that has occurred in the application by using the same method names as previously mentioned. These methods take two input values, the log message and the exception. logger.debug(“Unable to open the “ + “network connection”, throwableobj); The microlog.formatter-property is used to select the format of the log output. In the mi- shown in the example we used the SimpleFormatter. As the name sug- gests this provides a simple format that uses a default pattern to print the output messages. 2222:[DEBUG]-The Log button was pushed If you want to change the format it is possible to customize the output. Maybe you want the date and the classname in the beginning of all log statements, this is where the PatternFor- matter comes in handy. This formatter gives you the opportunity to provide your own pat- tern that is used to format the log statements. The PatternFormatter is easily configured by adding these properties to your configuration: microlog.formatter=PatternFormatter microlog.formatter.PatternFormatter .pattern=%d %c [%P] %m %T EMBEDDED protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {“Exiting MIDlet”); LoggerFactory.shutdown(); } protected void pauseApp() {} public void commandAction(final Command command, final Displayable displayable) { if(command == logCommand) { logger.debug(“The Log button was pushed”); } else if(command == exitCommand) { logger.debug(“The Exit button was pushed”); notifyDestroyed(); } } } 20 The ConsoleAppender now prints the log statements like this: 17:20:11,121 LoggingMidlet [DEBUG] The Log button was pushed The PatternFormatter provides many cus- tomization options. Take a look at the table below and use them in the combination that works best for you. Pattern Prints %i The client id %c The name of the logger %d The absolute time %m The logged message %P The priority, i.e. level of the message %r The relative time of the logging %t The thread Name %T The throwable object Now that you have seen the basics of what Microlog can do, lets move on to one of the really exciting features; the Bluetooth Ap- pender. Using the Bluetooth logging features Using Bluetooth for logging in Java ME de- vices provides several benefits: • Most mobile phones include Bluetooth capabilities so hardware support is usually not an issue. • The Microlog Bluetooth GUI provides real-time logging. You will be able to monitor your application when it is run- ning and do not need to read a file or the RMS memory (Record Management Store, using the RecordStoreAppender) after the application is closed. • You can connect several devices to the Bluetooth server at the same time. • It is easy to set up. Microlog handles all the network communication for you. • Connecting and sending information with Bluetooth to the Microlog Server is fast. This section provides a few useful tips on how you can get the most out of the Bluetooth fea- tures that are available in Microlog. Be aware that the Bluetooth appender is not always the best choice. In certain cases you might be de- pendent on the Bluetooth connection in your own application. Microlog will in these cases affect the overall Bluetooth performance in the system and it might be better to use other features like HttpAppender or RecordSto- reAppender. With that being said, the Blue- tooth appender does provide several interest- ing features and will in many situations be one of the best ways to use Microlog. The receiving application The Microlog distribution comes packaged with an application that receives the logs called Microlog Bluetooth Server. This is a Java SE application (requiring Java 1.5 or newer) and runs perfectly on both Windows and Linux. For all Bluetooth communication the server application uses an open-source li- brary called BlueCove ( EMBEDDED 21 The Microlog Bluetooth Server has several useful features. When you start the applica- tion it will present the Server URL. This is the entire connection String and includes the Bluetooth address. It is also possible to find the Bluetooth address by pressing Help -> Server Bluetooth address. This is important, since you should use this address in your cli- ent configuration. When a client connects a new entry will appear on the left side of the window, “Nokia-e61” in the screenshot. The name that is shown is the Bluetooth name of your device. It is possible to connect several clients simultaneously and any new connec- tions will just be shown in this list. All logging data is added to the main part of the window. This text area that consists of the logging statements is updated runtime. Any new log messages received from the clients are added instantly to the UI. It is also possible to save the log content to a file, you can save it as a txt-file and open it in your favourite editor later. A very convenient option since text highlighting and search functions in external editors are a powerful and very useful tool. In the example MIDlet previously shown in this article the LoggerFactory.shutDown() method was used when exiting the MIDlet. This will make sure all resources are cleaned up properly. For the BluetoothSerialAppend- er this will close the open connection and the Microlog Bluetooth server will remove the mobile device from the list of connected cli- ents. Configuration As previously mentioned we recommend us- ing a properties- or JAD-file to configure the appenders. One of the most productive ways of using Microlog in the development stage of your project is to add both the Console- Appender and BluetoothSerialAppender as shown in the example below. The advantage of this setup is flexibility. When you run the MIDlet in an emulator you will be able to view all logging statements in the Console. Then, when you decide to deploy the applica- tion on the mobile device the BluetoothSeri- alAppender will be used. No need to change the configuration, it will simply work on both the emulator and mobile device with exactly the same configuration. using both the Blue- toothSerialAppender and ConsoleAppender: microlog.level=DEBUG microlog.appender=BluetoothSerialAppender;ConsoleAppender microlog.formatter=SimpleFormatter microlog.appender.BluetoothSerialAppender.btAddress=002608BDB48C Another important aspect to notice in this configuration is the microlog.appender.Blue- toothSerialAppender.btAddress-property. This tells Microlog where it should connect to, the server Bluetooth address. Adding this property in an external resource and not in the actual source code makes it much easier to change in the future. In most cases there are no differences in your code if you are using a ConsoleAppender or BluetoothSeri- alAppender, or any other appender for that matter. This is how it should be, you should be able to change the appender in the configura- tion file and it should simply work. Summary Using a logging library is in most software development projects a very useful tool to help the developer understand exactly what is happening in an executing application and is especially useful when there are bugs and other problems. For Java ME projects the Mi- crolog open-source library is available to pro- vide a flexible and Log4J-like structure to the logging and configuration. This article gave a quick overview of the basics and then contin- ued by going more in-depth when looking at the Bluetooth logging features. Hopefully you have gained little insight into how Microlog works and how to use it in your own projects. The main recommenda- tions presented in this article are: 1. Using multiple appenders is a very useful and flexible way of configuring Microlog. One good tip is to use the both the Con- soleAppender and BluetoothSerialAp- pender in the development stage of your project to get log statements sent directly to your computer. This configuration will work in an emulator as well as on a real device by using the Microlog Bluetooth server. 2. Use an external resource, like microlog. properties or JAD-file, for the configura- tion. Remember to add the microlog. appender.BluetoothSerialAppender.btAd- dress-property when using the Bluetooth- SerialAppender. This is the Bluetooth address of the computer running the Microlog Bluetooth server application. EMBEDDED 22 Introducing Akka – Simpler scalability, fault-tolerance, concurrency & remoting through actors Writing correct concurrent, fault- tolerant and scalable applications is too hard. Most of the time it’s because we are using the wrong tools and the wrong level of abstraction. Akka is an attempt to change that. Akka uses the Actor Model together with Software Transactional Memory to raise the abstraction level and provide a better platform to build correct concurrent and scalable applications. Jonas Bonér/Jayway For fault-tolerance Akka adopts the “Let it crash”, also called “Embrace failure”, model which have been used with great success in the telecom industry to build applica- tions that self-heals, systems that never stop. Actors also provides the abstraction for transparent distribution and the ba- sis for truly scalable and fault-tolerant applications. Akka is Open Source and available under the Apache 2 License. In this article we will introduce you to Akka and see how we can utilize it to build a highly concurrent, scalable and fault-tolerant net- work server. But first let’s take a step back and discuss what Actors really are and what they are useful for. Actors The Actor Model provides a higher level of abstraction for writing concurrent and dis- tributed systems. It alleviates the developer from having to deal with explicit locking and thread management. It makes it eas- ier to write correct concurrent and paral- lel systems. Actors are really nothing new, they were defined in the 1963 paper by Carl Hewitt and have been popularized by the Er- lang language which emerged in the mid 80s. It has been used by for example at Ericsson with great success to build highly concur- rent and extremely reliable (99.9999999 % availability – 31 ms/year downtime) telecom systems. Actors encapsulate state and behavior into a lightweight process/thread. In a sense they are like OO objects but with a major semantic difference; they do not share state with any other Actor. Each Actor has their own view of the world and can only have impact on other Actors by sending messages to them. Messag- es are sent asynchronously and non-blocking in a so-called “fire-and-forget” manner where the Actor sends off a message to some other Actor and then do not wait for a reply but goes off doing other things or are suspended by the runtime. Each Actor has a mailbox (ordered message queue) in which incoming messages are processed one by one. Since all processing is done asynchronously and Ac- tors do not block and consume any resources while waiting for messages, Actors tend to give very good concurrency and scalability characteristics and are excellent for building event-based systems. Creating Actors Akka has both a Scala API and a Java API. In this article we will only look at the Scala API since that is the most expressive one. The article assumes some basic Scala knowledge, but even if you don’t know Scala I don’t think it will not be too hard to follow along anyway. Akka has adopted the same style of writing Actors as Erlang in which each Actor has an explicit message handler which does pattern matching to match on the incoming mes- sages. Actors can be created either by: • Extending the ‘Actor’ class and implement- ing the ‘receive’ method. • Create an anonymous Actor using one of the ‘actor’ methods. JAVA Akka 23 Here is a little example before we dive into a more interesting one. class MyActor extends Actor { def receive = { case “test” => println(“received test”) case _ => println(“received unknown message”) } } val myActor = new MyActor myActor.start Here is the same Actor with the anonymous syntax. Anonymous Actors are implicitly started: val myActor = actor { case “test” => println(“received test”) case _ => println(“received unknown message”) } Akka Actors are extremely lightweight. Each Actor consume ~600 bytes, which means that you can create 6.5 million on 4 G RAM. Messages are sent using the ‘!’ operator: myActor ! “test” Sample application We will try to write a simple chat/IM system. It is client-server based and uses remote Ac- tors to implement remote clients. Even if it is not likely that you will ever write a chat system I think that it can be a useful exercise since it uses patterns and idioms found in many other use-cases and domains. We will use many of the features of Akka along the way. In particular; Actors, fault-tol- erance using Actor supervision, remote Ac- tors, Software Transactional Memory (STM) and persistence. But let’s start by defining the messages that will flow in our system. Creating messages It is very important that all messages that will be sent around in the system are immutable. The Actor model relies on the simple fact that no state is shared between Actors and the only way to guarantee that is to make sure we don’t pass mutable state around as part of the messages. In Scala we have something called case classes. These make excellent messages since they are both immutable and great to pattern match on. Let’s now start by creating the messages that will flow in our system. JAVA 24 /** * ChatServer’s internal events. */ sealed trait Event case class Login(username: String) extends Event case class Logout(username: String) extends Event case class ChatMessage(fromUser: String, message: String) extends Event case class GetChatLog(fromUser: String) extends Event case class ChatLog(messages: List[String]) extends Event As you can see with these messages we can log in and out, send a chat message and ask for and get a reply with all the messages in the chat log so far. Client: Sending messages Our client wraps each message send in a function, making it a bit easier to use. Here we assume that we have a reference to the chat service so we can communicate with it by sending messages. Messages are sent with the ‘!’ operator (pronounced “bang”). This sends a message of asynchronously and do not wait for a reply. Sometimes however, there is a need for se- quential logic, sending a message and wait for the reply before doing anything else. In Akka we can achieve that using the ‘!!’ (“bangbang”) operator. When sending a message with ‘!!’ we do not return immediately but wait for a reply using a Future (see references). A ‘Fu- ture’ is a promise that we will get a result later but with the difference from regular method dispatch that the OS thread we are running on is put to sleep while waiting and that we can set a time-out for how long we wait before bailing out, retrying or doing something else. The ‘!!’ function returns a scala.Option (see ref) which implements the Null Object pat- tern (see ref) . It has two subclasses; ‘None’ which means no result and ‘Some(value)’ which means that we got a reply. The ‘Option’ class has a lot of great methods to work with the case of not getting a defined result. F.e. as you can see below we are using the ‘getOrElse’ method which will try to return the result and if there is no result defined invoke the “... OrElse” statement. /** * Chat client. */ class ChatClient(val name: String) { import Actor.Sender.Self def login = ChatService ! Login(name) def logout = ChatService ! Logout(name) def post(message: String) = ChatService ! ChatMessage(name, name + “: “ + message) def chatLog: ChatLog = { JAVA 25 val option = ChatService !! (GetChatLog(name), 1000) // timeout 1s option.getOrElse( throw new Exception(“Couldn’t get the chat log”)) } } Session: Receiving messages Now we are done with the client side and let’s dig into the server code. We start by creating a user session. The session is an Actor and is defined by extending the ‘Actor’ trait. This trait has one abstract method that we have to define; ‘receive’ which implements the mes- sage handler for the Actor. In our example the session has state in the form of a ‘List’ with all the messages sent by the user during the session. In takes two parameters in its constructor; the user name and a reference to an Actor implementing the persistent message storage. For both of the messages it responds to, ‘ChatMessage’ and ‘GetChatLog’, it passes them on to the stor- age Actor. If you look closely (in the code below) you will see that when passing on the ‘GetChat- Log’ message we are not using ‘!’ but ‘forward’. This is similar to ‘!’ but with the important difference that it passes the original sender reference, in this case to the storage Actor. This means that the storage can use this refer- ence to reply to the original sender (our cli- ent) directly. /** * Internal chat client session. */ class Session(user: String, storage: Actor) extends Actor { private val loginTime = System.currentTimeMillis private var userLog: List[String] = Nil“New session for user [%s] has been created at [%s]”, user, loginTime) def receive = { case event: ChatMessage => userLog ::= event.message storage ! event case event: GetChatLog => storage forward event } } Let it crash: Implementing fault-tolerance Akka’s approach to fault-tolerance (doc.ak-; the “let it crash” model, is implemented by linking Actors. It is very different to what Java and most non-concurrency oriented languages/ frameworks have adopted. It’s a way of deal- ing with failure that is designed for concur- rent and distributed systems. Looking at concurrency first: Let’s assume we are using non-linked Actors. Throwing an exception in concurrent code, will just simply blow up the thread that currently executes the Actor. There is no way to find out that things went wrong (apart from see the stack trace in the log). There is nothing you can do about it. Here linked Actors provide a clean way of both getting notification of the error so you know what happened, as well as the Actor that crashed, so you can do something about it. Linking Actors allow you to create sets of Actors where you can be sure that either: • All are dead • All are alive This is very useful when you have hundreds of thousands of concurrent Actors. Some Ac- tors might have implicit dependencies and together implement a service, computation, user session etc. for these being able to group them is very nice. Akka encourages non-defensive program- ming. Don’t try to prevent things from go wrong, because they will, whether you want it or not. Instead; expect failure as a natural state in the life-cycle of your app, crash early and let someone else (that sees the whole pic- ture), deal with it. Now let’s look at distributed Actors. As you probably know, you can’t build a fault- tolerant system with just one single node, but you need at least two. Also, you (usu- ally) need to know if one node is down and/ or the service you are talking to on the other node is down. Here Actor supervision/link- ing is a critical tool for not only monitoring the health of remote services, but to actually manage the service, do something about the problem if the Actor or node is down. This could be restarting him on the same node or on another node. To sum things up, it is a very different way of thinking but a way that is very useful (if not critical) to building fault-tolerant highly concurrent and distributed applications. Supervisor hierarchies A supervisor is a regular Actor that is respon- sible for starting, stopping and monitoring its child Actors. The basic idea of a supervisor is that it should keep its child Actors alive by restarting them when necessary. This makes for a completely different view on how to write fault-tolerant servers. Instead of trying all things possible to prevent an error from happening, this approach embraces failure. It shifts the view to look at errors as something JAVA 26 natural and something that will happen and instead of trying to prevent it; embrace it. Just “let it crash” and reset the service to a stable state through restart. Akka has two different restart strategies; All-For-One and One-For-One. • OneForOne: Restart only the component that has crashed. • AllForOne: Restart all the components that the supervisor is managing, including the one that have crashed. The latter strategy should be used when you have a certain set of components that are coupled in some way that if one is crashing they all need to be reset to a stable state before continuing. Chat server: Supervision, Traits and more There are two ways you can define an Actor to be a supervisor; declaratively and dynami- cally. In this example we use the dynamic ap- proach. There are two things we have to do: • Define the fault handler by setting the ‘faultHandler’ member field to the strategy we want. • Define the exceptions we want to “trap”, e.g. which exceptions should be handled according to the fault handling strategy we have defined. This in done by setting the ‘trapExit’ member field to a ‘List’ with all exceptions we want to trap. The last thing we have to do to supervise Ac- tors (in our example the storage Actor) is to ‘link’ the Actor. Invoking ‘link(actor)’ will create a link between the Actor passed as ar- gument into ‘link’ and ourselves. This means that we will now get a notification if the linked Actor is crashing and if the cause of the crash, the exception, matches one of the exceptions in our ‘trapExit’ list then the crashed Actor is re- started according the the fault handling strat- egy defined in our ‘faultHandler’. We also have the ‘unlink(actor)’ function which disconnects the linked Actor from the supervisor. In our example we are using a method called ‘startLink(actor)’ which starts the Ac- tor and links him in an atomic operation. The linking and unlinking is done in ‘init’ and ‘shutdown’ callback methods, which are invoked by the runtime when the Actor is started and shut down (shutting down is done by invoking ‘actor.stop’). In these methods we initialize our Actor, by starting and linking the storage Actor and clean up after ourselves by shutting down all the user session Actor- sand the storage Actor. That is it. Now we have implemented the supervising part of the fault-tolerance for the storage Actor. But before we dive into the ‘ChatServer’ code there are some more things worth mentioning about its implementation. It defines an abstract member field hold- ing the ‘ChatStorage’ implementation the server wants to use. We do not define that in the ‘ChatServer’ directly since we want to de- couple it from the actual storage implementa- tion. The ‘ChatServer’ is a ‘trait’, which is Scala’s version of mixins. A mixin can be seen as an interface with an implementation and is a very powerful tool in Object-Oriented design that makes it possible to design the system into small, reusable, highly cohesive, loosely coupled parts that can be composed into larg- er object and components structures. I’ll try to show you how we can make use Scala’s mixins to decouple the Actor imple- mentation from the business logic of man- aging the user sessions, routing the chat messages and storing them in the persistent storage. Each of these separate parts of the server logic will be represented by its own trait; giving us four different isolated mixins; ‘Actor’, ‘SessionManagement’, ‘ChatManage- ment’ and ‘ChatStorageFactory’ This will give us as loosely coupled system with high cohe- sion and reusability. At the end of the article I’ll show you how you can compose these mixins into a complete runtime component we like. /** * Chat server. Manages sessions and redirects all * other messages to the Session for the client. */ trait ChatServer extends Actor { faultHandler = Some(OneForOneStrategy(5, 5000)) trapExit = List(classOf[Exception]) val storage: ChatStorage“Chat service is starting up...”) // actor message handler def receive = sessionManagement orElse chatManagement // abstract methods to be defined somewhere else protected def chatManagement: PartialFunction[Any, Unit] protected def sessionManagement: PartialFunction[Any, Unit] protected def shutdownSessions: Unit override def init = startLink(storage) override def shutdown = {“Chat server is shutting down...”) shutdownSessions unlink(storage) storage.stop } } JAVA 27 If you look at the ‘receive’ message handler function you can see that we have defined it but instead of adding our logic there we are delegating to two different functions; ‘ses- sionManagement’ and ‘chatManagement’, chaining them with ‘orElse’. These two func- tions are defined as abstract in our ‘Chat- Server’ which means that they have to be provided by some another mixin or class when we instantiate our ‘ChatServer’. Natu- rally we will put the ‘sessionManagement’ implementation in the ‘SessionManagement’ trait and the ‘chatManagement’ implemen- tation in the ‘ChatManagement’ trait. First let’s create the ‘SessionManagement’ trait. Chaining partial functions like this is a great way of composing functionality in Actors. You can for example put define one default message handle handling generic messages in the base Actor and then let deriving Actors extend that functionality by defining addi- tional message handlers. There is a section on how that is done, see references Akkas docu- mentation on Actors. Session management The session management is defined in the ‘SessionManagement’ trait in which we implement the two abstract methods in the ‘ChatServer’; ‘sessionManagement’ and ‘shut- downSessions’. The ‘SessionManagement’ trait holds a ‘HashMap’ with all the session Actors mapped by user name as well as a reference to the stor- age (to be able to pass it in to each newly cre- ated ‘Session’). The ‘sessionManagement’ function per- forms session management by responding to the ‘Login’ and ‘Logout’ messages. For each ‘Login’ message it creates a new ‘Session’ Ac- tor, starts it and puts it in the ‘sessions’ Map and for each ‘Logout’ message it does the op- posite; shuts down the user’s session and re- moves it from the ‘sessions’ Map. The ‘shutdownSessions’ function simply shuts all the sessions Actors down. That com- pletes the user session management. /** * Implements user session management. *

* Uses self-type annotation ‘this: Actor =>’ * to declare that it needs to be mixed in with an Actor. */ trait SessionManagement { this: Actor => val storage: ChatStorage // needs someone to provide the ChatStorage val sessions = new HashMap[String, Actor] protected def sessionManagement: PartialFunction[Any, Unit] = { case Login(username) =>“User [%s] has logged in”, username) val session = new Session(username, storage) session.start sessions += (username -> session) case Logout(username) =>“User [%s] has logged out”, username) val session = sessions(username) session.stop sessions -= username } protected def shutdownSessions = sessions.foreach { case (_, session) => session.stop } } Chat message management Chat message management is implemented by the ‘ChatManagement’ trait. It has an ab- stract ‘HashMap’ session member field with all the sessions. Since it is abstract it needs to be mixed in with someone that can provide this reference. If this dependency is not re- solved when composing the final component, you will get a compilation error. It implements the ‘chatManagement’ func- tion, which responds to two different messag- es; ‘ChatMessage’ and ‘GetChatLog’. It simply gets the session for the user (the sender of the message) and routes the message to this ses- sion. Here we also use the ‘forward’ function to make sure the original sender reference is passed along to allow the end receiver to reply back directly. /** * Implements chat management, e.g. chat message dispatch. *

* Uses self-type annotation ‘this: Actor =>’ * to declare that it needs to be mixed in with an Actor. */ trait ChatManagement { this: Actor => val sessions: HashMap[String, Actor] // someone to provide Session map protected def chatManagement: PartialFunction[Any, Unit] = { case msg @ ChatMessage(from, _) => sessions(from) ! msg case msg @ GetChatLog(from) => sessions(from) forward msg } } JAVA 28 Using an Actor as a message broker, as in this example, is a very common pattern with many variations; load-balancing, master/ worker, map/reduce, replication, logging etc. It becomes even more useful with remote Ac- tors when we can use it to route messages to different nodes. STM and Transactors Actors are excellent for solving problems where you have many independent processes that can work in isolation and only interact with other Actors through message passing. This model fits many problems. But the Actor model is unfortunately a terrible model for implementing truly shared state. E.g. when you need to have consensus and a stable view of state across many components. The classic example is the bank account where clients can deposit and withdraw, in which each opera- tion needs to be atomic. For detailed discus- sion on the topic see the JavaOne presenta- tion in the references. Software Transactional Memory (STM) on the other hand is excellent for problems where you need consensus and a stable view of the state by providing compositional trans- actional shared state. Some of the really nice traits of STM are that transactions compose and that it raises the abstraction level from lock-based concurrency. Akka has a STM implementation that is based on the same ideas as found in the Clo- jure language; Managed References working with immutable data. Akka allows you to combine Actors and STM into what we call Transactors (short for Transactional Actors), these allow you to op- tionally combine Actors and STM provides IMHO the best of the Actor model (simple concurrency and asynchronous event-based programming) and STM (compositional transactional shared state) by providing transactional, compositional, asynchronous, event-based message flows. You don’t need Transactors all the time but when you do need them then you really need them. Akka currently provides three different transactional abstractions; ‘Map’, ‘Vector’ and ‘Ref’. They can be shared between multiple Actors and they are managed by the STM. You are not allowed to modify them outside a transaction, if you do so, an exception will be thrown. What you get is transactional memory in which multiple Actors are allowed to read and write to the same memory concurrently and if there is a clash between two transactions then both of them are aborted and retried. Abort- ing a transaction means that the memory is rolled back to the state it were in when the transaction was started. In database terms STM gives you ‘ACI’ se- mantics; ‘Atomicity’, ‘Consistency’ and ‘Isola- tion’. The ‘D’ in ‘ACID’; ‘Durability’, you can’t get with an STM since it is in memory. This however is addressed by the persistence mod- ule in Akka. Persistence: Storing the chat log Akka provides the possibility of taking the transactional data structures we dis- cussed above and making them persis- tent. It is an extension to the STM which guarantees that it has the same semantics. The persistence module ( persistence) has pluggable storage back-ends. At the time of the writing it has three different storage back-ends: • Cassandra – A distributed structured stor- age database. • MongoDB – A high performance schema- free, document oriented data store with SQL like query facilities. • Redis – An advanced key-value store, also called a data structure server, with lists, ordered sets etc. They all implement persistent ‘Map’, ‘Vector’ and ‘Ref’. Which can be created and retrieved by id through one of the storage modules. val map = RedisStorage.newMap(id) val vector = CassandraStorage.newVector(id) val ref = MongoStorage.newRef(id) Chat storage: Backed by Redis Now let’s implement the persistent storage. We start by creating a ‘ChatStorage’ trait al- lowing us to have multiple different storage backend. For example one in-memory and one persistent. /** * Abstraction of chat storage holding the chat log. */ trait ChatStorage extends Actor Let’s use Redis to implementation the per- sistent storage. Redis is an excellent storage backend, blazingly fast with a rich data model. Our ‘RedisChatStorage’ extends the ‘ChatStorage’ trait. The only state it holds is the ‘chatLog’ which is a ‘Vector’ man- aged by Redis. We give it an explicit id (the String “”) to be able to retrieve the same vector across remote nodes and/or through server restarts. It responds to two different messages; ‘ChatMessage’ and ‘GetChatLog’. The ‘Chat- Message’ message handler takes the ‘message’ JAVA 29 attribute and appends it to the ‘chatLog’ vec- tor. Here you can see that we are using the ‘atomic { ... }’ block to run the vector opera- tion in a transaction. Redis works with binary data so we need to convert the message into a binary representation. Since we are us- ing Strings we just have to invoke ‘message. getBytes(“UTF-8”)’, but if we would have had a richer message that we wanted to persist then we would have had to use one of the Akka’s serialization traits or serializers. You can read more about that at doc.akkasource. org/serialization. The ‘GetChatLog’ message handler re- trieves all the messages in the chat log storage inside an atomic block, iterates over them us- ing the ‘map’ combinator transforming them from ‘ArrayByte to ‘String’. Then it invokes the ‘reply(message)’ function that will send the chat log to the original sender; the ‘Chat- Client’. You might rememeber that the ‘ChatServ- er’ was supervising the ‘ChatStorage’ actor. When we discussed that we showed you the supervising Actor’s view. Now is the time for the supervised Actor’s side of things. First, a supervised Actor need to define a life-cycle in which it declares if it should be seen as a: • ‘Permanent’: which means that the actor will always be restarted. • ‘Temporary’: which means that the actor will not be restarted, but it will be shut down through the regular shutdown pro- cess so the ‘shutdown’ callback function will called. We define the ‘RedisChatStorage’ as ‘Perma- nent’ by setting the ‘lifeCycle’ member field to ‘Some(LifeCycle(Permanent))’. The idea with this crash early style of de- signing your system is that the services should just crash and then they should be restarted and reset into a stable state and continue from there. The definition of “stable state” is domain specific and up to the application de- veloper to define. Akka provides two callback functions; ‘preRestart’ and ‘postRestart’ that are called right before and right after the Ac- tor is restarted. Both of these functions take a ‘Throwable’, the reason for the crash, as argu- ment. In our case we just need to implement the ‘postRestart’ hook and there re-initialize the ‘chatLog’ member field with a fresh per- sistent ‘Vector’ from Redis. /** * Redis-backed chat storage implementation. */ class RedisChatStorage extends ChatStorage { lifeCycle = Some(LifeCycle(Permanent)) private var chatLog = RedisStorage.getVector(“”)“Redis-based chat storage is starting up...”) def receive = { case msg @ ChatMessage(from, message) => log.debug(“New chat message [%s]”, message) atomic { chatLog + message.getBytes(“UTF-8”) } case GetChatLog(_) => val messageList = atomic { => new String(bytes, “UTF-8”)).toList } reply(ChatLog(messageList)) } override def postRestart(reason: Throwable) = chatLog = RedisStorage.getVector(“”) } The last thing we need to do in terms of per- sistence is to create a ‘RedisChatStorageFac- tory’ that will take care of instantiating and resolving the ‘val storage: ChatStorage’ field in the ‘ChatServer’ with a concrete imple- mentation of our persistence Actor. /** * Creates and a RedisChatStorage. */ trait RedisChatStorageFactory { val storage: ChatStorage = new RedisChatStorage } Composing the full Chat Service We have now created the full functionality for the chat server, all nicely decoupled into iso- lated and well-defined traits. Now let’s bring all these traits together and compose the complete concrete ‘ChatService’. /** * Object encapsulating the full Chat Service. */ object ChatService extends ChatServer with SessionManagement with ChatManagement with RedisChatStorageFactory JAVA 30 Making the ChatService remote Now that we have the ‘ChatService’ object how do we make it into a remote service that we can use from different nodes? It is very simple. We only need to do two things. First we need to start up a remote serv- er to run the ‘ChatService’. Then for each cli- ent that wants to use the ‘ChatService’ we just need to invoke ‘ChatService.makeRemote’ to get a handle to the remote ‘ChatService’. Starting the first step. We have two options on how we can start up a remote server. Either start up the ‘RemoteNode’ in some part of the code that runs on the machine you want to run the server on (can just be a simple class with a ‘main’ method). We start the ‘RemoteNode’ by invoking ‘start’ and passing in the host name and port. RemoteNode.start(“darkstar”, 9999) You can also choose to use the version of ‘start’ that takes a ‘ClassLoader’ as argument if you want to be explicit on through which class loader you want to load the class of the Actor that you want to run as remote service. The second option is to put your ap- plication in a JAR file and drop it into the ‘AKKA_HOME/deploy’ directory and then start up the Akka microkernel. This will de- ploy your application and start the ‘Remote- Node’ for you. Then you use the ‘AKKA_ HOME/config/akka.conf ’ configuration file to configure the remote server (among many other things). The microkernel is started up like this: export AKKA_HOME=... cd $AKKA_HOME java -jar $AKKA_HOME/dist/akka-0.6.jar That was the server part. The client part is just as simple. We only need to tell the run- time system that we want to use the ‘Chat- Service’ as a remote Actor by invoking the ‘makeRemote(hostname, port)’ function on it. This will instantiate the Actor on the re- mote host and turn the local Actor instance into a proxy or handle through which we can use the remote Actor transparently with the exact same semantics as if it was a regular lo- cal Actor. That’s it. Now let’s run a sample client ses- sion. ChatService.makeRemote(“darkstar”, 9999) ChatService.start That’s it. Were done. Now we have a, very sim- ple, but scalable, fault-tolerant, event-driven, persistent chat server that can without prob- lem serve a million concurrent users on a regular workstation. Let’s use it. Sample client chat session Now let’s create a simple test runner that logs in posts some messages and logs out. /** * Test runner emulating a chat session. */ object Runner { // create a handle to the remote ChatService ChatService.makeRemote(“localhost”, 9999) ChatService.start def run = { val client = new ChatClient(“jonas”) client.login“Hi there”) println(“CHAT LOG: “ + client.chatLog.log)“Hi again”) println(“CHAT LOG: “ + client.chatLog.log) client.logout } } Sample code All this code is available as part of the Akka distribution. It resides in the ‘akka-samples- chat’ module and have a ‘README’ file ex- plaining how to run it as well as a Maven ‘pom.xml’ build file so it is easy to build, run, hack, rebuild, run etc. Or if you rather browse it online please see ref. Run it First we need to start up Redis: 1. Download Redis from here – http://code. 2. Run redis-server: ‘./redis-server’. For details on how to set up Redis server have a look here: wiki/QuickStart Now when the storage server is up and running let’s run the sample application: 1. Set ‘AKKA_HOME’ to the root of the Akka distribution. 2. Open up a shell. Step into the ‘akka-sam- ples-chat’ module in the Akka distribu- tion and first invoke ‘mvn install’. This will build the sample application and deploy it to the ‘AKKA_HOME/deploy’ directory. JAVA 31 3. Open up a new shell. Step up to the ‘AKKA_HOME’ root. Invoke ‘java -jar $AKKA_HOME/dist/akka-0.6.jar’ to start up the microkernel. 4. Then go back to the first shell and invoke ‘mvn scala:console -o’. This will give you a REPL with the application and all its dependency JARs on the classpath. Here you can simply paste in the ‘Runner’ above and invoke ‘’ and it will connect to the running server in the mi- crokernel. 5. Invoke ‘’ again and again... Onward There is much much more to Akka than what we have covered in this article. For example Active Objects, Cluster Membership API, a Comet module, REST (JAX-RS) integra- tion, a Security module, AMQP integration, Spring integration, Google Guice integra- tion, Lift integration, a rich Transaction API, tons of configuration possibilities etc. Have fun. References • The Actor Model: • Akka Documentation: • Actors API in Akka: • Scala Case classes: • Future and Promises: • Scala Option Pattern: • Null Object Pattern: • JavaOne Presentation • Software transactional memory: memory • Clojure: • Transactors: • Cassandra: • MongoDB: • Redis: • The Akka source: • Akka: Jayway Stockholm Knowledge Network invites you to a series of free seminars! If you want to have invitations, pls sign up to our Google Group and you will be added to the Jayways Knowledge Network email list! Topics: AkkaScala AndroidScalability Cloud computing Content Delivery Networks Neo4j / NOSQL ... more subjects to come! JAVA 32 We have been talking about rich user interfaces under the name Swing Rocks at several big con- ferences throughout the years. In the spirit of the book Filthy Rich Clients, written by Chet Haase and Romain Guy, our weapons of choice has always been Swing and Java2D. Being GUI geeks, we have of course also followed the de- velopment of JavaFX closely, and from its some- what shaky start, we’ve been impressed with the great improvements in every new version. When we were asked to write a Swing ar- ticle, we sat down and discussed the subject for quite some time, struggling to find a new and interesting angle, but for every problem we came up with, the most elegant solution always seemed to be JavaFX. Why should we waste our readers’ time talking about how to implement effects and animations in Swing and Java2D when there is a whole language de- signed for exactly that, just waiting to be used? We’re now leaving Swing and Java2D be- hind when it comes to rich user interfaces, and in this article we’ll explain why we think JavaFX is a better tool for the job. Code showdown The biggest difference between Swing/Java2D and JavaFX, at least for us developers, is the language. JavaFX applications are written in JavaFX script, which is nothing short of a do- main specific language for graphics and user interfaces. JavaFX script differs from Java in many ways (see JayView 2/2009 for an intro- duction). One of the biggest differences is that JavaFX code is, or at least can be, a lot more declarative than Java code. When defining a user interface in Java2D, you tell the com- puter what to do in a very detailed way. As a result of this, you have to write a lot of code, even for fairly simple operations. A lot of the methods in Java2D take many numerical ar- guments, and without looking at the docu- mentation, it can be very hard to tell what all these parameters (and the rest of the code as well, to be honest) actually does. In JavaFX script on the other hand, you rarely tell the computer to do anything at all. Instead, you set up a scene, fill it with objects, and that’s it. This is a big advantage when it comes to performance, when you as a devel- oper specify what you want done, instead of exactly how to do it, the underlying imple- mentation can decide upon the most effective way of fulfilling your requests. Let’s have a look at a code example, we’re going to implement a simple blur effect called a box blur in Swing/Java2D and JavaFX re- spectively, just to get a feeling for what the code looks like. Swing Rocks – The last dance? Result @Override protected void paintComponent(Graphics g) { // Draw source image BufferedImage source = new BufferedImage( getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics sg = source.getGraphics(); sg.setColor(Color.RED); sg.fillOval(50, 50, 200, 200); // Create blur effect int radius = 5; int size = radius * 2 + 1; float weight = 1.0f / (size * size); float[] data = new float[size * size]; for (int i = 0; i < data.length; i++) { data[i] = weight; } Kernel kernel = new Kernel(size, size, data); ConvolveOp blur = new ConvolveOp(kernel); g.drawImage(blur.filter(source, null), 0, 0, null); } JAVA Pär Sikö & Martin Gunnarson/Epsilon 33 Shown above is the paintComponent imple- mentation of our Swing component, which draws a blurred, red circle. We won’t go through the code in detail, but this is a good example of what Java2D code looks like. We have borrowed the code for the blur effect from Filthy Rich Clients, and the reason for this is a very strong argument against Java2D: the code for even a simple blur effect is way too complicated to remember off the top of your head. Like we said before, it’s also very hard to understand what the code actually does. Kernel? ConvolveOp? I’m sorry, what? Let’s have a look at the same code in JavaFX. Now that’s more like it, the JavaFX code is short, clean, and easy to understand. Things have familiar names like stage, scene and circle, and every parameter is preceded by its name, making the code very self explanatory. One of the main reasons why the JavaFX ex- ample looks so much better is of course that we’ve lied and cheated. We’ve compared a box blur implementation to some code simply us- ing a ready made box blur, which, you might argue, is totally unfair. In our defense, this is a pretty realistic comparison after all. JavaFX comes with almost any effect you can think of built in, where as soon as you want to do something similar in Swing, there are a lot of hoops to be jumped through. Capabilities There are things that you can do in Swing but not in JavaFX, for example scaling images with a specific algorithm, and painting on the glass pane.1 Are these features missing from JavaFX? It’s easy to think so, but the truth is that there’s no place for them in JavaFX, since details like these are taken care of in the back- ground. There are other things in Java2D that you might miss in JavaFX, but you can always fall back on a Java2D implementation since JavaFX integrates well with Java. JavaFX is still missing some important components like table and tree, but that is temporary and not something that will both- er us in the future (version 1.3 will likely pro- vide many of the missing components) and if you expect JavaFX to meet the same require- ments that Swing have then, yes, it’s not yet ready. But JavaFX was never intended to be a replacement for Swing, it was built for an- other purpose, namely to bring RIA to the Java market. Now let’s look at it from the other side of the fence: What can JavaFX do that Swing/ Java2D can’t? A lot we say. This is not a Ja- vaFX tutorial and we are not going to go into details but it’s important to understand all the things that JavaFX can do so much bet- ter than Swing/Java2D. Language support for animations, built in effects, binding and other things that Swing never could do easily, be- cause the language was never meant for that. The aforementioned can be achieved in Swing as well but at a high cost. Countless bugs have been introduced by developers who were try- ing to do it themselves. The upsides of JavaFX vs. Swing/Java2D weigh in far heavier than the downsides. Performance When Christopher Oliver wrote F3 (Form Follows Function), the forerunner to JavaFX, he didn’t implement a completely new graph- ics stack. Instead he reused the already exist- ing power of Swing and just mapped JavaFX components into Swing components, e.g. a JavaFX Button turned into a JButton. The result was stunning, even though it was still Swing in the end, and he showed the world some really impressive demos.2 But if JavaFX is built on top of Swing and Java2D, doesn’t that mean that it will never be faster than what it’s built on top of? Yes that’s correct and also one of the reasons why some of the early JavaFX demos had bad performance. Luck- ily the JavaFX architecture has evolved and today (the current 1.2 release is much faster than the previous versions and 1.3 will be even faster) JavaFX components don’t rely on Swing, instead a new scene graph has been built from scratch (the new scene graph is named Prism and will be introduced in 1.3) which in turn relies on OpenGL to achieve high performance. Not only is JavaFX easier to read and write and supports animations and effects, it’s faster too and that’s perhaps the main reason why we gladly nail the Swing coffin shut, at least when it comes to building RIA applications. Don’t get us wrong, Swing is still useful, and the right tool, for writing normal applications without effects and animations, it’s just that a piece of the responsibility that Swing had be- fore has now been lost to JavaFX. Custom components and skinning It’s possible to write your own custom compo- nents with Swing and Java2D but if you ever tried it you know how painful it can be. Yes the result can be fantastic, but it doesn’t make it any less painful. Developers were forced to use one of the few available look and feels, and none of them were really good looking, which means that most of all swing applications ever written relies on an ugly look and feel. With that in mind it should be no surprise that Java GUIs have such a bad reputation. Guess what would have happened if someone, back in 1995, had said: “We need to make sure that it’s easy to skin Java GUIs”. The impact on today’s Java developers would have been enormous. At least they got it right this time. JavaFX has three different ways of skinning com- ponents, the first one being the same as in Swing, simply do it yourself. Even though it’s powerful it fails for the same reason that Swing did. It’s simply too much work, at least for most cases. The second way of skinning is not yet fully implemented but the idea is that BoxBlur.fx Result Stage { title: “JavaFX Blur” scene: Scene { width: 300 height: 300 content: [ Circle { translateX: 150 translateY: 150 radius: 100 fill: Color.RED effect: BoxBlur { width: 10 height: 10 } } ] } } 1 If you want to know more of how you can get the most out of Swing then the book “Filty Rich Clients” is for you. 2 JAVA 34 all graphical artifacts are created in Photo- shop or Illustrator and imported into JavaFX as a skin. The third and perhaps easiest way to handle skinning is through CSS. If your application is developed with CSS skinning in mind then a total redesign without any changes to the code. All you need to change is in a CSS file, a simple example is shown below. Style.css /* Style.css */ “Text”#MainText { fill: navy; font: bold italic 35pt “sans-serif”; } Main.fx /* Main.fx */ Stage { title: “Style Sheet in Action” scene: Scene { stylesheets: [“{__DIR__}Style.css”] content: Text { id: “MainText” x: 10 y: 10 content: “CSS styled text” } } } Tool support There are a lot of Swing tools and editors out there, but most of them are complicated, bug- gy and hard to use. The best one we’ve used is Matisse, the visual editor that was introduced in NetBeans 5.0. Since JavaFX has only been around for a few years, it’s easy to assume that its tool support would be even worse, but there are actually a number of really good and use- ful tools to choose from. The first and the one that’s been available the longest is the JavaFX Production Suite,3 a tool that exports graph- ics from Photoshop and Illustrator to JavaFX making it available to the developers through regular JavaFX objects. The second tool, Ja- vaFX Composer4 is a Rapid Application De- velopment tool that lets developers create GUIs by dragging and dropping components on the screen, much like we do with Matisse in NetBeans. The last and perhaps most im- portant tool is the JavaFX Authoring Tool,5 a tool for non developers that lets them create RIA applications without writing a single line of code, that includes support for all standard components, effects, and animations. Conclusions Now, a shiny new tool that makes cool effects and smooth animations more accessible to all developers doesn’t necessarily mean the web will be flooded with awesome user interfaces. As a matter of fact, it might have the oppo- site effect. The first thing you need to create something really good looking is talent, and it seems like people who are good at cod- ing are really bad at drawing and designing user interfaces. A lot of people seem to have got this all wrong, especially one speaker at JavaOne, who proclaimed to the developers in the audience that “You are the designers now”. Nothing could be more wrong, what he should have said was “You now have bet- ter tools than ever to work together with the designers, focus on the code and leave the vi- suals to them”. Another thing that bugs us is that many of the most important JavaFX evangelists, au- thors, Sun representatives delivering keynote speeches and so on, seem to be satisfied with showing off really ugly demos. If you want to impress your audience, whether they are readers or sitting in front of you, you can’t bring something that looks like it was slapped together on a lunch break. Sun should have a team of artists and programmers who do nothing but create absolutely stunning exam- ples of what JavaFX is capable of. It’s a great piece of technology, but without the right in- troduction to the market, people won’t take their time and get to know it. For traditional, form based user interfaces, Swing is still the best tool for the job, and Ja- vaFX was never meant to change that. For rich GUIs, the torch has been passed on, and Ja- vaFX is definitely the way to go. It’s faster, eas- ier to read and write, and allows designers and developers to work more closely together. It’s a way to break free from the old ways, and fi- nally, after 15 years of struggling, we’re ready to change the way people think of Java GUIs. 3 4 5 (not yet released) Resources Useful reading: • JavaFX Developing Rich Internet Applications by Jim Clarke, Jim Connors and Eric Bruno • Filthy Rich Clients by Chet Haase and Romain Guy • JavaFX Special Effects by Lucas L. Jordan Blogs worth reading: • • • JAVA 35 Agile development has during the past years become more and more popular with many organizations involved with software. Unfor- tunately many organizations tend to forget the QA department when developing soft- ware using agile processes. I have worked as a consultant for many different companies and organizations where they talked about how ‘agile’ they were when developing soft- ware. If we scratch the surface of their devel- opment processes its often turn out to be just another classic waterfall where the developer gets a bunch of requirements or features to implement before an end date, usually several months ahead. This gives us long develop- ment cycles. The only real elements of agile in use are the daily meetings in which they discuss what has been achieved, what is out- standing and any impediments encountered. When the developers have implemented the requirements, the software is handed over to the QA department to test the software in a classic manner. At the end of the cycle there is usually a release, a milestone taking pressure from two sides: On one side are the developers who want their new features in the release, and on the other side is the QA department that is in- terested in getting the bugs fixed before ship- ping to customers. Clearly, these two forces do not push in the same direction, which can lead to hostility between the two groups. To complicate the situation further, we then have the product management, marketing and sales pushing to get the new features onto the market as fast as possible, even if this means a reduction in quality. We end up with the business side butting heads with R&D. This scenario describes a very common way of de- veloping software today. What is agile? Before we get ahead of ourselves here, let’s take a closer look at the agile manifesto, which was written in February 2001 by seventeen people that needed an alternative to the document- driven, heavy-weight software development processes that were predominant at that time. What make a process agile are the follow- ing values: • Individuals and interactions over pro- cesses and tools • Working software over comprehensive documentation • Customer collaboration over contract negotiation • Responding to change over following plans One of the principal misunderstandings of the agile manifesto is the part regarding doc- umentation. Many think that agile processes are all about coding and not documenting anything. The agile manifesto says nothing about not writing documents, and many peo- ple in the agile community want to restore the credibility lost to the methodology due to this Agile Testing – a new profile for testers Are you an agile tester? Do you work in an agile process and feel lost, or is it still just waterfall? Do you need new skills as a tester to contribute to an agile team? These questions and many more beg for attention from us testers. Henrik Andersson/Jayway Test TEST 36 misunderstanding. It is about finding a bal- ance between what documentation to write, and what not to write. In many companies large amounts of documentation is written, stored in some repository, and then left where it will often remain untouched, unread and (even worse) unmaintained. Two of the twelve principles behind the agile manifesto are: • Working software is the primary measure of progress • Deliver working software frequently, from a couple of weeks to a couple of months, with a preference to the shorter time scale. So we should deliver working software within a couple of weeks if we want to be agile. In short, we have to program and test the new features in very short time scales. Having both the development and testing teams work as one is a must and an accepted fact in the ag- ile world. The agile manifesto talks about co- operation within the team and that the team should have the flexibility to change direction when required. I have worked with many or- ganizations seeing a connection between the agile manifesto and how they develop their software. But when it comes to testing, the connection is lost, and they stick to having a ‘phase’ between the point when development stops and the delivery of the product to the customer. This phase is the test phase where the QA team does their ‘stuff’. Classic testing I am using the term ‘classic testing’ to make a distinction between agile testing and test- ing as many of us know it. Classic testing is a process that starts with planning the test, creation of test cases in form of written in- structions, manual execution of the test cases, creation of test reports, and finally evaluation to see if more testing is needed. This process can be started as the project starts up, but of- ten has no or minimal involvement with the development process. Rather, it forks off on a parallel track to the development. Why doesn’t this kind of testing work in a true agile project? Let’s take an example. In an agile project there is continuous integration, and we get a new piece of software every morning when we come to the office ready for being tested. If we are to follow the process taught by the ISTQB, the first thing to do would be to plan the test. After you planned, you will come to more insight regarding the test coverage. Are the existing test cases enough? Do we need to de- sign new test cases for the new functionalities? If you decide to design new test cases, you have to do that before starting to test the new version of software. Please don’t forget your regression testing, so that you know whether previous features or requirements still work. As the software grows, both in complexity and number of features, regression testing will consume more and more valuable testing time. To keep up the same pace as before, you can go to your manager to get other people from the team to help you, but this will slow down other parts of the team. As my beloved testing friends try to say that we don’t need to run the regression tests each day, run them as a last overall test at the end of each sprint. So don’t we then have a classic waterfall, where we do development and then test? By testing this way, you will have a long start time before you can start to test and a lot of overhead time in which to plan and design your test cases. However, we want to have as short a start time as possible. This cannot be done by this ap- proach of testing. How to test in an agile project? Now finally the big questions: How can we make testing agile and what is agile testing? The first thing, from my perspective, is that testing has to become a natural part of an agile project and not just a phase after the developers have stopped coding and before customers receives their software. It’s about reaching the same goal as a team, i.e. to de- liver working software within as short a space of time as possible. With this short time scale, we as testers need to change our working methods. We need to step away from the nag- ging position we have in a waterfall process and become more active in the process of de- veloping the software. Active does not mean that we should be sitting down hammering in code, but we should be involved from the beginning and be giving the developers feed- back instantly on how the new feature works, instead of complaining at the end when ev- erything is built. To make this possible, we need to have frequent integration of new features, for example through nightly builds, where every morning there is new software ready to be tested. The testing should be per- formed manually as an exploratory test, since this is a rapid way of testing instantly, no extra documentation, no extra work, just the mini- mum effort required to succeed, namely test- ing the new features. But how can we possibly do regression testing when we have no test cases? When I talk to fellow testers who are into classic test- ing, we soon run into disagreements: • We need the test cases for regression test- ing later on when the product has come in to a maintenances phase, they say. • Let’s spend time to automate these test cases, so we don’t need to spend time run- ning them manually later on, I reply. Let me explain how I see things. Assume that we have a software project that is at the be- ginning of its life cycle. Not too complex, the GUI has started to fall in place, let’s say it is a web application. We need to start to test au- tomatically as soon as possible, which can be easily achieved using a tool that records our clicks and key strokes. After each nightly run of the automated tests, we then add new test cases the next morning. This means that we get some extra time to add new automated test cases, or do some exploratory testing, since we don’t need to test the existing ones manually. Does it need a new profile for the testers? The profile for an agile tester is not the same as the profile of a classic tester. I see the ag- ile tester as a person with a more technically, more programming oriented profile, a person that enjoys developing, debugging and test- ing. This profile is wanted in agile projects, since it will help the developers to find where the bug is, instead of just pointing out the symptom of the bug. Summary To summarize, in agile testing you need to start to execute tests fast. Having working software early is a key aspect, and this means you have to have testable software early. Ex- ploratory testing is ideal for you to be able to make a fast evaluation of the software with a minimum of preparation, and experience has shown that it is also very effective for finding problems in the software. Critical parts of the software should never fail, and you need to make sure that they are not broken in the fast development cycle. Because of this, it is worth putting effort into implementing automated regression tests. As an agile tester you must be able to tackle these things, which calls for an interest in technology and a deeper technical understanding. Programming and debugging are key skills for the agile tester. TEST 37 COP and Qi4j: Reviving object-orientation Have you ever had the feeling that you are really productive, but not getting things done? Did you too notice that while we are getting better at generating code and leaner syntax for this and that, it doesn’t really help us focus on the important things? Rickard Öberg/Jayway Expressing your domain models in a way that allows you to define a concept once, and then reuse it whenever it appears, is something that can really allow you to spend the time necessary to get things right. The key to ac- complishing this is the insight that classes are the wrong abstraction for objects, and that breaking them into smaller and more focused pieces is the solution to this problem. This article will describe the issues at hand, and how to solve them using Composite Ori- ented Programming (COP), which is imple- mented by the Qi4j framework on the Java platform. The dark age of POJO-programming For some time now so-called “POJO-pro- gramming” has been all the rage. The prob- lem is that the main thing it allows you to do is create the wrong thing faster instead of do- ing the right thing. Trying to squeeze a rich, highly contextual reality into a programming model where everything has to fit into a Java class causes unnecessary repetition. It would be better if objects were composed of smaller parts, each of which had its own purpose separate from all the others. By comparison, atoms were for a long time thought to be in- divisible, as a law of nature. However, just as we found that atoms are composed of elec- trons and protons, we now realize that objects are composed of smaller fragments. These smaller fragments can be composed in many different ways to create different objects, and this enables reuse on a whole new level. This is simply impossible to achieve using class- based POJO-programming. So what are the main problems with PO- JO-programming that COP tackle? On the simplest level, consider this sce- nario: you have ten entities in your model. All of them should have a textual description that you want to use for presenting instances in a UI. You may have some rules for it, such as “a description may only contain letters and spaces and may not be longer than 20 charac- ters”. This usually translates into a JavaBean property “description” of type String. Put it into your POJO, generate the setters and get- ters, add the validation logic in the setter, and go! This is great, except that you have to du- plicate this code ten times. Even worse, your client code has to explicitly understand these ten different properties and handle them sep- arately. So not only do you have duplication in your model, but you also have duplication in the client code. How do you deal with it? You might get a tool to generate all this code for you, but you would just be generating waste at breakneck speeds. Is there a better way to solve this? Light in the tunnel In COP objects have no class. Instead, they are declared as being a collection of mixins. Each mixin implements an interface that you want your object to expose. In Java this trans- lates to an interface for the object itself, which extends a number of interfaces, one for each context that the object has to be able to deal with. The mixins are just regular Java classes. In this context Java classes are just fine; don’t try to use them for implementing objects, that’s all! To give you an example of what this would look like I will use the description case as an example. Let’s start with the typical POJO- version. “... you would just be generating waste at breakneck speeds.” ARCHITECTURE 38 This may seem like a perfectly normal class with a simple property, but there are a num- ber of problems with it. First of all, if you want this description capability in several entities you would have to duplicate the code in those entities. This is just one property. Any con- cept that occurs in many places would have to be duplicated, leading to waste in terms of coding. Also keep in mind that clients have to deal with each description as a separate kind of domain notion, rather than having gener- alized code for dealing with “entities that have description”. Another issue with the above is that the constraint for the newDescription param- eter is hidden within the implementation of the setter. The only way a client could know about it is if the JavaDoc is properly written. Even if the JavaDoc is written, in this and all the other nine places where descriptions are used, the developer also needs to ensure that it is kept in synch with the actual validation as the system evolves. In short, there are a lot of issues with even such a simple example when using POJO- based programming as it is commonly prac- ticed today. Let’s mix it up! Can we avoid these issues? Is it possible to allow the developer to write this code only once, and also make the constraint more vis- ible? Let’s take a look at the Qi4j version of the same example. Here’s the mixin interface and implementation (see Listing 1). As you can see this is just regular Java- code. What we have is a class that deals with exactly one thing: handling a description. The parameter constraint is implemented using an annotation, which Qi4j will use to validate the input parameter. Parameters are by de- fault defined as non-null, so there’s no need to perform a null-check either. The quality of your code will increase immensely just by us- ARCHITECTURE public class MyEntity { String description; public void setDescription(String newDescription) { if (newDescription == null || !Pattern.matches( “[\\p{Alnum}\\s]{0,20}”, newDescription)) { throw new IllegalArgumentException(“Descriptions “ + “must only contain letters and numbers”); } description = newDescription; } public String getDescription() { return description; } ... } Listing 1 public interface Describable { void changeDescription(@Matches(“[\\p{Alnum}\\s]{0,20}”) String newDescription); String getDescription(); } public class DescribableMixin implements Describable { String description; public void changeDescription(String newDescription) { this.description = newDescription; } public String getDescription() { return this.description; } } 39 ing these parameter constraint features and with only a little effort on your behalf. Notice also that since the constraint is now declared in the interface, rather than being an implementation detail, it’s ok to not have any JavaDoc for it. There’s also no risk of the doc- umentation and implementation to get out of synch. For large models that need to evolve over time this will ensure that your model is always consistent and easy to understand and use. Now we can create an entity that uses this mixin: @Mixins(DescribableMixin.class) interface MyEntity extends Describable, ..., EntityComposite {} The interface for the entity extends the inter- face exposed by the mixin. We can add any number of these, for as many contexts as we want our entity to be able to handle. Finally we add the EntityComposite interface, which is a Qi4j-specific interface that contains all methods that you want to be able to use on an entity, such as getting its identity. Qi4j tries to use the terminology from Domain Driven Design as much as possible. This means that things like entities, values and services are explicitly modelled as such, which makes it easier to express such design concepts. What we would want now is to somehow be able to instantiate an object that imple- ments MyEntity. When the methods on De- scribable are called we want to delegate to the mixin implementing that interface. If we had more interfaces, then the object would in- ternally know how to delegate to each of the mixins that together are composed to create the MyEntity object. We could theoretically implement this interface in one single Java class, but then it would be very big. Every entity that wanted to expose the Describable interface would have to reinvent the imple- mentation of it, which would be a waste. Us- ing inheritance to fix it doesn’t work either, at least not if the list of interfaces is large and you want to reuse them in many different combinations in various entities. Enter Qi4j Qi4j does indeed provide a way to implement this interface, without requiring all of the im- plementation to be in a single class. It knows how to instantiate a proxy object that imple- ments the entity interface and can perform this delegation to the composed mixins. The @Mixins annotation tells Qi4j what to use for the implementation. This makes it very clear what the implementation of this inter- face is, which helps readability. It is basically an interface that will “glue” everything in an entity together, so that Qi4j can know how to instantiate it. It also serves as a reference for the developer about what to expect from in- stances of this composite type. So what do you gain by this? First, of all, you did not have to duplicate the code for handling descriptions ten times, so that’s a good start. But, perhaps even more impor- tantly, client code now does not have to know about the description property in MyEntity. Instead you can write generic client code that knows how to deal with something that is Describable. This will be a tremendous gain for you in the long run, and can allow you to really leverage the fact that you can reuse do- main model code, since there’s less repetition in the client, and you can spend time getting it right and know that it’s a good investment. In today’s projects we need to have tools that al- low us to be more effective in the long run, and the COP model does just this. Conclusion This was a very simple example of how PO- JO-programming doesn’t really work well to implement rich domain models, and how COP as implemented in Qi4j can avoid these issues. It gets even more interesting when you have a number of roles implemented by mix- ins that refer to each other, and where using standard POJO programming would com- pletely tangle up the code to handle all the cross-references between entities. By using mixins such dependencies can be extracted into specific contexts, which makes the code much more manageable, and it also makes it possible to apply such mixins to any entity where it makes sense, without the mixin code having to know about the specific entities at all. We finally have a model where reuse on a large scale is possible, since the abstractions are at a level where each physical Java class can deal with one thing, and one thing only. In this article I have focused on one of the key benefits of the COP model, which is the ability to use mixins as a way to create reus- able domain model fragments. This is just one part of how to reuse domain code, and there are many other such features in Qi4j, such as handling crosscutting concerns and method side effects. All of these features help you ex- press your domain model as effectively and concisely as possible, and ensures that you will get a maintainable model in the long run. By contrast, the current POJO-oriented pro- gramming model promotes short-term gains but causes long-term waste, both in the mod- el itself and in any code that uses the model. After two years of hard work Qi4j ver- sion 1.0 is now available from, along with documentation, tutorials and an SDK that contains everything you need to get started with COP. Check it out, and see how it can improve your domain models! ARCHITECTURE “... deal with one thing, and one thing only.” Certified Java Super HeroeS! Möt oss på Sveriges största Javakonferens, Jfokus, den 26 -27 januari 2010 på Filmstaden Sergel i Stockholm. Läs mer på Sweden Malmö: Hans Michelsensgatan 9, SE-211 20 Malmö, +46 40 602 31 00 Stockholm: Drottninggatan 108, SE-113 60 Stockholm, +46 8 750 88 20 Helsingborg: Möllegränden 22, SE-252 23 Helsingborg, +46 42 453 53 00 Halmstad: Science Park, Pilefeltsgatan 73, SE-302 50 Halmstad, +46 35 12 59 97 Linköping: Teknikringen 8, SE-583 30 Linköping, +46 704 106 982 Göteborg: Lilla bommen 6, SE-411 04 Göteborg, +46 31 745 00 22 Karlskrona: Campus Gräsvik 5, SE-371 75 Karlskrona, +46 702 19 74 11 Denmark København: Jakob Dannefærds Vej 6B, 1973 Frederiksberg C, +45 26 62 64 34 Malaysia Kuala Lumpur: Jayway Malaysia Sdn. Bhd., B-7-6, Megan Avenue 1, 189 Jalan Tun Razak, 50400 Kuala Lumpur France Sophia Antipolis: Way France SARL, Drakkar Bâtiment C et D, 2405 route des Dolines, BP65, 06902 Sophia Antipolis |



需要 5 金币 [ 分享文档获得金币 ] 0 人已下载