Learnitweb

Setting Up the Protobuf Playground Project

1. Creating the Maven Project

You can use any IDE you like, but in this tutorial we will assume IntelliJ IDEA. Create a new Maven project with the name:

gRPC-playground

The word playground is intentional. It reminds us that:

  • This project is for learning and experimentation, not for production.
  • We will try many small examples and throw them away.
  • We will focus on understanding features, not on building a polished application.

Use:

  • Language: Java
  • Build tool: Maven
  • Java version: Java 21 or Java 17 (it does not matter, because we are not using any version-specific features).

Once the project is created, let Maven finish importing and indexing.

2. Installing the Protobuf IntelliJ Plugin (Optional but Helpful)

In IntelliJ, go to:

Settings → Plugins → Search for “Proto” or “Protocol Buffers”

You will find a Protobuf-related plugin. Install it.

This plugin is not mandatory. If you are not using IntelliJ, or if you do not see this plugin, it is perfectly fine. The project will still work. The plugin mainly gives you:

  • Syntax highlighting for .proto files
  • Some basic IDE assistance

It does not affect the build or runtime behavior.

3. Adding Dependencies to pom.xml

Conceptually, the dependencies fall into a few groups:

  • gRPC and Protobuf dependencies, which we will slowly understand as we progress through the course.
  • Java 9+ compatibility dependencies, because some older annotations and APIs were removed from the JDK after Java 8.
  • Logback, so that we do not rely on System.out.println and can use proper logging.
  • Jackson, which we will use only for a performance comparison test between JSON and Protobuf.
  • JUnit, for writing and running tests.

In addition to dependencies, we also configure:

  • Maven Compiler Plugin, to control the Java version.
  • Maven Surefire Plugin, so we can run tests from the command line.
  • Protobuf Maven Plugin, which is the most important part for this section.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.learnitweb</groupId>
    <artifactId>grpc-playground</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <grpc.version>1.72.0</grpc.version>
        <protoc.version>3.25.5</protoc.version>
        <logback.version>1.5.12</logback.version>
        <jackson.version>2.18.1</jackson.version>
        <tomcat.annotations.version>6.0.53</tomcat.annotations.version>
        <os.maven.plugin.version>1.7.1</os.maven.plugin.version>
        <protobuf.maven.plugin.version>0.6.1</protobuf.maven.plugin.version>
        <junit.version>5.11.3</junit.version>
        <surefire.version>3.5.2</surefire.version>
    </properties>

    <dependencies>
        <!-- grpc / proto related dependencies -->
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <!-- necessary for Java 9+ -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>annotations-api</artifactId>
            <version>${tomcat.annotations.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- logback -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>
        <!-- Jackson lib for a performance test -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <!-- junit -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>${os.maven.plugin.version}</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>21</source>
                    <target>21</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${surefire.version}</version>
            </plugin>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>${protobuf.maven.plugin.version}</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                    <protoSourceRoot>
                        ${basedir}/src/main/proto/
                    </protoSourceRoot>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

4. Why We Need the Protobuf Maven Plugin

This part is extremely important conceptually.

A .proto file is:

  • Language-neutral
  • Platform-neutral

But the JVM is not platform-neutral in terms of native binaries. To convert a .proto file into Java source code, we need a tool called:

protoc (the Protobuf compiler)

This tool is:

  • Different for Windows, Mac, and Linux
  • Different for different CPU architectures

The Protobuf Maven plugin solves this problem for us by:

  • Detecting your operating system and architecture
  • Downloading the correct protoc binary automatically
  • Running it during the Maven build
  • Generating Java source files from your .proto files

So you never have to manually install or manage protoc.

Even if this feels a little abstract right now, do not worry. Once you see the generated code, this will become very clear.

5. Adding Logback Configuration

Because we want to use logging instead of System.out.println, copy the provided logback.xml file and place it under:

src/main/resources/logback.xml

Whenever you change pom.xml, always remember to:

Reload Maven Project in IntelliJ

Sometimes IntelliJ shows red errors simply because it has not yet downloaded the dependencies. A Maven reload usually fixes that.

6. Creating Your First .proto File

Now that the project is set up, let us create our very first Protobuf file.

6.1 Where to Put .proto Files in a Maven Project

The recommended convention is:

src/main/proto

Just like:

src/main/java

If the proto directory does not exist, create it.

Your project structure should now look like this:

src
 └── main
     ├── java
     ├── resources
     └── proto

The Protobuf Maven plugin is already configured to look in this directory.

6.2 Creating person.proto

Under src/main/proto, create a file:

person.proto

Add the following content:

syntax = "proto3";

message Person {
  string name = 1;
  int32 age = 2;
}

At this point, you might not fully understand:

  • What proto3 means
  • What = 1 and = 2 mean
  • Why types look like string and int32

That is completely fine. Right now, the goal is to see the workflow, not to master the syntax.

7. Proto2 vs Proto3 and the syntax Line

If you do not write:

syntax = "proto3";

Your IDE will complain and say something like:

Fields must have labels: optional, required, or repeated

That is because, by default, it assumes Proto2.

Proto2 had:

  • required
  • optional
  • repeated

This turned out to cause many API compatibility and evolution problems in real systems. Because of this, Proto3 was introduced, which simplifies many rules and removes several dangerous concepts.

Proto1 was never public. It was internal to Google and can be ignored.

So, for all modern systems:

Always use Proto3

8. Generating Java Code Using Maven

Now go to the project root and run:

mvn clean compile

If everything is set up correctly, the build should succeed.

Now look inside:

target/generated-sources/protobuf/java

You will see something like:

PersonOuterClass.java

This file is:

  • Automatically generated
  • Not meant to be edited by hand
  • The result of running protoc on your .proto file

If you open it, you will see many methods like getName(), getAge(), builders, parsers, and many other things. It looks complex, but you are not supposed to write or maintain this file yourself. You only use it.

9. Adding a Java Package to the Generated Code

Right now, the generated Java file has no package. That is because:

A .proto file is language-neutral.

If you want Java-specific behavior, you must specify it explicitly.

Modify person.proto like this:

syntax = "proto3";

option java_package = "com.learnitweb.models";

message Person {
  string name = 1;
  int32 age = 2;
}

Now run:

mvn clean compile

You will see that the generated file is now under:

com.learnitweb.models

Protobuf also supports:

  • go_package
  • Other language-specific options

Because each language has its own conventions.

10. Fixing IntelliJ Not Recognizing Generated Sources

Sometimes Maven works perfectly, but IntelliJ shows errors and cannot import the generated classes. This is an IDE issue, not a build issue. It is very similar to how Lombok behaves.

To fix this:

  1. Go to File → Project Structure → Modules
  2. Find: target/generated-sources/protobuf/java
  3. Right-click it and choose: Mark Directory As → Generated Sources Root

Now IntelliJ will immediately recognize the classes.

If it still behaves strangely, use:

Invalidate Caches and Restart

11. Writing the First Demo Program

package org.learnitweb;

import com.learnitweb.models.PersonOuterClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
    private static final Logger logger = LoggerFactory.getLogger(Main.class);

    public static void main(String[] args) {

        var person = PersonOuterClass.Person
                                    .newBuilder()
                                    .setName("sam")
                                    .setAge(31)
                                    .build();

        logger.info("{}", person);
    }
}

Output

SLF4J(I): Connected with provider of type [ch.qos.logback.classic.spi.LogbackServiceProvider]
00:53:36.781 INFO  [           main] org.learnitweb.Main            : name: "sam"
age: 31