Archive

Archive for the ‘groovy’ Category

Merry Christmas and a Happy New Year!

December 23, 2010 4 comments

I wanted to thank all readers for their feedback and support. Flattr this Writing this blog makes a lot of fun and I am looking forward to future posts.

I wanted to make you a Groovy christmas present – just throw it in your preferred Groovy console and execute it ­čÖé

String.metaClass.or = { other ->
    return "$delegate$other"
}

String.metaClass.div = { other ->
    return " " * other + delegate
}

[(','/11 | 'i' | '!' * 6 | ';' | ',' ),
('.'/6  | ',' | ';' | 'i' | '!' * 5 | '\'' | '`' | ',' | 'u' * 2 | ',' | 'o' | '$' * 2 | 'bo.'),
('!' * 7 / 4 | '\'.e' | '$' * 14 | '.' ),
('!' * 7 / 3 | ' ' | '$' * 17 | 'P' ),
('!' * 7 / 3 | ',`' | '$' * 8 | 'P""`,,`"' ),
('i' / 2 | '!' * 8 | ',' |'$' * 4 | '",oed' | '$' * 6 ),
('!' * 9 / 1 | '\'P".,e' | '$' * 8 | '"\'?' ),
('`' / 1 | '!' * 8 | ' z$\'J' | '$' * 5 | '\'.,$bd$b,'),
('`' / 2 | '!' * 6 | 'f;$\'d' | '$' * 13 | 'P\',c,.' ),
('!' * 6 / 3 | ' $B,"?' | '$' * 5 | 'P\',uggg' | '$' * 5 | 'P"' ),
('!' * 6 / 3 | '.' | '$' * 4 | 'be."\'zd' | '$' * 3 | 'P".,uooe' | '$' * 2 | 'r' ),
('`' / 3 | '!' * 3 | '\',' | '$' * 9 | 'c,"",ud' | '$' * 9 | 'L' ),
('!' * 2 / 4 | ' ' | '$' * 29),
('!\'j' / 4 | '$' * 29 ),
('d@@,?' / 2 | '$' * 28 | 'P'),
('?@@f:' / 2 | '$' * 27 | '\''),
('""' / 3 | ' `' | '$' * 26 | 'F'),
('`3' / 7 | '$' * 22 | 'F' ),
('`"' / 10 | '$' * 4 | 'P?' | '$' * 7 | '"`' ),
('`""' / 19), "Merry Christmas and a Happy New Year!"].each { println it }  
Advertisements
Categories: groovy

Funktionale Webtests mit Geb @ jaxenter.de

December 21, 2010 1 comment

Auf Jaxenter [0] ist ein Artikel von mir ├╝ber Geb [1], einem neuen Webautomations- und Testframework auf Basis von Groovy und Seleniums WebDriver API, erschienen:

Funktionale Webtests mit Geb

Lesen und gl├╝cklich werden!

[0] Jaxenter – Funktionale Webtests mit Geb
[1] Geb Homepage

Categories: grails, groovy

The Micro Framework Approach

December 3, 2010 5 comments


Flattr this
The demand for Grails and Groovy is clearly raising these days – at least here in Austria and Germany. Although most of my workshops have their individual adaptions (depending on the previous knowledge and programming language experience of participants) there are parts which can more or less be found unmodified in every workshop: Groovy essentials/advanced topics and what I call micro framework examples. This article is about the idea behind micro framework examples and why I find them that useful as workshop examples.

What is a Micro Framework Example?

I strongly believe that true understanding on patterns behind frameworks like Hibernate and Spring can’t easily be treat in a bunch of slides. Explaining patterns is one thing but to actually see how those are applied is another one. One approach I’ve found to be really useful in workshops is the use of micro framework examples. A micro framework example implements the core functionality behind a specific framework – reduced to the very fundamentals. One advantage to implement a micro framework example together with participants is to force triggering a thinking process of what functionality is needed and how it can be implemented. Another side-effect is that it allows to slightly introduce the original frameworks ubiquitous language simply by using the same class and method names.

Let me give you an example. The most threatening topic for many of my clients is to understand Hibernate and its persistence patterns. One approach to create a better understanding would be to implement a micro Hibernate framework example. This can be done in a simple Groovy script MicroHibernate.groovy which defines two classes and a simple test case. The first class implements the registry pattern and is called SessionFactory:

class SessionFactory {

    private def storage

    def SessionFactory(def storage)  {
        this.storage = storage
    }

    def newStorageConnection()  {
        return storage
    }
}

The SessionFactory acts as the main access point to get a reference to some storage connection. In the micro framework example this will simply be a Map. Dealing with SQL or even a real database would uselessly complicate the example and we want to concentrate on the core essentials. Let’s go on to the next class which implements the persistence context pattern:


class Session {

    static Log log = LogFactory.getLog(Session)

    private def sessionFactory

    def Session(def sessionFactory) { this.sessionFactory = sessionFactory }

    def snapshots = [:] // a Map(Domain-Class, Map(Identifier, Properties))
    def identityMap = [:] // a Map(Domain-Class, Map(Identifier, ObjectRef))
    def modifiedPersistentObjects = [:] // a Map(Domain-Class, List(Identifier))
    
    def propertyChanged(def obj)  {
        if (!modifiedPersistentObjects[obj.getClass()]) modifiedPersistentObjects[obj.getClass()] = []

        modifiedPersistentObjects[obj.getClass()] << obj.id

        log.info "propertyChanged of object: ${obj} with id ${obj.id}"
    }

    def load(Class<?> domainClassType, Long identifier)  {
        
        if (identityMap[domainClassType] && identityMap[domainClassType][identifier])  {
            return identityMap[domainClassType][identifier]
        }
        
        def conn = sessionFactory.newStorageConnection()
        def loadedObj = conn[domainClassType][identifier]
        if (!loadedObj) throw new Exception("Object of type ${domainClassType} with id ${identifier} could not be found!")
        
        if (!snapshots[domainClassType]) snapshots[domainClassType] = [:]
        if (!identityMap[domainClassType]) identityMap[domainClassType] = [:]


        def properties = loadedObj.getProperty("props")
        snapshots[domainClassType][identifier] = properties.inject([:], { m, property -> m[property] = loadedObj[property]; m })

        log.info "create snapshot of ${domainClassType} id ${identifier} with properties ${snapshots[domainClassType][identifier]}"

        identityMap[domainClassType][identifier] = loadedObj
        
        loadedObj.metaClass.getId = { -> identifier } 
        loadedObj.metaClass.setProperty = { String name, Object arg ->
            def metaProperty = delegate.metaClass.getMetaProperty(name)

            if (metaProperty)  {
                owner.propertyChanged(loadedObj)
                
                metaProperty.setProperty(delegate, arg)
            }
        }
        
        return loadedObj
    }
}

A Session object can be used to retrieve already persistent objects and to persist so-called transient objects. I like to start by implementing the load method which loads a persistent object from the storage connection of the current session factory. Of course, this is not an example for Groovy beginners but with a little knowledge of MOP and with some programming guidance it should not be a big thing to understand what is going on. At the end let’s define some test case which shows how both classes are actually used:

def storage = [:]

class Person {
    String name

    String toString() { name }

    static props = ['name']
}

storage[Person] = [:]
storage[Person][1 as Long] = new Person(name: 'Max Mustermann')
storage[Person][2 as Long] = new Person(name: 'Erika Mustermann')

def sessionFactory = new SessionFactory(storage)
def session = sessionFactory.newSession()

def person = session.load(Person, 1)

Interestingly, even without considering SQL, DB connection handling, threading issues etc. participants already get a feeling of several Hibernate gotchas beginners otherwise often struggle with:

  • the first level cache
  • the need for proxies or MOP modifications
  • Hibernate’s use of object snapshots
  • the IdentityMap pattern
  • repeatable read transaction isolation level
  • etc.

It is amazing how much can be explained by implementing some framework’s core functionality in about 5 minutes. The Session functionality gets than extended by flush, discard and save/delete functionality. If programmers have been through the process of implementing such a micro Hibernate example they often get a basic and fundamental understanding of how an orm framework could work and what the main challenging problems are. By keeping the class and method names in sync with the concrete Hibernate implementations participants learn the framework’s basic domain language.

GSamples – A Repository for Sharing Workshop Examples

The example mentioned above is available in a public github repository what I called GSamples [0], a collection of Groovy and Grails workshop examples. At the time of publishing this article it contains two micro framework examples, the other one is a simple dependency injection container. In addition, GSamples holds Groovy scripts dealing with Groovy Essentials and another one dealing with advanced Groovy topics like the Meta-Object Protocol and Closures. Feel free to extend, distribute or use it!

[0] GSamples – https://github.com/andresteingress/gsamples

GContracts 1.1.3 Released!

November 16, 2010 2 comments

I am proud to announce that GContracts 1.1.3 has just been released and is available now in the Central Maven repository [0] and at Github [1].

The release includes bug fixes and advanced support Spring bean classes annotated with GContracts’ annotations.

Concrete AssertionError types

In parallel with @Invariant, @Requires and @Ensures GContracts 1.1.3 provides separate exception types which all are descendants of AssertionError and I guess do not need further explanation:

ClassInvariantViolation, PreconditionViolation and PostconditionViolation.

Advanced Support for Spring Beans

One of the problems with GContracts is its lack of compatibility especially with Grails artifact classes, i.e. controllers, services, domain classes etc. This topic has been brought to me a few times and if you just want to apply let’s say class invariants on Spring bean classes the Spring application container’s initialization lifecycle can quickly become your enemy.

Let’s consider the following service:

@Invariant({ anotherService != null })
class MyService {

    def anotherService

    static transactional = true

    // ...
}

MyService is a simple Grails service class, annotated with a class invariant which ensures that the reference to anotherService will never be null, during the entire life-time of the MyService bean. Sadly enough, whenever the Spring application container tries to create a new instance of a MyService bean it fails because GContracts checks the class invariant directly after the object’s constructor has completed successfully. By that time, the Spring application container is not yet done with object construction.

From a theoretical perspective throwing a ClassInvariantViolation in this place is completely reasonable since an object’s class invariant needs to be fulfilled by the object instance during its entire life-time. But from a practical perspective, this is kind of pain. It means that you have to duplicate the class invariant to all methods in the service class, e.g.

// avoid invariant checks during construction
class MyService {

    def anotherService

    static transactional = true

    @Requires({ anotherService != null })
    def myServiceMethod()  {
        // ...
    }

    @Requires({ anotherService != null })
    def yetAnotherServiceMethod()  {
        // ...
    }
}

It is not that Design by Contract can not be applied here, it is that Spring simply does define its own rules for object creation and destruction, meaning that whenever an object is created with its constructor the initialization process is not done in the case of the Spring application container.

This circumstance confuses programmers who simply want to apply DbC on their bean classes without thinking about Spring initialization mechanisms and GContracts assertion injection.

GContracts 1.1.3 is the starting point for a bunch of features which are targeted to iron out such integration issues with Spring/Grails applications. Of course it might be questionable whether it makes sense to focus primarily on the Spring framework, but that has been a practical decision (GContracts is for Groovy, Grails uses Groovy, Grails uses Spring…). Furthermore, I tried to make the integration as light as possible without actually introducing a classpath dependency on Spring framework classes.

The first feature which comes with 1.1.3 is a solution for the problem described above: class invariant checks on Spring beans. Henceforth, GContracts will not make class invariant checks on Spring beans during constructor calls, but will create a synthetic @PostConstruct method which overtakes the class invariant check. Since GContracts has no mechanism to know that the given class is a Spring-managed class at runtime, programmers need to annotate their artifact classes with appropriate stereotype annotations [2], e.g. a service class with @Service, a controller with @Controller, etc.

@Service
@Invariant({ anotherService != null })
class MyService {

    def anotherService

    static transactional = true

    // ...
} 

The @Service stereotype annotation above triggers GContracts’ “Spring compliance mode”. Whenever the service bean above has been constructed by the Spring application container it will automatically check the class invariant.

The programmer clearly needs to make a compromise in this case, because the class invariant won’t be checked after a construction call anymore (e.g. in a test-case where the programmer simply creates an instance of this service class). But this is a consequence of outsourcing object creation to the application container.

Hint: GContracts supports the -ea/-da VM parameters [3] which you can use to deactivate assertion checking during application execution, whereas activating assertion checking only during test-cases.

Changes

ISSUE-25: better support for Spring component classes with class invariants
ISSUE-24: wrong class invariant placement with inherited invariant
ISSUE-23: @Requires on constructor with super statement causes compilation error
ISSUE-22: Separate Exception Types for Pre-, Postconditions and Class Invariant
ISSUE-17: remove direct reference to PowerAssertionError

[0] GContracts 1.1.3 in Central Maven Repository
[1] GContracts at Github
[2] Spring Stereotype Annotations and Classpath Scanning
[3] GContracts VM Parameter Support


Flattr this

Categories: gcontracts, groovy, releases

Happy Messaging with ActiveMQ and SI (Part 1)

October 14, 2010 1 comment


Flattr this
In one of my current projects we needed to set up a communication channel between two distinct Grails applications. One of them is the master application which runs an embedded ActiveMQ message broker [0]. The second one – the slave application – provides service APIs to the master application.

Since Grails heavily relies on Spring, we decided to use Spring Integration as messaging framework [1]. Spring Integration is a messaging framework which supports various Enterprise Application Integration Patterns [2], without being bound to any specific messaging protocol. Since our project team chose to use ActiveMQ we go with JMS as underlying messaging protocol in our project.

Setting up an embedded ActiveMQ message broker

ActiveMQ is a fully JMS 1.1 compliant messaging provider which is available under the Apache Software License. It has quite a bag of features, the most important ones for us where persistent messages support. Besides running ActiveMQ as a distinct server, one can choose to run ActiveMQ as an embedded server inside the application.

Configuring an embedded ActiveMQ broker using Grails’ Beans DSL is pretty straight-forward (once you get used to the Beans DSL of course):

xmlns amq:'http://activemq.apache.org/schema/core'

def brokerName = 'myEmbeddedBroker'

amq.'broker'(brokerName: brokerName, useJmx: true, persistent: false) {
  amq.'managementContext'  {
    amq.'managementContext'(connectorPort: 2011, jmxDomainName: 'embeddedBroker')
  }

  amq.'plugins'  {
    amq.'loggingBrokerPlugin'
  }

  amq.'transportConnectors'  {
    amq.'transportConnector'(name: 'openwire', uri: 'tcp://localhost:61616')
  }
}

The code above configures an embedded broker called myEmbeddedBroker which only persists messages in-memory (persist: false), exposes itself as JMX bean (useJmx: true) and configures a transport connector using Openwire over TCP.

In order to let the master application (which holds the configuration above) connect to its embedded message broker, we need to set up a connection factory:

connectionFactoryLocal(ActiveMQConnectionFactory)  {
  brokerURL = "vm://${brokerName}"
}

After all, we will define two message queues one for outgoing API requests to the slave application and one for incoming responses:

"requestQueue"(org.apache.activemq.command.ActiveMQQueue, "QUEUE.REQUEST")
"responseQueue"(org.apache.activemq.command.ActiveMQQueue, "QUEUE.RESPONSE")

Spring Integration comes into play

So far we have set up an embedded message broker which could be used for plain JMS API message exchange. In our project we decided to go with Spring Integration because it already implements several EAI patterns (e.g. router, gateway, etc.) and abstract from the underlying messaging protocol.

A reference manual on Spring Integration can be found at [3], but let me give you a short introduction. Spring Integration (SI) is a messaging framework which implements patterns found in the book Enterprise Application Integration Patterns [4]. That is, SI is all about messages and message exchange. To exchange a message from point A to point B there needs to be a channel between A and B. Besides messages, channels are the second most important domain entity in SI.

Channels are injected into your application components just like any other Spring bean. The basic MessageChannel interface is pretty rudimentary:

public interface MessageChannel {

	boolean send(Message<?> message);
	boolean send(Message<?> message, long timeout);
}

The use-case in our project was to automatically create a message and send it to some preconfigured channel whenever the programmers chooses to call a service API method:


def someApi

def doSomething()  {   
   someApi.executeRemotely('first param', 'second param') // this should trigger message creation and sending/receiving
}

A call to executeRemotely should automatically create a message object from the input parameters and send it to some sort of API request channel.

Luckily, SI provides the concept of gateways which solve that particular problem. At runtime, a gateway is a proxy object for a particular interface which, on a method call, creates a message object and sends it via some preconfigured channel. Like channels, gateways are Spring beans and can therefore be configured via the Beans DSL:

xmlns integration:'http://www.springframework.org/schema/integration'
xmlns jms:'http://www.springframework.org/schema/integration/jms'

integration.'channel'(id: 'apiChannelRequest')
integration.'channel'(id: 'apiChannelResponse')

integration.'gateway'(id: 'someApi', 'service-interface': org.ast.SomeApi.class.getName(), 'default-request-channel': 'apiChannelRequest', 'default-reply-channel': 'apiChannelResponse')  {

  integration.'method'(name: 'executeRemotely')  {
    integration.'header'(name: 'API_METHOD_NAME', value: 'executeRemotely')
  }
}

As you can see from the configuration snippet above, the gateway has a request/reply channel configured since gateways are synchronous (in SI 2.0 there is asynchronous gateway support) and bidirectional. The SomeApi interface uses SI annotations for further message configuration:

interface SomeApi {
    Boolean executeRemotely(final @Header("HEADER_NAME") String param1, final String param2)
}

From the gateway’s view the interface above means: whenever executeRemotely is called, put param1 into a message header with name HEADER_NAME and put the second parameter into the message’s payload. Maybe you noticed the API_METHOD_NAME parameter in the gateway configuration above – that was a message header too. We needed to manually inject a unique method identification token (in our case the method name only was enough) in order to call the correct method on the slave application side.

Configuring JMS messaging

So far we’ve set up an environment with an embedded ActiveMQ message broker and two ActiveMQ message queues. Now we need to configure the link between the SI channels configured in the last section and those JMS queues. Since gateways are bidirectional, SI needs to store some reply channel information whenever instantiating an API request. This is automatically done via the gateway implementation. If we would run inside a SI environment only we wouldn’t need to care about this fact. In our case, we chose to use gatways to communicate between a master and a slave application which are in production deployed on separate server instances.

In SI, a JMSOutboundGateway can be used for those JMS request/reply scenarios. It is the clue between SI channels and out ActiveMQ JMS queues:

jms.'outbound-gateway'(id: "jmsGateway", 'connection-factory': 'pooledJmsConnectionFactoryLocal', 'request-destination': "requestQueue", 'request-channel': "apiChannelRequest", 'reply-destination': "responseQueue",'reply-channel': 'apiChannelResponse')

In the slave application, there needs to be an inverse configuration using a JMS inbound gateway:

jms.'inbound-gateway'(id: 'jmsInbound', 'connection-factory': 'pooledJmsConnectionFactoryRemote', 'request-destination-name': 'QUEUE.REQUEST', 'request-channel': 'incomingRequest', 'reply-destination-name': 'QUEUE.RESPONSE')

The configuration snippet inside the slave application simply routes incoming messages to the incomingRequests channel. Notice that no reply channel has been specified in order to keep the reply channel which has been added by the master application in the message.

In the next part of this article series we’ll have a closer look at the slave application and how it is configured to invoke methods an Grails service beans.

[0] ActiveMQ Message Broker
[1] Spring Integration
[2] Enterprise Application Integration
[3] Spring Integration – Reference Manual
[4] Amazon: Enterprise Integration Patterns

Towards Null-Safe Groovy

August 6, 2010 5 comments

Currently I am implementing some experimental features from which some of them hopefully make it into GContracts next major release.

One of the issue I’ve been playing with is support for null-safe aka void-safe code. The main idea is to let GContracts provide compile-time checks for null-safe code parts. Letting it be ‘code parts’ only is the first restriction which we will have to introduce. We all know null is evil, but: null is not the new goto.

There are use-cases where assigning null to a variable might still be appropriate. Think of a double-linked list data-structure, where null could be needed to mark the first previous and the last next reference. However, the use of null more often leads to errors whenever a method call is executed on a variable which has been assigned to null.

In order to provide compile-time checking of instance variables, local variables and parameters I added a new annotation to GContracts annotation package: @NullSafe:

@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER})
public @interface NullSafe {}

In addition, I added the NullSafeASTTransformation which is supposed to do the compile-time checking stuff. To do that, we need a set of rules which enforce null-safety for variables annotated with @NullSafe:

Local Variables, Parameters

Whenever a local variable or a parameter is marked as null-safe, only assignments to

  • constants (except null)
  • other null-safe variables

are allowed. The AST transformation done so far checks for local variables marked with @NullSafe and takes care of valid assignments. Whenever an assignment does not conform to the rules stated above, a compile-time error is thrown. E.g. the following source code snippet fails during compilation:

package tests

import org.gcontracts.annotations.*

class A {

  def some_operation(@NullSafe def param1)  {
     def bla = "test"
     param1 = bla
  }
}

BUG! exception in phase 'semantic analysis' in source unit 'script1281039992077748355994.groovy' param1 must be assigned to NullSafe variables!
	at org.gcontracts.ast.visitor.NullSafeVisitor.visitBinaryExpression(NullSafeVisitor.java:62)

The same applies for local variables:

package tests

import org.gcontracts.annotations.*

class A {
  
  def some_operation()  {
     @NullSafe def bla = "test"
     def otherBla = "test2"

     bla = otherBla
  }
}

package tests

import org.gcontracts.annotations.*

class A {

  def some_operation()  {
     @NullSafe def bla = "test"
     def otherBla = "test2"

     bla = otherBla
  }
}

BUG! exception in phase 'semantic analysis' in source unit 'script12810425282531878012710.groovy' bla must be assigned to NullSafe variables!
	at org.gcontracts.ast.visitor.NullSafeVisitor.visitBinaryExpression(NullSafeVisitor.java:31)

Notice, the last code snippet won’t compile according to the rules defined above, as an assignment of a not-null safe variable to a null-safe variable is not valid. If we want to pass compile-time checks, we simply had to annotate otherBla as @NullSafe.

Instance Variables, Fields

Instance variables need to be assigned to a non-null value whenever the construction of the according object is finished, thus the object complies to the explicit class invariant. Whenever a method is called an assignment to null would trigger a compile-time failure:

package tests

import org.gcontracts.annotations.*

class A {

  @NullSafe String myProp

  def A()  {
    myProp = "test"
  }

  def some_op() { myProp = null }
}

BUG! exception in phase 'semantic analysis' in source unit 'script12810407257201736681757.groovy' myProp must be assigned to NullSafe variables!
	at org.gcontracts.ast.visitor.NullSafeVisitor.visitBinaryExpression(NullSafeVisitor.java:59)

Assignments from Non-Void Method Calls

In fact, this is only half the story. Whenever a variable is assigned to the return value of a method, we have no simple way to check at compile-time whether that method returns null. A possible solution for solving this scenario in terms of keeping code null-safe is to specify variable default values via annotation closures (btw: here starts the part I actually did not implement by now, so don’t get confused with the annotation class above):

package tests

import org.gcontracts.annotations.*

class A {

  def some_op() {
      @NullSafe({ "empty" }) def i = "aloha"

      i = some_function()
      // at this point, i will be "empty" not null!
  }

  def some_function() { return null }
}

The default value is assigned only when an assignment to the null-safe variable would result in a null reference.

Annotation closures would provide great flexibility to define arbitrary complex initialization code:


class Address {
    
    @NullSafe({ new City(name: 'dummy') }) City city
    // ... 
}

An alternative approach would be to annotate methods as @NullSafe, which would avoid calling methods not being marked with @NullSafe in null-safe assignments.

What’s next?

That’s it with my experimental @NullSafe feature. I know that this is a naive first approach, but at least its a starting point and I would really appreciate your feedback on this topic either in this article’s comment section or at the Groovy-Dev mailing list [1].

It would be very important to know whether this method works or if it is not applicable in real-world projects from your point of view. One issue is, that although GContracts uses AST transformations, it is not an integral part of the programming language, which could result in e.g. meta- or reflection-calls which make null-safe variables null etc. Just imagine Hibernate injecting null values into @NullSave instance variables – levering null-safe variables immediately . In fact, this is the point I am not completely clear about – does it make sense to introduce such a feature at library/AST transformation level?

[0] GContracts – Project Home Page
[1] Groovy-Dev mailing list @ nabble

Categories: groovy, patterns

GContracts in JavaMagazin 9.2010 (German)

August 5, 2010 1 comment

In der August Ausgabe 9.2010 des JavaMagazins [0] findet sich ein ausf├╝hrlicher Artikel ├╝ber GContracts [1] – eine Groovy Erweiterung f├╝r Design by Contract(tm).

Kaufen und gl├╝cklich(er) werden!

PS: Wenn du mehr ├╝ber GContracts und Programming by Contract wissen m├Âchtest, wirf einen Blick auf den Wiki Artikel “An Introduction to Programming by Contract” [2].

[0] JavaMagazin – August Ausgabe 2010
[1] GContracts @ Github
[2] Github Wiki: An Introduction to Programming by Contract

Categories: gcontracts