Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Using Grails to power your Electric Car

Using Grails to power your Electric Car

Marco Pas

May 22, 2013
Tweet

More Decks by Marco Pas

Other Decks in Programming

Transcript

  1. Small Intro • Marco Pas (1973) › Education in Chemistry

    and then moved to Transportation & Logistics to finally IT.. what is next? • Software Engineer • Burgundian lifestyle Wikipedia tells us : 'enjoyment of life, good food, and extravagant spectacle'
  2. Agenda • What the heck am I trying to solve!!

    • How on earth did we solve it? • What are our plans for the future?
  3. Convention Fueling != Electric Vehicle Charging • EV Charging Challenges:

    › Impossible to store & forward electricity › Charge often (Limited Range) › Time to charge (from minutes to hours) › Compatibility (plugs, charging cables)
  4. Public EV Chargers in numbers • 2011: EU 12.000 •

    2020: EU 660.000 2011 2020 Denmark 280 5.000 Germany 1.900 150.000 Italy 1.300 125.000 Netherlands 1.700 32.000 United Kingdom 703 122.000
  5. Charging Process Back Office Validation / Verification • Is the

    card valid? • Electricity pricing? • How much can I charge? • Who is the customer? • Did you make a reservation? • ….
  6. Requirements • Implement a platform that enables EV Infra management:

    › To monitor a Charge Point (CP) network › Supports remote management › Track charging sessions • Including charge authorization & transaction storage › Multi-tenancy › 3rd party integration using Web Services (SOAP / REST)
  7. Open Charge Point Protocol (OCPP) • Open protocol between charging

    stations and a managing central system aka back office › Asynchronous › Based on SOAP (v1.2) › Working group with support from all manufacturers!
  8. Agenda • What the heck am I trying to solve!!

    • How on earth did we solve it? • What are our plans for the future?
  9. Release 1.0 ! ! SOAP ~ Contract First with Grails

    was not that easy, so we moved to a pure JAVA/Spring OCPP application
  10. Jackson JSON Mapper public class JsonMapper extends ObjectMapper { public

    JsonMapper() { super(); /** * De-Serialization options JSON -> OBJECT */ // - ignore unknown fields - otherwise the construction of the object will fail! this.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // - make it possible to also construct empty objects this.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); /** * Serialization options OBJECT -> JSON */ // properties with non-null values are to be included in the resulting JSON this.setSerializationInclusion(JsonInclude.Include.NON_NULL); } }
  11. Jackson JSON Mapper • JSON → Object • Object →

    JSON resources.groovy beans = { // a preconfigured Jackson JSON mapper with defaults jsonMapper(JsonMapper){} } def jsonContent = “{'name': 'John Doe'}” Person person = jsonMapper.readValue(jsonContent, Person.class) Person person = new Person(name: 'John Doe') def jsonContent = jsonMapper.valueToTree(person)
  12. Theming support • Login-Logout & user configurable themes • Using

    Spring Security Core Plugin Config.groovy // make sure we can act on security events grails.plugins.springsecurity.useSecurityEventListener = true // executed when a user succesfully authenticates into the application grails.plugins.springsecurity.onInteractiveAuthenticationSuccessEvent = { e, appCtx → // .. code intentionally emitted .. // session.theme = “MyNameOfTheTheme” } page.gsp <g:if test="${session?.theme == null}"> <% session.theme="${grailsApplication.config.default.theme}"%> </g:if> <r:require module="${session?.theme}"/> <g:external uri="/css/themes/${session?.theme - "theme_"}/images/favicon.ico"/>
  13. Technical Debt • Client was very happy but.... shame on

    us: › Tightly coupled › Poor test coverage › Spring project & Grails project › Adding functional value is just to much fun! • But... ready for round 2.. › Thanks to Grails we could refactor with great ease and speed!
  14. Release 2.0 • Guidelines: › Focus on creating a modular

    platform › Test Driven Development › Use Grails for everything!! › Minimize the use of plugins!!!
  15. Grails CXF Plugin • WSDL › Contract First & Code

    First • Wire Interceptors › Logging, Security • Support for versioning @GrailsCxfEndpoint(address='/myCustomSoapService/v2/')
  16. Annotated example @GrailsCxfEndpoint( address='/centralsystem/ocpp/v1/5', wsdl = 'wsdl/ocpp_15_centralsystem.wsdl', expose = EndpointType.JAX_WS_WSDL,

    soap12 = true, inInterceptors = ["logSoapInboundInterceptor", "setReplyToSOAPHeaderInInterceptor"], outInterceptors = ["logSoapOutboundInterceptor"]) @WebService( name = "CentralSystemService", targetNamespace = "urn://Ocpp/Cs/2012/06/", serviceName = "CentralSystemService", portName = "CentralSystemServiceSoap12" ) @GZIP class CentralSystemOcpp15Service implements CentralSystemService { // ... code intentionally omitted // ... contains the methods that needs to be implemented due to the 'implements' }
  17. Demo • Create a contract first webservice using Grails CXF

    plugin › Source WSDL: CustomerService.wsdl › Steps: • Create grails project • Install CXF plugin • Use WSDL2JAVA to generate web service implementation • Create Grails service that implements the web service interface • Test using SOAPUI
  18. AMQP - Advanced Message Queuing Protocol • Asynchronous and synchronous

    message exchange › Enables modular platform architecture
  19. RabbitMQ – an AMQP implementation • Grails RabbitMQ Plugin ›

    High-level abstraction for sending and receiving messages › Fallback to Spring Template class MessageReceiveService { static rabbitQueue = 'helloQ' void handleMessage(message) { // handle message… } } class MessageSendController { def sendMessage = { rabbitSend 'helloQ', 'Hello!' } }
  20. RabbitMQ Synchronous class MessageSendController { def rabbitTemplate // use the

    Spring rabbitTemplate directly def sendMessage = { def response = rabbitTemplate.convertSendAndReceive 'helloQ', 'Hello World' println response } } class MessageReceiveService { static rabbitQueue = [queues: 'helloQ', messageConverterBean: ''] void handleMessage(Message message) { // determine the reply queue def returnQueue = message.messageProperties.replyTo // return the response to temporary return queue.. rabbitSend returnQueue, 'Hello there' } }
  21. Demo • Send and Consume a message via RabbitMQ ›

    Steps: • Install RabbitMQ plugin • Configure Grails app to use RabbitMQ • Create code to publish and consume a message
  22. Testing • Functional & Unit Testing › Build-Test-Data › Spock

    • Load & Performane Testing › BadBoy / Apache JMeter
  23. Some stuff we have used • Grails Plugins › Spring

    Security › Export › RabbitMQ › CXF › Fixture › Spock › Build-Test-Data • Non-Plugins › Twitter BootStrap › Jackson JSON
  24. Agenda • What the heck am I trying to solve!!

    • How on earth did we solve it? • What are our plans for the future?
  25. Release 3.0 • Additional protocol implementation in JSON › Eliminating

    the verbosity of SOAP! › To ([<MessageTypeId>, “<UniqueId>”, “<Action>”, {<Payload>}]) <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:cp="urn://Ocpp/Cp/2012/06/" xmlns:cs="urn://Ocpp/Cs/2012/06/" xmlns:imp="urn://iMOVE/Cp/2011/09/" xmlns:ims="urn://iMOVE/Cs/2011/09/"> <SOAP-ENV:Header> <wsa5:MessageID>940</wsa5:MessageID> <wsa5:From> <wsa5:Address>http://127.0.0.1:1234</wsa5:Address> </wsa5:From> <wsa5:ReplyTo SOAP-ENV:mustUnderstand="true"> <wsa5:Address>http://127.0.0.1:1234</wsa5:Address> </wsa5:ReplyTo> <wsa5:To SOAP-ENV:mustUnderstand="true">http://www.starwarsrules.com/services/centralsystem/ocpp/v1/5</wsa5:To> <wsa5:Action SOAP-ENV:mustUnderstand="true">/Heartbeat</wsa5:Action> <cs:chargeBoxIdentity SOAP-ENV:mustUnderstand="true">CHARGER_001_1234</cs:chargeBoxIdentity> </SOAP-ENV:Header> <SOAP-ENV:Body> <cs:heartbeatRequest /> </SOAP-ENV:Body> </SOAP-ENV:Envelope> [2, “19223201”, “HeartBeat”, {“”}] <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope" xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa5="http://www.w3.org/2005/08/addressing" xmlns:cp="urn://Ocpp/Cp/2012/06/" xmlns:cs="urn://Ocpp/Cs/2012/06/" xmlns:imp="urn://iMOVE/Cp/2011/09/" xmlns:ims="urn://iMOVE/Cs/2011/09/"> <SOAP-ENV:Header> <wsa5:MessageID>940</wsa5:MessageID> <wsa5:From> <wsa5:Address>http://127.0.0.1:1234</wsa5:Address> </wsa5:From> <wsa5:ReplyTo SOAP-ENV:mustUnderstand="true"> <wsa5:Address>http://127.0.0.1:1234</wsa5:Address> </wsa5:ReplyTo> <wsa5:To SOAP-ENV:mustUnderstand="true">http://www.starwarsrules.com/services/centralsystem/ocpp/v1/5</wsa5:To> <wsa5:Action SOAP-ENV:mustUnderstand="true">/Heartbeat</wsa5:Action> <cs:chargeBoxIdentity SOAP-ENV:mustUnderstand="true">CHARGER_001_1234</cs:chargeBoxIdentity> </SOAP-ENV:Header> <SOAP-ENV:Body> <cs:heartbeatRequest /> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
  26. WebSockets • Defines an API establishing a "socket" connections between

    a client and a server › Providing full-duplex communication channel over a single TCP connection › HTTP upgrade by Protocol Negotiation › Firewall friendly! (port 80) › No reconnect handling or guaranteed message delivery
  27. WebSocket handshake • Client Request • Server Response GET /mychat

    HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat Sec-WebSocket-Version: 13 Origin: http://example.com HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
  28. Vertx.io • Asynchronous Application Development › Polyglot, Simplicity, Scalability, Concurrency

    › Distributed Event Bus › WebSocket support • Verticles Server.groovy vertx.createHttpServer().requestHandler { req -> req.response.headers().put("Content-Type", "text/html; charset-UTF-8"); req.response.end("<html><body><h1>Hello from vert.x!</h1></body></html>"); }.listen(8080) vertx run Server.groovy vertx run Server.groovy -instances 4