Skip to content

Commit 57517be

Browse files
author
Tobias Fuhrimann
committed
add suggestions
1 parent b52fba9 commit 57517be

9 files changed

+116
-150
lines changed

bind-service.html.md.erb

+83-122
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ The <a href="../service-offerings/index.html" target="_blank">service marketplac
1010
Create the Redis datastore:
1111

1212
<pre class="terminal">
13-
$ cf create-service redis small my-redis-service
14-
Creating service instance my-redis-service in org MyOrg / space MySpace as [email protected]...
13+
$ cf create-service redis small my-redis
14+
Creating service instance my-redis in org MyOrg / space MySpace as [email protected]...
1515
OK
1616

17-
Create in progress. Use 'cf services' or 'cf service my-redis-service' to check operation status.
17+
Create in progress. Use 'cf services' or 'cf service my-redis' to check operation status.
1818

19-
Attention: The plan `small` of service `redis` is not free. The instance `my-redis-service` will incur a cost. Contact your administrator if you think this is in error.
19+
Attention: The plan `small` of service `redis` is not free. The instance `my-redis` will incur a cost. Contact your administrator if you think this is in error.
2020
</pre>
2121

2222
This creates a small Redis datastore for you which you now have to bind to your application. Binding means that the credentials and URL of the service will be written dynamically into the environment variables of the app as `VCAP_SERVICES` and can hence be used directly from there.
2323

2424
Now bind the new service to your existing application:
2525

2626
<pre class="terminal">
27-
$ cf bind-service my-java-app my-redis-service
28-
Binding service my-redis-service to app my-java-app in org MyOrg / space MySpace as [email protected]...
27+
$ cf bind-service my-java-app my-redis
28+
Binding service my-redis to app my-java-app in org MyOrg / space MySpace as [email protected]...
2929
OK
3030
TIP: Use 'cf restage my-java-app' to ensure your env variable changes take effect
3131
</pre>
@@ -50,7 +50,7 @@ System-Provided:
5050
"port": 59282
5151
},
5252
"label": "redis",
53-
"name": "my-redis-service",
53+
"name": "my-redis",
5454
"plan": "small",
5555
"provider": null,
5656
"syslog_drain_url": null,
@@ -74,150 +74,111 @@ Restaging app my-java-app in MyOrg monitor / space MySpace as [email protected].
7474
...
7575
</pre>
7676

77-
Now you want to consume your new Redis datastore from within your application. In order to do that, you have to adjust the code to store and retrieve data via Redis datastore. But first you have to add following <a href="https://github.com/xetorthio/jedis" target="_blank">Jedis</a> dependency to the `build.gradle` file. Jedis is a Java Redis client that lets your app interact with the Redis server.
77+
Now you want to consume your new Redis datastore from within your application. In order to do that, you have to adjust the code to store and retrieve data via Redis. But first you have to add the <a href="https://github.com/xetorthio/jedis" target="_blank">Jedis</a> dependency to the "dependencies" section of your `build.gradle` file. Jedis is a Java Redis client that lets your app interact with the Redis server.
7878

79-
```
79+
```java
8080
compile 'redis.clients:jedis:2.6.2'
8181
```
8282

83-
The `build.gradle` file should look as follows:
84-
85-
```
86-
apply plugin: 'java'
87-
...
88-
89-
sourceCompatibility = 1.8
90-
version = '1.0'
91-
92-
jar {
93-
manifest {
94-
attributes 'Implementation-Title': 'CF Java Sample App',
95-
'Implementation-Version': version,
96-
'Main-Class': 'com.swisscom.cloud.cloudfoundry.sampleapp.java.ProductService'
97-
}
98-
from {
99-
(configurations.runtime).collect {
100-
it.isDirectory() ? it : zipTree(it)
101-
}
102-
}
103-
exclude 'META-INF/*.RSA', 'META-INF/*.SF','META-INF/*.DSA'
104-
}
105-
106-
repositories {
107-
mavenCentral()
108-
}
109-
110-
dependencies {
111-
compile 'com.sparkjava:spark-core:2.2'
112-
compile 'com.fasterxml.jackson.core:jackson-databind:2.6.2'
113-
compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.2'
114-
compile 'redis.clients:jedis:2.6.2'
115-
testCompile 'junit:junit:4.12'
116-
testCompile 'org.mockito:mockito-core:1.10.19'
117-
}
83+
Now adjust the existing `ProductService` in `src/main/.../ProductService.java` to setup a Redis-based `ProductRepository`:
11884

119-
...
120-
```
121-
122-
Now adjust the existing `ProductService` to setup a Redis-based `ProductRepository`:
123-
124-
```
85+
```java
12586
private static ProductRepository getProductRepository() {
126-
ProcessBuilder processBuilder = new ProcessBuilder();
127-
String servicesJson = processBuilder.environment().get("VCAP_SERVICES");
128-
if (servicesJson != null) {
129-
Map<String,Object> redisCredentials = getRedisCredentials(servicesJson);
130-
return new RedisProductRepository(redisCredentials);
131-
} else {
132-
return new SimpleProductRepository();
133-
}
87+
ProcessBuilder processBuilder = new ProcessBuilder();
88+
String servicesJson = processBuilder.environment().get("VCAP_SERVICES");
89+
if (servicesJson != null) {
90+
Map<String,Object> redisCredentials = getRedisCredentials(servicesJson);
91+
return new RedisProductRepository(redisCredentials);
92+
} else {
93+
return new SimpleProductRepository();
94+
}
13495
}
13596
```
13697

137-
```
98+
```java
13899
@SuppressWarnings("unchecked")
139100
private static Map<String,Object> getRedisCredentials(String servicesJson) {
140-
ObjectMapper mapper = new ObjectMapper();
141-
if (servicesJson != null) {
142-
try {
143-
Map<String,Object> services = mapper.readValue(servicesJson, new TypeReference<Map<String,Object>>() { });
144-
List<Map<String,Object>> redisServices = (List<Map<String, Object>>) services.get("redis");
145-
for (Map<String,Object> redisService : redisServices) {
146-
// It is assumed that only one Redis service is bound to this app. Evaluate the name property
147-
// of the credentials in case multiple Redis services are bound to an app
148-
return (Map<String, Object>) redisService.get("credentials");
149-
}
150-
} catch (Exception exception) {
151-
throw new RuntimeException("Redis service declaration not found", exception);
152-
}
153-
}
154-
throw new RuntimeException("Redis service declaration not found");
101+
ObjectMapper mapper = new ObjectMapper();
102+
if (servicesJson != null) {
103+
try {
104+
Map<String,Object> services = mapper.readValue(servicesJson, new TypeReference<Map<String,Object>>() { });
105+
List<Map<String,Object>> redisServices = (List<Map<String, Object>>) services.get("redis");
106+
for (Map<String,Object> redisService : redisServices) {
107+
// It is assumed that only one Redis service is bound to this app. Evaluate the name property
108+
// of the credentials in case multiple Redis services are bound to an app
109+
return (Map<String, Object>) redisService.get("credentials");
110+
}
111+
} catch (Exception exception) {
112+
throw new RuntimeException("Redis service declaration not found", exception);
113+
}
114+
}
115+
throw new RuntimeException("Redis service declaration not found");
155116
}
156117
```
157118

158-
Thd code snippet above ensures that your app reads the credentials of your Redis service from the environment variables in order to setup the `ProductRepository`.
119+
The code snippet above ensures that your app reads the credentials of your Redis service from the environment variables in order to setup the `ProductRepository`.
159120

160121
Now adjust the existing `ProductRepository` (a static inner class of `ProductServce`) to store and retrieve products via Redis datastore:
161122

162-
163-
```
123+
```java
164124
public static class ProductRepository {
165-
166-
private Jedis jedis;
167-
private ObjectMapper mapper;
168-
169-
public ProductRepository(Map<String,Object> redisCredentials) {
170-
jedis = new Jedis((String) redisCredentials.get("host"), (int) redisCredentials.get("port"));
171-
jedis.auth((String) redisCredentials.get("password"));
172-
mapper = new ObjectMapper();
173-
}
174-
175-
public long add(Product product) {
176-
product.setId(nextId());
177-
jedis.rpush("products", mapToJson(product));
178-
return product.getId();
179-
}
180-
181-
public Collection<Product> findAll() {
182-
return jedis.lrange("products", 0, jedis.llen("products")).stream()
183-
.map(productJson -> mapToProduct(productJson))
184-
.collect(Collectors.toList());
185-
}
186-
187-
private Long nextId() {
188-
return jedis.incr("productid");
189-
}
190-
191-
private String mapToJson(Product product) {
192-
try {
193-
return mapper.writeValueAsString(product);
194-
} catch (JsonProcessingException e) {
195-
throw new RuntimeException("Product cannot be serialized as JSON");
196-
}
197-
}
198-
199-
private Product mapToProduct(String productJson) {
200-
try {
201-
return mapper.readValue(productJson, Product.class);
202-
} catch (IOException exception) {
203-
throw new RuntimeException("Product cannot be deserialized from JSON");
204-
}
205-
}
125+
126+
private Jedis jedis;
127+
private ObjectMapper mapper;
128+
129+
public ProductRepository(Map<String,Object> redisCredentials) {
130+
jedis = new Jedis((String) redisCredentials.get("host"), (int) redisCredentials.get("port"));
131+
jedis.auth((String) redisCredentials.get("password"));
132+
mapper = new ObjectMapper();
133+
}
134+
135+
public long add(Product product) {
136+
product.setId(nextId());
137+
jedis.rpush("products", mapToJson(product));
138+
return product.getId();
139+
}
140+
141+
public Collection<Product> findAll() {
142+
return jedis.lrange("products", 0, jedis.llen("products")).stream()
143+
.map(productJson -> mapToProduct(productJson))
144+
.collect(Collectors.toList());
145+
}
146+
147+
private Long nextId() {
148+
return jedis.incr("productid");
149+
}
150+
151+
private String mapToJson(Product product) {
152+
try {
153+
return mapper.writeValueAsString(product);
154+
} catch (JsonProcessingException e) {
155+
throw new RuntimeException("Product cannot be serialized as JSON");
156+
}
157+
}
158+
159+
private Product mapToProduct(String productJson) {
160+
try {
161+
return mapper.readValue(productJson, Product.class);
162+
} catch (IOException exception) {
163+
throw new RuntimeException("Product cannot be deserialized from JSON");
164+
}
165+
}
166+
206167
}
207168
```
208169

209-
With this latest code you can still experiment locally, since your app uses a simple default `ProductRepository` instead of the Redis-based repository in case your Redis service binding cannot be found. This allows you to run your app locally as well as in the cloud without having to configure anything differently. However, you must install Redis on your local machine, if you want to test your app with a local Redis server.
170+
With this latest code you can still experiment locally, since your app uses a simple default `ProductRepository` instead of the Redis-based repository in case your Redis service binding cannot be found. This allows you to run your app locally as well as in the cloud without having to configure anything differently. However, you must install <a href="http://redis.io/topics/quickstart" target="_blank">Redis</a> on your local machine, if you want to test your app with a local Redis server.
210171

211172
Now compile/build your changes and push them to the cloud:
212173

213174
<pre class="terminal">
214-
$ gralde build
215-
$ cf push my-java-app -b java_buildpack -p target/build/libs/cf-sample-app-java-1.0.jar
175+
$ gradle build
176+
$ cf push my-java-app -p target/build/libs/cf-sample-app-java-1.0.jar
216177
</pre>
217178

218179
You can access other services like MongoDB or MariaDB in a similar matter, simply by binding them to your app and accessing them through the environment variables.
219180

220-
You can checkout the finished version of all these changes in the `advanced` branch of the sample app repository, at: https://github.com/swisscom/cf-sample-app-java/tree/advanced
181+
You can checkout the finished version of all these changes in the `advanced` branch of the sample app repository on <a href="https://github.com/swisscom/cf-sample-app-java/tree/advanced" target="_blank">Github</a>.
221182

222183
<div style="text-align:center;padding:3em;">
223184
<a href="./manifest.html" class="btn btn-primary">I've bound a service to my App</a>

dependencies.html.md.erb

+9-9
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,25 @@ owner: Daniel Freitag
55

66
<strong><%= modified_date %></strong>
77

8-
<%= vars.product_short %> uses the Java buildpack to execute the uploaded JAR file as application package. The Java buildpack will run your application using the `main()` method of the `Main` class declard in the `build.gradle` file.
8+
<%= vars.product_short %> uses the Java buildpack to execute the uploaded JAR file as application package. The Java buildpack will run your application using the `main()` method of the `Main` class declared in the `build.gradle` file.
99

10-
The dependencies needed for the application are declared in the `build.gradle` file and are included in the standalone JAR.
10+
The dependencies needed for the application are also declared in the `build.gradle` file and are therefore included in the standalone JAR.
1111

12-
The `build.gradle` file of your deployed sample app should look as follows:
12+
The `build.gradle` file of your deployed sample app looks something like this:
1313

1414

15-
```
15+
```java
1616
apply plugin: 'java'
17-
...
17+
apply plugin: 'eclipse'
1818

1919
sourceCompatibility = 1.8
2020
version = '1.0'
2121

2222
jar {
2323
manifest {
24-
attributes 'Implementation-Title': 'CF Java Sample App',
25-
'Implementation-Version': version,
26-
'Main-Class': 'com.swisscom.cloud.cloudfoundry.sampleapp.java.ProductService'
24+
attributes 'Implementation-Title': 'CF Java Sample App',
25+
'Implementation-Version': version,
26+
'Main-Class': 'com.swisscom.cloud.cloudfoundry.sampleapp.java.ProductService'
2727
}
2828
from {
2929
(configurations.runtime).collect {
@@ -40,7 +40,7 @@ repositories {
4040
dependencies {
4141
compile 'com.sparkjava:spark-core:2.2'
4242
compile 'com.fasterxml.jackson.core:jackson-databind:2.6.2'
43-
compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.2'
43+
compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.6.2'
4444
testCompile 'junit:junit:4.12'
4545
testCompile 'org.mockito:mockito-core:1.10.19'
4646
}

deploy.html.md.erb

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ BUILD SUCCESSFUL
3131
Total time: 6.78 secs
3232
</pre>
3333

34-
Gradle will build a Java archive (JAR) file of your app. It is located under `target/build/libs/cf-sample-app-java-1.0.jar`. The Java archive contains all dependencies.
34+
Gradle will build a Java archive (JAR) file of your app. It is located under `target/build/libs/cf-sample-app-java-1.0.jar`. The Java archive contains all dependencies needed by your app.
3535

3636
Push your app to the cloud by executing the following command and replacing the "my-random-hostname" with your own hostname. This will be part of the URL your app will be reached at and it has to be globally unique so be creative.
3737

38-
The `-b java_buildpack` tells Cloud Foundry to use the Java buildpack for our app and the `-p build/libs/cf-sample-app-java-1.0.jar` tells Cloud Foundry where to find the compiled application to push to the Cloud. Note that the `-b` option can be ommitted since the buildpack auto-detection support ensures that the appropriate buildpack for a given app will be found.
38+
The `-p build/libs/cf-sample-app-java-1.0.jar` tells Cloud Foundry where to find the compiled application to push to the Cloud.
3939

4040
<pre class="terminal">
41-
$ cf push my-java-app -b java_buildpack -p target/buld/libs/cf-sample-app-java-1.0.jar -n my-random-hostname`
41+
$ cf push my-java-app -p target/buld/libs/cf-sample-app-java-1.0.jar -n my-random-hostname
4242
Creating app my-java-app in org MyOrg / space MySpace as [email protected]...
4343
OK
4444

environment.html.md.erb

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ owner: Daniel Freitag
77

88
<%= vars.product_short %> lets you externalize configuration - storing application properties in <a href="../devguide/deploy-apps/environment-variable.html" target="_blank">environment variables</a>.
99

10-
At runtime, environment variables are exposed to the application through its environment. You can use this for example to run your app in production mode setting the APP_MODE environment variable.
10+
At runtime, environment variables are exposed to the application through its environment. You can use this for example to run your app in production mode setting the `APP_MODE` environment variable.
1111

1212
<pre class="terminal">
1313
$ cf set-env my-java-app APP_MODE production
@@ -26,7 +26,7 @@ Restaging app my-java-app in org MyOrg / space MySpace as [email protected]...
2626
...
2727
</pre>
2828

29-
All environment variables can be accessed using `System.getenv().get('ENV_VARIABLE')` or `new ProcessBuilder().environment().get("ENV_VARIABLE")` from within your Java app.
29+
All environment variables can be accessed using `System.getenv().get('ENV_VARIABLE')` or `new ProcessBuilder().environment().get("ENV_VARIABLE")` from within your Java app.
3030

3131
<div style="text-align:center;margin:3em;">
3232
<a href="./bind-service.html" class="btn btn-primary">I've set my environment variable</a>

index.html.md.erb

+6-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ owner: Daniel Freitag
55

66
<strong><%= modified_date %></strong>
77

8+
This tutorial will have you deploying a Java app in minutes.
9+
10+
Hang on for a few more minutes to learn how it all works, so you can make the most out of <%= vars.product_full %>.
11+
812
The tutorial assumes that you have:
913

1014
* An <a href="https://console.developer.swisscom.com" target="_blank"><%= vars.product_short %> account</a>
11-
* <a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" target="_blank">Java 8</a> installed locally
12-
* <a href="http://gradle.org/gradle-download/" target="_blank">Gradle 2</a> installed locally</li>
15+
* The <a href="http://www.oracle.com/technetwork/java/javase/downloads/index.html" target="_blank">Java 8 JDK</a> installed locally
16+
* <a href="https://gradle.org/gradle-download/" target="_blank">Gradle 2</a> installed locally</li>
1317

1418
Furthermore, you must have a first Cloud Foundry <a href="../concepts/roles.html#orgs" target="_blank">Organization</a> and <a href="../concepts/roles.html#spaces" target="_blank">Space</a> to push your apps to. In case you don't have that yet, please follow the <a href="https://console.developer.swisscom.com" target="_blank">tutorials in the web console</a> before returning here.
1519

logs.html.md.erb

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ View information about your running app using one of the logging commands, `cf l
1313
$ cf logs my-java-app
1414
Connected, tailing logs for app my-java-app in org MyOrg / space MySpace as [email protected]...
1515

16-
2016-05-29T15:58:44.80+0200 [RTR/0] OUT cf-sample-app-java.scapp.io - [29/05/2016:13:58:44 +0000] "GET / HTTP/1.1" 200 0 83 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, lik
16+
2016-05-29T15:58:44.80+0200 [RTR/0] OUT my-random-hostname.scapp.io - [29/05/2016:13:58:44 +0000] "GET / HTTP/1.1" 200 0 83 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, lik
1717
e Gecko) Chrome/50.0.2661.102 Safari/537.36" 85.4.61.216:58977 x_forwarded_for:"-" x_forwarded_proto:"http" vcap_request_id:7d4a0666-baa2-4513-44fa-54adea9fc874 response_time:0.172195463 app_id:a83336
1818
14-5870-484b-b57b-a84bac0666f6
19-
2016-05-29T15:58:46.08+0200 [RTR/0] OUT cf-sample-app-java.scapp.io - [29/05/2016:13:58:46 +0000] "GET / HTTP/1.1" 200 0 83 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, lik
19+
2016-05-29T15:58:46.08+0200 [RTR/0] OUT my-random-hostname.scapp.io - [29/05/2016:13:58:46 +0000] "GET / HTTP/1.1" 200 0 83 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, lik
2020
e Gecko) Chrome/50.0.2661.102 Safari/537.36" 85.4.61.216:58977 x_forwarded_for:"-" x_forwarded_proto:"http" vcap_request_id:3470b5dd-8f98-441d-5e4a-1c68e0964844 response_time:0.00505579 app_id:a833361
2121
4-5870-484b-b57b-a84bac0666f6
2222
</pre>

next-steps.html.md.erb

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ owner: Daniel Freitag
55

66
You now know how to deploy an app, change its configuration, view logs, scale, and bind it to services.
77

8-
Here’s some recommended reading. The first one describes the buildpack which Cloud Foundry uses for Java applications. The second one is a collection of factors you should consider when creating a modern cloud-native application.
8+
Here’s some recommended reading. The first one is a set of tips for Java developers in the cloud. The second one describes the buildpack which Cloud Foundry uses to bootstrap Java applications. The third one is a collection of factors you should consider when creating a modern cloud-native application.
99

10+
* <a href="../buildpacks/java/java-tips.html" target="_blank">Tips for Java Applications in the cloud</a>
1011
* <a href="../buildpacks/java/index.html" target="_blank">Java Buildpack</a>
1112
* <a href="http://12factor.net" target="_blank">12 facfors for cloud-native apps</a>

0 commit comments

Comments
 (0)