Search

Dark theme | Light theme
Showing posts with label GPars. Show all posts
Showing posts with label GPars. Show all posts

May 1, 2015

Groovy Goodness: Share Data in Concurrent Environment with Dataflow Variables

To work with data in a concurrent environment can be complex. Groovy includes GPars, yes we don't have to download any dependencies, to provide some models to work easily with data in a concurrent environment. In this blog post we are going to look at an example where we use dataflow variables to exchange data between concurrent tasks. In a dataflow algorithm we define certain functions or tasks that have an input and output. A task is started when the input is available for the task. So instead of defining an imperative sequence of tasks that need to be executed, we define a series of tasks that will start executing when their input is available. And the nice thing is that each of these tasks are independent and can run in parallel if needed.

The data that is shared between tasks is stored in dataflow variables. The value of a dataflow variable can only be set once, but it can be read multiple times. When a task wants to read the value, but it is not yet available, the task will wait for the value in a non-blocking way.

In the following example Groovy script we use the Dataflows class. This class provides an easy way to set multiple dataflow variables and get their values. In the script we want to get the temperature in a city in both Celcius and Fahrenheit and we are using remote web services to the data:

import groovyx.gpars.dataflow.Dataflows
import static groovyx.gpars.dataflow.Dataflow.task

// Create new Dataflows instance to hold
// dataflow variables.
final Dataflows data = new Dataflows()

// Convert temperature from Celcius to Fahrenheit.
task {
    println "Task 'convertTemperature' is waiting for dataflow variable 'cityWeather'"

    // Get dataflow variable cityWeather value from
    // Dataflows data object. The value
    // is set by findCityWeather task.
    // If the value is not set yet, wait.
    final cityWeather = data.cityWeather
    final cityTemperature = cityWeather.temperature

    println "Task 'convertTemperature' got dataflow variable 'cityWeather'"

    // Convert value with webservice at
    // www.webservicex.net.
    final params = 
        [Temperature: cityTemperature, 
         FromUnit: 'degreeCelsius', 
         ToUnit: 'degreeFahrenheit']
    final url = "http://www.webservicex.net/ConvertTemperature.asmx/ConvertTemp"
    final result = downloadData(url, params)

    // Assign converted value to dataflow variable
    // temperature in Dataflows data object.
    data.temperature = result.text()
}

// Find temperature for city.
task {
    println "Task 'findCityWeather' is waiting for dataflow variable 'searchCity'"

    // Get value for city attribute in
    // Dataflows data object. This is 
    // set in another task (startSearch) 
    // at another time.
    // If the value is not set yet, wait.
    final city = data.searchCity

    println "Task 'findCityWeather' got dataflow variable 'searchCity'"

    // Get temperature for city with 
    // webservice at api.openweathermap.org.
    final params = 
        [q: city, 
         units: 'metric', 
         mode: 'xml']
    final url = "http://api.openweathermap.org/data/2.5/find"
    final result = downloadData(url, params)
    final temperature = result.list.item.temperature.@value

    // Assign map value to cityWeather dataflow 
    // variable in Dataflows data object. 
    data.cityWeather = [city: city, temperature: temperature]
}

// Get city part from search string.
task {
    println "Task 'parseCity' is waiting for dataflow variable 'searchCity'"

    // Get value for city attribute in
    // Dataflows data object. This is 
    // set in another task (startSearch) 
    // at another time.
    // If the value is not set yet, wait.
    final city = data.searchCity
    
    println "Task 'parseCity' got dataflow variable 'searchCity'"

    final cityName = city.split(',').first()

    // Assign to dataflow variable in Dataflows object.
    data.cityName = cityName
}

final startSearch = task {
    // Use command line argument to set
    // city dataflow variable in 
    // Dataflows data object.
    // Any code that reads this value
    // was waiting, but will start now,
    // because of this assigment.
    data.searchCity = args[0]  
}

// When a variable is bound we log it. 
final printValueBound = { dataflowVar, value ->
    println "Variable '$dataflowVar' bound to '$value'" 
}
data.searchCity printValueBound.curry('searchCity')
data.cityName printValueBound.curry('cityName')
data.cityWeather printValueBound.curry('cityWeather')
data.temperature printValueBound.curry('temperature')


// Here we read the dataflow variables cityWeather and temperature
// from Dataflows data object. Notice once the value is
// is set it is not calculated again. For example cityWeather 
// will not do a remote call again, because the value is already known
// by now.
println "Main thread is waiting for dataflow variables 'cityWeather', 'temperature' and 'cityName'"
final cityInfo = 
    data.cityWeather + [tempFahrenheit: data.temperature] + [cityName: data.cityName]


println """\

Temperature in city $cityInfo.cityName (searched with $cityInfo.city):
$cityInfo.temperature Celcius
$cityInfo.tempFahrenheit Fahrenheit
"""


// Helper method to get XML response from URL
// and parse it using XmlSlurper. Returns GPathResult.
def downloadData(requestUrl, requestParams) {
    final params = requestParams.collect { it }.join('&')
    final url = "${requestUrl}?${params}"

    final response = new XmlSlurper().parseText(url.toURL().text)
    response
}

Now when we run the script we get the following output:

$ groovy citytemp.groovy Tilburg,NL
Task 'convertTemperature' is waiting for dataflow variable 'cityWeather'
Task 'parseCity' is waiting for dataflow variable 'searchCity'
Task 'findCityWeather' is waiting for dataflow variable 'searchCity'
Task 'findCityWeather' got dataflow variable 'searchCity'
Task 'parseCity' got dataflow variable 'searchCity'
Main thread is waiting for dataflow variables 'cityWeather', 'temperature' and 'cityName'
Variable 'searchCity' bound to 'Tilburg,NL'
Variable 'cityName' bound to 'Tilburg'
Task 'convertTemperature' got dataflow variable 'cityWeather'
Variable 'cityWeather' bound to '[city:Tilburg,NL, temperature:11.76]'
Variable 'temperature' bound to '53.167999999999985'

Temperature in city Tilburg (searched with Tilburg,NL):
11.76 Celcius
53.167999999999985 Fahrenheit

Notice how tasks are waiting for values and continue when they receive their input. The order of the definition of the tasks is not important, because they will wait for their input to start the real work.

Written with Groovy 2.4.3.

May 20, 2011

Gr8Conf 2011 Conference Report: Day 3

The final day of the conference, the last day I could go to informative and inspiring sessions. The whole day there will be parallel sessions, so here is a report of the sessions I have choosen to attend. We can also read the conference reports of day 1 and day 2.

Introducing GContracts presented by Andre Steingress was the first session of the day and it was great. GContracts is a cool product and can really improve the quality of your code. This is really something I want to use in my projects. With GContracts we can add annotation to our classes and method to ensure post-conditions, set required pre-conditions and class invariants. The syntax is simple so it easy to get started.

The next session was about myBalsamiq. This is project colaboration website to work on Balsamiq mockups that will be released later this year. And it is built using Grails! It is cool to see such a site with a lot of interaction and big user base is created with Grails. Also the architecture of the application was very intesting and looked really great. I wish them good luck with the launch of their product and it will be a great new testimonial for Grails applications.

Gaelyk was the subject of the following session presented by Guillaume Laforge the author of Gaelyk. Gaelyk allows us developers to use Groovy on the Google App Engine. First we learned about the way Google App Engine is positioned in the cloud and the payment model.
Also we took a good look at the services Google gives us developers to use, like datastore, memcache, image manipulation, email. Then it was time to see how we can use all this in a Groovy way. All Google services are directly usable in our Groovy code, because the services are added automatically through bindings. Also several DSLs are applied to make using the services easier. With a bit of live coding we could see this in action.
Gaelyk also has a plugin system, so we could write reusable parts as plugins and use them in our code. The presentation is available on SlideShare.

The session about Spock, my favorite test framework, was presented by Peter NiederWiesser who wrote Spock. The first part of the session was an introduction to Spock for those that didn't know Spock before. With small samples we could see how JUnit tests turned into beautiful Spock specifications. With a state machine and interaction sample this was very helpful.
The second half of the presentation was about the more advanced features in Spock for those that already knew Spock. Writing extensions for example is very easy in Spock and Peter showed this with a sample.
The code Peter used is available on GitHub.

Václav Pech gave a session about GPars. And because GPars is now part of Groovy 1.8 we can all use the very usable features of GPars to create concurrent and parallel running application code. Concurrency will play a big role in the future to create performant applications and with GPars we use Groovy to this. Multi-cores are already here and we will get more of them in the future in all our computers and appliances. And the good thing is, the implementation of the code is not difficult. For example to work with collections in parallel we only add two lines of code and we are done. The impact on the code is also small.
We can also choose to use the dataflow variable paradigm to create concurrent code, which will require a code rewrite, but the result is very elegant and will probably be worth it.
Finally Václav showed how actors can be used and how easy it to write the code. GPars is really something for me to get into in the near future and want to implement in my projects.

Testing is very important when we write code and the next session, Testing with Fitnesse, was just about that. Fitnesse is a framework that is used to write test scenarios in a Wiki with a very simple syntax. Erik Pragt and Marcin Erdmann have created a Grails plugin to use Fitnesse in Grails applications. First Erik gave a small introduction on how we can use Fitnesse and how the plugin uses Fitnesse to run tests. Fitnesse is best used for more complex business logic and is addition to unit tests written in for example Spock.
Then Marcin gave an impressive live coding demo to show TDD with Fitnesse and the plugin. Starting from scratch by writing the test scenarios and then implementing the logic in code.
The presentation was very good and gave a good idea about what Fitnesse is about and the power of it.

The final session of the day I attended was about Building Progressive UIs with Grails by Rob Fletcher. Rob explained why it is important to write progressive UIs for the web. Although you might think everybody has Javascript enabled in their browser and that it is fast, that might not be the case. For example in corporate environments IE6 is still used and upgrading is not on option. But computers nowadays are not the only devices to access your website, mobile phones, tablets and more devices can access your site. These devices don't have all the latest and greatest features, so be humble when you design a site.
By separating markup, CSS and Javascript and providing a functional website with the lowest standard, we are ready to enhance the user experience with Javascript for those users that can use it. He showed some real life examples for Grails applications. The samples showed the different user experiences for the browsers with disabled and enabled Javascript. This was really cool to see and very useful to use in projects. The code is on GitHub.
A very nice tip was to first test of a request is a AJAX request before applying a layout. This way the views can be reused in a AJAX and normal request without changing the view.

I had to leave for the airport before the panel discussion, which I couldn't attend unfortunately.

The overall feeling of this conference is that the Groovy community is very social, friendly and open. I learned a lot during these 3 days and just as last year my list-with-things-to-look-into-and-learn-more-about has grown again. Hopefully I will be able to cross most things off, before next year's Gr8Conf. Because Gr8Conf 2012 is already marked in my agenda.