Quick start guide

Edit
Update time: 2019-07-25

In this document, we will create a Spring Boot project and introduce the basic dependencies of SOFABoot as well as its Health Check expansion capability, to demonstrate how to get started quickly with SOFABoot.

Environment Preparation

To use SOFABoot, we need to prepare the basic environment first. SOFABoot depends on the following environment: - JDK7 or JDK8 - Needs to be compiled with Apache Maven 3.2.5 or above

Create Project

SOFABoot is directly built on Spring Boot, so it can be generated by Spring Boot Generators. In this document, we need to add a web dependency for final view of its effect in the browser.

Add SOFABoot dependencies

When creating a Spring Boot project, we need to import SOFABoot dependencies. First, extract the ‘zip’ package of the project generated above and modify the ‘pom.xml’ file, or the maven project configuration file. Replace

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>${spring.boot.version}</version>
    <relativePath/> 
</parent>

as:

<parent>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>sofaboot-dependencies</artifactId>
    <version>${sofa.boot.version}</version>
</parent>

Here, ${sofa.boot.version} denotes the SOFABoot version (please refer to release note). Then, add a SOFABoot dependency of Health Check extension and Spring Boot Web Starter.

<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>healthcheck-sofa-boot-starter</artifactId>
</dependency>

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Finally, configure parameters commonly used in the SOFABoot project in the application.properties file. The spring.application.name parameter is required to name the current application; the logging path specifies the output directory for logging information.

# Application Name
spring.application.name=SOFABoot Demo
# logging path
logging.path=./logs

Advice to refer to the SOFABoot Module document before learn this demo.

Run it

We can import the project into IDE and run the ‘main’ method in the generated project (generally in the XXXApplication class) to start the application, or we can execute the mvn spring-boot:run command under the project’s root directory, which will print the startup logging in the console:

2018-04-05 21:36:26.572  INFO ---- Initializing ProtocolHandler ["http-nio-8080"]
2018-04-05 21:36:26.587  INFO ---- Starting ProtocolHandler [http-nio-8080]
2018-04-05 21:36:26.608  INFO ---- Using a shared selector for servlet write/read
2018-04-05 21:36:26.659  INFO ---- Tomcat started on port(s): 8080 (http)

We can browse http://localhost:8080/sofaboot/versions to view the version summary generated by Maven plugin in SOFABoot. The result is like the following:

[
  {
    GroupId: "com.alipay.sofa",
    Doc-Url: "https://github.com/sofastack/sofa-boot",
    ArtifactId: "infra-sofa-boot-starter",
    Built-Time: "2018-04-05T20:55:26+0800",
    Commit-Time: "2018-04-05T20:54:26+0800",
    Commit-Id: "049bf890bb468aafe6a3e07b77df45c831076996",
    Version: "2.4.4"
  }
]

Note: In SOFABoot 3.x, the endpoint path has been changed from sofaboot/versions to actuator/versions

We can browse http://localhost:8080/health/readiness to check the status of the Readiness Check. The result is like the following:

{
  status: "UP",
  sofaBootComponentHealthCheckInfo: {
    status: "UP"
  },
  springContextHealthCheckInfo: {
    status: "UP"
  },
  DiskSpace: {
    status: "UP",
    total: 250140434432,
    free: 22845308928,
    threshold: 10485760
  }
}

Note: In SOFABoot 3.x, the endpoint path has been changed from health/readiness to actuator/readiness.

status: "UP" indicates that Readiness Check is in good condition. We can browse http://localhost:8080/health to get the application’s running state (it may change over time).

Viewing logs

In the above application.properties file, the logging directory we have configured is ./logs, namely the application root directory (which can be configured according to our needs), where log files may have the following structure:

./logs
├── health-check
│   ├── sofaboot-common-default.log
│   └── sofaboot-common-error.log
├── infra
│   ├── common-default.log
│   └── common-error.log
└── spring.log

If the application fails to start or the Health Check fails, we can find the error cause in the corresponding log file (sometimes we need to pay attention to the common-error.log file)

Testing

As we know, SpringBoot provides the SpringRunner integrated with JUnit 4 for developers to write integration test cases. In SOFABoot, we can still use the native SpringRunner, but we recommend that you use SOFABoot’s SofaBootRunner and SofaJUnit4Runner to write integration tests and unit tests; beyond that, the application requires additional import of the following Starter:

<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>test-sofa-boot-starter</artifactId>
</dependency>

Note that if you need to use SOFABoot’s class Isolation, import the above dependencies, and run testing with SofaBootRunner and SofaJUnit4Runner.

Modular Development

This section will focus on how we can carry out modular development under the SOFABoot environment, you can find this document’s sample code under the project. The project is composed of four modules:

.
│
├── service-facade 
│ 
├── service-provider
│ 
├── service-consumer
│ 
└── sofa-boot-run

The functions of each module are:

  • service-facade: an API package that tells you how to publish and reference JVM services.
  • service-provider: demonstrate how to publish JVM services in XML, annotation, or API mode.
  • service-consumer: demonstrate how to reference JVM services in XML, annotation, or API mode.
  • sofa-boot-run: start the SOFA Boot application that contains the SOFABoot module.

Defining Service API

The service-facade module contains APIs that demonstrate how to publish and reference JVM services:

public interface SampleJvmService {
    String message();
}

Publishing JVM services

Service-provider is a SOFABoot module used to demonstrate how to publish JVM services in XML, annotation, or API mode.

Defining SOFABoot Module

To define a SOFABoot module, add the ‘sofa-module.properties’ file to the service-provider module:

Module-Name=com.alipay.sofa.service-provider

Publishing Services in XML Mode

Implement the SampleJvmService interface:

public class SampleJvmServiceImpl implements SampleJvmService {
    private String message;

    @Override
    public String message() {
        System.out.println(message);
        return message;
    }

    // getters and setters
}

Add the META-INF/spring/service-provide.xml file, and publish SampleJvmServiceImpl as a JVM service:

<bean id="sampleJvmService" class="com.alipay.sofa.isle.sample.SampleJvmServiceImpl">
    <property name="message" value="Hello, jvm service xml implementation."/>
</bean>

<sofa:service ref="sampleJvmService" interface="com.alipay.sofa.isle.sample.SampleJvmService">
    <sofa:binding.jvm/>
</sofa:service>

Publishing Service in Annotation Mode

Implement the SampleJvmService interface and add the @SofaService annotation:

@SofaService(uniqueId = "annotationImpl")
public class SampleJvmServiceAnnotationImpl implements SampleJvmService {
    @Override
    public String message() {
        String message = "Hello, jvm service annotation implementation.";
        System.out.println(message);
        return message;
    }
}

To differentiate the JVM services published in XML mode, we need to add a ‘uniqueId’ attribute to the annotation.

Configure the SampleJvmServiceAnnotationImpl class as a Spring Bean to ensure that the @SofaService annotation takes effect:

<bean id="sampleJvmServiceAnnotation" class="com.alipay.sofa.isle.sample.SampleJvmServiceAnnotationImpl"/>

Publishing Services in API Mode

Create a class named PublishServiceWithClient to demonstrate how to publish a service in API mode:

public class PublishServiceWithClient implements ClientFactoryAware {
    private ClientFactory clientFactory;

    public void init() {
        ServiceClient serviceClient = clientFactory.getClient(ServiceClient.class);
        ServiceParam serviceParam = new ServiceParam();
        serviceParam.setInstance(new SampleJvmServiceImpl(
            "Hello, jvm service service client implementation."));
        serviceParam.setInterfaceType(SampleService.class);
        serviceParam.setUniqueId("serviceClientImpl");
        serviceClient.service(serviceParam);
    }

    @Override
    public void setClientFactory(ClientFactory clientFactory) {
        this.clientFactory = clientFactory;
    }
}

Configure the PublishServiceWithClient class as Spring Bean, and set ‘init-method’ in the class so that the service will be published when Spring refreshes it:

<bean id="publishServiceWithClient" class="com.alipay.sofa.isle.sample.PublishServiceWithClient" init-method="init"/>

Referencing JVM Services

Service-consumer is a SOFABoot module used to demonstrate how to reference JVM services in XML, annotation, or API mode.

Defining SOFABoot Module

Add the ‘sofa-module.properties’ file for the service-consumer module, and define it as a SOFABoot module:

Module-Name=com.alipay.sofa.service-consumer
Require-Module=com.alipay.sofa.service-provider

We need to specify the Require-Module in the ‘sofa-module.properties’ file to ensure that the service-provider module is refreshed before the service-consumer module.

Referencing Service in XML Mode

Add the META-INF/spring/service-consumer.xml file to reference the services published by the service-provider module:

<sofa:reference id="sampleJvmService" interface="com.alipay.sofa.isle.sample.SampleJvmService">
    <sofa:binding.jvm/>
</sofa:service>

Referencing Service in Annotation Mode

Define the JvmServiceConsumer class, and add the @SofaReference annotation to its sampleJvmServiceAnnotationImpl property:

public class JvmServiceConsumer implements ClientFactoryAware {
    @SofaReference(uniqueId = "annotationImpl")
    private SampleJvmService sampleJvmServiceAnnotationImpl;
}

Configure the JvmServiceConsumer as a Spring Bean to ensure that the @SofaReference annotation takes effect:

<bean id="consumer" class="com.alipay.sofa.isle.sample.JvmServiceConsumer" init-method="init" />

Referencing Service in API Mode

The JvmServiceConsumer class implements the ClientFactoryAware interface and references the JVM services in its init method:

public class JvmServiceConsumer implements ClientFactoryAware {
    private ClientFactory    clientFactory;

    public void init() {
        ReferenceClient referenceClient = clientFactory.getClient(ReferenceClient.class);
        ReferenceParam<SampleJvmService> referenceParam = new ReferenceParam<>();
        referenceParam.setInterfaceType(SampleJvmService.class);
        referenceParam.setUniqueId("serviceClientImpl");
        SampleJvmService sampleJvmServiceClientImpl = referenceClient.reference(referenceParam);
        sampleJvmServiceClientImpl.message();
    }

    @Override
    public void setClientFactory(ClientFactory clientFactory) {
        this.clientFactory = clientFactory;
    }
}

Starting SOFABoot Application

Configure the module parent as SOFABoot:

<parent>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>sofaboot-dependencies</artifactId>
    <version>${sofa.boot.version}</version>
</parent>

Add the isle-sofa-boot-starter, service-provider, and service-consumer dependencies to the module:

<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>isle-sofa-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>service-provider</artifactId>
</dependency>
<dependency>
    <groupId>com.alipay.sofa</groupId>
    <artifactId>service-consumer</artifactId>
</dependency>

Run the ApplicationRun class, and the console will display the following:

Hello, jvm service xml implementation.
Hello, jvm service annotation implementation.
Hello, jvm service service client implementation.

Writing Test Cases

SOFABoot’s modular test method is consistent with that of SpringBoot. We only need to add the @SpringBootTest and @RunWith (SpringRunner.class) annotations into the test cases. We can also use the @SofaReference annotation to test the services published by the SOFABoot module:

@SpringBootTest
@RunWith(SpringRunner.class)
public class SofaBootWithModulesTest {
    @SofaReference
    private SampleJvmService sampleJvmService;

    @SofaReference(uniqueId = "annotationImpl")
    private SampleJvmService sampleJvmServiceAnnotationImpl;

    @SofaReference(uniqueId = "serviceClientImpl")
    private SampleJvmService sampleJvmServiceClientImpl;

    @Test
    public void test() {
        Assert.assertEquals("Hello, jvm service xml implementation.", sampleJvmService.message());
        Assert.assertEquals("Hello, jvm service annotation implementation.",
            sampleJvmServiceAnnotationImpl.message());
        Assert.assertEquals("Hello, jvm service service client implementation.",
            sampleJvmServiceClientImpl.message());
    }
}