Featured FREE Whitepapers

What's New Here?

javascript-logo

Node, Grunt, Bower and Yeoman – A Modern web dev’s Toolkit

This article aims at introducing you to some of the currently most popular tools when developing modern web applications with JavaScript. These are totally not new at all and have been around for a couple of years now. Still, I found many devs still don’t use or know about them (as you might), wherefore this article tries to give you a quick, concise intro to get you started. Node and NPM Node.js brings JavaScript to the server and the desktop. While initially JavaScript was mainly used as a browser based language, with Node you can now also create your server-side backend or even a desktop application with node-webkit (for the crazy ones among you). Node.js® is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient, perfect for data-intensive real-time applications that run across distributed devices. nodejs.orgCreating a web server is as simple as these couple of lines of code. var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }).listen(1337, '127.0.0.1'); console.log('Server running at http://127.0.0.1:1337/'); To run it, execute: $ node start Server running at http://172.0.0.1:1337/ One of the great things about node is its enourmous community which creates and publishes so-called node modules on the NPM directory, Node’s package manager. Currently there are about ~90.000 modules and there have been around ~390.000 downloads last month.Besides creating server-side applications with Node, it has also become the VM for JavaScript development tools like minifiers, code linters etc. Both, Grunt and Yeoman (described in this article) are based upon Node’s infrastructure. More on nodejs.org and npmjs.org. Installing Node So, to get started, you have to first install the Node runtime. The best way to do so is to download the desired package from the official site. This will also automatically install NPM on your machine. Once that’s done, typing.. $ node -v ..into your terminal should output the installed version of node and thus confirm you’re ready to go.Installing node packages Installing a node package is as simple as executing: $ npm install grunt This installs the grunt node package into a folder called node_modules. The best practices approach though is to create a package.json file. Since the suggested approach is to not commit the content of your node_modules folder to your VCS, but rather to automatically reinstall them during the build process, you need a place to keep track about the installed package and its according version: package.json. To create a new package.json file, simply execute npm init inside a clean folder. You’ll have to answer a few questions but ultimately you will get a nice new package config file. Whenever you install new packages you then use the --save or --save-dev option to persist the package into the package.json file. For instance, executing… $ npm install --save-dev grunt …will automatically add grunt to the devDependencies section of the package config file: { ... "devDependencies": { "grunt": "^0.4.5" } } Similarly, if you add --save it’ll be added to the dependencies section. The difference is mainly that dependencies are actively used by your appliation and should be deployed together with it. On the other side, devDependencies are tools you use during the development of the application, which normally do not require to be deployed together with it. Examples are code minifier scripts, test runners etc. To uninstall a package, use.. $ npm uninstall --save-dev grunt ..which uninstalls grunt and removes it from package.json. Restoring packages As I mentioned, you normally don’t commit the node_modules folder to your VCS. Thus, when a you as a developer, or the buildserver retrieves the source code from your VCS, somehow, the packages need to be restored. This is where the package.json file comes into play again. By having it in the root directory, executing.. $ npm init ..instructs NPM to read the dependencies in the config file and to restore them using the specified version. Versioning NPM packages use Semantic Versioning. Given a version number MAJOR.MINOR.PATCH, increment the:MAJOR version when you make incompatible API changes, MINOR version when you add functionality in a backwards-compatible manner, and PATCH version when you make backwards-compatible bug fixes.http://semver.org/ Each package inside package.json is listed with its according version and upgrade behavior. You can have the following schemes:1.3.5: tells npm to use exactly this given version of the package (most restrictive). ~1.3.5 or 1.3.x: tells npm to only upgrade the given package for increments of the patch version (normally just bugfixes). NPM defines it as ~1.3.5 := >=1.3.5-0 <1.4.0-0. ^1.3.5: tells npm it can upgrade to any version smaller than the next major release: <2.0.0. This is the new default behavior when you install node packages (before it was ~). NPM defines it as 1.3.5 := >=1.3.5-0 <2.0.0-0. latest or *: tells npm to always update to the latest version (not recommended).Bower Bower is to the web browser what NPM is to Node.js. It is a package manager for your front-end development libraries like jQuery, Bootstrap and so on.You install Bower as a global package through NPM (obviously) $ npm install -g bower Then, similarly as you did with NPM, you execute bower init on your terminal to create a new bower.json configuration file (the equivalent of package.json for NPM).Installing packages is identical to NPM. $ bower install --save jquery You can also download a specific version by appending jquery#1.9.1. Note, the --save (or -S) option adds the dependency to your bower.json config file. Installed packages will be placed in the bower_components directory. It is suggested to not commit that one to your VCS (just as with the node_modules directory). To uninstall a package simply use: $ bower uninstall --save jquery What’s particularly interesting is that Bower allows you to install packages from any git repository or even a plain URL. $ bower install git:/github.com/user/package.git or $ bower install http://example.com/script.js If you require some more advanced configuration, like changing the name of the dependencies directory or its location, you may want to use a .bowerrc configuration file placed at the root of your project directory structure. More about the available configuration options can be found at the official site. There’s another nice article I found on Medium that gives a quick introduction to Bower which you might want to take a look at as well. Yeoman Yeoman has become the de-facto standard scaffolding toolkit for creating modern JavaScript applications.Yeoman is build around generators which are either developed by the Yeoman team (official generators) or by the open source community. Yeoman itself basically just provides the infrastructure for building and running those generators. Yeoman helps you kickstart new projects, prescribing best practices and tools to help you stay productive. From the official site What’s nice about such approach is:that you can quickly get up to speed. Creating a project setup with proper tools and dev support can cost you lots of time and requires expert knowledge. that you don’t necessarly have to know all the best practices tools that are currently available on the market. Yeoman assembles them for you, s.t. you can get started immediately. Then once you get more expertise, you can adjust Yeoman’s configuration to make it fit even more to your project needs. a great way for you to learn lots and lots of new tools.Yeoman as well as its generators are distributed as a node modules. Simply install it globally: $ npm install -g yo Then find your generator (i.e. for angular) and install it using the following command. $ npm install -g generator-angular Finally, execute the generator within your project directory to create a new app. $ yo angular [app-name] This will create the initial scaffold from which you can then start building your application. But Yeoman goes even further, based on the generator you use, you may also generate single components, like Angular controllers, directives etc. while you develop. $ yo angular:controller user That’s all regarding Yeoman’s usage. More advanced topics are about creating your own custom generators. Simply study the docs as they’re quite detailed. Grunt Grunt is automation. It is a task-based command line build tool for JavaScript projects. The official headline: “The JavaScript Task Runner”.To get started, simply follow the online guide on the official site. There’s also a great book Getting Started with Grunt – The JavaScript Task Runner published by PacktPub, which is ideal for beginners. Installation Grunt runs on top of the Node.js platform and is distributed through the npm repository. It comes as two different tools:grunt-cli which is the Grunt Command-line interface grunt moduleThe reason for having two components is to make sure we can run different grunt versions side-by-side (i.e. legacy versions in older projects). Hence, grunt-cli is installed globally while grunt is installed on a per-project basis. $ npm install -g grunt-cli Then enter the project where you wish to use Grunt and execute: $ npm install grunt Gruntfile.js The Gruntfile.js is the place where you configure the Grunt tasks for your project. It starts as simple as this file: module.exports = function(grunt) { // Do grunt-related things in here }; The grunt object is Grunt’s API: http://gruntjs.com/api/grunt. It allows you to interact with Grunt, to register your tasks and adjust its configuration. Grunt modules Grunt modules are distributed through Node’s NPM directory. Normally, they are prefixed with grunt- and official grunt plugins are prefixed with grunt-contrib. Example: grunt-contrib-uglify. Hence, Grunt modules are node modules and thus you install them just as I’ve shown before. $ npm install --save-dev grunt-contrib-uglify Anatomy of Grunt tasks You normally start by defining the build tasks like this example of a stringCheck task taken from the Grunt book I mentioned before. module.exports = function(grunt){ ... grunt.initConfig({ stringCheck: { file: './src/somefile.js', string: 'console.log(' } }); } As you can see, a task is simply a function that you register with Grunt. module.exports = function(grunt){ grunt.registerTask('stringCheck', function() { //fail if configuration is not provided grunt.config.requires('stringCheck.file'); grunt.config.requires('stringCheck.string');//retrieve filename and load it var file = grunt.config('stringCheck.file'); var contents = grunt.file.read(file);//retrieve string to search for var string = grunt.config('stringCheck.string');if(contents.indexOf(string >= 0)) grunt.fail.warn('"' + string + '" found in "' + file + '"'); }); } Note, externally downloaded tasks through NPM have to be loaded first, in order to be used in your Gruntfile.js. This is done by using the loadNpmTasks on the grunt object. module.exports = function(grunt){ grunt.loadNpmTasks('grunt-contrib-concat'); ... } In order not having to do this for every single task you use (which can be quite a lot), you may want to use the load-grunt-tasks plugin and execute require('load-grunt-tasks')(grunt) at the beginning of your Gruntfile.js. This will autoload all grunt modules, ready to be used. Multitasks Grunt also allows you to group a task execution as follows: module.exports = function(grunt){ ... grunt.initConfig({ stringCheck: { target1: { file: './src/somefile.js', string: 'console.log(' }, target2: { file: './src/somefile.js', string: 'eval(' } } }); } You can then execute them with grunt stringCheck:target1 and runt stringCheck:target2. target1 and target2 can (and should) obviously be named differently. Globbing File globbing or wildcard matching is a way to capture a large group of files with a single expression rather than listing all of them individually which is often not even possible. From the official docs:* matches any number of characters, but not / ? matches a single character, but not / ** matches any number of characters, including /, as long as it’s the only thing in a path part {} allows for a comma-separated list of “or” expressions ! at the beginning of a pattern will negate the matchAll most people need to know is that foo/*.js will match all files ending with .js in the foo/ subdirectory, but foo/**/*.js will match all files ending with .js in the foo/ subdirectory and all of its subdirectories. Since most of the tasks ultimately interact with the file system, Grunt already predisposes a structure to make task devs’ life easier. If a globbing expession is specified, Grunt tries to match it against the file system and places all matches in the this.files array within your Grunt task function. Hence, you will see a lot of tasks having a syntax like: target1: { src: ['src/a.js', 'src/b.js'] } or target1: { src: `src/{a,b}.js`, dest: `dest/ab.js` } It is also possible to define multiple source sets with according destination. For this purpose the files array is used. target1: { files: [ { src: 'src/{a,b,c}.js', dest: 'dest/abc.js' }, { src: 'src/{x,y,z}.js', dest: 'dest/xyz.js' } ] } The following, more compact, object notation is equivalent: target1: { files: { 'dest/abc.js': 'src/{a,b,c}.js', 'dest/xyz.js': 'src/{x,y,z}.js' } } Another common task is to copy a set of files to a given directory (for example with preprocessors like SASS or CoffeeScript compilers). Instead of providing the single src and dest instructions we can use the following syntax: target2: { files: [ { expand: true, cwd: 'lib/', src: '**/*.js', dest: 'build/', ext: '.min.js' }, ], } The expand property tells Grunt to generate a corresponding destination for each matched file. cwd stands for the current working directory, src and dest are self explanatory and ext is the extension to be used for the destination files. More options can be found in the official docs. Running tasks Ultimately your goal is to execute the Grunt tasks you defined. If you remember, you previously installed the grunt-cli tool globally which you can now use to run a task. $ grunt task1 task2 If you have a multitarget task, then use : to specify it. $ grunt task:target1 If you run $ grunt instead, the default task will be executed which you can configure as follows: module.exports = function(grunt) { grunt.registerTask('build', function() { console.log('building...'); });grunt.registerTask('test', function() { console.log('testing...'); });grunt.registerTask('default', ['build', 'test']); }; Having this Gruntfile.js configuration executes build and test when you type grunt into your console.gulp. Gulp This intro wouldn’t be complete if it doesn’t mention Gulp. Gulp is the JavaScript task runner newcomer build on top of Node.js streams. It aims at making build scripts easier to use by “preferring code over configuration” (unlike Grunt which is based on configuration).gulp’s use of streams and code-over-configuratin makes for a simpler and more intuitive build. gulpjs.com I didn’t study it in detail yet, but you should definitely keep an eye on it as it is fastly growing and gaining in popularity. For now I won’t include more details, but I definitely will update this article once I’ve taken a closer look at it.Reference: Node, Grunt, Bower and Yeoman – A Modern web dev’s Toolkit from our JCG partner Juri Strumpflohner at the Juri Strumpflohner’s TechBlog blog....
json-logo

REST API with JSON

What is REST API? REST stands for Representational State Transfer. It relies on a stateless, client-server, cacheable communications. In most cases it is used with the HTTP protocol. RESTful applications use HTTP requests to POST (create), PUT (create and/or update), GET (e.g., make queries), and DELETE data. REST uses HTTP for all four CRUD (Create/Read/Update/Delete) operations.       What is JSON? JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. Protocols HTTP allows different protocols to be used for the communication between the client and the server. We won’t explain all of them but the four most commonly used: GET, PUT, POST and DELETE. GET, PUT and DELETE should be implemented as idempotent methods. No matter how many times requests are repeated, the result should be the same. POST, on the other hand, should not be idempotent. GET GET performs some kind of a query. It does not request any change to the state of the system in any form or way. This does not mean that the server is not performing some change to its state but that the client did not request it. When performing a GET request, server should respond with the result in the form of JSON. POST POST is a request to create a new entity. Content of that entity should be enclosed in the request body. PUT PUT is similar to POST with a difference that it should create a new entity if one does not exist or modify the existing one. DELETE DELETE is a request to delete a specified entity from the server. Requests to the server Avoid using non-nouns like getAllBooks or createNewBook. Type of the action to be performed is specified with the HTTP methods GET, POST, PUT and DELETE. URI should specify entity upon which operations should be performed on. For example, GET /books should retrieve books from the server, DELETE /books should delete the book, PUT /books should modify or create the book and POST /book should request creation of the book in the server. In case of a GET method, the rest of the URI should provide information regarding type of the query server should use to retrieve requested data. Use query parameters within the URI itself. For example, /books should return all books. /books/id/24 should return the book identified with the ID 24. /books/pageSize/25 should return only 25 books. The rest of methods (POST, PUT and DELETE) should have all the information enclosed in the message body in the JSON format. Similarly as with the GET method, DELETE might apply to one specific data, subset of data or all of them (if that’s allowed by the server). If one would want to delete one book DELETE /book request would have JSON {id: 24} in the body. Similarly, if one would want to delete books that match some broader criteria, JSON would be {author: ‘Viktor Farcic’}. Finally, if there is no body, all books would be deleted. POST and PUT work in a similar fashion like DELETE. Their request body has the information what should be created or modified. One could put a single entity into the body of the request PUT /books. That request would create or modify a single book. An example could be {id: 12345, title: ‘Behavior-Driven Development’, author: ‘Viktor Farcic’}. If bulk insert is allowed, multiple entities can be passed as an array [{title: 'Behavior-Driven Development', author: 'Viktor Farcic'}, {title: 'Continuous Integration', author: 'Viktor Farcic'}]. Keep in mind that the server is responsible to verify whether some request is allowed or not. Operation to delete more than one book could be restricted only to certain users and the DELETE request without the body (delete all) can be denied for all users. The ultimate responsibility to decide whether a request is allowed or not is in the hands of the server. To summarize, GET does not have the body and uses URI to specify entity (i.e. /books) and, when needed, additional query parameters (i.e. id/24). POST, PUT and DELETE should not specify query parameters through the URI but use the message body to pass the information regarding what should be created, modified or deleted. Responses from the server Responses from the server should be always in JSON format and consistent. They should always contain meta information and, optionally, data. Consistency allows consumers of our API to know what to expect. Also, it allows us to write less implementation code since some parsing can be performed in a unified way across all request types. HTTP response already contains status (more information about the status codes can be found in Status Code Definitions). We can enhance that with meta information could contain additional information. Additional information specific to the implementation in the server could be provided with, for example, error and message. As a general rule, when the client receives a response with HTTP status 2XX, the request was successful. Responses with status 4XX represent errors provoked by the client (i.e. mandatory data is missing) and 5XX are server errors. Data should be specified even when none is sent from the server. Few examples would be: { meta: { }, data: { id: 24, title: 'Behavior-Driven Development', author: 'Viktor Farcic' } } { meta: { error: 789, message: 'Title field is required' }, data: { } } Versioning Since API will not be the only entry point for HTTP requests, it is often a good idea to differentiate API URIs from others. I tend to put prefix api to all URIs. It is important to understand that once an API is published and others start using it, we need to make sure that future changes do not break the usage for those who did not update their clients accordingly. For that reason it is a good practice to prefix all API URIs with version number. First one could be called v1, next one v2 and so on. This way we’ll have freedom to implement changes that might potentially introduce incompatibilities as a separate URI. With those two things in mind, our previous example with book would look something like following: /api/v1/books Examples Finally, let’s go through few examples with books. Request the list of books Protocol: GET URI: /api/v1/books Request body: EMPTY Response: { meta: { }, data: [{ id: 24, title: 'Behavior-Driven Development', author: 'Viktor Farcic' }, { id: 25, title: 'Continuous Integration', author: 'Viktor Farcic' }] } Description: Requests all books to be retrieved from the server. Request a single book Protocol: GET URI: /api/v1/books/id/24 Request body: EMPTY Response: { meta: { }, data: { id: 24, title: 'Behavior-Driven Development', author: 'Viktor Farcic' } } Description: Requests the book with ID 24 to be retrieved from the server. Request book creation Protocol: POST URI: /api/v1/books Request body: { id: 24, title: 'Behavior-Driven Development', author: 'Viktor Farcic' } Response with status 201: { meta: { }, data: { uri: /api/v1/books/id/24 } } Description: Requests the book to be created. Server response is 201 (created) with URI that can be used by the client to retrieve the requested book. Request the book to be created or modified Protocol: PUT URI: /api/v1/books Request body: { id: 24, author: 'Viktor Farcic' } Response with status 412: { meta: { error: 789, message: 'Title field is required' }, data: { } } Description: Requests the book to be modified or created. Server response is 412 (precondition failed) with error code and message describing the problem. Request the book removal Protocol: DELETE URI: /api/v1/books Request body: { id: 24 } Response with status 202: { meta: { }, data: { } } Description: Requests the book to be removed. Server response is 202 (accepted) meaning that the server accepted the request but the processing has not been completed. In other words, server responded immediately and has the removal pending.Reference: REST API with JSON from our JCG partner Viktor Farcic at the Technology conversations blog....
jcg-logo

Java Code Geeks are giving away FREE JCG Academy Subscriptions (worth $900)!

Ready to take your skill-set to the next level? We are running a short duration contest giving away annual subscriptions for our premium content website, JCG Academy. JCG Academy is packed with high end courses covering a wide spectrum of technologies, ranging from Java and JavaScript, to Android and NoSQL platforms. Here are some of the courses that are featured there:      Java Concurrency Essentials (FREE!) Introduction to Nginx (FREE!) Java Design Patterns Spring Integration for EAI Android UI Design – Basics Building web apps with Node.js… and many more! Enter the contest now to win your very own FREE annual subscription. There will be a total of 10 winners! In addition, we will send you free tips and the latest news from the Java community to master your technical knowledge (you can unsubscribe at any time). In order to increase your chances of winning, don’t forget to refer as much of your friends as possible! You will get 3 more entries for every friend you refer, that is 3 times more chances! Make sure to use your lucky URL to spread the word! Good luck and may the force be with you! Join the Contest! ...
java-logo

Programming a simple slot machine game using Java

No mat­ter how sim­ple or com­plex the game is, Java can do the job! On this post, let’s take a look at how begin­ners of Java pro­gram­ming can make a sim­ple, yet fully func­tional slot machine. Slot machines have been around for a long time, but its enter­tain­ment value doesn’t seem to fade one bit. Inter­Casino, the first web­site to offer online casino gam­ing to the world in 1996, is still around and its slot games seem to get updated often. In addi­tion, accord­ing to the Amer­i­can Gam­ing Asso­ci­a­tion, slots gen­er­ate around 62% — 90% of gam­ing money, mak­ing the machines the cash cows of casi­nos. With these facts in mind, don’t you ever want to cre­ate your very own slot machine that mil­lions of casino gam­ing fans might like in the future? If you’re inter­ested in cre­at­ing Java-based slot games, the code below might prove use­ful for you. package slotMachineGUI;import java.awt.*; import javax.swing.*; import javax.swing.LayoutStyle.ComponentPlacement; import java.text.DecimalFormat; import java.util.Random; import java.util.ArrayList; import javax.swing.border.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;public class SlotMachineGUI { private JButton btnCash, btnSpin; private JCheckBox cbAlwaysWin, cbSuperJackpot, cbTrollface; private JFrame frmFrame; private JLabel lblCredits, lblLost, lblMatchThree, lblMatchTwo, lblMoney, lblReel1, lblReel2, lblReel3, lblStatus, lblWon; private JPanel pnlReels, pnlReel1, pnlReel2, pnlReel3; private JProgressBar prgbarCheatUnlocker; private JSeparator sepCheats, sepStats, sepStats2, sepStatus; private JToggleButton tgglSound; private int credits = 100, boughtCredits = 100, bet = 15, matchThree, matchTwo, win, lost; private double payout = 25.0, creditBuyout = 10.0, funds; private int reel1 = 7, reel2 = 7, reel3 = 7; // starting values of the reels. private ArrayList<ImageIcon> images = new ArrayList<ImageIcon>(); private DecimalFormat df = new DecimalFormat("0.00"); public SlotMachineGUI(int credits, int boughtCredits, int bet, double payout, double creditBuyout, int reel1, int reel2, int reel3) { this.credits=credits; this.boughtCredits=boughtCredits; this.bet=bet; this.payout=payout; this.creditBuyout=creditBuyout; this.reel1=reel1; this.reel2=reel2; this.reel3=reel3; createForm(); loadImages(); addFields(); addButtons(); layoutFrame(); layoutReels(); layoutOther(); } public SlotMachineGUI() { createForm(); loadImages(); addFields(); addButtons(); layoutFrame(); layoutReels(); layoutOther(); } /** Creates the JFrame and Panels. */ private void createForm() { frmFrame = new JFrame(); frmFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frmFrame.setTitle("Warner Slots"); frmFrame.setResizable(false); frmFrame.setVisible(true); pnlReels = new JPanel(); pnlReels.setBorder(BorderFactory.createEtchedBorder()); pnlReel1 = new JPanel(); pnlReel1.setBackground(new Color(255, 215, 0)); pnlReel1.setBorder(new SoftBevelBorder(BevelBorder.LOWERED)); pnlReel2 = new JPanel(); pnlReel2.setBackground(new Color(255, 216, 0)); pnlReel2.setBorder(new SoftBevelBorder(BevelBorder.LOWERED)); pnlReel3 = new JPanel(); pnlReel3.setBackground(new java.awt.Color(255, 215, 0)); pnlReel3.setBorder(new SoftBevelBorder(BevelBorder.LOWERED)); } /** Adds labels to the form. */ private void addFields() { lblReel1 = new JLabel(); lblReel2 = new JLabel(); lblReel3 = new JLabel(); sepStats = new JSeparator(); lblMatchTwo = new JLabel(); lblMatchTwo.setText("Matched Two: "); lblMatchThree = new JLabel(); lblMatchThree.setText("Matched Three: "); lblWon = new JLabel(); lblWon.setText("Won: "); sepStats2 = new JSeparator(); sepStats2.setOrientation(SwingConstants.VERTICAL); lblCredits = new JLabel(); lblCredits.setText("Credits: "+credits); lblMoney = new JLabel(); lblMoney.setText("Money: £"+df.format(funds)); lblLost = new JLabel(); lblLost.setText("Lost: "); sepStatus = new JSeparator(); lblStatus = new JLabel(); lblStatus.setBackground(new Color(255, 255, 255)); lblStatus.setFont(new Font("Arial", 1, 14)); lblStatus.setHorizontalAlignment(SwingConstants.CENTER); lblStatus.setText("Welcome to WARNER SLOTS!!! ©2012"); sepCheats = new JSeparator(); prgbarCheatUnlocker = new JProgressBar(); prgbarCheatUnlocker.setToolTipText("Fill the bar to unlock the cheat menu."); lblReel1.setIcon(images.get(reel1)); lblReel2.setIcon(images.get(reel2)); lblReel3.setIcon(images.get(reel3)); } /** Adds buttons to the form. */ private void addButtons() { btnSpin = new JButton(); btnSpin.setBackground(new Color(50, 255, 50)); btnSpin.setText("Spin"); btnSpin.setToolTipText("Click to spin the reels!"); btnSpin.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); btnSpin.setInheritsPopupMenu(true); btnSpin.setMaximumSize(new Dimension(200, 50)); btnSpin.setMinimumSize(new Dimension(200, 50)); btnSpin.addActionListener(new SpinHandler()); btnCash = new JButton(); btnCash.setBackground(new Color(255, 0, 0)); btnCash.setText("Buy Credits"); btnCash.setToolTipText("£"+df.format(bet)+" converts to "+boughtCredits+" credits."); btnCash.setHorizontalTextPosition(SwingConstants.CENTER); btnCash.addActionListener(new BuyCreditsHandler()); tgglSound = new JToggleButton(); tgglSound.setSelected(false); tgglSound.setText("Sound ON"); tgglSound.addActionListener(new SoundHandler()); cbAlwaysWin = new JCheckBox(); cbAlwaysWin.setText("Always Win Mode"); cbAlwaysWin.setEnabled(false); cbAlwaysWin.addActionListener(new AlwaysWinHandler()); cbTrollface = new JCheckBox(); cbTrollface.setText("Trollface"); cbTrollface.setEnabled(false); cbTrollface.addActionListener(new TrollfaceHandler()); cbSuperJackpot = new JCheckBox(); cbSuperJackpot.setText("Super Jackpot"); cbSuperJackpot.setEnabled(false); cbSuperJackpot.addActionListener(new SuperPrizeHandler()); } /** Lays out the frame. */ private void layoutFrame() { GroupLayout frameLayout = new GroupLayout(frmFrame.getContentPane()); frmFrame.getContentPane().setLayout(frameLayout); frameLayout.setHorizontalGroup( frameLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGap(0, 400, Short.MAX_VALUE) ); frameLayout.setVerticalGroup( frameLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGap(0, 300, Short.MAX_VALUE) ); } /** Lays out the panels and reels. */ private void layoutReels() { GroupLayout pnlReelsLayout = new GroupLayout(pnlReels); pnlReels.setLayout(pnlReelsLayout); pnlReelsLayout.setHorizontalGroup( pnlReelsLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(pnlReelsLayout.createSequentialGroup() .addContainerGap() .addComponent(pnlReel1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) .addComponent(pnlReel2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) .addComponent(pnlReel3, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); pnlReelsLayout.setVerticalGroup( pnlReelsLayout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(pnlReelsLayout.createSequentialGroup() .addContainerGap() .addGroup(pnlReelsLayout.createParallelGroup(GroupLayout.Alignment.TRAILING, false) .addComponent(pnlReel2, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pnlReel1, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pnlReel3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); GroupLayout pnlReel1Layout = new GroupLayout(pnlReel1); pnlReel1.setLayout(pnlReel1Layout); pnlReel1Layout.setHorizontalGroup( pnlReel1Layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(pnlReel1Layout.createSequentialGroup() .addContainerGap() .addComponent(lblReel1) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); pnlReel1Layout.setVerticalGroup( pnlReel1Layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(pnlReel1Layout.createSequentialGroup() .addContainerGap() .addComponent(lblReel1) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); GroupLayout pnlReel2Layout = new GroupLayout(pnlReel2); pnlReel2.setLayout(pnlReel2Layout); pnlReel2Layout.setHorizontalGroup( pnlReel2Layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(pnlReel2Layout.createSequentialGroup() .addContainerGap() .addComponent(lblReel2) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); pnlReel2Layout.setVerticalGroup( pnlReel2Layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(pnlReel2Layout.createSequentialGroup() .addContainerGap() .addComponent(lblReel2) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); GroupLayout pnlReel3Layout = new GroupLayout(pnlReel3); pnlReel3.setLayout(pnlReel3Layout); pnlReel3Layout.setHorizontalGroup( pnlReel3Layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(pnlReel3Layout.createSequentialGroup() .addContainerGap() .addComponent(lblReel3) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); pnlReel3Layout.setVerticalGroup( pnlReel3Layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(pnlReel3Layout.createSequentialGroup() .addContainerGap() .addComponent(lblReel3) .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); } /** lays out the remaining labels, check boxes, progress bars, etc. */ private void layoutOther() { GroupLayout layout = new GroupLayout(frmFrame.getContentPane()); frmFrame.getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING, false) .addComponent(sepCheats) .addComponent(prgbarCheatUnlocker, GroupLayout.DEFAULT_SIZE, 426, Short.MAX_VALUE)) .addGap(0, 0, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING, false) .addGroup(layout.createSequentialGroup() .addComponent(cbAlwaysWin) .addGap(18, 18, 18) .addComponent(cbTrollface) .addGap(18, 18, 18) .addComponent(cbSuperJackpot) .addPreferredGap(ComponentPlacement.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(tgglSound)) .addComponent(btnSpin, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(pnlReels, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(sepStats, GroupLayout.Alignment.TRAILING) .addComponent(lblStatus, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING, false) .addComponent(lblMatchTwo, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lblWon, GroupLayout.Alignment.LEADING, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lblMatchThree, GroupLayout.DEFAULT_SIZE, 149, Short.MAX_VALUE)) .addPreferredGap(ComponentPlacement.UNRELATED) .addComponent(sepStats2, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING, false) .addComponent(lblLost, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lblCredits, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lblMoney, GroupLayout.DEFAULT_SIZE, 154, Short.MAX_VALUE)) .addGap(0, 0, Short.MAX_VALUE))) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING) .addComponent(btnCash) .addComponent(sepStatus, GroupLayout.PREFERRED_SIZE, 426, GroupLayout.PREFERRED_SIZE))) .addContainerGap()))) ); layout.setVerticalGroup( layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(pnlReels, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(btnSpin, GroupLayout.PREFERRED_SIZE, 56, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.UNRELATED) .addComponent(sepStats, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(lblWon, GroupLayout.PREFERRED_SIZE, 19, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(lblMatchTwo, GroupLayout.PREFERRED_SIZE, 19, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(lblMatchThree, GroupLayout.DEFAULT_SIZE, 25, Short.MAX_VALUE)) .addComponent(sepStats2) .addGroup(layout.createSequentialGroup() .addComponent(lblLost, GroupLayout.PREFERRED_SIZE, 19, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(lblCredits, GroupLayout.PREFERRED_SIZE, 19, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(lblMoney, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addComponent(btnCash, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addPreferredGap(ComponentPlacement.UNRELATED) .addComponent(sepStatus, GroupLayout.PREFERRED_SIZE, 2, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.UNRELATED) .addComponent(lblStatus, GroupLayout.PREFERRED_SIZE, 30, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.UNRELATED) .addComponent(sepCheats, GroupLayout.PREFERRED_SIZE, 5, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.RELATED) .addComponent(prgbarCheatUnlocker, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .addPreferredGap(ComponentPlacement.UNRELATED) .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) .addComponent(cbAlwaysWin) .addComponent(cbTrollface) .addComponent(cbSuperJackpot) .addComponent(tgglSound)) .addContainerGap()) ); frmFrame.pack(); } /** Performs action when Buy Credits button is clicked. */ class BuyCreditsHandler implements ActionListener { public void actionPerformed(ActionEvent event) { buyCredits(); } } /** if the player has enough funds credits are added. */ public void buyCredits() { if (funds >= creditBuyout) { funds -= creditBuyout; lblMoney.setText("Money: £"+df.format(funds)); credits += boughtCredits; lblCredits.setText("Credits: "+credits); lblStatus.setText("+"+boughtCredits+" credits purchased! -£"+df.format(creditBuyout)); } else { lblStatus.setText("Insufficient £ to purchase credits!"); } buyCreditsCheck(); } /** if user has enough funds to buy credits changes buttons colour to alert user. */ public void buyCreditsCheck() { if (funds < bet) { btnCash.setBackground(new java.awt.Color(255, 0, 0)); } else { btnCash.setBackground(new java.awt.Color(50, 255, 50)); } } /** Performs action when Spin button is clicked. */ class SpinHandler implements ActionListener { public void actionPerformed(ActionEvent event) { if (funds < creditBuyout && credits < bet) { lblStatus.setText("<html><a href='http://www.gambleaware.co.uk/'>www.gambleaware.co.uk</a></html>"); } else if ((credits - bet) >= 0) { pnlReel1.setBackground(new java.awt.Color(255, 215, 0)); pnlReel2.setBackground(new java.awt.Color(255, 215, 0)); pnlReel3.setBackground(new java.awt.Color(255, 215, 0)); genReelNumbers(); matchCheck(); } else { lblStatus.setText("Bet is "+bet+" credits, purchase more with £!"); } buyCreditsCheck(); } } /** Generates the 3 reel numbers. */ public void genReelNumbers() { Random rand = new Random(); if (cbAlwaysWin.isSelected() == true) { // If the Always win cheat mode is enabled. int winType = rand.nextInt(4); // generates number between 0-3 to determine the type of win reel1 = rand.nextInt(images.size()); if (winType == 0) { // winType = 0 - Reels 1, 2 and 3 will all match. reel2 = reel1; reel3 = reel1; } else if (winType == 1) { // winType = 1 - Reels 1 and 2 will match. reel2 = reel1; } else if (winType == 2) { // winType = 2 - Reels 1 and 3 will match. reel3 = reel1; } else { // winType = 3 - Reels 2 and 3 will match. if (reel1 >= 0 ) { reel2 = reel1 + 1; reel3 = reel1 + 1; } if (reel1 == images.size()-1) { reel2 = reel1 - 1; reel3 = reel1 - 1; } } } else { // If the Always win cheat mode is disabled play a normal game. reel1 = rand.nextInt(images.size()); reel2 = rand.nextInt(images.size()); reel3 = rand.nextInt(images.size()); } setReelIcon(reel1, reel2, reel3); // Set the reel image } /** Sets the reels icon based on loaded image in images ArrayList. */ public void setReelIcon(int ico1, int ico2, int ico3) { lblReel1.setIcon(images.get(ico1)); // icon = the ArrayList index = random reel number lblReel2.setIcon(images.get(ico2)); lblReel3.setIcon(images.get(ico3)); } /** Checks for number matches and adjusts score depending on result. */ public void matchCheck() { if (reel1 == reel2 && reel2 == reel3) { lblStatus.setText("You matched THREE symbols ("+images.get(reel1).getDescription()+")! +£"+df.format(getPrize(payout))+"!"); lblMatchThree.setText("Matched Three: "+matchThree()); pnlReel1.setBackground(new java.awt.Color(255, 0, 0)); // Highlights matched icons. pnlReel2.setBackground(new java.awt.Color(255, 0, 0)); pnlReel3.setBackground(new java.awt.Color(255, 0, 0)); } else if (reel1 == reel2 || reel1 == reel3) { lblStatus.setText("You matched TWO symbols ("+images.get(reel1).getDescription()+")! +£"+df.format(getPrize(payout))+"!"); lblMatchTwo.setText("Matched Two: "+matchTwo()); if (reel1 == reel2) { pnlReel1.setBackground(new java.awt.Color(255, 0, 0)); // Highlights matched icons. pnlReel2.setBackground(new java.awt.Color(255, 0, 0)); } else if (reel1 == reel3){ pnlReel1.setBackground(new java.awt.Color(255, 0, 0)); // Highlights matched icons. pnlReel3.setBackground(new java.awt.Color(255, 0, 0)); } } else if (reel2 == reel3) { lblStatus.setText("You matched TWO symbols ("+images.get(reel2).getDescription()+")! +£"+df.format(getPrize(payout))+"!"); lblMatchTwo.setText("Matched Two: "+matchTwo()); pnlReel2.setBackground(new java.awt.Color(255, 0, 0)); // Highlights matched icons. pnlReel3.setBackground(new java.awt.Color(255, 0, 0)); } else { lblStatus.setText("Sorry, you didn't match any symbols. -"+bet+" credits!"); lblLost.setText("Lost: "+lose()); } lblCredits.setText("Credits: "+(credits -= bet)); // deduct bet amount from available credits. lblMoney.setText("Money: £"+df.format((funds += getPrize(payout)))); // If there is a win add amount to cash pot. lblWon.setText("Wins: "+win()); // increment win amount. } /** sets progress bar equal to the current win count. if bar is full it unlocks cheat menu */ public void prgBarCheck() { if (prgbarCheatUnlocker.getValue() <= 99) { prgbarCheatUnlocker.setValue(win); } else if (prgbarCheatUnlocker.getValue() == 100) { // after 100 wins unlock the cheats. prgbarCheatUnlocker.setValue(100); lblStatus.setText("100 wins! Congratulations you've unlocked the cheat menu!"); cbTrollface.setEnabled(true); cbSuperJackpot.setEnabled(true); cbAlwaysWin.setEnabled(true); } } /** calculates prize to be awarded for win based on number of matches and cheats. */ public double getPrize(double prize) { if (reel1 == reel2 && reel2 == reel3) { if (cbSuperJackpot.isSelected() == true) { prize *= 100; // if cheating and all are matched return the full pay out x100. } else { prize = payout; // if all are matched return the full pay out. } } else if (reel1 == reel2 || reel1 == reel3 || reel2 == reel3) { if (cbSuperJackpot.isSelected() == true) { prize *= 50; // if cheating and two are matched return the pay out x50. } else { prize = payout / 5; // if two are matched return 1/5th of the pay out. } } else { prize = 0; // If no win return no prize. } return prize; } /** Performs action when Super Jack pot check box is clicked. */ class SuperPrizeHandler implements ActionListener{ public void actionPerformed(ActionEvent e) { if (cbSuperJackpot.isSelected() == true) { lblStatus.setText("Super Prize mode ENABLED! The £ won is now x100!"); } if (cbSuperJackpot.isSelected() == false) { lblStatus.setText("Super Prize mode DISABLED! :'("); } } } /** Performs action when Troll face check box is clicked. */ class AlwaysWinHandler implements ActionListener{ public void actionPerformed(ActionEvent e) { if (cbAlwaysWin.isSelected() == true) { lblStatus.setText("Always Win mode ENABLED! 7-7-7's here we come!"); } if (cbAlwaysWin.isSelected() == false) { lblStatus.setText("Always Win mode DISABLED! :'("); } } } /** Performs action when Troll face check box is clicked. */ class TrollfaceHandler implements ActionListener{ public void actionPerformed(ActionEvent e) { if (cbTrollface.isSelected() == true && images.get(images.size()-1) != createImageIcon("images/Trollface.png", "Trollface")) { images.add(createImageIcon("images/Trollface.png", "Trollface")); // adds a bonus image to the images ArrayList. lblStatus.setText("Trollface mode ENABLED! Trolololololol!"); } if (cbTrollface.isSelected() == false && images.get(images.size()-1) != createImageIcon("images/Trollface.png", "Trollface")) { images.remove(images.size()-1); // removes the bonus image (or last one added to the ArrayList) from the images ArrayList. lblStatus.setText("Trollface mode DISABLED! :'("); } } } /** Performs action when sound toggle button is clicked. * NOT IMPLEMENTED */ class SoundHandler implements ActionListener{ public void actionPerformed(ActionEvent e) { if (tgglSound.isSelected() == false) { tgglSound.setText("Sound ON"); lblStatus.setText("Sound effects have been ENABLED!"); // allowed to play sounds } else { tgglSound.setText("Sound OFF"); lblStatus.setText("Sound effects have been DISABLED!"); // disable sounds } } } /** Loads ImageIcons into the images ArrayList. * The difficulty is determined by the number of images present in the ArrayList: * • Add images here to make game more difficult. * • Remove images here to make game easier. */ public void loadImages() { images.add(createImageIcon("images/Banana.png", "Banana")); images.add(createImageIcon("images/Bar.png", "Bar")); images.add(createImageIcon("images/Bell.png", "Bell")); images.add(createImageIcon("images/Cherry.png", "Cherry")); images.add(createImageIcon("images/Clover.png", "Clover")); images.add(createImageIcon("images/Diamond.png", "Diamond")); images.add(createImageIcon("images/Plum.png", "Plum")); images.add(createImageIcon("images/Seven.png", "Seven")); images.add(createImageIcon("images/Watermelon.png", "Watermelon")); } /** Create a new ImageIcon, unless the URL is not found. */ public ImageIcon createImageIcon(String path, String description) { java.net.URL imgURL = getClass().getResource(path); if (imgURL != null) { return new ImageIcon(imgURL, description); } else { System.err.println("Couldn't find file: " + path); return null; } } /** Increments matchThree by 1 and returns value. */ public int matchThree() { matchThree++; return matchThree; } /** Increments matchTwo by 1 and returns value. */ public int matchTwo() { matchTwo++; return matchTwo; } /** Increments lost by 1 and returns value. */ public int lose() { lost++; return lost; } /** Increments win by 1, increases progress bar and returns value. */ public int win() { win = matchThree + matchTwo; prgBarCheck(); // Increments the progress bar to unlock cheat menu. return win; } public static void main(String args[]) { try { for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { javax.swing.UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (ClassNotFoundException ex) { java.util.logging.Logger.getLogger(SlotMachineGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (InstantiationException ex) { java.util.logging.Logger.getLogger(SlotMachineGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (IllegalAccessException ex) { java.util.logging.Logger.getLogger(SlotMachineGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } catch (javax.swing.UnsupportedLookAndFeelException ex) { java.util.logging.Logger.getLogger(SlotMachineGUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); } java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new SlotMachineGUI(); } }); } } Kudos to M ajes­tic, a YouTube user, for the code above. Here are the images that he used in the cre­ation of the game.Reference: Programming a simple slot machine game using Java from our JCG partner Brian Porter at the Poornerd blog....
javafx-logo

JavaFX Tip 14: StackPane Children – Hidden But Not Gone

Another short tip: Swing provides a layout manager called CardLayout, which manages a set of components (cards) inside a container but always only shows one of them. The method CardLayout.show(Container, String) allows to switch between the components / the cards. The same behaviour can be accomplished in JavaFX by using the StackPane, adding several children (each using the entire width and height of the pane) and calling the Node.toFront() method to switch between the children. However, there is one big difference: the StackPane will always layout all of its children, independent of wether they are currently showing or not. This might result in bad performance of your application and can be noticed when resizing the window that contains the pane. My advice: manage your “cards” by adding them to or removing them from the scene graph. These operations are fast and flicker-free (this is JavaFX in Java 8, not Swing before Java 6).Reference: JavaFX Tip 14: StackPane Children – Hidden But Not Gone from our JCG partner Dirk Lemmermann at the Pixel Perfect blog....
java-logo

Java Concurrency Tutorial – Visibility between threads

When sharing an object’s state between different threads, other issues besides atomicity come into play. One of them is visibility. The key fact is that without synchronization, instructions are not guaranteed to be executed in the order in which they appear in your source code. This won’t affect the result in a single-threaded program but, in a multi-threaded program, it is possible that if one thread updates a value, another thread doesn’t see the update when it needs it or doesn’t see it at all. In a multi-threaded environment, it is the program’s responsibility to identify when data is shared between different threads and act in consequence (using synchronization). The example in NoVisibility consists in two threads that share a flag. The writer thread updates the flag and the reader thread waits until the flag is set: public class NoVisibility { private static boolean ready; public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { @Override public void run() { while (true) { if (ready) { System.out.println("Reader Thread - Flag change received. Finishing thread."); break; } } } }).start(); Thread.sleep(3000); System.out.println("Writer thread - Changing flag..."); ready = true; } } This program might result in an infinite loop, since the reader thread may not see the updated flag and wait forever.With synchronization we can guarantee that this reordering doesn’t take place, avoiding the infinite loop. To ensure visibility we have two options:Locking: Guarantees visibility and atomicity (as long as it uses the same lock). Volatile field: Guarantees visibility.The volatile keyword acts like some sort of synchronized block. Each time the field is accessed, it will be like entering a synchronized block. The main difference is that it doesn’t use locks. For this reason, it may be suitable for examples like the above one (updating a shared flag) but not when using compound actions. We will now modify the previous example by adding the volatile keyword to the ready field. public class Visibility { private static volatile boolean ready; public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { @Override public void run() { while (true) { if (ready) { System.out.println("Reader Thread - Flag change received. Finishing thread."); break; } } } }).start(); Thread.sleep(3000); System.out.println("Writer thread - Changing flag..."); ready = true; } } Visibility will not result in an infinite loop anymore. Updates made by the writer thread will be visible to the reader thread: Writer thread - Changing flag... Reader Thread - Flag change received. Finishing thread. Conclusion We learned about another risk when sharing data in multi-threaded programs. For a simple example like the one shown here, we can simply use a volatile field. Other situations will require us to use atomic variables or locking.You can take a look at the source code at github.Reference: Java Concurrency Tutorial – Visibility between threads from our JCG partner Xavier Padro at the Xavier Padró’s Blog blog....
career-logo

The Full-Time Developer ‘Curse’

Some time ago I wrote an article with my advice to (junior) developers about their career.  One of the things I explained was that by the word “young” I meant all developer who feel young so it doesn’t matter how many years of experience you’re carrying on your back.  You’re still “young”! A few days ago, I came back from two-weeks of vacations and I realized that during this period I had plenty of time to apply at least once all these little advice (learn new things, promote yourself, be part of an opensource project, etc). That was really cool. And the funniest of all is that I spent no more than 2-3 hours  a day. Amazing! But very quickly I realized also, that the only reason I managed to do it, was the fact that I was in vacations. Why I can’t do all these nice things every week, every month? The answer is really simple. I work as a full-time developer : This practically means that I spend 9 hours daily working with legacy code, existing or new products of a specific domain. The technologies could be considered modern but they’re not state-of-the art so my learning curve is dropping down. My main responsibility is to delivery product value based on customer requirements and occasionally trouble-shot software issues. I love my job and I feel very creative with what I do but I still feel that something’s missing. I don’t have spare time (in my working hours) to  learn new languages or new tools / frameworks etc. so I try to fill the blanks in my free time. However, do I have any free time? Let me explain my thoughts : Add to the 9 hours I commit to work one more hour to get prepared in the morning and rest for a while when I get back. So far 10 hours of the day are out. Add 8 hours of sleep needed and what’s left? 6 hours / weekday. One could argue that this is fair enough for all the other activities I’m suggesting. Well, I partially agree but… You need some time to hangout with your friends, some time to relax by yourself, to do some housekeeping tasks. Now imagine you’re married with kids. Kids need (at least) your attention so 1-2 hours are clearly dedicated to them, and trust me this is the happiest time of the day, no matter how tired you are or how many problems jump in your mind. Now do the maths and you’ll see that nothing’s left during the week days. And my calculations were very rough. Some people are committing  more than 10 hours / day and spend more than 2 hours for preparation and going to work. So the root cause of the problem is that I’m a full-time developer!! Right, yeah! Wouldn’t be great if I had to work only 4 hours/day? Then I would have the required free time for the rest of my tasks and activities.  But what about my salary? Probably if I work less I will be paid less but then I will have difficulties with my loan, my every day expenses. So, thank God, I’m a full-time developer. Or not? For me the ideal scenario would be the following :Part time developer ( 4 hours/day ) Enough free time for my other activities that generate some income to cover the missing salary.Am I asking too much? I don’t know but what I know for sure is that I’m cursed and blessed for being a full-time developer. You?Reference: The Full-Time Developer ‘Curse’ from our JCG partner Patroklos Papapetrou at the Only Software matters blog....
junit-logo

This is Stuff: jUnit: Dynamic Tests Generation

Dynamic tests generation is useful when you need to run the same set of tests on many different input values or configurations. It can be achieved either using parametrized tests or using theories. Theories are valuable when you have a bunch of data to be used as parameters and want to run tests on all their combinations. You get less control, but you do not have to write combining and iterating code by yourself. Basics about how theories work are explained on java code geeks (original at java advent calendar), so this post focus on parametrized tests. Parametrized tests are better when you need to have good control over the input values, e.g. directory with files that are served as an input or a list of meaningful parameters combinations. Parametrized Tests Parametrized test is a test case able to accept parameters and a list of all parameter combinations you want it to run at. JUnit goes through the list of parameters, initializes the test case with each of them and then runs all its test methods. Both GUI and Maven runners then interpret each parametrized test run as a separate test. If some of them fails, it is immediately clear which did failed and how many of them failed. Example Use Case Less4j is less to css compiler, so each of its tests is defined by an input less file and an expected css file. The compiler is run on input file and its output is compared to the expected css. If they match, test is passed. All .less files are stored in a directory. Parametrized test case reads that directory and creates one jUnit test for each file. Therefore we can add new tests just by creating new .less and .css, run tests via “run all” button and see new test in all reports. How to Use It Parametrized test case must have following things:a @RunWith(Parameterized.class) class annotation, a constructor that accepts test case parameters, a static method annotated with @Parameters to generate parameters, test methods that runs on parameters supplied in constructor.Constructor Parametrized constructor must have at least one parameter. For example, the compiler test case can take input less as a first argument and expected compiled css as second argument. The third argument name is ignored and will be explained later: @RunWith(Parameterized.class) public class ParametrizedTest {public ParametrizedTest(String less, String expectedCss, String name) { this.less = less; this.expectedCss = expectedCss; }} Parameters The static method generating parameters must return an implementation of the Iterable interface. The iterator returns arrays containing sets of parameters. Each array is used to create one test case instance and objects in it are used as constructor parameters. For example, following method returns two arrays and thus leads to two test case instances: @Parameters(name="Name: {2}") public static Iterable<Object[]> generateParameters() { List<Object[]> result = new ArrayList<Object[]>(); result.add(new Object[] {"less", "css", "pass"}); result.add(new Object[] {"less", "error", "fail"}); return result; } The name annotation parameter is optional. Its value will be shown in GUI or maven report as the test case name. The {n} is placeholder for n-th array value. They are indexed from 0, so the first test case will be named Name: pass and second test case will be named Name: fail. Test Methods Parametrized test case can have any number of tests and they must be annotated with @Test annotation: @Test public void testCss() { //dummy test method String actualCss = compile(less); assertEquals(expectedCss, actualCss); }@Test public void testSourceMap() { //another test method String actualCss = compile(less); assertEquals(expectedCss, actualCss); }private String compile(String less) { //dummy compile method return "css"; } Output If you run the above test class, the JUnit view will show following structure: [F] com.github.sommeri.jUnit4Examples.ParametrizedTest [ ] |-- [Name: pass] [ ] |---------------- testCss[Name: pass] [ ] |---------------- testSourceMap[Name: pass] [F] |-- [Name: fail] [F] |---------------- testCss[Name: fail] [F] |---------------- testSourceMap[Name: fail] Full Test Case @RunWith(Parameterized.class) public class ParametrizedTest { private String less; private String expectedCss;public ParametrizedTest(String less, String expectedCss, String name) { this.less = less; this.expectedCss = expectedCss; } @Parameters(name="Name: {2}") public static Iterable<Object[]> generateParameters() { List<Object[]> result = new ArrayList<Object[]>(); result.add(new Object[] {"less", "css", "pass"}); result.add(new Object[] {"less", "error", "fail"}); return result; } @Test public void testCss() { String actualCss = compile(less); assertEquals(expectedCss, actualCss); }@Test public void testSourceMap() { String actualCss = compile(less); assertEquals(expectedCss, actualCss); }//dummy compile method private String compile(String less) { return "css"; } }Reference: This is Stuff: jUnit: Dynamic Tests Generation from our JCG partner Maria Jurcovicova at the This is Stuff blog....
redis-logo

URL shortener service in 42 lines of code in… Java (?!) Spring Boot + Redis

Apparently writing a URL shortener service is the new “Hello, world!” in the IoT/microservice/era world. It all started with A URL shortener service in 45 lines of Scala – neat piece of Scala, flavoured with Spray and Redis for storage. This was quickly followed with A url shortener service in 35 lines of Clojure and even URL Shortener in 43 lines of Haskell. So my inner anti-hipster asked: how long would it be in Java? But not plain Java, for goodness’ sake. Spring Boot with Spring Data Redis are a good starting point. All we need is a simple controller handling GET and POST:         import com.google.common.hash.Hashing; import org.apache.commons.validator.routines.UrlValidator; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.*; import org.springframework.web.bind.annotation.*; import javax.servlet.http.*; import java.nio.charset.StandardCharsets; @org.springframework.boot.autoconfigure.EnableAutoConfiguration @org.springframework.stereotype.Controller public class UrlShortener { public static void main(String[] args) { SpringApplication.run(UrlShortener.class, args); } @Autowired private StringRedisTemplate redis; @RequestMapping(value = "/{id}", method = RequestMethod.GET) public void redirect(@PathVariable String id, HttpServletResponse resp) throws Exception { final String url = redis.opsForValue().get(id); if (url != null) resp.sendRedirect(url); else resp.sendError(HttpServletResponse.SC_NOT_FOUND); } @RequestMapping(method = RequestMethod.POST) public ResponseEntity<String> save(HttpServletRequest req) { final String queryParams = (req.getQueryString() != null) ? "?" + req.getQueryString() : ""; final String url = (req.getRequestURI() + queryParams).substring(1); final UrlValidator urlValidator = new UrlValidator(new String[]{"http", "https"}); if (urlValidator.isValid(url)) { final String id = Hashing.murmur3_32().hashString(url, StandardCharsets.UTF_8).toString(); redis.opsForValue().set(id, url); return new ResponseEntity<>("http://mydomain.com/" + id, HttpStatus.OK); } else return new ResponseEntity<>(HttpStatus.BAD_REQUEST); } } The code is nicely self-descriptive and is functionally equivalent to a version in Scala. I didn’t try to it squeeze too much to keep line count as short as possible, code above is quite typical with few details:I don’t normally use wildcard imports I don’t use fully qualified class names (I wanted to save one import line, I admit) I surround if/else blocks with braces I almost never use field injection, ugliest brother in inversion of control family. Instead I would go for constructor to allow testing with mocked Redis:@Autowired private final StringRedisTemplate redis; public UrlShortener(StringRedisTemplate redis) { this.redis = redis; } The thing I struggled the most was… obtaining the original, full URL. Basically I needed everything after .com or port. No bloody way (neither servlets, nor Spring MVC), hence the awkward getQueryString() fiddling. You can use the service as follows – creating shorter URL: $ curl -vX POST localhost:8080/https://www.google.pl/search?q=tomasz+nurkiewicz > POST /https://www.google.pl/search?q=tomasz+nurkiewicz HTTP/1.1 > User-Agent: curl/7.30.0 > Host: localhost:8080 > Accept: */* > < HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Type: text/plain;charset=ISO-8859-1 < Content-Length: 28 < Date: Sat, 23 Aug 2014 20:47:40 GMT GET /50784f51 HTTP/1.1 > User-Agent: curl/7.30.0 > Host: localhost:8080 > Accept: */* > < HTTP/1.1 302 Found < Server: Apache-Coyote/1.1 < Location: https://www.google.pl/search?q=tomasz+nurkiewicz < Content-Length: 0 < Date: Sat, 23 Aug 2014 20:48:00 GMT ...
java-logo

Java 8 : Functional VS Traditional

The business logic is the same : Given a String expression composed of visits / time like : “1/24h,1..3/3h,5/*” Then the result should be the following list of Strings: “1/24h”, “1/3h”,”2/3h”,”3/3h”, “5/1h”,”5/2h”,”5/3h”,”5/4h”,”5/5h”,until ,”24/1h” So, 2 things need to be solved, the dots and the stars for the visits and for the time. I will use Java 8 , but  I’ll show you the difference between implementing this logic using Functional  and implementing it in a traditional way with loops and ifs. package com.marco; import java.util.ArrayList; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; public class ExpressionConverter {         private static final String COMMA = ",";         private static final String SEPARATOR = "/";        private final ImmutableList<Function<String, List<String>>> visitFunctions;         private final ImmutableList<Function<String, List<String>>> timeFunctions;        public ExpressionConverter(DotsVisitFunction dotsVisitFunction, StarVisitFunction starVisitFunction, StandardFunction standardFunction,                         StarTimeFunction starTimeFunction) {                 this.visitFunctions = ImmutableList.of(dotsVisitFunction, starVisitFunction, standardFunction);                 this.timeFunctions = ImmutableList.of(starTimeFunction, standardFunction);         }        public List<String> convertVisitTimeExpressionFunctional(String visitTimeExpression) {                 return Lists.newArrayList(visitTimeExpression.split(COMMA)).parallelStream().filter(it -> !it.isEmpty())                                 .map(it -> interpretSingleExpressionFunctional(it)).flatMap(it -> it.parallelStream()).collect(Collectors.toList());         }        public List<String> convertVisitTimeExpressionTraditional(String visitTimeExpression) {                 List<String> result = new ArrayList<String>();                for (String singleVisitTime : visitTimeExpression.split(COMMA)) {                         if (!singleVisitTime.isEmpty()) {                                 result.addAll(interpretSingleVisitTimeExpressionTraditional(singleVisitTime));                         }                 }                return result;        }        private List<String> interpretSingleExpressionFunctional(String singleExpression) {                 String visit = singleExpression.split(SEPARATOR)[0];                 String time = singleExpression.split(SEPARATOR)[1];                 List<String> result = Lists.newArrayList();                visitFunctions.stream().map(it -> it.apply(visit)).flatMap(it -> it.stream()).forEach(visitIt -> {                         timeFunctions.stream().map(it -> it.apply(time)).flatMap(it -> it.stream()).forEach(timeIt -> {                                 result.add(visitIt + SEPARATOR + timeIt);                         });                 });                 return result;         }        private List<String> interpretSingleVisitTimeExpressionTraditional(String singleExpression) {                String visit = singleExpression.split(SEPARATOR)[0];                 String time = singleExpression.split(SEPARATOR)[1];                List<String> result = Lists.newArrayList();                 List<String> visists = Lists.newArrayList();                 List<String> times = Lists.newArrayList();                for (Function<String, List<String>> visitFunction : visitFunctions) {                         visists.addAll(visitFunction.apply(visit));                 }                 for (Function<String, List<String>> timeFunction : timeFunctions) {                         times.addAll(timeFunction.apply(time));                 }                 for (String visitIt : visists) {                         for (String timeIt : times) {                                 result.add(visitIt + SEPARATOR + timeIt);                         }                 }                 return result;         } } As you can see we have 2 public and 2 private methods, functional and traditional. Here is a simple test focusing on the performance of the two styles : package com.marco; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.math.BigDecimal; import java.util.Set; import org.junit.Before; import org.junit.Test; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; public class ExpressionConverterTest {        private static final String EXPRESSIONS = "6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,6/7400,9/65h,55..57/14400";        private Cache cache;        private ExpressionConverter expressionConverter;        @Before         public void init() {                 cache = mock(Cache.class);                 expressionConverter = new ExpressionConverter(new DotsVisitFunction(cache), new StarVisitFunction(cache), new StandardFunction(),                                 new StarTimeFunction(cache));                Set<Integer> totVisists = Sets.newHashSet();                 for (int i = 1; i <= 100; i++) {                         totVisists.add(i);                 }                when(cache.getVisits(anyLong(), (BigDecimal) any(), anyInt())).thenReturn(ImmutableSet.copyOf(totVisists));         }        @Test         public void testPerf() {                long averageTraditional = 0l;                 long averageFuntional = 0l;                for (int a = 0; a < 10; a++) {                         long start = System.currentTimeMillis();                        for (int i = 0; i < 1000; i++) {                                 expressionConverter.convertVisitTimeExpressionTraditional(EXPRESSIONS);                        }                         System.out.println("Traditional java " + (System.currentTimeMillis() - start) + " ms");                         averageTraditional += (System.currentTimeMillis() - start);                        long start2 = System.currentTimeMillis();                         for (int i = 0; i < 1000; i++) {                                 expressionConverter.convertVisitTimeExpressionFunctional(EXPRESSIONS);                         }                         System.out.println("Functional java " + (System.currentTimeMillis() - start2) + " ms");                         averageFuntional += (System.currentTimeMillis() - start2);                 }                System.out.println("Average Traditional java : " + (averageTraditional / 10) + " ms");                 System.out.println("Average Functional java : " + (averageFuntional / 10) + " ms");        } } And this is the output : Traditional java 1274 msFunctional java 773 msTraditional java 1054 msFunctional java 531 msTraditional java 961 msFunctional java 493 msTraditional java 948 msFunctional java 492 msTraditional java 949 msFunctional java 491 msTraditional java 958 msFunctional java 481 msTraditional java 1004 msFunctional java 474 msTraditional java 949 msFunctional java 471 msTraditional java 947 msFunctional java 475 msTraditional java 942 msFunctional java 472 msAverage Traditional java : 998 msAverage Functional java : 515 ms Functional is 2 times faster than Traditional. Why? Because of this : parallelStream() Parallel Stream will split the job in several tasks leveraging your multicore system. So can you put parallelStream() everywhere and replace all the stream() methods with parallelStream() ?? No! The parallelStream()can in fact cause a big degradation of performance if used in the wrong place. There are loads of posts out there on this argument that should be read before using this feature, but just remember, if you want to use parallelStream() make sure to measure the performance before and after!!!!!!!!!!Reference: Java 8 : Functional VS Traditional from our JCG partner Marco Castigliego at the Remove duplication and fix bad names blog....
Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close