SlideShare a Scribd company logo
ScalaModules
   Scala and OSGi
OSGi Services


• The key to modularity
• Arguably, the key to OOP itself?
Dr Alan Kay
Dr Alan Kay

“Dude I
invented
friggin’ OOP.
Have you
heard of it?”
Dr Alan Kay

“OOP to me means only
messaging, local
retention and protection
and hiding of state-
process, and extreme
late-binding of all things.”
Late Binding with
         Services

• Services answer the question:where do I
  get an implementation of this interface?
• The registry decentralises the architecture.
Consuming Services
Harder than Expected!
Why...?
Scala modules
Slippery
Dynamics

• A service can come and go
• At any time
• On any thread
• Yes... even while you’re using it
Result


• Service usage code can be
 very complex
Service Consumption
        in Java
Consuming a Service

public void consumeService(BundleContext context) {
   ServiceReference ref = context.getServiceReference(Greeting.class.getName());
   Greeting greeting = (Greeting) context.getService(ref);
   System.out.println(greeting.getMessage());
}
Consuming a Service

public void consumeService(BundleContext context) {
   ServiceReference ref = context.getServiceReference(Greeting.class.getName());
   Greeting greeting = (Greeting) context.getService(ref);
   System.out.println(greeting.getMessage());
}




             WRONG
Consuming a Service

public void consumeService(BundleContext context) {
   ServiceReference ref = context.getServiceReference(Greeting.class.getName());
   if(ref != null) {
      Greeting greeting = (Greeting) context.getService(ref);
      System.out.println(greeting.getMessage());
   }
}
Consuming a Service

public void consumeService(BundleContext context) {
   ServiceReference ref = context.getServiceReference(Greeting.class.getName());
   if(ref != null) {
      Greeting greeting = (Greeting) context.getService(ref);
      System.out.println(greeting.getMessage());
   }
}




             WRONG
Consuming a Service

    public void consumeService(BundleContext context) {
       ServiceReference ref = context.getServiceReference(Greeting.class.getName());
       if(ref != null) {
          Greeting greeting = (Greeting) context.getService(ref);
          if(greeting != null) {
         	 System.out.println(greeting.getMessage());
          }
       }
    }
Consuming a Service

    public void consumeService(BundleContext context) {
       ServiceReference ref = context.getServiceReference(Greeting.class.getName());
       if(ref != null) {
          Greeting greeting = (Greeting) context.getService(ref);
          if(greeting != null) {
         	 System.out.println(greeting.getMessage());
          }
       }
    }
	




                 WRONG
Consuming a Service
public void consumeService(BundleContext context) {
   ServiceReference ref = context.getServiceReference(Greeting.class.getName());
   if(ref != null) {
      Greeting greeting = (Greeting) context.getService(ref);
      if(greeting != null) {
     	 System.out.println(greeting.getMessage());
      }
      context.ungetService(ref);
   }
}
Consuming a Service
public void consumeService(BundleContext context) {
   ServiceReference ref = context.getServiceReference(Greeting.class.getName());
   if(ref != null) {
      Greeting greeting = (Greeting) context.getService(ref);
      if(greeting != null) {
     	 System.out.println(greeting.getMessage());
      }
      context.ungetService(ref);
   }
}




             WRONG
Consuming a Service
public void consumeService(BundleContext context) {
   ServiceReference ref = context.getServiceReference(Greeting.class.getName());
   if(ref != null) {
      try {
         Greeting greeting = (Greeting) context.getService(ref);
         if(greeting != null) {
     	     System.out.println(greeting.getMessage());
         }
      } finally {
     	 context.ungetService(ref);
      }
   }
}
Consuming a Service
public void consumeService(BundleContext context) {
   ServiceReference ref = context.getServiceReference(Greeting.class.getName());
   if(ref != null) {
      try {
         Greeting greeting = (Greeting) context.getService(ref);
         if(greeting != null) {
     	     System.out.println(greeting.getMessage());
         }
      } finally {
     	 context.ungetService(ref);
      }
   }
}




NOT TYPE SAFE
Consuming a Service
public void consumeService(BundleContext context) {
   ServiceReference ref = context.getServiceReference(Greeting.class.getName());
   if(ref != null) {
      try {
         Greeting greeting = (Greeting) context.getService(ref);
         if(greeting != null) {
     	     System.out.println(greeting.getMessage());
         }
      } finally {
     	 context.ungetService(ref);
      }
   }
}




NOT TYPE SAFE
An Answer:
Stop Writing Code!
Declarative Services
              (DS)
@Reference
public void setGreeting(Greeting greeting) {
   this.greeting = greeting;
}

public void consumeService() {
   System.out.println(greeting.getMessage());
}
Blueprint
   public void setGreeting(Greeting greeting) {
      this.greeting = greeting;
   }

   public void consumeService() {
      System.out.println(greeting.getMessage());
   }


<blueprint>

   <reference id="greeting" interface="org.example.Greeting"/>

   <bean id="foo" class="com.foo.Foo"/>
      <property name="greeting" ref="greeting"/>
   </bean>

</blueprint>
Problem

• These frameworks provide abstractions
• Abstraction hide detail
• Sometimes we need the detail
Example: Cardinality?
                  Service



                  Service

      Component

                  Service



                  Service
Single
            Service



            Service

Component

            Service



            Service
Multiple
            Service



            Service

Component

            Service



            Service
???
  Component         Service



  Component         Service



  Component         Service



  Component         Service




Not supported by DS
Declarative
I    Services
       (and Blueprint)
Declarative
I    Services
       (and Blueprint)
The “80%” Solution
              “Normal” Users
The “80%” Solution
              “Normal” Users
              DS support
The “80%” Solution
              “Normal” Users
              DS support
The “80%” Solution
              “Normal” Users
              DS support
              “Power” Users
The “80%” Solution
              “Normal” Users
              DS support
              “Power” Users
The “80%” Solution
              “Normal” Users
              DS support
              “Power” Users
              Easy in Java
The “80%” Solution
              “Normal” Users
              DS support
              “Power” Users
              Easy in Java
The “80%” Solution
              “Normal” Users
              DS support
              “Power” Users
              Easy in Java
              Easy in Scala
The “80%” Solution
              “Normal” Users
              DS support
              “Power” Users
              Easy in Java
              Easy in Scala
Java
ServiceTracker tracker = new ServiceTracker(context, Greeting.class.getName(),
                                            new ServiceTrackerCustomizer() {

      public Object addingService(ServiceReference reference) {
         Greeting greeting = (Greeting) context.getService(reference);	
         ServiceRegistration reg = context.registerService(IFoo.class.getName(),
                                                           new Foo(greeting), null);
         return reg;
      }

      public void modifiedService(ServiceReference reference, Object service) {
      }

      public void removedService(ServiceReference reference, Object service) {
         ServiceRegistration reg = (ServiceRegistration) service;
         reg.unregister();
      }
});
Java
ServiceTracker tracker = new ServiceTracker(context, Greeting.class.getName(),
                                            new ServiceTrackerCustomizer() {

      public Object addingService(ServiceReference reference) {
         Greeting greeting = (Greeting) context.getService(reference);	
         ServiceRegistration reg = context.registerService(IFoo.class.getName(),
                                                           new Foo(greeting), null);
         return reg;
      }

      public void modifiedService(ServiceReference reference, Object service) {
      }

      public void removedService(ServiceReference reference, Object service) {
         ServiceRegistration reg = (ServiceRegistration) service;
         reg.unregister();
      }
});



                                                               Type Safety???
What is ScalaModules?
NOT Another Module
  System (phew!)
Scala modules
Scala modules
“A Scala internal DSL to ease
    OSGi development”
• Now:
 • Services
• Future:
 • Extender Pattern?
 • Resources?
An Eclipse Project!
Examples
Java
ServiceTracker tracker = new ServiceTracker(context, Greeting.class.getName(),
                                            new ServiceTrackerCustomizer() {

      public Object addingService(ServiceReference reference) {
         Greeting greeting = (Greeting) context.getService(reference);	
         ServiceRegistration reg = context.registerService(IFoo.class.getName(),
                                                           new Foo(greeting), null);
         return reg;
      }

      public void modifiedService(ServiceReference reference, Object service) {
      }

      public void removedService(ServiceReference reference, Object service) {
         ServiceRegistration reg = (ServiceRegistration) service;
         reg.unregister();
      }
});
ScalaModules


val greetingTrack = context track classOf[Greeting] on {
   case Adding(greeting) => context createService (classOf[IFoo],
                                                   new Foo(greeting))
   case Removed(_, reg) => reg unregister
}
Plan
• Started moving the code & docs to Eclipse
• 19 March: M1
• 7 May: M2
• 28 May: RC1
• 11 June: RC2
• 23 June: ScalaModules 2.0 Release &
  Graduation
Get Involved

http://www.eclipse.org/forums/
      eclipse.scalamodules

More Related Content

PDF
Kogito: cloud native business automation
KEY
DDSUG 2009 Back-porting DSpace 2.0 Services to DSpace 1.6.0
PPT
Vs2010and Ne Tframework
PDF
Rapid development tools for java ee 8 [tut2998]
PPT
Spring and Cloud Foundry; a Marriage Made in Heaven
PDF
BeJUG Meetup - What's coming in the OSGi R7 Specification
PDF
Rapid Development Tools for Java EE 8 [TUT2998]
PDF
React table tutorial project setup, use table, and usefilter
Kogito: cloud native business automation
DDSUG 2009 Back-porting DSpace 2.0 Services to DSpace 1.6.0
Vs2010and Ne Tframework
Rapid development tools for java ee 8 [tut2998]
Spring and Cloud Foundry; a Marriage Made in Heaven
BeJUG Meetup - What's coming in the OSGi R7 Specification
Rapid Development Tools for Java EE 8 [TUT2998]
React table tutorial project setup, use table, and usefilter

Viewers also liked (6)

PPTX
Oop lecture1-chapter1(review of java)
ODP
Oscar reiken jr on our success at manheim
PDF
Scala e xchange 2013 haoyi li on metascala a tiny diy jvm
PDF
5 things cucumber is bad at by Richard Lawrence
ODP
Patterns for slick database applications
PPT
enhanced er diagram
Oop lecture1-chapter1(review of java)
Oscar reiken jr on our success at manheim
Scala e xchange 2013 haoyi li on metascala a tiny diy jvm
5 things cucumber is bad at by Richard Lawrence
Patterns for slick database applications
enhanced er diagram
Ad

Similar to Scala modules (20)

PDF
OSGi DevCon 09 - OSGi on Scala
PDF
JAX 09 - OSGi on Scala
PDF
W-JAX 08 - Declarative Services versus Spring Dynamic Modules
PPT
Service oriented component model
PDF
Weld-OSGi, injecting easiness in OSGi
PDF
iPOJO - The Simple Life - Richard Hall, Visiting Assistant Professor at Tufts...
PDF
Dynamic and modular Web Applications with Equinox and Vaadin
PPTX
Introduction to OSGi - Part-2
PDF
S313937 cdi dochez
PDF
What's new in the OSGi 4.2 Enterprise Release
PDF
Spark IT 2011 - Context & Dependency Injection in the Java EE 6 Ecosystem
PDF
Introduction to OSGi (Tokyo JUG)
PDF
Introduction to cdi given at java one 2014
ODP
Peaberry - Blending Services And Extensions
PPT
OSGi patterns v1.0.11
PDF
Osgi cdi
PDF
The Basic Concept Of IOC
PDF
Popular patterns revisited on OSGi - Christian Schneider (Adobe)
PDF
Devoxx 2012 (v2)
PDF
Using Contexts & Dependency Injection in the Java EE 6 Platform
OSGi DevCon 09 - OSGi on Scala
JAX 09 - OSGi on Scala
W-JAX 08 - Declarative Services versus Spring Dynamic Modules
Service oriented component model
Weld-OSGi, injecting easiness in OSGi
iPOJO - The Simple Life - Richard Hall, Visiting Assistant Professor at Tufts...
Dynamic and modular Web Applications with Equinox and Vaadin
Introduction to OSGi - Part-2
S313937 cdi dochez
What's new in the OSGi 4.2 Enterprise Release
Spark IT 2011 - Context & Dependency Injection in the Java EE 6 Ecosystem
Introduction to OSGi (Tokyo JUG)
Introduction to cdi given at java one 2014
Peaberry - Blending Services And Extensions
OSGi patterns v1.0.11
Osgi cdi
The Basic Concept Of IOC
Popular patterns revisited on OSGi - Christian Schneider (Adobe)
Devoxx 2012 (v2)
Using Contexts & Dependency Injection in the Java EE 6 Platform
Ad

More from Skills Matter (20)

ODP
Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations ...
PDF
Cukeup nyc ian dees on elixir, erlang, and cucumberl
PDF
Cukeup nyc peter bell on getting started with cucumber.js
PDF
Agile testing & bdd e xchange nyc 2013 jeffrey davidson & lav pathak & sam ho...
ODP
Progressive f# tutorials nyc rachel reese & phil trelford on try f# from zero...
ODP
Progressive f# tutorials nyc don syme on keynote f# in the open source world
PDF
Agile testing & bdd e xchange nyc 2013 gojko adzic on bond villain guide to s...
PPTX
Dmitry mozorov on code quotations code as-data for f#
PDF
A poet's guide_to_acceptance_testing
PDF
Russ miles-cloudfoundry-deep-dive
KEY
Serendipity-neo4j
PDF
Simon Peyton Jones: Managing parallelism
PDF
Plug 20110217
PDF
Lug presentation
PPT
I went to_a_communications_workshop_and_they_t
PDF
Plug saiku
PDF
Huguk lily
PDF
Bootstrapping a-devops-matter
PDF
Personal kanban-workshop
PDF
Agilex retrospectives
Progressive f# tutorials nyc dmitry mozorov & jack pappas on code quotations ...
Cukeup nyc ian dees on elixir, erlang, and cucumberl
Cukeup nyc peter bell on getting started with cucumber.js
Agile testing & bdd e xchange nyc 2013 jeffrey davidson & lav pathak & sam ho...
Progressive f# tutorials nyc rachel reese & phil trelford on try f# from zero...
Progressive f# tutorials nyc don syme on keynote f# in the open source world
Agile testing & bdd e xchange nyc 2013 gojko adzic on bond villain guide to s...
Dmitry mozorov on code quotations code as-data for f#
A poet's guide_to_acceptance_testing
Russ miles-cloudfoundry-deep-dive
Serendipity-neo4j
Simon Peyton Jones: Managing parallelism
Plug 20110217
Lug presentation
I went to_a_communications_workshop_and_they_t
Plug saiku
Huguk lily
Bootstrapping a-devops-matter
Personal kanban-workshop
Agilex retrospectives

Recently uploaded (20)

PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Electronic commerce courselecture one. Pdf
PDF
Spectral efficient network and resource selection model in 5G networks
PDF
NewMind AI Weekly Chronicles - August'25-Week II
PDF
Machine learning based COVID-19 study performance prediction
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPTX
Spectroscopy.pptx food analysis technology
PPTX
Cloud computing and distributed systems.
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
Encapsulation theory and applications.pdf
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PDF
Unlocking AI with Model Context Protocol (MCP)
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Electronic commerce courselecture one. Pdf
Spectral efficient network and resource selection model in 5G networks
NewMind AI Weekly Chronicles - August'25-Week II
Machine learning based COVID-19 study performance prediction
Dropbox Q2 2025 Financial Results & Investor Presentation
Optimiser vos workloads AI/ML sur Amazon EC2 et AWS Graviton
“AI and Expert System Decision Support & Business Intelligence Systems”
Spectroscopy.pptx food analysis technology
Cloud computing and distributed systems.
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Programs and apps: productivity, graphics, security and other tools
Encapsulation theory and applications.pdf
The AUB Centre for AI in Media Proposal.docx
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
Unlocking AI with Model Context Protocol (MCP)

Scala modules

  • 1. ScalaModules Scala and OSGi
  • 2. OSGi Services • The key to modularity • Arguably, the key to OOP itself?
  • 4. Dr Alan Kay “Dude I invented friggin’ OOP. Have you heard of it?”
  • 5. Dr Alan Kay “OOP to me means only messaging, local retention and protection and hiding of state- process, and extreme late-binding of all things.”
  • 6. Late Binding with Services • Services answer the question:where do I get an implementation of this interface? • The registry decentralises the architecture.
  • 12. Dynamics • A service can come and go • At any time • On any thread • Yes... even while you’re using it
  • 13. Result • Service usage code can be very complex
  • 15. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); Greeting greeting = (Greeting) context.getService(ref); System.out.println(greeting.getMessage()); }
  • 16. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); Greeting greeting = (Greeting) context.getService(ref); System.out.println(greeting.getMessage()); } WRONG
  • 17. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); if(ref != null) { Greeting greeting = (Greeting) context.getService(ref); System.out.println(greeting.getMessage()); } }
  • 18. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); if(ref != null) { Greeting greeting = (Greeting) context.getService(ref); System.out.println(greeting.getMessage()); } } WRONG
  • 19. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); if(ref != null) { Greeting greeting = (Greeting) context.getService(ref); if(greeting != null) { System.out.println(greeting.getMessage()); } } }
  • 20. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); if(ref != null) { Greeting greeting = (Greeting) context.getService(ref); if(greeting != null) { System.out.println(greeting.getMessage()); } } } WRONG
  • 21. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); if(ref != null) { Greeting greeting = (Greeting) context.getService(ref); if(greeting != null) { System.out.println(greeting.getMessage()); } context.ungetService(ref); } }
  • 22. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); if(ref != null) { Greeting greeting = (Greeting) context.getService(ref); if(greeting != null) { System.out.println(greeting.getMessage()); } context.ungetService(ref); } } WRONG
  • 23. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); if(ref != null) { try { Greeting greeting = (Greeting) context.getService(ref); if(greeting != null) { System.out.println(greeting.getMessage()); } } finally { context.ungetService(ref); } } }
  • 24. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); if(ref != null) { try { Greeting greeting = (Greeting) context.getService(ref); if(greeting != null) { System.out.println(greeting.getMessage()); } } finally { context.ungetService(ref); } } } NOT TYPE SAFE
  • 25. Consuming a Service public void consumeService(BundleContext context) { ServiceReference ref = context.getServiceReference(Greeting.class.getName()); if(ref != null) { try { Greeting greeting = (Greeting) context.getService(ref); if(greeting != null) { System.out.println(greeting.getMessage()); } } finally { context.ungetService(ref); } } } NOT TYPE SAFE
  • 27. Declarative Services (DS) @Reference public void setGreeting(Greeting greeting) { this.greeting = greeting; } public void consumeService() { System.out.println(greeting.getMessage()); }
  • 28. Blueprint public void setGreeting(Greeting greeting) { this.greeting = greeting; } public void consumeService() { System.out.println(greeting.getMessage()); } <blueprint> <reference id="greeting" interface="org.example.Greeting"/> <bean id="foo" class="com.foo.Foo"/> <property name="greeting" ref="greeting"/> </bean> </blueprint>
  • 29. Problem • These frameworks provide abstractions • Abstraction hide detail • Sometimes we need the detail
  • 30. Example: Cardinality? Service Service Component Service Service
  • 31. Single Service Service Component Service Service
  • 32. Multiple Service Service Component Service Service
  • 33. ??? Component Service Component Service Component Service Component Service Not supported by DS
  • 34. Declarative I Services (and Blueprint)
  • 35. Declarative I Services (and Blueprint)
  • 36. The “80%” Solution “Normal” Users
  • 37. The “80%” Solution “Normal” Users DS support
  • 38. The “80%” Solution “Normal” Users DS support
  • 39. The “80%” Solution “Normal” Users DS support “Power” Users
  • 40. The “80%” Solution “Normal” Users DS support “Power” Users
  • 41. The “80%” Solution “Normal” Users DS support “Power” Users Easy in Java
  • 42. The “80%” Solution “Normal” Users DS support “Power” Users Easy in Java
  • 43. The “80%” Solution “Normal” Users DS support “Power” Users Easy in Java Easy in Scala
  • 44. The “80%” Solution “Normal” Users DS support “Power” Users Easy in Java Easy in Scala
  • 45. Java ServiceTracker tracker = new ServiceTracker(context, Greeting.class.getName(), new ServiceTrackerCustomizer() { public Object addingService(ServiceReference reference) { Greeting greeting = (Greeting) context.getService(reference); ServiceRegistration reg = context.registerService(IFoo.class.getName(), new Foo(greeting), null); return reg; } public void modifiedService(ServiceReference reference, Object service) { } public void removedService(ServiceReference reference, Object service) { ServiceRegistration reg = (ServiceRegistration) service; reg.unregister(); } });
  • 46. Java ServiceTracker tracker = new ServiceTracker(context, Greeting.class.getName(), new ServiceTrackerCustomizer() { public Object addingService(ServiceReference reference) { Greeting greeting = (Greeting) context.getService(reference); ServiceRegistration reg = context.registerService(IFoo.class.getName(), new Foo(greeting), null); return reg; } public void modifiedService(ServiceReference reference, Object service) { } public void removedService(ServiceReference reference, Object service) { ServiceRegistration reg = (ServiceRegistration) service; reg.unregister(); } }); Type Safety???
  • 48. NOT Another Module System (phew!)
  • 51. “A Scala internal DSL to ease OSGi development”
  • 52. • Now: • Services • Future: • Extender Pattern? • Resources?
  • 55. Java ServiceTracker tracker = new ServiceTracker(context, Greeting.class.getName(), new ServiceTrackerCustomizer() { public Object addingService(ServiceReference reference) { Greeting greeting = (Greeting) context.getService(reference); ServiceRegistration reg = context.registerService(IFoo.class.getName(), new Foo(greeting), null); return reg; } public void modifiedService(ServiceReference reference, Object service) { } public void removedService(ServiceReference reference, Object service) { ServiceRegistration reg = (ServiceRegistration) service; reg.unregister(); } });
  • 56. ScalaModules val greetingTrack = context track classOf[Greeting] on { case Adding(greeting) => context createService (classOf[IFoo], new Foo(greeting)) case Removed(_, reg) => reg unregister }
  • 57. Plan • Started moving the code & docs to Eclipse • 19 March: M1 • 7 May: M2 • 28 May: RC1 • 11 June: RC2 • 23 June: ScalaModules 2.0 Release & Graduation