SlideShare a Scribd company logo
Iván López - @ilopmar
Mum, I want to be a Groovy
full-stack developer
Hello!
I am Iván López
@ilopmar
http://greachconf.com@madridgug
Thank you very much!
Q&A
Just kidding!
GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer
What's a full-stack developer?
Full-stack developer
Backend language
Javascript
HTML
Mobile App
GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer
Polaromatic
GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer
1.
Demo
2.
Application flow
GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer
3.
Backend
GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer
Polaromatic
▷ Spring Boot
▷ Core App
▷ Spring MVC
▷ Spring Integration Flow
<file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/>
<chain input-channel="incommingFilesChannel">
<service-activator ref="fileService" method="preprocessFile"/>
<service-activator ref="imageConverterService" method="applyEffect"/>
<service-activator ref="browserPushService" method="pushToBrowser"/>
<service-activator ref="metricsService" method="updateMetrics"/>
<service-activator ref="fileService" method="deleteTempFiles"/>
</chain>
Spring Integration Flow
<file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/>
<chain input-channel="incommingFilesChannel">
<service-activator ref="fileService" method="preprocessFile"/>
<service-activator ref="imageConverterService" method="applyEffect"/>
<service-activator ref="browserPushService" method="pushToBrowser"/>
<service-activator ref="metricsService" method="updateMetrics"/>
<service-activator ref="fileService" method="deleteTempFiles"/>
</chain>
Spring Integration Flow
Photo preprocessFile(File file) {
def pr = new PolaroidRequest(file)
this.preprocessFile(pr)
}
File service
<file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/>
<chain input-channel="incommingFilesChannel">
<service-activator ref="fileService" method="preprocessFile"/>
<service-activator ref="imageConverterService" method="applyEffect"/>
<service-activator ref="browserPushService" method="pushToBrowser"/>
<service-activator ref="metricsService" method="updateMetrics"/>
<service-activator ref="fileService" method="deleteTempFiles"/>
</chain>
Spring Integration Flow
File service
Photo preprocessFile(File file) {
def pr = new PolaroidRequest(file)
this.preprocessFile(pr)
} Photo preprocessFile(PolaroidRequest polaroidRequest) {
String outputFile = File.createTempFile("output", ".png").path
return new Photo(input: polaroidRequest.inputFile.absolutePath,
output: outputFile, text: polaroidRequest.text)
}
class ImageConverterService {
private static final String DEFAULT_CAPTION = "#LearningSpringBoot with Polaromaticn"
Random rnd = new Random()
Photo applyEffect(Photo photo) {
log.debug "Applying effect to file: ${photo.input}..."
def inputFile = photo.input
def outputFile = photo.output
double polaroidRotation = rnd.nextInt(6).toDouble()
String caption = photo.text ?: DEFAULT_CAPTION
def op = new IMOperation()
op.addImage(inputFile)
op.thumbnail(300, 300)
.set("caption", caption)
.gravity("center")
.pointsize(20)
.background("black")
.polaroid(rnd.nextBoolean() ? polaroidRotation : -polaroidRotation)
.addImage(outputFile)
def command = new ConvertCmd()
command.run(op)
photo
}
}
Image converter
GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer
FlickrDownloader
▷ Spring Boot CLI
▷ Download Flickr Interesting pictures
▷ Jsoup, GPars
▷ 55 lines of Groovy code
(microservice?)
@Slf4j
@EnableScheduling
@Grab('org.jsoup:jsoup:1.8.1')
@Grab('commons-io:commons-io:2.4')
@Grab('org.codehaus.gpars:gpars:1.2.1')
class FlickrDownloader {
static final String FLICKER_INTERESTING_URL =
"https://www.flickr.com/explore/interesting/7days"
static final String WORK_DIR = "./work"
final File workDir = new File(WORK_DIR)
@Scheduled(fixedRate = 30000L)
void downloadFlickrInteresting() {
def photos = extractPhotosFromFlickr()
withPool {
photos.eachParallel { photoUrl ->
log.info "Downloading photo ${photoUrl}"
def tempFile = download(photoUrl)
FileUtils.moveFileToDirectory(tempFile, workDir, true)
}
}
}
}
FlickrDownloader
@Slf4j
@EnableScheduling
@Grab('org.jsoup:jsoup:1.8.1')
@Grab('commons-io:commons-io:2.4')
@Grab('org.codehaus.gpars:gpars:1.2.1')
class FlickrDownloader {
static final String FLICKER_INTERESTING_URL =
"https://www.flickr.com/explore/interesting/7days"
static final String WORK_DIR = "./work"
final File workDir = new File(WORK_DIR)
@Scheduled(fixedRate = 30000L)
void downloadFlickrInteresting() {
def photos = extractPhotosFromFlickr()
withPool {
photos.eachParallel { photoUrl ->
log.info "Downloading photo ${photoUrl}"
def tempFile = download(photoUrl)
FileUtils.moveFileToDirectory(tempFile, workDir, true)
}
}
}
}
FlickrDownloader
FlickrDownloader
@Slf4j
@EnableScheduling
@Grab('org.jsoup:jsoup:1.8.1')
@Grab('commons-io:commons-io:2.4')
@Grab('org.codehaus.gpars:gpars:1.2.1')
class FlickrDownloader {
static final String FLICKER_INTERESTING_URL =
"https://www.flickr.com/explore/interesting/7days"
static final String WORK_DIR = "./work"
final File workDir = new File(WORK_DIR)
@Scheduled(fixedRate = 30000L)
void downloadFlickrInteresting() {
def photos = extractPhotosFromFlickr()
withPool {
photos.eachParallel { photoUrl ->
log.info "Downloading photo ${photoUrl}"
def tempFile = download(photoUrl)
FileUtils.moveFileToDirectory(tempFile, workDir, true)
}
}
}
}
private List extractPhotosFromFlickr() {
Document doc = Jsoup.connect(FLICKER_INTERESTING_URL).get()
Elements images = doc.select("img.pc_img")
def photos = images
.listIterator()
.collect { it.attr('src').replace('_m.jpg', '_b.jpg') }
photos
}
FlickrDownloader
@Slf4j
@EnableScheduling
@Grab('org.jsoup:jsoup:1.8.1')
@Grab('commons-io:commons-io:2.4')
@Grab('org.codehaus.gpars:gpars:1.2.1')
class FlickrDownloader {
static final String FLICKER_INTERESTING_URL =
"https://www.flickr.com/explore/interesting/7days"
static final String WORK_DIR = "./work"
final File workDir = new File(WORK_DIR)
@Scheduled(fixedRate = 30000L)
void downloadFlickrInteresting() {
def photos = extractPhotosFromFlickr()
withPool {
photos.eachParallel { photoUrl ->
log.info "Downloading photo ${photoUrl}"
def tempFile = download(photoUrl)
FileUtils.moveFileToDirectory(tempFile, workDir, true)
}
}
}
}
private File download(String url) {
def tempFile = File.createTempFile('flickr_downloader', '')
tempFile << url.toURL().bytes
tempFile
}
FlickrDownloader
@Slf4j
@EnableScheduling
@Grab('org.jsoup:jsoup:1.8.1')
@Grab('commons-io:commons-io:2.4')
@Grab('org.codehaus.gpars:gpars:1.2.1')
class FlickrDownloader {
static final String FLICKER_INTERESTING_URL =
"https://www.flickr.com/explore/interesting/7days"
static final String WORK_DIR = "./work"
final File workDir = new File(WORK_DIR)
@Scheduled(fixedRate = 30000L)
void downloadFlickrInteresting() {
def photos = extractPhotosFromFlickr()
withPool {
photos.eachParallel { photoUrl ->
log.info "Downloading photo ${photoUrl}"
def tempFile = download(photoUrl)
FileUtils.moveFileToDirectory(tempFile, workDir, true)
}
}
}
}
FlickrDownloader
2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader
2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader
2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader
2016-05-27 21:56:11.354 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader
2016-05-27 21:56:11.375 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader
2016-05-27 21:56:11.527 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader
2016-05-27 21:56:11.537 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader
2016-05-27 21:56:11.612 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader
2016-05-27 21:56:11.693 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader
2016-05-27 22:02:17.019 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:19.451 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:21.661 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:22.079 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:22.877 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:23.392 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:23.749 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:24.250 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:24.695 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
FlickrDownloader
2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader
2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader
2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader
2016-05-27 21:56:11.354 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader
2016-05-27 21:56:11.375 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader
2016-05-27 21:56:11.527 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader
2016-05-27 21:56:11.537 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader
2016-05-27 21:56:11.612 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader
2016-05-27 21:56:11.693 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader
2016-05-27 22:02:17.019 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:19.451 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:21.661 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:22.079 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:22.877 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:23.392 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:23.749 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:24.250 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
2016-05-27 22:02:24.695 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
4.
Frontend
GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer
Frontend
▷ MarkupTemplateEngine (HTML)
▷ Websockets
▷ Grooscript
HTML
yieldUnescaped '<!DOCTYPE html>'
html {
head {
title "Polaromatic"
link(rel: 'stylesheet', href: '/css/app.css')
link(rel: 'stylesheet', href: '/css/gh-fork-ribbon.css')
['webjars/sockjs-client/0.3.4-1/sockjs.min.js',
'webjars/stomp-websocket/2.3.1-1/stomp.min.js',
'webjars/jquery/2.1.3/jquery.min.js',
'webjars/handlebars/2.0.0-1/handlebars.min.js',
'js/Connection.js']
.each {
yieldUnescaped "<script src='$it'></script>"
}
}
}
HTML
body {
...
div(id: 'header') {
div(class: 'center') {
a(href: 'https://github.com/ilopmar/contest', target: 'blank') {
img(src: 'images/polaromatic-logo.png')
}
p('Polaromatic')
span('Powered by Spring Boot')
}
}
div(id: 'timeline', class: 'center')
}
script(id: 'photo-template', type: 'text/x-handlebars-template') {
div(class: 'photo-cover') {
div(class: 'photo', style: 'visibility:hidden; height:0') {
img(src: '{{image}}')
}
}
}
yieldUnescaped "<script>Connection().start()</script>"
}
Websockets
@Configuration
@EnableWebSocketMessageBroker
class WebsocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker '/notifications'
}
@Override
void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint('/polaromatic').withSockJS()
}
}
Websockets
class BrowserPushService {
@Autowired
SimpMessagingTemplate template
Photo pushToBrowser(Photo photo) {
log.debug "Pushing file to browser: ${photo.output}"
String imageB64 = new File(photo.output).bytes.encodeBase64().toString()
template.convertAndSend "/notifications/photo", imageB64
return photo
}
}
Websockets
class BrowserPushService {
@Autowired
SimpMessagingTemplate template
Photo pushToBrowser(Photo photo) {
log.debug "Pushing file to browser: ${photo.output}"
String imageB64 = new File(photo.output).bytes.encodeBase64().toString()
template.convertAndSend "/notifications/photo", imageB64
return photo
}
}
<chain input-channel="incommingFilesChannel">
<service-activator ref="fileService" method="preprocessFile"/>
<service-activator ref="imageConverterService" method="applyEffect"/>
<service-activator ref="browserPushService" method="pushToBrowser"/>
<service-activator ref="metricsService" method="updateMetrics"/>
<service-activator ref="fileService" method="deleteTempFiles"/>
</chain>
class Connection {
@GsNative
def initOn(source, path) {/*
var socket = new SockJS(path);
return [Handlebars.compile(source), Stomp.over(socket)];
*/}
def start() {
def source = $("#photo-template").html()
def (template, client) = initOn(source, '/polaromatic')
client.debug = null
client.connect(gs.toJavascript([:])) { ->
client.subscribe('/notifications/photo') { message ->
def context = [image: 'data:image/png;base64,' + message.body]
def html = template(context)
$("#timeline").prepend(html)
$("#timeline .photo:first-child img").on("load") {
$(this).parent().css(gs.toJavascript(display: 'none', visibility: 'visible', height: 'auto'))
$(this).parent().slideDown()
}
}
}
}
}
Grooscript (Javascript)
5.
Android
GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer
Android App
▷ Disclaimer: I'm not an Android developer
▷ Lazybones template (@marioggar)
▷ Traits, @CompileStatic
▷ SwissKnife
GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer
Android
trait Toastable {
@OnUIThread
void showToastMessage(String message) {
Toast toast = Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT)
toast.show()
}
}
Android
trait Toastable {
@OnUIThread
void showToastMessage(String message) {
Toast toast = Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT)
toast.show()
}
}
@CompileStatic
public class ShareActivity extends Activity implements Toastable {
...
showToastMessage(getString(R.string.share_ok_msg))
...
}
6.
Tests
Tests
▷ Spock Framework
▷ Version 1.0 (more than 2 years now)
▷ JUnit compatible (but way better)
Spock
class BrowserPushServiceSpec extends Specification {
void 'should push a converted photo to the browser'() {
given: 'a photo'
def output = File.createTempFile("output", "")
def photo = new Photo(output: output.path)
and: 'a mocked SimpMessagingTemplate'
def mockSimpMessagingTemplate = Mock(SimpMessagingTemplate)
and: 'the push service'
def browserPushService = new BrowserPushService(template: mockSimpMessagingTemplate)
when: 'pushing the photo to the browser'
browserPushService.pushToBrowser(photo)
then: 'the photo is pushed'
1 * mockSimpMessagingTemplate.convertAndSend('/notifications/photo', "")
}
}
7.
Build tool
Build tool
▷ Gradle
▷ Multiproject to build backend, documentation
and android
Gradle
subprojects {
buildscript {
repositories {
jcenter()
}
}
repositories {
jcenter()
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.2.1'
}
include 'polaromatic-back'
include 'polaromatic-groid'
include 'polaromatic-docs'
build.gradle settings.gradle
8.
Documentation
Documentation
▷ Asciidoctor (FTW!)
▷ Gradle plugin
▷ Backends: html, epub, pdf,...
Asciidoctor
buildscript {
dependencies {
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2'
}
}
apply plugin: 'org.asciidoctor.convert'
asciidoctor {
sourceDir 'src/docs'
outputDir "${buildDir}/docs"
attributes 'source-highlighter': 'coderay',
toc : 'left',
icons : 'font'
}
Asciidoctor
[source,xml,indent=0]
.src/main/resources/resources.xml
----
include::{polaromaticBackResources}/resources.xml[tags=appFlow]
----
<1> Define the integration with the file system
<2> Preprocess the file received
<3> Apply the Polaroid effect
<4> Send the new photo to the browser using Websockets
<5> Update the metrics
<6> Delete all temporary files
Asciidoctor
[source,xml,indent=0]
.src/main/resources/resources.xml
----
include::{polaromaticBackResources}/resources.xml[tags=appFlow]
----
<1> Define the integration with the file system
<2> Preprocess the file received
<3> Apply the Polaroid effect
<4> Send the new photo to the browser using Websockets
<5> Update the metrics
<6> Delete all temporary files
<!-- tag::appFlow[] -->
<file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/> <!--1-->
<chain input-channel="incommingFilesChannel">
<service-activator ref="fileService" method="preprocessFile"/> <!--2-->
<service-activator ref="imageConverterService" method="applyEffect"/> <!--3-->
<service-activator ref="browserPushService" method="pushToBrowser"/> <!--4-->
<service-activator ref="metricsService" method="updateMetrics"/> <!--5-->
<service-activator ref="fileService" method="deleteTempFiles"/> <!--6-->
</chain>
<!-- end::appFlow[]-->
Asciidoctor
9.
Summary
“
Groovy, groovy everywhere...
Thanks!
Any questions?
@ilopmar
lopez.ivan@gmail.com
https://github.com/ilopmar
Iván López
http://bit.ly/fullstack-groovy

More Related Content

What's hot

Reactive Programming with Rx
 Reactive Programming with Rx Reactive Programming with Rx
Reactive Programming with Rx
C4Media
 
Git Real
Git RealGit Real
Git Real
Gong Haibing
 
groovy & grails - lecture 13
groovy & grails - lecture 13groovy & grails - lecture 13
groovy & grails - lecture 13
Alexandre Masselot
 
The Future of Futures - A Talk About Java 8 CompletableFutures
The Future of Futures - A Talk About Java 8 CompletableFuturesThe Future of Futures - A Talk About Java 8 CompletableFutures
The Future of Futures - A Talk About Java 8 CompletableFutures
Haim Yadid
 
Everything you wanted to know about writing async, concurrent http apps in java
Everything you wanted to know about writing async, concurrent http apps in java Everything you wanted to know about writing async, concurrent http apps in java
Everything you wanted to know about writing async, concurrent http apps in java
Baruch Sadogursky
 
分布式版本管理
分布式版本管理分布式版本管理
分布式版本管理
jeffz
 
Akka streams - Umeå java usergroup
Akka streams - Umeå java usergroupAkka streams - Umeå java usergroup
Akka streams - Umeå java usergroup
Johan Andrén
 
JavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good PartsJavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good Parts
Konrad Malawski
 
Presentation joelperez thailand2014
Presentation joelperez thailand2014Presentation joelperez thailand2014
Presentation joelperez thailand2014
OUGTH Oracle User Group in Thailand
 
Presentation: Everything you wanted to know about writing async, high-concurr...
Presentation: Everything you wanted to know about writing async, high-concurr...Presentation: Everything you wanted to know about writing async, high-concurr...
Presentation: Everything you wanted to know about writing async, high-concurr...
Baruch Sadogursky
 
Angular Optimization Web Performance Meetup
Angular Optimization Web Performance MeetupAngular Optimization Web Performance Meetup
Angular Optimization Web Performance Meetup
David Barreto
 
WORKING WITH FILE AND PIPELINE PARAMETER BINDING
WORKING WITH FILE AND PIPELINE PARAMETER BINDINGWORKING WITH FILE AND PIPELINE PARAMETER BINDING
WORKING WITH FILE AND PIPELINE PARAMETER BINDING
Hitesh Mohapatra
 
Reactive Programming in Java 8 with Rx-Java
Reactive Programming in Java 8 with Rx-JavaReactive Programming in Java 8 with Rx-Java
Reactive Programming in Java 8 with Rx-Java
Kasun Indrasiri
 
groovy & grails - lecture 10
groovy & grails - lecture 10groovy & grails - lecture 10
groovy & grails - lecture 10
Alexandre Masselot
 
決済サービスのSpring Bootのバージョンを2系に上げた話
決済サービスのSpring Bootのバージョンを2系に上げた話決済サービスのSpring Bootのバージョンを2系に上げた話
決済サービスのSpring Bootのバージョンを2系に上げた話
Ryosuke Uchitate
 

What's hot (15)

Reactive Programming with Rx
 Reactive Programming with Rx Reactive Programming with Rx
Reactive Programming with Rx
 
Git Real
Git RealGit Real
Git Real
 
groovy & grails - lecture 13
groovy & grails - lecture 13groovy & grails - lecture 13
groovy & grails - lecture 13
 
The Future of Futures - A Talk About Java 8 CompletableFutures
The Future of Futures - A Talk About Java 8 CompletableFuturesThe Future of Futures - A Talk About Java 8 CompletableFutures
The Future of Futures - A Talk About Java 8 CompletableFutures
 
Everything you wanted to know about writing async, concurrent http apps in java
Everything you wanted to know about writing async, concurrent http apps in java Everything you wanted to know about writing async, concurrent http apps in java
Everything you wanted to know about writing async, concurrent http apps in java
 
分布式版本管理
分布式版本管理分布式版本管理
分布式版本管理
 
Akka streams - Umeå java usergroup
Akka streams - Umeå java usergroupAkka streams - Umeå java usergroup
Akka streams - Umeå java usergroup
 
JavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good PartsJavaOne 2013: Java 8 - The Good Parts
JavaOne 2013: Java 8 - The Good Parts
 
Presentation joelperez thailand2014
Presentation joelperez thailand2014Presentation joelperez thailand2014
Presentation joelperez thailand2014
 
Presentation: Everything you wanted to know about writing async, high-concurr...
Presentation: Everything you wanted to know about writing async, high-concurr...Presentation: Everything you wanted to know about writing async, high-concurr...
Presentation: Everything you wanted to know about writing async, high-concurr...
 
Angular Optimization Web Performance Meetup
Angular Optimization Web Performance MeetupAngular Optimization Web Performance Meetup
Angular Optimization Web Performance Meetup
 
WORKING WITH FILE AND PIPELINE PARAMETER BINDING
WORKING WITH FILE AND PIPELINE PARAMETER BINDINGWORKING WITH FILE AND PIPELINE PARAMETER BINDING
WORKING WITH FILE AND PIPELINE PARAMETER BINDING
 
Reactive Programming in Java 8 with Rx-Java
Reactive Programming in Java 8 with Rx-JavaReactive Programming in Java 8 with Rx-Java
Reactive Programming in Java 8 with Rx-Java
 
groovy & grails - lecture 10
groovy & grails - lecture 10groovy & grails - lecture 10
groovy & grails - lecture 10
 
決済サービスのSpring Bootのバージョンを2系に上げた話
決済サービスのSpring Bootのバージョンを2系に上げた話決済サービスのSpring Bootのバージョンを2系に上げた話
決済サービスのSpring Bootのバージョンを2系に上げた話
 

Similar to GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer

Analysing Github events with Neo4j
Analysing Github events with Neo4jAnalysing Github events with Neo4j
Analysing Github events with Neo4j
Christophe Willemsen
 
HotPush with Ionic 2 and CodePush
HotPush with Ionic 2 and CodePushHotPush with Ionic 2 and CodePush
HotPush with Ionic 2 and CodePush
Evan Schultz
 
Shaping Clouds with Terraform
Shaping Clouds with TerraformShaping Clouds with Terraform
Shaping Clouds with Terraform
Mike Fowler
 
Implementing a Backup Catalog… on a Student Budget
Implementing a Backup Catalog… on a Student BudgetImplementing a Backup Catalog… on a Student Budget
Implementing a Backup Catalog… on a Student Budget
Roy Zimmer
 
(1) c sharp introduction_basics_dot_net
(1) c sharp introduction_basics_dot_net(1) c sharp introduction_basics_dot_net
(1) c sharp introduction_basics_dot_net
Nico Ludwig
 
Puppet Performance Profiling
Puppet Performance ProfilingPuppet Performance Profiling
Puppet Performance Profiling
ripienaar
 
Enabling Microservices @Orbitz - Velocity Conf 2015
Enabling Microservices @Orbitz - Velocity Conf 2015Enabling Microservices @Orbitz - Velocity Conf 2015
Enabling Microservices @Orbitz - Velocity Conf 2015
Steve Hoffman
 
Proactive Web Performance Optimization.(Marcel Duran)
Proactive Web Performance Optimization.(Marcel Duran)Proactive Web Performance Optimization.(Marcel Duran)
Proactive Web Performance Optimization.(Marcel Duran)
Ontico
 
ADF in action 1.2
ADF in action 1.2ADF in action 1.2
ADF in action 1.2
Eugenio Romano
 
About Flink streaming
About Flink streamingAbout Flink streaming
About Flink streaming
용휘 김
 
前瞻性Web性能优化pwpo
前瞻性Web性能优化pwpo前瞻性Web性能优化pwpo
前瞻性Web性能优化pwpo
Michael Zhang
 
Automating Your Workflow with Gulp.js - php[world] 2016
Automating Your Workflow with Gulp.js - php[world] 2016Automating Your Workflow with Gulp.js - php[world] 2016
Automating Your Workflow with Gulp.js - php[world] 2016
Colin O'Dell
 
Database Change Management as a Service
Database Change Management as a ServiceDatabase Change Management as a Service
Database Change Management as a Service
Andrew Solomon
 
BPMS1
BPMS1BPMS1
BPMS1
BPMS1BPMS1
2012 coscup - Build your PHP application on Heroku
2012 coscup - Build your PHP application on Heroku2012 coscup - Build your PHP application on Heroku
2012 coscup - Build your PHP application on Heroku
ronnywang_tw
 
Breaking bad habits with GitLab CI
Breaking bad habits with GitLab CIBreaking bad habits with GitLab CI
Breaking bad habits with GitLab CI
Ivan Nemytchenko
 
Fastlane
FastlaneFastlane
Fastlane
Warren Lin
 
Workshop: Introduction to Web Components & Polymer
Workshop: Introduction to Web Components & Polymer Workshop: Introduction to Web Components & Polymer
Workshop: Introduction to Web Components & Polymer
John Riviello
 
Making the most out of kubernetes audit logs
Making the most out of kubernetes audit logsMaking the most out of kubernetes audit logs
Making the most out of kubernetes audit logs
Laurent Bernaille
 

Similar to GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer (20)

Analysing Github events with Neo4j
Analysing Github events with Neo4jAnalysing Github events with Neo4j
Analysing Github events with Neo4j
 
HotPush with Ionic 2 and CodePush
HotPush with Ionic 2 and CodePushHotPush with Ionic 2 and CodePush
HotPush with Ionic 2 and CodePush
 
Shaping Clouds with Terraform
Shaping Clouds with TerraformShaping Clouds with Terraform
Shaping Clouds with Terraform
 
Implementing a Backup Catalog… on a Student Budget
Implementing a Backup Catalog… on a Student BudgetImplementing a Backup Catalog… on a Student Budget
Implementing a Backup Catalog… on a Student Budget
 
(1) c sharp introduction_basics_dot_net
(1) c sharp introduction_basics_dot_net(1) c sharp introduction_basics_dot_net
(1) c sharp introduction_basics_dot_net
 
Puppet Performance Profiling
Puppet Performance ProfilingPuppet Performance Profiling
Puppet Performance Profiling
 
Enabling Microservices @Orbitz - Velocity Conf 2015
Enabling Microservices @Orbitz - Velocity Conf 2015Enabling Microservices @Orbitz - Velocity Conf 2015
Enabling Microservices @Orbitz - Velocity Conf 2015
 
Proactive Web Performance Optimization.(Marcel Duran)
Proactive Web Performance Optimization.(Marcel Duran)Proactive Web Performance Optimization.(Marcel Duran)
Proactive Web Performance Optimization.(Marcel Duran)
 
ADF in action 1.2
ADF in action 1.2ADF in action 1.2
ADF in action 1.2
 
About Flink streaming
About Flink streamingAbout Flink streaming
About Flink streaming
 
前瞻性Web性能优化pwpo
前瞻性Web性能优化pwpo前瞻性Web性能优化pwpo
前瞻性Web性能优化pwpo
 
Automating Your Workflow with Gulp.js - php[world] 2016
Automating Your Workflow with Gulp.js - php[world] 2016Automating Your Workflow with Gulp.js - php[world] 2016
Automating Your Workflow with Gulp.js - php[world] 2016
 
Database Change Management as a Service
Database Change Management as a ServiceDatabase Change Management as a Service
Database Change Management as a Service
 
BPMS1
BPMS1BPMS1
BPMS1
 
BPMS1
BPMS1BPMS1
BPMS1
 
2012 coscup - Build your PHP application on Heroku
2012 coscup - Build your PHP application on Heroku2012 coscup - Build your PHP application on Heroku
2012 coscup - Build your PHP application on Heroku
 
Breaking bad habits with GitLab CI
Breaking bad habits with GitLab CIBreaking bad habits with GitLab CI
Breaking bad habits with GitLab CI
 
Fastlane
FastlaneFastlane
Fastlane
 
Workshop: Introduction to Web Components & Polymer
Workshop: Introduction to Web Components & Polymer Workshop: Introduction to Web Components & Polymer
Workshop: Introduction to Web Components & Polymer
 
Making the most out of kubernetes audit logs
Making the most out of kubernetes audit logsMaking the most out of kubernetes audit logs
Making the most out of kubernetes audit logs
 

More from Iván López Martín

SalmorejoTech 2024 - Spring Boot <3 Testcontainers
SalmorejoTech 2024 - Spring Boot <3 TestcontainersSalmorejoTech 2024 - Spring Boot <3 Testcontainers
SalmorejoTech 2024 - Spring Boot <3 Testcontainers
Iván López Martín
 
CommitConf 2024 - Spring Boot <3 Testcontainers
CommitConf 2024 - Spring Boot <3 TestcontainersCommitConf 2024 - Spring Boot <3 Testcontainers
CommitConf 2024 - Spring Boot <3 Testcontainers
Iván López Martín
 
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdfVoxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
Iván López Martín
 
VMware - Testcontainers y Spring Boot
VMware - Testcontainers y Spring BootVMware - Testcontainers y Spring Boot
VMware - Testcontainers y Spring Boot
Iván López Martín
 
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud GatewaySpring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
Iván López Martín
 
Codemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring BootCodemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring Boot
Iván López Martín
 
CommitConf 2023 - Spring Framework 6 y Spring Boot 3
CommitConf 2023 - Spring Framework 6 y Spring Boot 3CommitConf 2023 - Spring Framework 6 y Spring Boot 3
CommitConf 2023 - Spring Framework 6 y Spring Boot 3
Iván López Martín
 
Construyendo un API REST con Spring Boot y GraalVM
Construyendo un API REST con Spring Boot y GraalVMConstruyendo un API REST con Spring Boot y GraalVM
Construyendo un API REST con Spring Boot y GraalVM
Iván López Martín
 
jLove 2020 - Micronaut and graalvm: The power of AoT
jLove 2020 - Micronaut and graalvm: The power of AoTjLove 2020 - Micronaut and graalvm: The power of AoT
jLove 2020 - Micronaut and graalvm: The power of AoT
Iván López Martín
 
Codemotion Madrid 2020 - Serverless con Micronaut
Codemotion Madrid 2020 - Serverless con MicronautCodemotion Madrid 2020 - Serverless con Micronaut
Codemotion Madrid 2020 - Serverless con Micronaut
Iván López Martín
 
JConf Perú 2020 - ¡Micronaut en acción!
JConf Perú 2020 - ¡Micronaut en acción!JConf Perú 2020 - ¡Micronaut en acción!
JConf Perú 2020 - ¡Micronaut en acción!
Iván López Martín
 
JConf Perú 2020 - Micronaut + GraalVM = <3
JConf Perú 2020 - Micronaut + GraalVM = <3JConf Perú 2020 - Micronaut + GraalVM = <3
JConf Perú 2020 - Micronaut + GraalVM = <3
Iván López Martín
 
JConf México 2020 - Micronaut + GraalVM = <3
JConf México 2020 - Micronaut + GraalVM = <3JConf México 2020 - Micronaut + GraalVM = <3
JConf México 2020 - Micronaut + GraalVM = <3
Iván López Martín
 
Developing Micronaut Applications With IntelliJ IDEA
Developing Micronaut Applications With IntelliJ IDEADeveloping Micronaut Applications With IntelliJ IDEA
Developing Micronaut Applications With IntelliJ IDEA
Iván López Martín
 
CommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
CommitConf 2019 - Micronaut y GraalVm: La combinación perfectaCommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
CommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
Iván López Martín
 
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
Iván López Martín
 
Greach 2019 - Creating Micronaut Configurations
Greach 2019 - Creating Micronaut ConfigurationsGreach 2019 - Creating Micronaut Configurations
Greach 2019 - Creating Micronaut Configurations
Iván López Martín
 
VoxxedDays Bucharest 2019 - Alexa, nice to meet you
VoxxedDays Bucharest 2019 - Alexa, nice to meet youVoxxedDays Bucharest 2019 - Alexa, nice to meet you
VoxxedDays Bucharest 2019 - Alexa, nice to meet you
Iván López Martín
 
JavaDay Lviv 2019 - Micronaut in action!
JavaDay Lviv 2019 - Micronaut in action!JavaDay Lviv 2019 - Micronaut in action!
JavaDay Lviv 2019 - Micronaut in action!
Iván López Martín
 
CrossDvlup Madrid 2019 - Alexa, encantado de conocerte
CrossDvlup Madrid 2019 - Alexa, encantado de conocerteCrossDvlup Madrid 2019 - Alexa, encantado de conocerte
CrossDvlup Madrid 2019 - Alexa, encantado de conocerte
Iván López Martín
 

More from Iván López Martín (20)

SalmorejoTech 2024 - Spring Boot <3 Testcontainers
SalmorejoTech 2024 - Spring Boot <3 TestcontainersSalmorejoTech 2024 - Spring Boot <3 Testcontainers
SalmorejoTech 2024 - Spring Boot <3 Testcontainers
 
CommitConf 2024 - Spring Boot <3 Testcontainers
CommitConf 2024 - Spring Boot <3 TestcontainersCommitConf 2024 - Spring Boot <3 Testcontainers
CommitConf 2024 - Spring Boot <3 Testcontainers
 
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdfVoxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
Voxxed Days CERN 2024 - Spring Boot <3 Testcontainers.pdf
 
VMware - Testcontainers y Spring Boot
VMware - Testcontainers y Spring BootVMware - Testcontainers y Spring Boot
VMware - Testcontainers y Spring Boot
 
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud GatewaySpring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
Spring IO 2023 - Dynamic OpenAPIs with Spring Cloud Gateway
 
Codemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring BootCodemotion Madrid 2023 - Testcontainers y Spring Boot
Codemotion Madrid 2023 - Testcontainers y Spring Boot
 
CommitConf 2023 - Spring Framework 6 y Spring Boot 3
CommitConf 2023 - Spring Framework 6 y Spring Boot 3CommitConf 2023 - Spring Framework 6 y Spring Boot 3
CommitConf 2023 - Spring Framework 6 y Spring Boot 3
 
Construyendo un API REST con Spring Boot y GraalVM
Construyendo un API REST con Spring Boot y GraalVMConstruyendo un API REST con Spring Boot y GraalVM
Construyendo un API REST con Spring Boot y GraalVM
 
jLove 2020 - Micronaut and graalvm: The power of AoT
jLove 2020 - Micronaut and graalvm: The power of AoTjLove 2020 - Micronaut and graalvm: The power of AoT
jLove 2020 - Micronaut and graalvm: The power of AoT
 
Codemotion Madrid 2020 - Serverless con Micronaut
Codemotion Madrid 2020 - Serverless con MicronautCodemotion Madrid 2020 - Serverless con Micronaut
Codemotion Madrid 2020 - Serverless con Micronaut
 
JConf Perú 2020 - ¡Micronaut en acción!
JConf Perú 2020 - ¡Micronaut en acción!JConf Perú 2020 - ¡Micronaut en acción!
JConf Perú 2020 - ¡Micronaut en acción!
 
JConf Perú 2020 - Micronaut + GraalVM = <3
JConf Perú 2020 - Micronaut + GraalVM = <3JConf Perú 2020 - Micronaut + GraalVM = <3
JConf Perú 2020 - Micronaut + GraalVM = <3
 
JConf México 2020 - Micronaut + GraalVM = <3
JConf México 2020 - Micronaut + GraalVM = <3JConf México 2020 - Micronaut + GraalVM = <3
JConf México 2020 - Micronaut + GraalVM = <3
 
Developing Micronaut Applications With IntelliJ IDEA
Developing Micronaut Applications With IntelliJ IDEADeveloping Micronaut Applications With IntelliJ IDEA
Developing Micronaut Applications With IntelliJ IDEA
 
CommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
CommitConf 2019 - Micronaut y GraalVm: La combinación perfectaCommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
CommitConf 2019 - Micronaut y GraalVm: La combinación perfecta
 
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
Codemotion Madrid 2019 - ¡GraalVM y Micronaut: compañeros perfectos!
 
Greach 2019 - Creating Micronaut Configurations
Greach 2019 - Creating Micronaut ConfigurationsGreach 2019 - Creating Micronaut Configurations
Greach 2019 - Creating Micronaut Configurations
 
VoxxedDays Bucharest 2019 - Alexa, nice to meet you
VoxxedDays Bucharest 2019 - Alexa, nice to meet youVoxxedDays Bucharest 2019 - Alexa, nice to meet you
VoxxedDays Bucharest 2019 - Alexa, nice to meet you
 
JavaDay Lviv 2019 - Micronaut in action!
JavaDay Lviv 2019 - Micronaut in action!JavaDay Lviv 2019 - Micronaut in action!
JavaDay Lviv 2019 - Micronaut in action!
 
CrossDvlup Madrid 2019 - Alexa, encantado de conocerte
CrossDvlup Madrid 2019 - Alexa, encantado de conocerteCrossDvlup Madrid 2019 - Alexa, encantado de conocerte
CrossDvlup Madrid 2019 - Alexa, encantado de conocerte
 

Recently uploaded

Computer HARDWARE presenattion by CWD students class 10
Computer HARDWARE presenattion by CWD students class 10Computer HARDWARE presenattion by CWD students class 10
Computer HARDWARE presenattion by CWD students class 10
ankush9927
 
Russian Girls Call Navi Mumbai 🎈🔥9920725232 🔥💋🎈 Provide Best And Top Girl Ser...
Russian Girls Call Navi Mumbai 🎈🔥9920725232 🔥💋🎈 Provide Best And Top Girl Ser...Russian Girls Call Navi Mumbai 🎈🔥9920725232 🔥💋🎈 Provide Best And Top Girl Ser...
Russian Girls Call Navi Mumbai 🎈🔥9920725232 🔥💋🎈 Provide Best And Top Girl Ser...
bellared2
 
The Path to General-Purpose Robots - Coatue
The Path to General-Purpose Robots - CoatueThe Path to General-Purpose Robots - Coatue
The Path to General-Purpose Robots - Coatue
Razin Mustafiz
 
leewayhertz.com-Generative AI tech stack Frameworks infrastructure models and...
leewayhertz.com-Generative AI tech stack Frameworks infrastructure models and...leewayhertz.com-Generative AI tech stack Frameworks infrastructure models and...
leewayhertz.com-Generative AI tech stack Frameworks infrastructure models and...
alexjohnson7307
 
How UiPath Discovery Suite supports identification of Agentic Process Automat...
How UiPath Discovery Suite supports identification of Agentic Process Automat...How UiPath Discovery Suite supports identification of Agentic Process Automat...
How UiPath Discovery Suite supports identification of Agentic Process Automat...
DianaGray10
 
Finetuning GenAI For Hacking and Defending
Finetuning GenAI For Hacking and DefendingFinetuning GenAI For Hacking and Defending
Finetuning GenAI For Hacking and Defending
Priyanka Aash
 
Using LLM Agents with Llama 3, LangGraph and Milvus
Using LLM Agents with Llama 3, LangGraph and MilvusUsing LLM Agents with Llama 3, LangGraph and Milvus
Using LLM Agents with Llama 3, LangGraph and Milvus
Zilliz
 
Semantic-Aware Code Model: Elevating the Future of Software Development
Semantic-Aware Code Model: Elevating the Future of Software DevelopmentSemantic-Aware Code Model: Elevating the Future of Software Development
Semantic-Aware Code Model: Elevating the Future of Software Development
Baishakhi Ray
 
Sonkoloniya documentation - ONEprojukti.pdf
Sonkoloniya documentation - ONEprojukti.pdfSonkoloniya documentation - ONEprojukti.pdf
Sonkoloniya documentation - ONEprojukti.pdf
SubhamMandal40
 
UX Webinar Series: Essentials for Adopting Passkeys as the Foundation of your...
UX Webinar Series: Essentials for Adopting Passkeys as the Foundation of your...UX Webinar Series: Essentials for Adopting Passkeys as the Foundation of your...
UX Webinar Series: Essentials for Adopting Passkeys as the Foundation of your...
FIDO Alliance
 
Tailored CRM Software Development for Enhanced Customer Insights
Tailored CRM Software Development for Enhanced Customer InsightsTailored CRM Software Development for Enhanced Customer Insights
Tailored CRM Software Development for Enhanced Customer Insights
SynapseIndia
 
Mastering OnlyFans Clone App Development: Key Strategies for Success
Mastering OnlyFans Clone App Development: Key Strategies for SuccessMastering OnlyFans Clone App Development: Key Strategies for Success
Mastering OnlyFans Clone App Development: Key Strategies for Success
David Wilson
 
Generative AI Reasoning Tech Talk - July 2024
Generative AI Reasoning Tech Talk - July 2024Generative AI Reasoning Tech Talk - July 2024
Generative AI Reasoning Tech Talk - July 2024
siddu769252
 
kk vathada _digital transformation frameworks_2024.pdf
kk vathada _digital transformation frameworks_2024.pdfkk vathada _digital transformation frameworks_2024.pdf
kk vathada _digital transformation frameworks_2024.pdf
KIRAN KV
 
leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
leewayhertz.com-AI agents for healthcare Applications benefits and implementa...leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
alexjohnson7307
 
Use Cases & Benefits of RPA in Manufacturing in 2024.pptx
Use Cases & Benefits of RPA in Manufacturing in 2024.pptxUse Cases & Benefits of RPA in Manufacturing in 2024.pptx
Use Cases & Benefits of RPA in Manufacturing in 2024.pptx
SynapseIndia
 
Vertex AI Agent Builder - GDG Alicante - Julio 2024
Vertex AI Agent Builder - GDG Alicante - Julio 2024Vertex AI Agent Builder - GDG Alicante - Julio 2024
Vertex AI Agent Builder - GDG Alicante - Julio 2024
Nicolás Lopéz
 
Integrating Kafka with MuleSoft 4 and usecase
Integrating Kafka with MuleSoft 4 and usecaseIntegrating Kafka with MuleSoft 4 and usecase
Integrating Kafka with MuleSoft 4 and usecase
shyamraj55
 
Zaitechno Handheld Raman Spectrometer.pdf
Zaitechno Handheld Raman Spectrometer.pdfZaitechno Handheld Raman Spectrometer.pdf
Zaitechno Handheld Raman Spectrometer.pdf
AmandaCheung15
 
Improving Learning Content Efficiency with Reusable Learning Content
Improving Learning Content Efficiency with Reusable Learning ContentImproving Learning Content Efficiency with Reusable Learning Content
Improving Learning Content Efficiency with Reusable Learning Content
Enterprise Knowledge
 

Recently uploaded (20)

Computer HARDWARE presenattion by CWD students class 10
Computer HARDWARE presenattion by CWD students class 10Computer HARDWARE presenattion by CWD students class 10
Computer HARDWARE presenattion by CWD students class 10
 
Russian Girls Call Navi Mumbai 🎈🔥9920725232 🔥💋🎈 Provide Best And Top Girl Ser...
Russian Girls Call Navi Mumbai 🎈🔥9920725232 🔥💋🎈 Provide Best And Top Girl Ser...Russian Girls Call Navi Mumbai 🎈🔥9920725232 🔥💋🎈 Provide Best And Top Girl Ser...
Russian Girls Call Navi Mumbai 🎈🔥9920725232 🔥💋🎈 Provide Best And Top Girl Ser...
 
The Path to General-Purpose Robots - Coatue
The Path to General-Purpose Robots - CoatueThe Path to General-Purpose Robots - Coatue
The Path to General-Purpose Robots - Coatue
 
leewayhertz.com-Generative AI tech stack Frameworks infrastructure models and...
leewayhertz.com-Generative AI tech stack Frameworks infrastructure models and...leewayhertz.com-Generative AI tech stack Frameworks infrastructure models and...
leewayhertz.com-Generative AI tech stack Frameworks infrastructure models and...
 
How UiPath Discovery Suite supports identification of Agentic Process Automat...
How UiPath Discovery Suite supports identification of Agentic Process Automat...How UiPath Discovery Suite supports identification of Agentic Process Automat...
How UiPath Discovery Suite supports identification of Agentic Process Automat...
 
Finetuning GenAI For Hacking and Defending
Finetuning GenAI For Hacking and DefendingFinetuning GenAI For Hacking and Defending
Finetuning GenAI For Hacking and Defending
 
Using LLM Agents with Llama 3, LangGraph and Milvus
Using LLM Agents with Llama 3, LangGraph and MilvusUsing LLM Agents with Llama 3, LangGraph and Milvus
Using LLM Agents with Llama 3, LangGraph and Milvus
 
Semantic-Aware Code Model: Elevating the Future of Software Development
Semantic-Aware Code Model: Elevating the Future of Software DevelopmentSemantic-Aware Code Model: Elevating the Future of Software Development
Semantic-Aware Code Model: Elevating the Future of Software Development
 
Sonkoloniya documentation - ONEprojukti.pdf
Sonkoloniya documentation - ONEprojukti.pdfSonkoloniya documentation - ONEprojukti.pdf
Sonkoloniya documentation - ONEprojukti.pdf
 
UX Webinar Series: Essentials for Adopting Passkeys as the Foundation of your...
UX Webinar Series: Essentials for Adopting Passkeys as the Foundation of your...UX Webinar Series: Essentials for Adopting Passkeys as the Foundation of your...
UX Webinar Series: Essentials for Adopting Passkeys as the Foundation of your...
 
Tailored CRM Software Development for Enhanced Customer Insights
Tailored CRM Software Development for Enhanced Customer InsightsTailored CRM Software Development for Enhanced Customer Insights
Tailored CRM Software Development for Enhanced Customer Insights
 
Mastering OnlyFans Clone App Development: Key Strategies for Success
Mastering OnlyFans Clone App Development: Key Strategies for SuccessMastering OnlyFans Clone App Development: Key Strategies for Success
Mastering OnlyFans Clone App Development: Key Strategies for Success
 
Generative AI Reasoning Tech Talk - July 2024
Generative AI Reasoning Tech Talk - July 2024Generative AI Reasoning Tech Talk - July 2024
Generative AI Reasoning Tech Talk - July 2024
 
kk vathada _digital transformation frameworks_2024.pdf
kk vathada _digital transformation frameworks_2024.pdfkk vathada _digital transformation frameworks_2024.pdf
kk vathada _digital transformation frameworks_2024.pdf
 
leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
leewayhertz.com-AI agents for healthcare Applications benefits and implementa...leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
leewayhertz.com-AI agents for healthcare Applications benefits and implementa...
 
Use Cases & Benefits of RPA in Manufacturing in 2024.pptx
Use Cases & Benefits of RPA in Manufacturing in 2024.pptxUse Cases & Benefits of RPA in Manufacturing in 2024.pptx
Use Cases & Benefits of RPA in Manufacturing in 2024.pptx
 
Vertex AI Agent Builder - GDG Alicante - Julio 2024
Vertex AI Agent Builder - GDG Alicante - Julio 2024Vertex AI Agent Builder - GDG Alicante - Julio 2024
Vertex AI Agent Builder - GDG Alicante - Julio 2024
 
Integrating Kafka with MuleSoft 4 and usecase
Integrating Kafka with MuleSoft 4 and usecaseIntegrating Kafka with MuleSoft 4 and usecase
Integrating Kafka with MuleSoft 4 and usecase
 
Zaitechno Handheld Raman Spectrometer.pdf
Zaitechno Handheld Raman Spectrometer.pdfZaitechno Handheld Raman Spectrometer.pdf
Zaitechno Handheld Raman Spectrometer.pdf
 
Improving Learning Content Efficiency with Reusable Learning Content
Improving Learning Content Efficiency with Reusable Learning ContentImproving Learning Content Efficiency with Reusable Learning Content
Improving Learning Content Efficiency with Reusable Learning Content
 

GR8Conf 2016 - Mum, I want to be a Groovy full-stack developer

  • 1. Iván López - @ilopmar Mum, I want to be a Groovy full-stack developer
  • 2. Hello! I am Iván López @ilopmar http://greachconf.com@madridgug
  • 3. Thank you very much! Q&A
  • 6. What's a full-stack developer?
  • 16. Polaromatic ▷ Spring Boot ▷ Core App ▷ Spring MVC ▷ Spring Integration Flow
  • 17. <file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/> <chain input-channel="incommingFilesChannel"> <service-activator ref="fileService" method="preprocessFile"/> <service-activator ref="imageConverterService" method="applyEffect"/> <service-activator ref="browserPushService" method="pushToBrowser"/> <service-activator ref="metricsService" method="updateMetrics"/> <service-activator ref="fileService" method="deleteTempFiles"/> </chain> Spring Integration Flow
  • 18. <file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/> <chain input-channel="incommingFilesChannel"> <service-activator ref="fileService" method="preprocessFile"/> <service-activator ref="imageConverterService" method="applyEffect"/> <service-activator ref="browserPushService" method="pushToBrowser"/> <service-activator ref="metricsService" method="updateMetrics"/> <service-activator ref="fileService" method="deleteTempFiles"/> </chain> Spring Integration Flow Photo preprocessFile(File file) { def pr = new PolaroidRequest(file) this.preprocessFile(pr) } File service
  • 19. <file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/> <chain input-channel="incommingFilesChannel"> <service-activator ref="fileService" method="preprocessFile"/> <service-activator ref="imageConverterService" method="applyEffect"/> <service-activator ref="browserPushService" method="pushToBrowser"/> <service-activator ref="metricsService" method="updateMetrics"/> <service-activator ref="fileService" method="deleteTempFiles"/> </chain> Spring Integration Flow File service Photo preprocessFile(File file) { def pr = new PolaroidRequest(file) this.preprocessFile(pr) } Photo preprocessFile(PolaroidRequest polaroidRequest) { String outputFile = File.createTempFile("output", ".png").path return new Photo(input: polaroidRequest.inputFile.absolutePath, output: outputFile, text: polaroidRequest.text) }
  • 20. class ImageConverterService { private static final String DEFAULT_CAPTION = "#LearningSpringBoot with Polaromaticn" Random rnd = new Random() Photo applyEffect(Photo photo) { log.debug "Applying effect to file: ${photo.input}..." def inputFile = photo.input def outputFile = photo.output double polaroidRotation = rnd.nextInt(6).toDouble() String caption = photo.text ?: DEFAULT_CAPTION def op = new IMOperation() op.addImage(inputFile) op.thumbnail(300, 300) .set("caption", caption) .gravity("center") .pointsize(20) .background("black") .polaroid(rnd.nextBoolean() ? polaroidRotation : -polaroidRotation) .addImage(outputFile) def command = new ConvertCmd() command.run(op) photo } } Image converter
  • 22. FlickrDownloader ▷ Spring Boot CLI ▷ Download Flickr Interesting pictures ▷ Jsoup, GPars ▷ 55 lines of Groovy code (microservice?)
  • 23. @Slf4j @EnableScheduling @Grab('org.jsoup:jsoup:1.8.1') @Grab('commons-io:commons-io:2.4') @Grab('org.codehaus.gpars:gpars:1.2.1') class FlickrDownloader { static final String FLICKER_INTERESTING_URL = "https://www.flickr.com/explore/interesting/7days" static final String WORK_DIR = "./work" final File workDir = new File(WORK_DIR) @Scheduled(fixedRate = 30000L) void downloadFlickrInteresting() { def photos = extractPhotosFromFlickr() withPool { photos.eachParallel { photoUrl -> log.info "Downloading photo ${photoUrl}" def tempFile = download(photoUrl) FileUtils.moveFileToDirectory(tempFile, workDir, true) } } } } FlickrDownloader
  • 24. @Slf4j @EnableScheduling @Grab('org.jsoup:jsoup:1.8.1') @Grab('commons-io:commons-io:2.4') @Grab('org.codehaus.gpars:gpars:1.2.1') class FlickrDownloader { static final String FLICKER_INTERESTING_URL = "https://www.flickr.com/explore/interesting/7days" static final String WORK_DIR = "./work" final File workDir = new File(WORK_DIR) @Scheduled(fixedRate = 30000L) void downloadFlickrInteresting() { def photos = extractPhotosFromFlickr() withPool { photos.eachParallel { photoUrl -> log.info "Downloading photo ${photoUrl}" def tempFile = download(photoUrl) FileUtils.moveFileToDirectory(tempFile, workDir, true) } } } } FlickrDownloader
  • 25. FlickrDownloader @Slf4j @EnableScheduling @Grab('org.jsoup:jsoup:1.8.1') @Grab('commons-io:commons-io:2.4') @Grab('org.codehaus.gpars:gpars:1.2.1') class FlickrDownloader { static final String FLICKER_INTERESTING_URL = "https://www.flickr.com/explore/interesting/7days" static final String WORK_DIR = "./work" final File workDir = new File(WORK_DIR) @Scheduled(fixedRate = 30000L) void downloadFlickrInteresting() { def photos = extractPhotosFromFlickr() withPool { photos.eachParallel { photoUrl -> log.info "Downloading photo ${photoUrl}" def tempFile = download(photoUrl) FileUtils.moveFileToDirectory(tempFile, workDir, true) } } } } private List extractPhotosFromFlickr() { Document doc = Jsoup.connect(FLICKER_INTERESTING_URL).get() Elements images = doc.select("img.pc_img") def photos = images .listIterator() .collect { it.attr('src').replace('_m.jpg', '_b.jpg') } photos }
  • 26. FlickrDownloader @Slf4j @EnableScheduling @Grab('org.jsoup:jsoup:1.8.1') @Grab('commons-io:commons-io:2.4') @Grab('org.codehaus.gpars:gpars:1.2.1') class FlickrDownloader { static final String FLICKER_INTERESTING_URL = "https://www.flickr.com/explore/interesting/7days" static final String WORK_DIR = "./work" final File workDir = new File(WORK_DIR) @Scheduled(fixedRate = 30000L) void downloadFlickrInteresting() { def photos = extractPhotosFromFlickr() withPool { photos.eachParallel { photoUrl -> log.info "Downloading photo ${photoUrl}" def tempFile = download(photoUrl) FileUtils.moveFileToDirectory(tempFile, workDir, true) } } } } private File download(String url) { def tempFile = File.createTempFile('flickr_downloader', '') tempFile << url.toURL().bytes tempFile }
  • 27. FlickrDownloader @Slf4j @EnableScheduling @Grab('org.jsoup:jsoup:1.8.1') @Grab('commons-io:commons-io:2.4') @Grab('org.codehaus.gpars:gpars:1.2.1') class FlickrDownloader { static final String FLICKER_INTERESTING_URL = "https://www.flickr.com/explore/interesting/7days" static final String WORK_DIR = "./work" final File workDir = new File(WORK_DIR) @Scheduled(fixedRate = 30000L) void downloadFlickrInteresting() { def photos = extractPhotosFromFlickr() withPool { photos.eachParallel { photoUrl -> log.info "Downloading photo ${photoUrl}" def tempFile = download(photoUrl) FileUtils.moveFileToDirectory(tempFile, workDir, true) } } } }
  • 28. FlickrDownloader 2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader 2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader 2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader 2016-05-27 21:56:11.354 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader 2016-05-27 21:56:11.375 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader 2016-05-27 21:56:11.527 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader 2016-05-27 21:56:11.537 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader 2016-05-27 21:56:11.612 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader 2016-05-27 21:56:11.693 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader 2016-05-27 22:02:17.019 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:19.451 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:21.661 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:22.079 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:22.877 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:23.392 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:23.749 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:24.250 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:24.695 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
  • 29. FlickrDownloader 2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader 2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader 2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader 2016-05-27 21:56:11.354 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader 2016-05-27 21:56:11.375 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader 2016-05-27 21:56:11.527 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader 2016-05-27 21:56:11.537 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader 2016-05-27 21:56:11.612 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader 2016-05-27 21:56:11.693 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader 2016-05-27 22:02:17.019 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:19.451 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:21.661 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:22.079 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:22.877 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:23.392 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:23.749 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:24.250 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader 2016-05-27 22:02:24.695 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader
  • 32. Frontend ▷ MarkupTemplateEngine (HTML) ▷ Websockets ▷ Grooscript
  • 33. HTML yieldUnescaped '<!DOCTYPE html>' html { head { title "Polaromatic" link(rel: 'stylesheet', href: '/css/app.css') link(rel: 'stylesheet', href: '/css/gh-fork-ribbon.css') ['webjars/sockjs-client/0.3.4-1/sockjs.min.js', 'webjars/stomp-websocket/2.3.1-1/stomp.min.js', 'webjars/jquery/2.1.3/jquery.min.js', 'webjars/handlebars/2.0.0-1/handlebars.min.js', 'js/Connection.js'] .each { yieldUnescaped "<script src='$it'></script>" } } }
  • 34. HTML body { ... div(id: 'header') { div(class: 'center') { a(href: 'https://github.com/ilopmar/contest', target: 'blank') { img(src: 'images/polaromatic-logo.png') } p('Polaromatic') span('Powered by Spring Boot') } } div(id: 'timeline', class: 'center') } script(id: 'photo-template', type: 'text/x-handlebars-template') { div(class: 'photo-cover') { div(class: 'photo', style: 'visibility:hidden; height:0') { img(src: '{{image}}') } } } yieldUnescaped "<script>Connection().start()</script>" }
  • 35. Websockets @Configuration @EnableWebSocketMessageBroker class WebsocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker '/notifications' } @Override void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint('/polaromatic').withSockJS() } }
  • 36. Websockets class BrowserPushService { @Autowired SimpMessagingTemplate template Photo pushToBrowser(Photo photo) { log.debug "Pushing file to browser: ${photo.output}" String imageB64 = new File(photo.output).bytes.encodeBase64().toString() template.convertAndSend "/notifications/photo", imageB64 return photo } }
  • 37. Websockets class BrowserPushService { @Autowired SimpMessagingTemplate template Photo pushToBrowser(Photo photo) { log.debug "Pushing file to browser: ${photo.output}" String imageB64 = new File(photo.output).bytes.encodeBase64().toString() template.convertAndSend "/notifications/photo", imageB64 return photo } } <chain input-channel="incommingFilesChannel"> <service-activator ref="fileService" method="preprocessFile"/> <service-activator ref="imageConverterService" method="applyEffect"/> <service-activator ref="browserPushService" method="pushToBrowser"/> <service-activator ref="metricsService" method="updateMetrics"/> <service-activator ref="fileService" method="deleteTempFiles"/> </chain>
  • 38. class Connection { @GsNative def initOn(source, path) {/* var socket = new SockJS(path); return [Handlebars.compile(source), Stomp.over(socket)]; */} def start() { def source = $("#photo-template").html() def (template, client) = initOn(source, '/polaromatic') client.debug = null client.connect(gs.toJavascript([:])) { -> client.subscribe('/notifications/photo') { message -> def context = [image: 'data:image/png;base64,' + message.body] def html = template(context) $("#timeline").prepend(html) $("#timeline .photo:first-child img").on("load") { $(this).parent().css(gs.toJavascript(display: 'none', visibility: 'visible', height: 'auto')) $(this).parent().slideDown() } } } } } Grooscript (Javascript)
  • 41. Android App ▷ Disclaimer: I'm not an Android developer ▷ Lazybones template (@marioggar) ▷ Traits, @CompileStatic ▷ SwissKnife
  • 43. Android trait Toastable { @OnUIThread void showToastMessage(String message) { Toast toast = Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT) toast.show() } }
  • 44. Android trait Toastable { @OnUIThread void showToastMessage(String message) { Toast toast = Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT) toast.show() } } @CompileStatic public class ShareActivity extends Activity implements Toastable { ... showToastMessage(getString(R.string.share_ok_msg)) ... }
  • 46. Tests ▷ Spock Framework ▷ Version 1.0 (more than 2 years now) ▷ JUnit compatible (but way better)
  • 47. Spock class BrowserPushServiceSpec extends Specification { void 'should push a converted photo to the browser'() { given: 'a photo' def output = File.createTempFile("output", "") def photo = new Photo(output: output.path) and: 'a mocked SimpMessagingTemplate' def mockSimpMessagingTemplate = Mock(SimpMessagingTemplate) and: 'the push service' def browserPushService = new BrowserPushService(template: mockSimpMessagingTemplate) when: 'pushing the photo to the browser' browserPushService.pushToBrowser(photo) then: 'the photo is pushed' 1 * mockSimpMessagingTemplate.convertAndSend('/notifications/photo', "") } }
  • 49. Build tool ▷ Gradle ▷ Multiproject to build backend, documentation and android
  • 50. Gradle subprojects { buildscript { repositories { jcenter() } } repositories { jcenter() } } task wrapper(type: Wrapper) { gradleVersion = '2.2.1' } include 'polaromatic-back' include 'polaromatic-groid' include 'polaromatic-docs' build.gradle settings.gradle
  • 52. Documentation ▷ Asciidoctor (FTW!) ▷ Gradle plugin ▷ Backends: html, epub, pdf,...
  • 53. Asciidoctor buildscript { dependencies { classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2' } } apply plugin: 'org.asciidoctor.convert' asciidoctor { sourceDir 'src/docs' outputDir "${buildDir}/docs" attributes 'source-highlighter': 'coderay', toc : 'left', icons : 'font' }
  • 54. Asciidoctor [source,xml,indent=0] .src/main/resources/resources.xml ---- include::{polaromaticBackResources}/resources.xml[tags=appFlow] ---- <1> Define the integration with the file system <2> Preprocess the file received <3> Apply the Polaroid effect <4> Send the new photo to the browser using Websockets <5> Update the metrics <6> Delete all temporary files
  • 55. Asciidoctor [source,xml,indent=0] .src/main/resources/resources.xml ---- include::{polaromaticBackResources}/resources.xml[tags=appFlow] ---- <1> Define the integration with the file system <2> Preprocess the file received <3> Apply the Polaroid effect <4> Send the new photo to the browser using Websockets <5> Update the metrics <6> Delete all temporary files <!-- tag::appFlow[] --> <file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/> <!--1--> <chain input-channel="incommingFilesChannel"> <service-activator ref="fileService" method="preprocessFile"/> <!--2--> <service-activator ref="imageConverterService" method="applyEffect"/> <!--3--> <service-activator ref="browserPushService" method="pushToBrowser"/> <!--4--> <service-activator ref="metricsService" method="updateMetrics"/> <!--5--> <service-activator ref="fileService" method="deleteTempFiles"/> <!--6--> </chain> <!-- end::appFlow[]-->