{"id":31688,"date":"2014-10-17T22:00:22","date_gmt":"2014-10-17T19:00:22","guid":{"rendered":"http:\/\/www.javacodegeeks.com\/?p=31688"},"modified":"2014-10-17T16:46:04","modified_gmt":"2014-10-17T13:46:04","slug":"java-ee-7-batch-processing-and-world-of-warcraft-part-1","status":"publish","type":"post","link":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html","title":{"rendered":"Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1"},"content":{"rendered":"<p>This was one of my sessions at the last <a href=\"http:\/\/www.radcortez.com\/java-one-2014-create-the-future\/\">JavaOne<\/a>. This post is going to expand the subject and look into a real application using the Batch <a href=\"https:\/\/jcp.org\/en\/jsr\/detail?id=352\">JSR-352<\/a> API. This application integrates with the MMORPG <a href=\"http:\/\/eu.battle.net\/wow\/en\/\">World of Warcraft<\/a>.<\/p>\n<p>Since the <a href=\"https:\/\/jcp.org\/en\/jsr\/detail?id=352\">JSR-352<\/a> is a new specification in the Java EE world, I think that many people don\u2019t know how to use it properly. It may also be a challenge to identify the use cases to which this specification apply. Hopefully this example can help you understand better the use cases.<br \/>\n&nbsp;<br \/>\n&nbsp;<br \/>\n&nbsp;<\/p>\n<h2>Abstract<\/h2>\n<p><a href=\"http:\/\/eu.battle.net\/wow\/en\/\">World of Warcraft<\/a> is a game played by more than 8 million players worldwide. The service is offered by region: United States <b>(US)<\/b>, Europe <b>(EU)<\/b>, China and Korea. Each region has a set of servers called <i><b>Realm<\/b><\/i> that you use to connect to be able to play the game. For this example, we are only looking into the <b>US<\/b> and <b>EU<\/b> regions.<\/p>\n<p><a href=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2014\/10\/orgrimmar-auction-house.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-31782\" src=\"http:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2014\/10\/orgrimmar-auction-house.jpg\" alt=\"orgrimmar-auction-house\" width=\"630\" height=\"315\" srcset=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2014\/10\/orgrimmar-auction-house.jpg 660w, https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2014\/10\/orgrimmar-auction-house-300x150.jpg 300w\" sizes=\"(max-width: 630px) 100vw, 630px\" \/><\/a><\/p>\n<p>One of the most interesting features about the game is that allows you to buy and sell in-game goods called <i><b>Items<\/b><\/i>, using an <i><b>Auction House<\/b><\/i>. Each <i><b>Realm<\/b><\/i> has two <i><b>Auction House\u2019s<\/b><\/i>. On average each <i><b>Realm<\/b><\/i> trades around <b>70.000<\/b> <i><b>Items<\/b><\/i>. Let\u2019s crunch some numbers:<\/p>\n<ul>\n<li>512 <i><b>Realm\u2019s<\/b><\/i> (<b>US<\/b> and <b>EU<\/b>)<\/li>\n<li><b>70 K<\/b> <i><b>Item\u2019s<\/b><\/i> per <i><b>Realm<\/b><\/i><\/li>\n<li>More than <b>35 M<\/b> <i><b>Item\u2019s<\/b><\/i> overall<\/li>\n<\/ul>\n<h2>The Data<\/h2>\n<p>Another cool thing about <a href=\"http:\/\/eu.battle.net\/wow\/en\/\">World of Warcraft<\/a> is that the developers provide a REST API to access most of the in-game information, including the <i><b>Auction House\u2019s<\/b><\/i> data. Check <a href=\"http:\/\/blizzard.github.io\/api-wow-docs\/\">here<\/a> the complete API.<\/p>\n<p>The <i><b>Auction House\u2019s<\/b><\/i> data is obtained in two steps. First we need to query the correspondent <i><b>Auction House<\/b><\/i> <i><b>Realm<\/b><\/i> REST endpoint to get a reference to a JSON file. Next we need to access this URL and download the file with all the <i><b>Auction House<\/b><\/i> <i><b>Item\u2019s<\/b><\/i> information. Here is an example:<\/p>\n<p><a href=\"http:\/\/eu.battle.net\/api\/wow\/auction\/data\/aggra-portugues\">http:\/\/eu.battle.net\/api\/wow\/auction\/data\/aggra-portugues<\/a><\/p>\n<h2>The Application<\/h2>\n<p>Our objective here is to build an application that downloads the <i><b>Auction House\u2019s<\/b><\/i>, process it and extract metrics. These metrics are going to build a history of the <i><b>Items<\/b><\/i> price evolution through time. Who knows? Maybe with this information we can predict price fluctuation and buy or sell <i><b>Items<\/b><\/i> at the best times.<\/p>\n<h2>The Setup<\/h2>\n<p>For the setup, we\u2019re going to use a few extra things to Java EE 7:<\/p>\n<ul>\n<li><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/tutorial\/doc\/home.htm\">Java EE 7<\/a><\/li>\n<li><a href=\"http:\/\/angularjs.org\/\">Angular JS<\/a><\/li>\n<li><a href=\"http:\/\/angular-ui.github.io\/ng-grid\/\">Angular ng-grid<\/a><\/li>\n<li><a href=\"http:\/\/angular-ui.github.io\/bootstrap\/\">UI Bootstrap<\/a><\/li>\n<li><a href=\"https:\/\/developers.google.com\/chart\/\">Google Chart<\/a><\/li>\n<li><a href=\"http:\/\/www.wildfly.org\/\">Wildfly<\/a><\/li>\n<\/ul>\n<h2>Jobs<\/h2>\n<p>The main work it\u2019s going to be performed by Batch <a href=\"https:\/\/jcp.org\/en\/jsr\/detail?id=352\">JSR-352<\/a> Jobs. A Job is an entity that encapsulates an entire batch process. A Job will be wired together via a Job Specification Language. With <a href=\"https:\/\/jcp.org\/en\/jsr\/detail?id=352\">JSR-352<\/a>, a Job is simply a container for the steps. It combines multiple steps that belong logically together in a flow.<\/p>\n<p>We\u2019re going to split the business login into three jobs:<\/p>\n<ul>\n<li><b>Prepare<\/b> \u2013 Creates all the supporting data needed. List <i><b>Realms<\/b><\/i>, create folders to copy files.<\/li>\n<li><b>Files<\/b> \u2013 Query realms to check for new files to process.<\/li>\n<li><b>Process<\/b> \u2013 Downloads the file, process the data, extract metrics.<\/li>\n<\/ul>\n<h2>The Code<\/h2>\n<h2>Back-end \u2013 Java EE 7 with Java 8<\/h2>\n<p>Most of the code is going to be in the back-end. We need Batch <a href=\"https:\/\/jcp.org\/en\/jsr\/detail?id=352\">JSR-352<\/a>, but we are also going to use a lot of other technologies from Java EE: like <a href=\"https:\/\/jcp.org\/en\/jsr\/detail?id=338\">JPA<\/a>, <a href=\"https:\/\/jax-rs-spec.java.net\">JAX-RS<\/a>, <a href=\"http:\/\/cdi-spec.org\">CDI<\/a> and <a href=\"https:\/\/jsonp.java.net\">JSON-P<\/a>.<\/p>\n<p>Since the <b>Prepare<\/b> Job is only to initialize application resources for the processing, I\u2019m skipping it and dive into the most interesting parts.<\/p>\n<h2>Files Job<\/h2>\n<p>The Files Job is an implementation of <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/api\/AbstractBatchlet.html\">AbstractBatchlet<\/a><\/code>. A Batchlet is the simplest processing style available in the Batch specification. It\u2019s a task oriented step where the task is invoked once, executes, and returns an exit status. This type is most useful for performing a variety of tasks that are not item-oriented, such as executing a command or doing file transfer. In this case, our <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/api\/Batchlet.html\">Batchlet<\/a><\/code> is going to iterate on every <i><b>Realm<\/b><\/i> make a REST request to each one and retrieve an URL with the file containing the data that we want to process. Here is the code:<\/p>\n<p><em>LoadAuctionFilesBatchlet<\/em><\/p>\n<pre class=\"brush:java;wrap-lines:false\">@Named\r\npublic class LoadAuctionFilesBatchlet extends AbstractBatchlet {\r\n    @Inject\r\n    private WoWBusiness woWBusiness;\r\n\r\n    @Inject\r\n    @BatchProperty(name = \"region\")\r\n    private String region;\r\n    @Inject\r\n    @BatchProperty(name = \"target\")\r\n    private String target;\r\n\r\n    @Override\r\n    public String process() throws Exception {\r\n        List&lt;Realm&gt; realmsByRegion = woWBusiness.findRealmsByRegion(Realm.Region.valueOf(region));\r\n        realmsByRegion.parallelStream().forEach(this::getRealmAuctionFileInformation);\r\n\r\n        return \"COMPLETED\";\r\n    }\r\n\r\n    void getRealmAuctionFileInformation(Realm realm) {\r\n        try {\r\n            Client client = ClientBuilder.newClient();\r\n            Files files = client.target(target + realm.getSlug())\r\n                                .request(MediaType.TEXT_PLAIN).async()\r\n                                .get(Files.class)\r\n                                .get(2, TimeUnit.SECONDS);\r\n\r\n            files.getFiles().forEach(auctionFile -&gt; createAuctionFile(realm, auctionFile));\r\n        } catch (Exception e) {\r\n            getLogger(this.getClass().getName()).log(Level.INFO, \"Could not get files for \" + realm.getRealmDetail());\r\n        }\r\n    }\r\n\r\n    void createAuctionFile(Realm realm, AuctionFile auctionFile) {\r\n        auctionFile.setRealm(realm);\r\n        auctionFile.setFileName(\"auctions.\" + auctionFile.getLastModified() + \".json\");\r\n        auctionFile.setFileStatus(FileStatus.LOADED);\r\n\r\n        if (!woWBusiness.checkIfAuctionFileExists(auctionFile)) {\r\n            woWBusiness.createAuctionFile(auctionFile);\r\n        }\r\n    }\r\n}<\/pre>\n<p>A cool thing about this is the use of Java 8. With <code>parallelStream()<\/code> invoking multiple REST request at once is easy as pie! You can really notice the difference. If you want to try it out, just run the sample and replace <code>parallelStream()<\/code> with <code>stream()<\/code> and check it out. On my machine, using <code>parallelStream()<\/code> makes the task execute around 5 or 6 times faster.<div style=\"display:inline-block; margin: 15px 0;\"> <div id=\"adngin-JavaCodeGeeks_incontent_video-0\" style=\"display:inline-block;\"><\/div> <\/div><\/p>\n<p><i><b>Update<\/b><br \/>\nUsually, I would not use this approach. I\u2019ve done it, because part of the logic involves invoking slow REST requests and parallelStreams really shine here. Doing this using batch partitions is possible, but hard to implement. We also need to pool the servers for new data every time, so it\u2019s not terrible if we skip a file or two. Keep in mind that if you don\u2019t want to miss a single record a Chunk processing style is more suitable. Thank you to <a href=\"https:\/\/twitter.com\/simas_ch\">Simon Martinelli<\/a> for bringing this to my attention.<\/i><\/p>\n<p>Since the <i><b>Realms<\/b><\/i> of <i><b>US<\/b><\/i> and <i><b>EU<\/b><\/i> require different REST endpoints to invoke, these are perfect to partitioned. Partitioning means that the task is going to run into multiple threads. One thread per partition. In this case we have two partitions.<\/p>\n<p>To complete the job definition we need to provide a JoB XML file. This needs to be placed in the <code>META-INF\/batch-jobs<\/code> directory. Here is the <code>files-job.xml<\/code> for this job:<\/p>\n<p><em>files-job.xml<\/em><\/p>\n<pre class=\"brush:xml\">&lt;job id=\"loadRealmAuctionFileJob\" xmlns=\"http:\/\/xmlns.jcp.org\/xml\/ns\/javaee\" version=\"1.0\"&gt;\r\n    &lt;step id=\"loadRealmAuctionFileStep\"&gt;\r\n        &lt;batchlet ref=\"loadAuctionFilesBatchlet\"&gt;\r\n            &lt;properties&gt;\r\n                &lt;property name=\"region\" value=\"#{partitionPlan['region']}\"\/&gt;\r\n                &lt;property name=\"target\" value=\"#{partitionPlan['target']}\"\/&gt;\r\n            &lt;\/properties&gt;\r\n        &lt;\/batchlet&gt;\r\n        &lt;partition&gt;\r\n            &lt;plan partitions=\"2\"&gt;\r\n                &lt;properties partition=\"0\"&gt;\r\n                    &lt;property name=\"region\" value=\"US\"\/&gt;\r\n                    &lt;property name=\"target\" value=\"http:\/\/us.battle.net\/api\/wow\/auction\/data\/\"\/&gt;\r\n                &lt;\/properties&gt;\r\n                &lt;properties partition=\"1\"&gt;\r\n                    &lt;property name=\"region\" value=\"EU\"\/&gt;\r\n                    &lt;property name=\"target\" value=\"http:\/\/eu.battle.net\/api\/wow\/auction\/data\/\"\/&gt;\r\n                &lt;\/properties&gt;\r\n            &lt;\/plan&gt;\r\n        &lt;\/partition&gt;\r\n    &lt;\/step&gt;\r\n&lt;\/job&gt;<\/pre>\n<p>In the <code>files-job.xml<\/code> we need to define our <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/api\/Batchlet.html\">Batchlet<\/a><\/code> in <code>batchlet<\/code> element. For the partitions just define the <code>partition<\/code> element and assign different <code>properties<\/code> to each <code>plan<\/code>. These <code>properties<\/code> can then be used to late bind the value into the <code>LoadAuctionFilesBatchlet<\/code> with the expressions <code>#{partitionPlan['region']}<\/code> and <code>#{partitionPlan['target']}<\/code>. This is a very simple expression binding mechanism and only works for simple properties and Strings.<\/p>\n<h2>Process Job<\/h2>\n<p>Now we want to process the <i><b>Realm Auction Data<\/b><\/i> file. Using the information from the previous job, we can now download the file and do something with the data. The JSON file has the following structure:<\/p>\n<p><em>item-auctions-sample.json<\/em><\/p>\n<pre class=\"brush:java\">{\r\n    \"realm\": {\r\n        \"name\": \"Grim Batol\",\r\n        \"slug\": \"grim-batol\"\r\n    },\r\n    \"alliance\": {\r\n        \"auctions\": [\r\n            {\r\n                \"auc\": 279573567,            \/\/ Auction Id\r\n                \"item\": 22792,               \/\/ Item for sale Id\r\n                \"owner\": \"Miljanko\",         \/\/ Seller Name\r\n                \"ownerRealm\": \"GrimBatol\",   \/\/ Realm\r\n                \"bid\": 3800000,              \/\/ Bid Value\r\n                \"buyout\": 4000000,           \/\/ Buyout Value\r\n                \"quantity\": 20,              \/\/ Numbers of items in the Auction\r\n                \"timeLeft\": \"LONG\",          \/\/ Time left for the Auction\r\n                \"rand\": 0,\r\n                \"seed\": 1069994368\r\n            },\r\n            {\r\n                \"auc\": 278907544,\r\n                \"item\": 40195,\r\n                \"owner\": \"Mongobank\",\r\n                \"ownerRealm\": \"GrimBatol\",\r\n                \"bid\": 38000,\r\n                \"buyout\": 40000,\r\n                \"quantity\": 1,\r\n                \"timeLeft\": \"VERY_LONG\",\r\n                \"rand\": 0,\r\n                \"seed\": 1978036736\r\n            }\r\n        ]\r\n    },\r\n    \"horde\": {\r\n        \"auctions\": [\r\n            {\r\n                \"auc\": 278268046,\r\n                \"item\": 4306,\r\n                \"owner\": \"Thuglifer\",\r\n                \"ownerRealm\": \"GrimBatol\",\r\n                \"bid\": 570000,\r\n                \"buyout\": 600000,\r\n                \"quantity\": 20,\r\n                \"timeLeft\": \"VERY_LONG\",\r\n                \"rand\": 0,\r\n                \"seed\": 1757531904\r\n            },\r\n            {\r\n                \"auc\": 278698948,\r\n                \"item\": 4340,\r\n                \"owner\": \"Celticpala\",\r\n                \"ownerRealm\": \"Aggra(Portugu\u00eas)\",\r\n                \"bid\": 1000000,\r\n                \"buyout\": 1000000,\r\n                \"quantity\": 10,\r\n                \"timeLeft\": \"LONG\",\r\n                \"rand\": 0,\r\n                \"seed\": 0\r\n            }\r\n        ]\r\n    }\r\n}<\/pre>\n<p>The file has a list of the <i><b>Auction\u2019s<\/b><\/i> from the <i><b>Realm<\/b><\/i> it was downloaded from. In each record we can check the item for sale, prices, seller and time left until the end of the auction. <i><b>Auction\u2019s<\/b><\/i> are algo aggregated by <i><b>Auction House<\/b><\/i> type: <i><b>Alliance<\/b><\/i> and <i><b>Horde<\/b><\/i>.<\/p>\n<p>For the <code>process-job<\/code> we want to read the JSON file, transform the data and save it to a database. This can be achieved by Chunk Processing. A Chunk is an ETL (Extract \u2013 Transform \u2013 Load) style of processing which is suitable for handling large amounts of data. A Chunk reads the data one item at a time, and creates chunks that will be written out, within a transaction. One item is read in from an <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/api\/chunk\/ItemReader.html\">ItemReader<\/a><\/code>, handed to an <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/api\/chunk\/ItemProcessor.html\">ItemProcessor<\/a><\/code>, and aggregated. Once the number of items read equals the commit interval, the entire chunk is written out via the <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/api\/chunk\/ItemWriter.html\">ItemWriter<\/a><\/code>, and then the transaction is committed.<\/p>\n<h4>ItemReader<\/h4>\n<p>The real files are so big that they cannot be loaded entirely into memory or you may end up running out of it. Instead we use JSON-P API to parse the data in a streaming way.<\/p>\n<p><em>AuctionDataItemReader<\/em><\/p>\n<pre class=\"brush:java;wrap-lines:false\">@Named\r\npublic class AuctionDataItemReader extends AbstractAuctionFileProcess implements ItemReader {\r\n    private JsonParser parser;\r\n    private AuctionHouse auctionHouse;\r\n\r\n    @Inject\r\n    private JobContext jobContext;\r\n    @Inject\r\n    private WoWBusiness woWBusiness;\r\n\r\n    @Override\r\n    public void open(Serializable checkpoint) throws Exception {\r\n        setParser(Json.createParser(openInputStream(getContext().getFileToProcess(FolderType.FI_TMP))));\r\n\r\n        AuctionFile fileToProcess = getContext().getFileToProcess();\r\n        fileToProcess.setFileStatus(FileStatus.PROCESSING);\r\n        woWBusiness.updateAuctionFile(fileToProcess);\r\n    }\r\n\r\n    @Override\r\n    public void close() throws Exception {\r\n        AuctionFile fileToProcess = getContext().getFileToProcess();\r\n        fileToProcess.setFileStatus(FileStatus.PROCESSED);\r\n        woWBusiness.updateAuctionFile(fileToProcess);\r\n    }\r\n\r\n    @Override\r\n    public Object readItem() throws Exception {\r\n        while (parser.hasNext()) {\r\n            JsonParser.Event event = parser.next();\r\n            Auction auction = new Auction();\r\n            switch (event) {\r\n                case KEY_NAME:\r\n                    updateAuctionHouseIfNeeded(auction);\r\n\r\n                    if (readAuctionItem(auction)) {\r\n                        return auction;\r\n                    }\r\n                    break;\r\n            }\r\n        }\r\n        return null;\r\n    }\r\n\r\n    @Override\r\n    public Serializable checkpointInfo() throws Exception {\r\n        return null;\r\n    }\r\n\r\n    protected void updateAuctionHouseIfNeeded(Auction auction) {\r\n        if (parser.getString().equalsIgnoreCase(AuctionHouse.ALLIANCE.toString())) {\r\n            auctionHouse = AuctionHouse.ALLIANCE;\r\n        } else if (parser.getString().equalsIgnoreCase(AuctionHouse.HORDE.toString())) {\r\n            auctionHouse = AuctionHouse.HORDE;\r\n        } else if (parser.getString().equalsIgnoreCase(AuctionHouse.NEUTRAL.toString())) {\r\n            auctionHouse = AuctionHouse.NEUTRAL;\r\n        }\r\n\r\n        auction.setAuctionHouse(auctionHouse);\r\n    }\r\n\r\n    protected boolean readAuctionItem(Auction auction) {\r\n        if (parser.getString().equalsIgnoreCase(\"auc\")) {\r\n            parser.next();\r\n            auction.setAuctionId(parser.getLong());\r\n            parser.next();\r\n            parser.next();\r\n            auction.setItemId(parser.getInt());\r\n            parser.next();\r\n            parser.next();\r\n            parser.next();\r\n            parser.next();\r\n            auction.setOwnerRealm(parser.getString());\r\n            parser.next();\r\n            parser.next();\r\n            auction.setBid(parser.getInt());\r\n            parser.next();\r\n            parser.next();\r\n            auction.setBuyout(parser.getInt());\r\n            parser.next();\r\n            parser.next();\r\n            auction.setQuantity(parser.getInt());\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n\r\n    public void setParser(JsonParser parser) {\r\n        this.parser = parser;\r\n    }\r\n}<\/pre>\n<p>To open a JSON Parse stream we need <code>Json.createParser<\/code> and pass a reference of an inputstream. To read elements we just need to call the <code>hasNext()<\/code> and <code>next()<\/code> methods. This returns a <code>JsonParser.Event<\/code> that allows us to check the position of the parser in the stream. Elements are read and returned in the <code>readItem()<\/code> method from the Batch API <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/api\/chunk\/ItemReader.html\">ItemReader<\/a><\/code>. When no more elements are available to read, return <code>null<\/code> to finish the processing. Note that we also implements the method <code>open<\/code> and <code>close<\/code> from <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/api\/chunk\/ItemReader.html\">ItemReader<\/a><\/code>. These are used to initialize and clean up resources. They only execute once.<\/p>\n<h4>ItemProcessor<\/h4>\n<p>The <code>ItemProcessor<\/code> is optional. It\u2019s used to transform the data that was read. In this case we need to add additional information to the <i><b>Auction<\/b><\/i>.<\/p>\n<p><em>AuctionDataItemProcessor<\/em><\/p>\n<pre class=\"brush:java\">@Named\r\npublic class AuctionDataItemProcessor extends AbstractAuctionFileProcess implements ItemProcessor {\r\n    @Override\r\n    public Object processItem(Object item) throws Exception {\r\n        Auction auction = (Auction) item;\r\n\r\n        auction.setRealm(getContext().getRealm());\r\n        auction.setAuctionFile(getContext().getFileToProcess());\r\n\r\n        return auction;\r\n    }\r\n}<\/pre>\n<h4>ItemWriter<\/h4>\n<p>Finally we just need to write the data down to a database:<\/p>\n<p><em>AuctionDataItemWriter<\/em><\/p>\n<pre class=\"brush:java\">@Named\r\npublic class AuctionDataItemWriter extends AbstractItemWriter {\r\n    @PersistenceContext\r\n    protected EntityManager em;\r\n\r\n    @Override\r\n    public void writeItems(List&lt;Object&gt; items) throws Exception {\r\n        items.forEach(em::persist);\r\n    }\r\n}<\/pre>\n<p>The entire process with a file of 70 k record takes around 20 seconds on my machine. I did notice something very interesting. Before this code, I was using an injected EJB that called a method with the persist operation. This was taking 30 seconds in total, so injecting the EntityManager and performing the persist directly saved me a third of the processing time. I can only speculate that the delay is due to an increase of the stack call, with EJB interceptors in the middle. This was happening in Wildfly. I will investigate this further.<\/p>\n<p>To define the chunk we need to add it to a process-job.xml file:<\/p>\n<p><em>process-job.xml<\/em><\/p>\n<pre class=\"brush:xml\">&lt;step id=\"processFile\" next=\"moveFileToProcessed\"&gt;\r\n    &lt;chunk item-count=\"100\"&gt;\r\n        &lt;reader ref=\"auctionDataItemReader\"\/&gt;\r\n        &lt;processor ref=\"auctionDataItemProcessor\"\/&gt;\r\n        &lt;writer ref=\"auctionDataItemWriter\"\/&gt;\r\n    &lt;\/chunk&gt;\r\n&lt;\/step&gt;<\/pre>\n<p>In the <code>item-count<\/code> property we define how many elements fit into each chunk of processing. This means that for every 100 the transaction is committed. This is useful to keep the transaction size low and to checkpoint the data. If we need to stop and then restart the operation we can do it without having to process every item again. We have to code that logic ourselves. This is not included in the sample, but I will do it in the future.<\/p>\n<h2>Running<\/h2>\n<p>To run a job we need to get a reference to a <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/operations\/JobOperator.html\">JobOperator<\/a><\/code>. The <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/operations\/JobOperator.html\">JobOperator<\/a><\/code> provides an interface to manage all aspects of job processing, including operational commands, such as start, restart, and stop, as well as job repository related commands, such as retrieval of job and step executions.<\/p>\n<p>To run the previous <code>files-job.xml<\/code> Job we execute:<\/p>\n<p><em>Execute Job<\/em><\/p>\n<pre class=\"brush:java\">JobOperator jobOperator = BatchRuntime.getJobOperator();\r\njobOperator.start(\"files-job\", new Properties());<\/pre>\n<p>Note that we use the name of job xml file without the extension into the <code><a href=\"http:\/\/docs.oracle.com\/javaee\/7\/api\/javax\/batch\/operations\/JobOperator.html\">JobOperator<\/a><\/code>.<\/p>\n<h2>Next Steps<\/h2>\n<p>We still need to aggregate the data to extract metrics and display it into a web page. This post is already long, so I will describe the following steps in a future post. Anyway, the code for that part is already in the Github repo. Check the Resources section.<\/p>\n<h2>Resources<\/h2>\n<p>You can clone a full working copy from my github repository and deploy it to Wildfly. You can find instructions there to deploy it.<\/p>\n<div class=\"attribution\">\n<table>\n<tbody>\n<tr>\n<td><span class=\"reference\">Reference: <\/span><\/td>\n<td><a href=\"http:\/\/www.radcortez.com\/java-ee-7-batch-processing-and-world-of-warcraft-part-1\/\">Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1<\/a> from our <a href=\"http:\/\/www.javacodegeeks.com\/jcg\/\">JCG partner<\/a> Roberto Cortez at the <a href=\"http:\/\/www.radcortez.com\/\">Roberto Cortez Java Blog<\/a> blog.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>This was one of my sessions at the last JavaOne. This post is going to expand the subject and look into a real application using the Batch JSR-352 API. This application integrates with the MMORPG World of Warcraft. Since the JSR-352 is a new specification in the Java EE world, I think that many people &hellip;<\/p>\n","protected":false},"author":592,"featured_media":112,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[8],"tags":[1003],"class_list":["post-31688","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-enterprise-java","tag-java-ee-7"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1<\/title>\n<meta name=\"description\" content=\"This was one of my sessions at the last JavaOne. This post is going to expand the subject and look into a real application using the Batch JSR-352 API.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1\" \/>\n<meta property=\"og:description\" content=\"This was one of my sessions at the last JavaOne. This post is going to expand the subject and look into a real application using the Batch JSR-352 API.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html\" \/>\n<meta property=\"og:site_name\" content=\"Java Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/javacodegeeks\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/radcortez\" \/>\n<meta property=\"article:published_time\" content=\"2014-10-17T19:00:22+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Roberto Cortez\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/radcortez\" \/>\n<meta name=\"twitter:site\" content=\"@javacodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Roberto Cortez\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html\"},\"author\":{\"name\":\"Roberto Cortez\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/d8791115988e922dbffae8d09223a72e\"},\"headline\":\"Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1\",\"datePublished\":\"2014-10-17T19:00:22+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html\"},\"wordCount\":1668,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"keywords\":[\"Java EE 7\"],\"articleSection\":[\"Enterprise Java\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html\",\"name\":\"Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"datePublished\":\"2014-10-17T19:00:22+00:00\",\"description\":\"This was one of my sessions at the last JavaOne. This post is going to expand the subject and look into a real application using the Batch JSR-352 API.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#primaryimage\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2012\\\/10\\\/enterprise-java-logo.jpg\",\"width\":150,\"height\":150,\"caption\":\"java-interview-questions-answers\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/2014\\\/10\\\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Enterprise Java\",\"item\":\"https:\\\/\\\/www.javacodegeeks.com\\\/category\\\/java\\\/enterprise-java\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#website\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"name\":\"Java Code Geeks\",\"description\":\"Java Developers Resource Center\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\"},\"alternateName\":\"JCG\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.javacodegeeks.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.javacodegeeks.com\\\/wp-content\\\/uploads\\\/2022\\\/06\\\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/javacodegeeks\",\"https:\\\/\\\/x.com\\\/javacodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.javacodegeeks.com\\\/#\\\/schema\\\/person\\\/d8791115988e922dbffae8d09223a72e\",\"name\":\"Roberto Cortez\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/3b40fa2b3df6cfc7ad81ed1509ed2a17fe6a200e409fb0aa8d9e2ab72b64b684?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/3b40fa2b3df6cfc7ad81ed1509ed2a17fe6a200e409fb0aa8d9e2ab72b64b684?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/3b40fa2b3df6cfc7ad81ed1509ed2a17fe6a200e409fb0aa8d9e2ab72b64b684?s=96&d=mm&r=g\",\"caption\":\"Roberto Cortez\"},\"description\":\"My name is Roberto Cortez and I was born in Venezuela, but I have spent most of my life in Coimbra \u2013 Portugal, where I currently live. I am a professional Java Developer working in the software development industry, with more than 8 years of experience in business areas like Finance, Insurance and Government. I work with many Java based technologies like JavaEE, Spring, Hibernate, GWT, JBoss AS and Maven just to name a few, always relying on my favorite IDE: IntelliJ IDEA. Most recently, I became a Freelancer \\\/ Independent Contractor. My new position is making me travel around the world (an old dream) to customers, but also to attend Java conferences. The direct contact with the Java community made me want to become an active member in the community itself. For that reason, I have created the Coimbra Java User Group, started to contribute to Open Source on Github and launched my own blog (www.radcortez.com), so I can share some of the knowledge that I gained over the years.\",\"sameAs\":[\"http:\\\/\\\/www.radcortez.com\\\/\",\"https:\\\/\\\/www.facebook.com\\\/radcortez\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/radcortez\",\"https:\\\/\\\/x.com\\\/https:\\\/\\\/twitter.com\\\/radcortez\"],\"url\":\"https:\\\/\\\/www.javacodegeeks.com\\\/author\\\/roberto-cortez\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1","description":"This was one of my sessions at the last JavaOne. This post is going to expand the subject and look into a real application using the Batch JSR-352 API.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html","og_locale":"en_US","og_type":"article","og_title":"Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1","og_description":"This was one of my sessions at the last JavaOne. This post is going to expand the subject and look into a real application using the Batch JSR-352 API.","og_url":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html","og_site_name":"Java Code Geeks","article_publisher":"https:\/\/www.facebook.com\/javacodegeeks","article_author":"https:\/\/www.facebook.com\/radcortez","article_published_time":"2014-10-17T19:00:22+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","type":"image\/jpeg"}],"author":"Roberto Cortez","twitter_card":"summary_large_image","twitter_creator":"@https:\/\/twitter.com\/radcortez","twitter_site":"@javacodegeeks","twitter_misc":{"Written by":"Roberto Cortez","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#article","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html"},"author":{"name":"Roberto Cortez","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/d8791115988e922dbffae8d09223a72e"},"headline":"Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1","datePublished":"2014-10-17T19:00:22+00:00","mainEntityOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html"},"wordCount":1668,"commentCount":0,"publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","keywords":["Java EE 7"],"articleSection":["Enterprise Java"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html","url":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html","name":"Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1","isPartOf":{"@id":"https:\/\/www.javacodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#primaryimage"},"image":{"@id":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#primaryimage"},"thumbnailUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","datePublished":"2014-10-17T19:00:22+00:00","description":"This was one of my sessions at the last JavaOne. This post is going to expand the subject and look into a real application using the Batch JSR-352 API.","breadcrumb":{"@id":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#primaryimage","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2012\/10\/enterprise-java-logo.jpg","width":150,"height":150,"caption":"java-interview-questions-answers"},{"@type":"BreadcrumbList","@id":"https:\/\/www.javacodegeeks.com\/2014\/10\/java-ee-7-batch-processing-and-world-of-warcraft-part-1.html#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.javacodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"Java","item":"https:\/\/www.javacodegeeks.com\/category\/java"},{"@type":"ListItem","position":3,"name":"Enterprise Java","item":"https:\/\/www.javacodegeeks.com\/category\/java\/enterprise-java"},{"@type":"ListItem","position":4,"name":"Java EE 7 Batch Processing and World of Warcraft \u2013 Part 1"}]},{"@type":"WebSite","@id":"https:\/\/www.javacodegeeks.com\/#website","url":"https:\/\/www.javacodegeeks.com\/","name":"Java Code Geeks","description":"Java Developers Resource Center","publisher":{"@id":"https:\/\/www.javacodegeeks.com\/#organization"},"alternateName":"JCG","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.javacodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.javacodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.javacodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.javacodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/javacodegeeks","https:\/\/x.com\/javacodegeeks"]},{"@type":"Person","@id":"https:\/\/www.javacodegeeks.com\/#\/schema\/person\/d8791115988e922dbffae8d09223a72e","name":"Roberto Cortez","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/3b40fa2b3df6cfc7ad81ed1509ed2a17fe6a200e409fb0aa8d9e2ab72b64b684?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/3b40fa2b3df6cfc7ad81ed1509ed2a17fe6a200e409fb0aa8d9e2ab72b64b684?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/3b40fa2b3df6cfc7ad81ed1509ed2a17fe6a200e409fb0aa8d9e2ab72b64b684?s=96&d=mm&r=g","caption":"Roberto Cortez"},"description":"My name is Roberto Cortez and I was born in Venezuela, but I have spent most of my life in Coimbra \u2013 Portugal, where I currently live. I am a professional Java Developer working in the software development industry, with more than 8 years of experience in business areas like Finance, Insurance and Government. I work with many Java based technologies like JavaEE, Spring, Hibernate, GWT, JBoss AS and Maven just to name a few, always relying on my favorite IDE: IntelliJ IDEA. Most recently, I became a Freelancer \/ Independent Contractor. My new position is making me travel around the world (an old dream) to customers, but also to attend Java conferences. The direct contact with the Java community made me want to become an active member in the community itself. For that reason, I have created the Coimbra Java User Group, started to contribute to Open Source on Github and launched my own blog (www.radcortez.com), so I can share some of the knowledge that I gained over the years.","sameAs":["http:\/\/www.radcortez.com\/","https:\/\/www.facebook.com\/radcortez","https:\/\/www.linkedin.com\/in\/radcortez","https:\/\/x.com\/https:\/\/twitter.com\/radcortez"],"url":"https:\/\/www.javacodegeeks.com\/author\/roberto-cortez"}]}},"_links":{"self":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/31688","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/users\/592"}],"replies":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/comments?post=31688"}],"version-history":[{"count":0,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/posts\/31688\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media\/112"}],"wp:attachment":[{"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/media?parent=31688"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/categories?post=31688"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.javacodegeeks.com\/wp-json\/wp\/v2\/tags?post=31688"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}