Skip to content

Commit 8a11ed9

Browse files
committed
Adding content to README. Enhancing the SimpleClient complete example to remove while/sleep loop
1 parent 108bfce commit 8a11ed9

File tree

2 files changed

+260
-10
lines changed

2 files changed

+260
-10
lines changed

README.md

Lines changed: 259 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,259 @@
1-
# openziti-spring-boot
2-
Example of hosting a dark service using OpenZiti and Spring Boot
1+
# Hosting an OpenZiti service using Spring Boot
2+
3+
## What you will need to get started
4+
* About 30 minutes
5+
* A favorite text editor or IDE
6+
* JDK 11 or later
7+
* Access to a Linux environment with Bash
8+
* A VM or WSL2 works for Windows users
9+
10+
## Get the code
11+
The code can be downloaded from [here](https://github.com/netfoundry/openziti-spring-boot/releases) or clone it using Git:
12+
```shell
13+
git clone https://github.com/netfoundry/openziti-spring-boot
14+
```
15+
16+
Like most Spring guides, you can start from scratch and complete each step, or you can bypass basic
17+
setup steps that are already familiar to you. Either way, you end up with working code.
18+
19+
# Create the test OpenZiti network
20+
This example will use a very simple OpenZiti network.
21+
22+
<p align="center">
23+
<img id="exampleNetworkImage" src="DemoNetwork.png" alt="Example OpenZiti Network" width="300"/>
24+
</p>
25+
26+
It isn't important right now to understand all of components of the OpenZiti network. The important things you need to know are:
27+
1. The controller manages the network. It is responsible for configuration, authentication, and authorization of components that connect to the OpenZiti network.
28+
2. The router delivers traffic from the client to the server and back again.
29+
30+
Want to know more about OpenZiti? Head over to https://openziti.github.io/ziti/overview.html#overview-of-a-ziti-network.
31+
32+
Let's get into it and create the test network!
33+
34+
## Get the OpenZiti quickstart shell extensions
35+
OpenZiti provides a set of shell functions that bootstrap the OpenZiti client and testing network. As with any script, it is a good idea to download it first
36+
and look it over before adding it to your shell.
37+
38+
Leave this terminal open, you'll need it to configure your network too.
39+
40+
```shell
41+
# Pull the shell extensions
42+
wget -q https://raw.githubusercontent.com/openziti/ziti/release-next/quickstart/docker/image/ziti-cli-functions.sh
43+
44+
# Source the shell extensions
45+
. ziti-cli-functions.sh
46+
47+
# Pull the latest Ziti CLI and put it on your shell's classpath
48+
getLatestZiti yes
49+
```
50+
51+
## Start the OpenZiti network
52+
The shell script you just downloaded includes a few functions to initialize a network.
53+
54+
To start the OpenZiti network overlay, run this in the terminal you used to download the client:
55+
```shell
56+
expressInstall
57+
startZitiController
58+
waitForController
59+
startExpressEdgeRouter
60+
```
61+
* **expressInstall**: Creates cryptographic material and configuration files required to run an OpenZiti network.
62+
* **startZitiController**: Starts the network controller.
63+
* **startExpressEdgerouter**: Starts the edge router.
64+
65+
## Log into the new network
66+
The OpenZiti network is now up and running. The next step is to log into the controller and
67+
establish an administrative session that we will use to configure the example services and identities.
68+
`ziti-cli-functions` has a function to do this:
69+
```shell
70+
zitiLogin
71+
```
72+
73+
## Configure the OpenZiti test network
74+
We'll use a script to configure the OpenZiti network.
75+
76+
The code for the example contains a network directory.
77+
To configure the network, run this in the same terminal you used to start the OpenZiti network:
78+
```shell
79+
./express-network-config.sh
80+
```
81+
82+
If the script errors with a lot of `ziti: command not found` then you can re-run this first to put
83+
ziti back in your terminal path:
84+
```shell
85+
getLatestZiti yes
86+
```
87+
88+
The script will write out the two identity files (client.json and private-service.json) that you will need for the Java code.
89+
90+
This repository includes a file called [NETWORK-SETUP.md](network/NETWORK-SETUP.md) if you want to learn more about what the script is doing and why.
91+
92+
# Resetting the Ziti demo network
93+
You're done with this article or things have gone really off the rails, and you want to start over.
94+
95+
There are a couple of commands that need to be run to stop the Ziti network and clean up.
96+
```
97+
stopAllEdgeRouters
98+
stopZitiController
99+
unsetZitiEnv
100+
rm -rf ~/.ziti/quickstart
101+
```
102+
103+
# Host service using Spring Boot
104+
There are three things that need to be done to host an OpenZiti service in a Spring Boot application:
105+
106+
1. Add the OpenZiti spring boot dependency.
107+
2. Add two properties to the service to configure the service identity and service name.
108+
3. Add an OpenZiti Tomcat customizer to the main application component scan.
109+
110+
The example code contains an initial/server project. Pull that up in your favorite editor and follow along.
111+
112+
## Add the OpenZiti spring boot dependency
113+
The openziti spring boot dependency is hosted on maven central.
114+
115+
Add the following to build.gradle:
116+
```kotlin
117+
implementation 'org.openziti:ziti-springboot:0.23.12'
118+
```
119+
120+
If you prefer maven, add the following to pom.xml:
121+
```xml
122+
<dependency>
123+
<groupId>org.openziti</groupId>
124+
<artifactId>ziti-springboot</artifactId>
125+
<version>0.23.12</version>
126+
</dependency>
127+
```
128+
## Add application properties
129+
Open the application properties file: `src/main/resources/application.properties`
130+
131+
The Tomcat customizer provided by OpenZiti needs an identity and the name of the service that the identity will bind. If you followed along with the network setup above, then the values will be:
132+
```
133+
ziti.id = ../network/private-service.json
134+
ziti.serviceName = demo-service
135+
```
136+
137+
## Configure the OpenZiti Tomcat customizer
138+
The Tomcat customizer replaces the standard socket protocol with an OpenZiti protocol that knows
139+
how Bind a service to accept connections over the Ziti network. To enable this adapter, open up the
140+
main application class `com.example.restservice.RestServiceApplication`.
141+
142+
Replace
143+
```
144+
@SpringBootApplication
145+
```
146+
147+
With
148+
```
149+
@SpringBootApplication (scanBasePackageClasses = {ZitiTomcatCustomizer.class, GreetingController.class})
150+
```
151+
152+
## Run the application
153+
That’s all you need to do! The OpenZiti Java SDK will connect to the test network, authenticate,
154+
and bind your service so that OpenZiti overlay network clients can connect to it.
155+
156+
To run the application, enter the following in a terminal window (in your project directory)
157+
```shell
158+
./gradlew bootRun
159+
```
160+
161+
If you use maven, run the following in a terminal window in your project directory:
162+
```shell
163+
./mvnw spring-boot:run
164+
```
165+
166+
# Test your new Spring Boot service
167+
The Spring Boot service you have just created is now totally dark. It has no listening ports at all.
168+
Go ahead - run `netcat` and find out for yourself!
169+
170+
```shell
171+
netstat -anp | grep 8080
172+
```
173+
You should find nothing `LISTENING`. Now, the only way to access it is via the OpenZiti network!
174+
Let’s write a simple client to connect to it and check that everything is working correctly.
175+
176+
This section will use the OpenZiti Java SDK to connect to the OpenZiti network. The example source
177+
includes a project and a class that takes care of the boilerplate stuff for you.
178+
179+
If you want to skip building the client then you can skip ahead to [Run The Client](#run-the-client).
180+
181+
## Connect to OpenZiti
182+
The Java SDK needs to be initialized with an OpenZiti identity. It is polite to destroy the context
183+
once the code is done, so we’ll wrap it up in a `try/catch` with a `finally` block.
184+
185+
```java
186+
ZitiContext zitiContext = null;
187+
try {
188+
zitiContext = Ziti.newContext(identityFile, "".toCharArray());
189+
190+
if (null == zitiContext.getService(serviceName,10000)) {
191+
throw new IllegalArgumentException(String.format("Service %s is not available on the OpenZiti network",serviceName));
192+
}
193+
194+
} catch (Throwable t) {
195+
log.error("OpenZiti network test failed", t);
196+
}
197+
finally {
198+
if( null != zitiContext ) zitiContext.destroy();
199+
}
200+
```
201+
* **Ziti.newContext:** Loads the OpenZiti identity and starts the connection process.
202+
* **zitiContext.getService** It can take a little while to establish the connection with the OpenZiti
203+
network fabric. For long-running applications this is typically not a problem, but for this little
204+
client we need to give the network some time to get everything ready.
205+
* **zitiContext.destroy():** Disposes of the context and cleans up resources locally and on the
206+
OpenZiti network.
207+
208+
## Send a request to the service
209+
The client has a connection to the test OpenZiti network. Now the client can ask OpenZiti to dial the service and send some data.
210+
211+
This client is for demonstration purposes only. You would never write a raw HTTP request like this
212+
in a real app. OpenZiti has a couple of examples using OKHttp and Netty if you want to work this
213+
code up using a real HTTP client. The examples can be found at
214+
https://github.com/openziti/ziti-sdk-jvm/tree/main/samples.
215+
216+
```java
217+
log.info("Dialing service");
218+
ZitiConnection conn = zitiContext.dial(serviceName);
219+
String request = "GET /greeting?name=MyName HTTP/1.1\n" +
220+
"Accept: */*\n" +
221+
"Host: example.web\n" +
222+
"\n";
223+
log.info("Sending request");
224+
conn.write(request.getBytes(StandardCharsets.UTF_8));
225+
```
226+
* **ZitiConnection:** A socket connection over the OpenZiti network fabric that can be used to exchange data with a Ziti service.
227+
* **zitiContext.dial:** Dialing a service opens a connection through the OpenZiti to the service.
228+
* **request:** The connection is essentially a plain socket. The contents of the request string is a plain HTTP GET to the greeting endpoint in the Spring Boot app.
229+
* **con.write:** Sends the request over the OpenZiti network.
230+
231+
## Read the service response
232+
The service will respond to the request with a json greeting. Read the greeting and write it to the log.
233+
```java
234+
byte[] buff = new byte[1024];
235+
int i;
236+
log.info("Reading response");
237+
while (0 < (i = conn.read(buff,0, buff.length))) {
238+
log.info("=== " + new String(buff, 0, i) );
239+
}
240+
```
241+
* **con.read:** Read data sent back from the Spring Boot service via the OpenZiti connection.
242+
243+
## Run The Client
244+
To run the client, run the following in a terminal window (in the client project):
245+
```shell
246+
./gradlew build run
247+
```
248+
249+
If you use maven, run the following in a terminal window (in the client project):
250+
```shell
251+
./mvnw package exec:java
252+
```
253+
# Dig Deeper
254+
* **OpenZiti documentation:** https://openziti.github.io/ziti/overview.html
255+
* **OpenZiti Github project:** https://github.com/openziti
256+
* **Spring Boot Rest Sample:** https://spring.io/guides/gs/rest-service/
257+
* **NetFoundry hosted OpenZiti NaaS offering:** https://netfoundry.io
258+
259+
**Spring and Spring Boot are trademarks of Pivotal Software, Inc. in the U.S. and other countries.*

complete/client/src/main/java/com/example/restservice/SimpleClient.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,7 @@ private static void hitZitiService(String identityFile, String serviceName) {
4545
try {
4646
zitiContext = Ziti.newContext(identityFile, "".toCharArray());
4747

48-
long end = System.currentTimeMillis() + 10000;
49-
50-
while (null == zitiContext.getService(serviceName) && System.currentTimeMillis() < end) {
51-
log.info("Waiting for {} to become available", serviceName);
52-
Thread.sleep(200);
53-
}
54-
55-
if (null == zitiContext.getService(serviceName)) {
48+
if (null == zitiContext.getService(serviceName,10000)) {
5649
throw new IllegalArgumentException(String.format("Service %s is not available on the OpenZiti network",serviceName));
5750
}
5851

0 commit comments

Comments
 (0)