SkillAgentSearch skills...

Grains

The "Grains" Java framework generates thread-safe, versionable objects.

Install / Use

/learn @cambecc/Grains
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Grains

... is a small Java framework that helps you make immutable, thread-safe, versionable objects. Spend less time on boring boilerplate code and more time solving problems.

  1. Create an interface with getters:

    @GrainSchema
    public interface Order {
    
        String getProduct();
    
        int getQuantity();
    }
    
  2. Run the Grains Maven plugin.

  3. Use the generated Factory, Builder, and Grain classes:

    OrderBuilder builder = OrderFactory.newBuilder();
    builder.setProduct("apples");
    builder.setQuantity(13);
    OrderGrain order = builder.build();
        
    System.out.println(order instanceof Order);  // prints: true
    System.out.println(order.getProduct());      // prints: apples
        
    System.out.println(order instanceof Map);    // prints: true
    System.out.println(order.get("quantity"));   // prints: 13
    System.out.println(order.entrySet());        // prints: [product=apples, quantity=13]
        
    OrderGrain changed = order.withQuantity(9);  // immutable :)
    System.out.println(changed);                 // prints: {product=apples, quantity=9}
    System.out.println(order);                   // prints: {product=apples, quantity=13}
        
    changed = changed.with("RMA", "9928");       // extensible and versionable :)
    System.out.println(changed);                 // prints: {product=apples, quantity=9, RMA=9928}
    

Serialization

Jackson serialization to JSON, Smile, YAML, etc. (with the grains-jackson library):

    ObjectMapper mapper = JacksonTools.newGrainsObjectMapper();

    String json = mapper.writeValueAsString(order);
    OrderGrain restored = mapper.readValue(json, OrderGrain.class);

    System.out.println(json);                    // prints: {"product":"apples","quantity":13}
    System.out.println(restored.equals(order));  // prints: true

Kryo serialization (with the grains-kryo library):

    Kryo kryo = KryoTools.newGrainsKryo();

    Output output = new Output(1024);
    kryo.writeClassAndObject(output, order);
    output.close();

    Input input = new Input(output.getBuffer(), 0, output.total());
    Object thawed = kryo.readClassAndObject(input);

    System.out.println(thawed.equals(order));  // prints: true

MessagePack serialization (with the grains-msgpack library):

    MessagePack msgpack = MessagePackTools.newGrainsMessagePack();

    byte[] data = msgpack.write(order);
    OrderGrain unpacked = msgpack.read(data, OrderGrain.class);

    System.out.println(unpacked.equals(order));  // prints: true

Native support for Java serialization:

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    new ObjectOutputStream(out).writeObject(order);

    ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
    Object read = new ObjectInputStream(in).readObject();

    System.out.println(read.equals(order));  // prints: true

Setup

The Grains framework (published on Maven Central) requires Java 7 or greater, and Maven 2.2.1 or greater.

  1. Create a new package to contain your object model, for example: com.acme.model

  2. Configure your POM to pre-compile this package during the generate-sources phase:

    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
            <source>1.7</source>
            <target>1.7</target>
        </configuration>
        <executions>
            <execution>
                <phase>generate-sources</phase>
                <goals><goal>compile</goal></goals>
                <configuration>
                    <includes>
                        <include>com/acme/model/**</include>
                    </includes>
                </configuration>
            </execution>
        </executions>
    </plugin>
    
  3. Configure the grains-plugin to run during the generate-sources phase:

    <plugin>
        <groupId>net.nullschool</groupId>
        <artifactId>grains-plugin</artifactId>
        <version>0.8.0</version>
        <executions>
            <execution>
                <phase>generate-sources</phase>
                <goals><goal>generate</goal></goals>
            </execution>
        </executions>
    </plugin>
    
  4. Add a dependency on grains-core:

    <dependency>
        <groupId>net.nullschool</groupId>
        <artifactId>grains-core</artifactId>
        <version>0.8.0</version>
    </dependency>
    
  5. (optional) Add a dependency on the serialization library of your choice, such as:

    <dependency>
        <groupId>net.nullschool</groupId>
        <artifactId>grains-jackson</artifactId>
        <version>0.8.0</version>
    </dependency>
    

Done! Any interface in com.acme.model annotated with @GrainSchema will have a grain implementation generated when mvn compile is invoked. By default, all generated sources appear in the target/generated-sources/grains/com/acme/model directory.

See the wiki for more details.

Acknowledgements

Clojure's defrecord macro provided the main inspiration for grains.

Grains uses Semantic Versioning

View on GitHub
GitHub Stars15
CategoryDevelopment
Updated4mo ago
Forks4

Languages

Java

Security Score

72/100

Audited on Nov 27, 2025

No findings