Featured FREE Whitepapers

What's New Here?

jboss-hibernate-logo

The anatomy of Hibernate dirty checking

Introduction The persistence context enqueues entity state transitions that get translated to database statements upon flushing. For managed entities, Hibernate can auto-detect incoming changes and schedule SQL UPDATES on our behalf. This mechanism is called automatic dirty checking.           The default dirty checking strategy By default Hibernate checks all managed entity properties. Every time an entity is loaded, Hibernate makes an additional copy of all entity property values. At flush time, every managed entity property is matched against the loading-time snapshot value:So the number of individual dirty checks is given by the following formula:where n = The number of managed entities p = The number of entities of a given entity Even if only one property of a single entity has ever changed, Hibernate will still check all managed entities. For a large number of managed entities, the default dirty checking mechanism may have a significant CPU and memory footprint. Since the initial entity snapshot is held separately, the persistence context requires twice as much memory as all managed entities would normally occupy. Bytecode instrumentation A more efficient approach would be to mark dirty properties upon value changing. Analogue to the original deep comparison strategy, it’s good practice to decouple the domain model structures from the change detection logic. The automatic entity change detection mechanism is a cross-cutting concern, that can be woven either at build-time or at runtime. The entity class can be appended with bytecode level instructions implementing the automatic dirty checking mechanism. Weaving types The bytecode enhancement can happen at:Build-timeAfter the hibernate entities are compiled, the build tool (e.g. ANT, Maven) will insert bytecode level instructions into each compiled entity class. Because the classes are enhanced at build-time, this process exhibits no extra runtime penalty. Testing can be done against enhanced class versions, so that the actual production code is validated before the project gets built. RuntimeThe runtime weaving can be done using:A Java agent, doing bytecode enhancement upon entity class loading A runtime container (e.g. Spring), using JDK Instrumentation supportTowards a default bytecode enhancement dirty checking Hibernate 3 has been offering bytecode instrumentation through an ANT target but it never became mainstream and most Hibernate projects are still currently using the default deep comparison approach. While other JPA providers (e.g. OpenJPA, DataNucleus) have been favouring the bytecode enhancement approach, Hibernate has only recently started moving in this direction, offering better build-time options and even custom dirty checking callbacks. In my next post I’ll show you how you can customize the dirty checking mechanism with your own application specific strategy.Reference: The anatomy of Hibernate dirty checking from our JCG partner Vlad Mihalcea at the Vlad Mihalcea’s Blog blog....
openjdk-logo

Debugging OpenJDK

Sometimes debugging Java code is not enough and we need to step over the native part of Java. I spent some time to achieve proper state of my JDK to do that, so short description probably will be useful for ones starting their trip. I’ll use the brand new OpenJDK 9! At first you have to obtain main repository by typing: hg clone http://hg.openjdk.java.net/jdk9/jdk9 openjdk9 Then in the openjdk9 directory type: bash get_source.sh That will download all sources to you local filesystem. Theoretically compiling openjdk is not a big deal, but there are some (hmmm….) strange behaviours if you want to use it for debugging. At first of course we need to call ./configure to prepare specific makefiles for our system. We can read in documentation that we have to add –enable-debug flag to prepare fastdebug build. If you don’t have proper libs or tools installed in your system it’s right moment to install dependencies (configure output will clearly point out any lacks). After configuring and invoking make command you can face with such problem: warning _FORTIFY_SOURCE requires compiling with optimization (-O) Generating buffer classes Generating exceptions classes cc1plus: all warnings being treated as errors Cool! It happens only on some specific linux instalations (unfortunately including Fedora 20!). To solve it we have to remove _FORTIFY_SOURCE flag. Just comment (#) lines containing _FORTIFY_SOURCE in the following files:hotspot/make/linux/makefiles/gcc.make common/autoconf/flags.m4Then you can move on with making jdk project and after dozen minutes you should see: Finished building OpenJDK for target 'default' Now it’s time to import project to IDE Since we’re still waiting for good C++ IDE from JetBrains we have to use NetBeans or even Eclipse. After finishing few steps needed to setup debugging commands (for example even for java -version). Start debugging, and… SIGSEGV received. Let’s solve it by creating .gdbinit file in user home directory containing following lines: handle SIGSEGV pass noprint nostop handle SIGUSR1 pass noprint nostop handle SIGUSR2 pass noprint nostop Start debugging one more time – now it’s better! Let’s continue by adding line breakpoint. Start debugging, and… not working…! I’ve extended .gdbinit by adding: set logging on One more debug try and in gdb.txt I saw such line: No source file named hotspot/src/share/vm/memory/genCollectedHeap.cpp I was pretty sure that –enable-debug will add -g flag to gcc compiler, but it seems I was wrong. I spent few hours googling and trying to solve it by changing gdb configurations, NetBeans config, etc. Still no effect. Fortunately Michal Warecki pointed me that probably OpenJDK during debug builds zips all debuginfo and if you want to debug (of course prepared debug build due to some other purposes?). After grepping makefiles I’ve found promising disable-zip-debug-info flag. So let’s include it into our configure invocation. Also believe me it’s hard to debug optimized code in C++ (you can try, but you will encounter strange thing happening, like debugger stepping lines in incorrect order (like starting method from line 4, going back to 2, then to 5 and to 3!). So we’ll choose slowdebug option to avoid code optimization. Whole proper configure command is: bash ./configure --with-debug-level=slowdebug --with-target-bits=64 --disable-zip-debug-info Now we can invoke: make and wait for compilation to finish. Now you can check if everything works correctly by invoking ./java -version in build/linux-x86_64-normal-server-slowdebug/jdk/bin directory. You should see: openjdk version "1.9.0-internal-debug" OpenJDK Runtime Environment (build 1.9.0-internal-debug-kuba_2014_08_20_14_02-b00) OpenJDK 64-Bit Server VM (build 1.9.0-internal-debug-kuba_2014_08_20_14_02-b00, mixed mode) Let’s try debugging. Add line breakpoint, start debugging, and… finally it’s green! Have fun!Reference: Debugging OpenJDK from our JCG partner Jakub Kubrynski at the Java(B)Log blog....
akka-logo

Akka Cluster with Docker containers

This article will show you how to build docker images that contain a single akka cluster application. You will be able to run multiple seed nodes and multiple cluster nodes. The code can be found on Github and will be available as a Typesafe Activator. If you don’t know docker or akka Docker is the new shiny star in the devops world. It lets you easily deploy images toany OS running docker, while providing an isolated environment for the applications running inside the container image. Akka is a framework to build concurrent, resilient, distributed and scalable software systems. The cluster feature lets you distribute your Actors across multiple machines to achieve load balancing, fail-over and the ability to scale up and out. The big picture This is what the running application will look like. No matter where your docker containers will run at the end of the day. The numbers at the top left describe the starting order of the containers.First you have to start your seed nodes, which will “glue” the cluster together. After the first node is started all following seed-nodes have to know the ip address of the initial seed node in order to build up a single cluster. The approach describe in this article is very simple, but easily configurable so you can use it with other provision technologies like chef, puppet or zookeeper. All following nodes that get started need at least one seed-node-ip in order to join the cluster. The application configuration We will deploy a small akka application which only logs cluster events. The entrypoint is fairly simple: object Main extends App {   val nodeConfig = NodeConfig parse args   // If a config could be parsed - start the system nodeConfig map { c => val system = ActorSystem(c.clusterName, c.config)   // Register a monitor actor for demo purposes system.actorOf(Props[MonitorActor], "cluster-monitor")   system.log info s"ActorSystem ${system.name} started successfully" }   } The tricky part is the configuration. First, the akka.remote.netty.tcp.hostname configuration needs to be set to the docker ip address. The port configuration is unimportant as we have unique ip address thanks  to docker. You can read more about docker networking here. Second, the seed nodes should add themselves to the akka.cluster.seed-nodes list. And at last, everything should be configurable through system properties and environment variables. Thanks to the Typesafe Config Library this is achievable (even with some sweat and tears).Generate a small commandline parser with scopt and the following two parameters: –seed flag which determines if  this node starting should act as a seed node ([ip]:[port])… unbounded list of [ip]:[port] which represent the seed nodes Split the configuration in three filesapplication.conf which contains the common configuration node.cluster.conf contains only  the node specific configuration node.seed.conf contains only the seed-node specific configurationA class NodeConfig which orchestrates all settings and cli parameters in the right order and builds a Typesafe Config object.Take a closer look at the NodeConfig  class. The core part is this: // seed nodes as generated string from cli (ConfigFactory parseString seedNodesString) // the hostname .withValue("clustering.ip", ipValue) // node.cluster.conf or node.seed.conf .withFallback(ConfigFactory parseResources configPath) // default ConfigFactory.load but unresolved .withFallback(config) // try to resolve all placeholders (clustering.ip and clustering.port) .resolve The part to resolve the IP address is a bit hacky, but should work in default docker environments. First the eth0 interfaces is searched and then the first isSiteLocalAddress is being returned. IP adresses in the following ranges are local: 172.16.xxx.xxx, 172.31.xxx.xxx , 192.168.xxx.xxx, 10.xxx.xxx.xxx. The main cluster configuration is done inside the clustering section of the application.conf: clustering { # ip = "127.0.0.1" # will be set from the outside or automatically port = 2551 cluster.name = "application" } The ip adress will be filled by the algorithm describe above if nothing else is set. You can easily override all settings with system properties. e.g if you want to run a seed node and a cluster node inside your IDE without docker start both like this: # the seed node -Dclustering.port=2551 -Dclustering.ip=127.0.0.1 --seed # the cluster node -Dclustering.port=2552 -Dclustering.ip=127.0.0.1 127.0.0.1:2551 For sbt this looks like this: # the seed node sbt runSeed # the cluster node sbt runNode The build Next we build our docker image. The sbt-native-packager plugin recently added experimental docker support, so we only need to  configure our build to be docker-ready. First add the plugin to your plugins.sbt. addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "0.7.4") Now we add a few required settings to our build.sbt. You should use sbt 0.13.5 or higher. // adds start script and jar mappings packageArchetype.java_application   // the docker maintainer. You could scope this to "in Docker" maintainer := "Nepomuk Seiler"   // Short package description packageSummary := s"Akka ${version.value} Server" And now we are set. Start sbt and run docker:publishLocal and a docker image will be created for you. The Dockerfile is in target/docker if you want to take a closer look what’s created. Running the cluster Now it’s time to run our containers. The image name is by default name:version. For the our activator it’s akka-docker:2.3.4. The seed ip adresses may vary. You can read it out of the console output of your seed nodes. docker run -i -t -p 2551:2551 akka-docker:2.3.4 --seed docker run -i -t -p 2551:2551 akka-docker:2.3.4 --seed 176.16.0.18:2551 docker run -i -t -p 2551:2551 akka-docker:2.3.4 176.16.0.18:2551 176.16.0.19:2551 docker run -i -t -p 2551:2551 akka-docker:2.3.4 176.16.0.18:2551 176.16.0.19:2551 What about linking? This blog entry describes a different approach to build an akka cluster with docker. I used some of the ideas, but the basic concept is build ontop of linking the docker contains. This allows you to get the ip and port information of the running seed nodes. While this is approach is suitable for single host machines, it seems to get more messy when working with multiple docker machines. The setup in this blog requires only one thing: A central way of assigning host ips. If your seed nodes don’t change their IP adresses you can basically configure almost everything already in your application.conf.Reference: Akka Cluster with Docker containers from our JCG partner Nepomuk Seiler at the mukis.de blog....
enterprise-java-logo

JPA Tutorial – Setting Up JPA in a Java SE Environment

JPA stands for Java Persistence API, which basically is a specification that describes a way to persist data into a persistent storage, usually a database. We can think of it as something similar to ORM tools like Hibernate, except that it is an official part of the Java EE specification (and it’s also supported on Java SE). There are many reasons to learn an ORM tool like JPA. I will not go into the details of this because there are already many posts on the web which perfectly answer this question, like this one, or this one. However, we should also keep in mind that this is not a single magic bullet which will solve our every problem. When I first started out with JPA, I had real difficulties to set it up because most of the articles on the web are written for Java EE environment only, whereas I was trying to use it in a Java SE environment. I hope that this article will be helpful for those who wish to do the same in the future. In this example we will use Maven to set up our required dependencies. Since JPA is only a specification, we will also need an implementation. There are many good implementations of JPA available freely (like EclipseLink, Hibernate etc.). For this article I have chosen to use Hibernate. As for the database, I will use MySQL. Let us first create a simple maven project. I have created mine using the quick start archetype from the command line. If you do not know how to do that, you can follow this tutorial. OK, so let us get the dependencies for the JPA next. Include the following lines in your pom.xml: <dependency> <groupId>javax.persistence</groupId> <artifactId>persistence-api</artifactId> <version>1.0.2</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.3.6.Final</version> <exclusions> <exclusion> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.1-api</artifactId> </exclusion> </exclusions> </dependency> The first dependency specifies the standard JPA interface, and the second one specifies the implementation. Including JPA dependencies this way is desirable because it gives us the freedom to switch vendor-specific implementation in the future without much problem (see details here). However we will not be able to use the latest version of the API this way because the API version 1.0.2 is the last version that is released as an independent JAR. At the time of writing this article, the latest version of the JPA specification is 2.1 which is not available independently (there are lots of requests for it though). If we want to use that one now then our only options are to choose from either a vendor-specific JAR or use an application server which provides the API along with its implementation. I have decided to use the API specification provided by Hibernate. In that case including only the following dependency will suffice: <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.3.6.Final</version> </dependency> Next step is to include the dependency for MySQL. Include the following lines in your pom.xml: <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.31</version> </dependency> After including the rest of the dependencies (i.e., jUnit, Hamcrest etc.) the full pom.xml looks like below: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.keertimaan.javasamples</groupId> <artifactId>jpa-example</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging><name>jpa-example</name> <url>http://sayemdb.wordpress.com</url><properties> <java.version>1.8</java.version> <hibernate.version>4.3.6.Final</hibernate.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties><dependencies> <!-- JPA --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> </dependency><!-- For connection pooling --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-c3p0</artifactId> <version>${hibernate.version}</version> </dependency><!-- Database --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.31</version> </dependency><!-- Test --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> <exclusions> <exclusion> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> <version>1.3</version> <scope>test</scope> </dependency> </dependencies><build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.5.1</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> <compilerArgument>-Xlint:all</compilerArgument> <showWarnings>true</showWarnings> <showDeprecation>true</showDeprecation> </configuration> </plugin> </plugins> </build> </project> Now it’s time to configure our database. I will use the following schema in all of my future JPA examples which I found from this excellent online book:  Create an equivalent database following the above schema in your local MySQL installation. Our next step is to create the persistence.xml file which will contain our database specific information for JPA to use. By default JPA expects this file to be in the class path under the META-INF folder. For our maven project, I have created this file under project_root/src/main/resources/META-INF folder: <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistencehttp://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"version="2.1"><persistence-unit name="jpa-example" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider><properties> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost/jpa_example" /> <property name="javax.persistence.jdbc.user" value="root" /> <property name="javax.persistence.jdbc.password" value="my_root_password" /> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" /><property name="hibernate.show_sql" value="true" /> <property name="hibernate.format_sql" value="true" /> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" /> <property name="hibernate.hbm2ddl.auto" value="validate" /><!-- Configuring Connection Pool --> <property name="hibernate.c3p0.min_size" value="5" /> <property name="hibernate.c3p0.max_size" value="20" /> <property name="hibernate.c3p0.timeout" value="500" /> <property name="hibernate.c3p0.max_statements" value="50" /> <property name="hibernate.c3p0.idle_test_period" value="2000" /> </properties> </persistence-unit> </persistence> The above file requires some explanation if you are an absolute begineer in JPA. In my next article I will try to explain it as much as possible, but for running this example you will only need to change the first three property values to match your environment (namely the database name, username and password). Also keep a note of the value of the name attribute of the persistence-unit element. This value will be used to instantiate our EntityManagerFactory instance later in the code. Ok, let us now create an entity to test our configuration. Create a class called Address with the following contents: import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table;@Entity @Table(name = "address") public class Address { @Id @GeneratedValue private Integer id;private String street; private String city; private String province; private String country; private String postcode;/** * @return the id */ public Integer getId() { return id; }/** * @param id the id to set */ public Address setId(Integer id) { this.id = id; return this; }/** * @return the street */ public String getStreet() { return street; }/** * @param street the street to set */ public Address setStreet(String street) { this.street = street; return this; }/** * @return the city */ public String getCity() { return city; }/** * @param city the city to set */ public Address setCity(String city) { this.city = city; return this; }/** * @return the province */ public String getProvince() { return province; }/** * @param province the province to set */ public Address setProvince(String province) { this.province = province; return this; }/** * @return the country */ public String getCountry() { return country; }/** * @param country the country to set */ public Address setCountry(String country) { this.country = country; return this; }/** * @return the postcode */ public String getPostcode() { return postcode; }/** * @param postcode the postcode to set */ public Address setPostcode(String postcode) { this.postcode = postcode; return this; } } This class has been properly mapped to the address table and its instances are fully ready to be persisted in the database. Now let us create a helper class called PersistenceManager with the following contents: import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence;public enum PersistenceManager { INSTANCE;private EntityManagerFactory emFactory;private PersistenceManager() { // "jpa-example" was the value of the name attribute of the // persistence-unit element. emFactory = Persistence.createEntityManagerFactory("jpa-example"); }public EntityManager getEntityManager() { return emFactory.createEntityManager(); }public void close() { emFactory.close(); } } Now let us write some sample persistence code in our Main method to test everything out: import javax.persistence.EntityManager;public class Main { public static void main(String[] args) { Address address = new Address(); address.setCity("Dhaka") .setCountry("Bangladesh") .setPostcode("1000") .setStreet("Poribagh");EntityManager em = PersistenceManager.INSTANCE.getEntityManager(); em.getTransaction() .begin(); em.persist(address); em.getTransaction() .commit();em.close(); PersistenceManager.INSTANCE.close(); } } If you check your database, you will see that a new record has been inserted in your address table. This article explains how to set up JPA without using any other frameworks like Spring. However it is a very good idea to use Spring to set up JPA because in that case we do not need to worry about managing entity managers, transactions etc. Beside setting up JPA, spring is also very good for many other purposes too. That’s it for today. In the next article I will try to explain the persistence.xml file and the corresponding configuration values as much as possible. Stay tuned!The full code can be found at github.Reference: JPA Tutorial – Setting Up JPA in a Java SE Environment from our JCG partner Sayem Ahmed at the Random Thoughts blog....
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! UPDATE: The giveaway has ended! Here is the list of the lucky winners! (emails hidden for privacy)er…nt@gmail.com vh…th@gmail.com nc…il@gmail.com su…dc@gmail.com er…ma@gmail.com or…an@gmail.com me…10@gmail.com je…ch@gmail.com rk…ks@gmail.com pr…77@outlook.comWe like to thank you all for participating to this giveaway. Till next time, Keep up the good work!...
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....
Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
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