- Prerequisites
- Creating the Project Structure
- Connecting to the Platform
- Creating a Service
- Sending Requests to Another Service
- Posting Events when the Job is Submitted and Completed
- Subscribing to Service Events
- Sending REST Requests to your Service
- Delivering the Service as an RPM on CentOS 6.x/RedHat 6.x
- Daemonizing the Service On CentOS 6.x/RedHat 6.x
Prerequisites
Before proceeding with the tutorial, please install the following software:
- Oracle VM VirtualBox
- HashiCorp Vagrant
- Avid Platform Virtual Machine
For instructions on setting up VirtualBox, Vagrant, and the Avid Platform Virtual Machine, see the README included in the Avid Platform Virtual Machine download file (avid-platform-vagrant-vm-YYYY-MM-DD-XXXX.zip).
In addition, make sure the gradle
, java
and javac
commands are available in your shell. Ensure that the Java version is 7 or later and the Gradle version is 2.7 or later.
Note: The code for this tutorial is available in the examples/java-service
directory.
Creating the Project Structure
Create a directory for your project. We will use the name
java-service
in this tutorial. Then create amodule
subfolder inside.1
2mkdir java-service
mkdir java-service/moduleMake
java-service/module
directory current1
cd java-service/module
Create a
build.gradle
file1
touch build.gradle
Add the following content to this file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25apply plugin: 'java'
apply from: "${projectDir}/versions.gradle"
apply plugin: 'application'
group = 'com.avid.connector.tutorial'
version = '0.0.1-SNAPSHOT'
applicationName = 'java-service'
repositories {
mavenCentral()
flatDir {
dirs '../../../libs'
}
}
dependencies {
compile fileTree(dir: '../../../libs', include: '*.jar')
compile group: 'ch.qos.logback', name: 'logback-core', version: "${logbackVersion}"
compile group: 'ch.qos.logback', name: 'logback-classic', version: "${logbackVersion}"
compile group: 'com.google.code.findbugs', name: 'jsr305', version: "${jsr305}"
}Create a
versions.gradle
file:1
touch versions.gradle
Add the following content to
versions.gradle
:1
2
3
4ext {
logbackVersion='1.1.3'
jsr305 = '2.0.0'
}Create
src
,src/main
,src/test
,src/main/java
andsrc/test/java
directories1
2
3
4
5mkdir src
mkdir src/main
mkdir src/test
mkdir src/main/java
mkdir src/test/javaVerify the build by outputting its list of dependencies:
1
gradle dependencies
Congratulations! A simple project layout and core dependencies are now ready.
Connecting to the Platform
Create a directory called
src/main/java/com/avid/connector/tutorial
:1
mkdir -p src/main/java/com/avid/connector/tutorial
Create a
src/main/java/com/avid/connector/tutorial/JavaServiceMain.java
file1
touch src/main/java/com/avid/connector/tutorial/JavaServiceMain.java
Add the following content to
src/main/java/com/avid/connector/tutorial/JavaServiceMain.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34package com.avid.connector.tutorial;
import com.avid.acs.bus.*;
import com.avid.acs.bus.connection.ConnectEvent;
import com.avid.acs.bus.connection.ConnectionListener;
import com.avid.acs.bus.connection.DisconnectEvent;
import com.avid.acs.service.IpcBusAccessFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JavaServiceMain {
private static final Logger LOG = LoggerFactory.getLogger(JavaServiceMain.class);
private static BusAccess bus;
public static void main(String[] args) {
try {
bus = new IpcBusAccessFactory().createBusAccess(null, false);
bus.connect(new ConnectionListener() {
public void onConnect(ConnectEvent event) {
LOG.info("Connected to Avid Platform");
}
public void onDisconnect(DisconnectEvent event) {
LOG.info("Disconnected from Avid Platform");
}
});
} catch (BusAccessException e) {
LOG.error(e.getMessage(), e);
}
}
}Modify the
build.gradle
file. Add the following line to the end of file:1
mainClassName = 'com.avid.connector.tutorial.JavaServiceMain'
Build the service:
1
gradle clean build installDist
Set environment variables to use a websocket connection with a protobuf payload:
1
export ACS_GATEWAY_HOST=avid-platform-zone1
Run the application you just created:
1
build/install/java-service/bin/java-service
You should see following log output in the shell:
1
2
3
4
5
6
7
811:06:23.371 [main] INFO c.a.acs.service.IpcBusAccessFactory - IPC is 'Websocket with protocol buffers'
11:06:23.631 [main] WARN com.avid.acs.utils.JarUtils - Missing 'ACS_PLATFORM_IDENTIFIER'. Using default 'unknown'.
11:06:23.634 [main] WARN com.avid.acs.utils.JarUtils - Missing 'ACS_SERVICE_BUILD_NUMBER'. Using default 'unknown'.
11:06:23.634 [main] WARN com.avid.acs.utils.JarUtils - Missing 'ACS_ENVIRONMENT_IDENTIFIER'. Using default 'unknown'.
11:06:23.639 [main] INFO c.a.a.service.access.ProxyBusAccess - Connecting to the bus
11:06:23.762 [main] DEBUG c.a.a.i.p.s.ServiceProtobufEndpoint - Starting wrapper client on host avid-platform-zone1, port 9900
11:06:23.992 [main] INFO c.a.c.tutorial.JavaServiceMain - Connected to Avid Platform
11:06:23.994 [main] INFO c.avid.acs.service.BusServiceControl - Gateway connection monitor is startedPress
Ctrl+C
to terminate the service.
Congratulations! You have successfully connected to the Platform as a client.
Creating a Service
Create
src/main/java/com/avid/connector/tutorial/JavaService.java
,src/main/java/com/avid/connector/tutorial/JavaServiceImpl.java
andsrc/main/java/com/avid/connector/tutorial/JavaServiceResponse.java
files:1
2
3touch src/main/java/com/avid/connector/tutorial/JavaService.java
touch src/main/java/com/avid/connector/tutorial/JavaServiceImpl.java
touch src/main/java/com/avid/connector/tutorial/JavaServiceResponse.javaAdd the following content to
src/main/java/com/avid/connector/tutorial/JavaService.java
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package com.avid.connector.tutorial;
import com.avid.acs.bus.annotations.*;
import com.avid.acs.bus.annotations.Error;
import com.avid.acs.bus.OperationContext;
import com.avid.acs.bus.error.ErrorSeverity;
import com.avid.acs.bus.message.Message;
import com.avid.acs.bus.service.info.RequestMethod;
public interface JavaService {
void submitJob(; Integer jobId, Integer execTime, OperationContext<JavaServiceResponse> operationContext)
}Add the following content to
src/main/java/com/avid/connector/tutorial/JavaServiceResponse.java
:1
2
3
4
5
6
7
8
9
10
11
12
13
14package com.avid.connector.tutorial;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
class JavaServiceResponse {
public final String jobStatus;
public JavaServiceResponse( { String jobStatus)
this.jobStatus = jobStatus;
}
}Create a
src/main/resources/com/avid/connector/tutorial
directory:1
mkdir -p src/main/resources/com/avid/connector/tutorial
Add
src/main/resources/com/avid/connector/tutorial/submit_valid_job.json
,src/main/resources/com/avid/connector/tutorial/submit_invalid_job_id.json
,src/main/resources/com/avid/connector/tutorial/submit_invalid_exec_time.json
files:1
2
3touch src/main/resources/com/avid/connector/tutorial/submit_valid_job.json
touch src/main/resources/com/avid/connector/tutorial/submit_invalid_job_id.json
touch src/main/resources/com/avid/connector/tutorial/submit_invalid_exec_time.jsonAdd the following content to
src/main/resources/com/avid/connector/tutorial/submit_valid_job.json
:1
2
3
4
5
6
7
8
9
10{
"serviceType": "avid.tutorial.java.service",
"serviceRealm": "global",
"serviceVersion": 3,
"op": "submitJob",
"paramSet": {
"jobId": 1,
"execTime": 1000
}
}Add the following content to
src/main/resources/com/avid/connector/tutorial/submit_invalid_job_id.json
:1
2
3
4
5
6
7
8
9
10{
"serviceType": "avid.tutorial.java.service",
"serviceRealm": "global",
"serviceVersion": 3,
"op": "submitJob",
"paramSet": {
"jobId": -1,
"execTime": 1000
}
}Add the following content to
src/main/resources/com/avid/connector/tutorial/submit_invalid_exec_time.json
:1
2
3
4
5
6
7
8
9
10{
"serviceType": "avid.tutorial.java.service",
"serviceRealm": "global",
"serviceVersion": 3,
"op": "submitJob",
"paramSet": {
"jobId": 2,
"execTime": 10
}
}Add the following content to
src/main/java/com/avid/connector/tutorial/JavaServiceImpl.java
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package com.avid.connector.tutorial;
import com.avid.acs.bus.*;
import com.avid.acs.bus.error.BusError;
import java.util.*;
public class JavaServiceImpl implements JavaService {
public void submitJob(Integer jobId, Integer execTime, OperationContext<JavaServiceResponse> operationContext) {
if (jobId == null || jobId < 1) {
Map<String, String> params = Collections.singletonMap("jobId", jobId == null ? "null" : String.valueOf(jobId));
operationContext.error(new BusError("INVALID_JOB_ID", params, "Invalid jobId was provided. Must be integer > 0."));
} else if (execTime == null || execTime < 100) {
Map<String, String> params = Collections.singletonMap("execTime", execTime == null ? "null" : String.valueOf(execTime));
operationContext.error(new BusError("INVALID_EXECUTION_TIME", params, "Invalid execution time provided. Must be integer >= 100."));
} else {
JavaServiceResponse response = new JavaServiceResponse("submitted");
operationContext.respond(response);
}
}
}Modify the
src/main/java/com/avid/connector/tutorial/JavaServiceMain.java
file:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55package com.avid.connector.tutorial;
import com.avid.acs.bus.*;
import com.avid.acs.bus.connection.ConnectEvent;
import com.avid.acs.bus.connection.ConnectionListener;
import com.avid.acs.bus.connection.DisconnectEvent;
import com.avid.acs.service.IpcBusAccessFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JavaServiceMain {
private static final Logger LOG = LoggerFactory.getLogger(JavaServiceMain.class);
private static BusAccess bus;
public static void main(String[] args) {
try {
bus = new IpcBusAccessFactory().createBusAccess(null, false);
bus.connect(new ConnectionListener() {
public void onConnect(ConnectEvent event) {
LOG.info("Connected to Avid Platform");
}
public void onDisconnect(DisconnectEvent event) {
LOG.info("Disconnected from Avid Platform");
}
});
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
bus.disconnect();
}
});
JavaService javaService = new JavaServiceImpl();
AsyncCallback<ServiceContext> asyncCallback = new AsyncCallback<ServiceContext>() {
public void onSuccess(ServiceContext serviceContext) {
LOG.info("Service registered with id={}", serviceContext.getServiceInfo().getId());
}
public void onError(CallbackError callbackError) {
LOG.info("Failed to register service: {}", callbackError.getMessage());
}
};
bus.registerService(javaService, null, asyncCallback);
} catch (BusAccessException e) {
LOG.error(e.getMessage(), e);
}
}
}Build the service:
1
gradle clean build installDist
Set environment variables to use a websocket connection with a protobuf payload:
1
export ACS_GATEWAY_HOST=avid-platform-zone1
Run the application:
1
build/install/java-service/bin/java-service
You should see logs output in the shell.
Test that the service is working:
- Open the ACS Monitor. Filter the services list by entering
java\.service
in the Service Types Filter field and clicking the Apply button. The list is reduced toavid.tutorial.java.service
. - Expand the Service Operations List for the service. The list should include the
submitJob
operation. - Click on the
Submit valid job
link. Its corresponding message should appear in the code editor area, under theRequest to
field. - Click the
Query
button. Your service’s response appears in theResponse
field. Your service’s response appears in theResponse
field.
- Open the ACS Monitor. Filter the services list by entering
Press
Ctrl+C
in the shell to terminate the service.
Congratulations! You have successfully registered your service on the Platform.
Sending Requests to Another Service
Modify
src/main/java/com/avid/connector/tutorial/JavaServiceResponse.java
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.avid.connector.tutorial;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
class JavaServiceResponse {
public final String jobStatus;
public final Integer numberOfSubmittedJobs;
public JavaServiceResponse( { String jobStatus, Integer numberOfSubmittedJobs)
this.jobStatus = jobStatus;
this.numberOfSubmittedJobs = numberOfSubmittedJobs;
}
}In this step you modify the
submitJob
method in thesrc/main/java/com/avid/connector/tutorial/JavaServiceImpl.java
file. You add a constructor and two new methods,setNumberOfSubmittedJobs
andgetNumberOfSubmittedJobs
, to update and request the number of submitted jobs. In addition, you add methods and interfaces to handle asynchronous requests:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166package com.avid.connector.tutorial;
import com.avid.acs.bus.BusAccess;
import com.avid.acs.bus.BusAccessException;
import com.avid.acs.bus.OperationContext;
import com.avid.acs.bus.ResponseHandler;
import com.avid.acs.bus.error.BusError;
import com.avid.acs.bus.error.ErrorSet;
import com.avid.acs.bus.message.Message;
import com.avid.acs.bus.message.MessageOptions;
import com.avid.acs.bus.message.MutableMessage;
import com.avid.acs.bus.message.data.Data;
import com.avid.acs.bus.message.data.JsonData;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.Map;
public class JavaServiceImpl implements JavaService {
private static final Logger LOG = LoggerFactory.getLogger(JavaServiceImpl.class);
private final BusAccess busAccess;
public JavaServiceImpl(BusAccess busAccess) {
this.busAccess = busAccess;
}
public void submitJob(Integer jobId, Integer execTime, final OperationContext<JavaServiceResponse> operationContext) {
if (jobId == null || jobId < 1) {
Map<String, String> params = Collections.singletonMap("jobId", jobId == null ? "null" : String.valueOf(jobId));
operationContext.error(new BusError("INVALID_JOB_ID", params, "Invalid jobId was provided. Must be integer > 0."));
} else if (execTime == null || execTime < 100) {
Map<String, String> params = Collections.singletonMap("execTime", execTime == null ? "null" : String.valueOf(execTime));
operationContext.error(new BusError("INVALID_EXECUTION_TIME", params, "Invalid execution time provided. Must be integer >= 100."));
} else {
final SetNumberOfSubmittedJobsCallback setNumberOfSubmittedJobsCallback = new SetNumberOfSubmittedJobsCallback() {
public void handle(boolean responseMissing, ErrorSet errors, Integer oldNumber, Integer newNumber) {
JavaServiceResponse response;
if (responseMissing || errors.hasErrors()) {
response = new JavaServiceResponse("submitted", oldNumber);
} else {
response = new JavaServiceResponse("submitted", newNumber);
}
operationContext.respond(response);
}
};
final GetNumberOfSubmittedJobsCallback getNumberOfSubmittedJobsCallback = new GetNumberOfSubmittedJobsCallback() {
public void handle(boolean responseMissing, ErrorSet errors, Integer number) {
Integer newNumber;
if (!responseMissing && !errors.hasErrors()) {
newNumber = number + 1;
setNumberOfSubmittedJobs(number, newNumber, setNumberOfSubmittedJobsCallback);
} else if (errors != null && errors.hasErrors() && "avid.acs.attributes/NOT_FOUND".equals(errors.iterator().next().getCode())) {
newNumber = 1;
setNumberOfSubmittedJobs(number, newNumber, setNumberOfSubmittedJobsCallback);
} else {
JavaServiceResponse response = new JavaServiceResponse("submitted", null);
operationContext.respond(response);
}
}
};
getNumberOfSubmittedJobs(getNumberOfSubmittedJobsCallback);
}
}
private void getNumberOfSubmittedJobs(GetNumberOfSubmittedJobsCallback callback) {
Message message = new MutableMessage("avid.acs.attributes", "global", 3, "fetch");
message.parameters().put("name", "java.service.store");
try {
MessageOptions options = new MessageOptions(2000);
busAccess.query(message, createGetNumberOfSubmittedJobsResponseHandler(callback), options);
} catch (BusAccessException e) {
callback.handle(true, null, null);
}
}
private void setNumberOfSubmittedJobs(Integer number, int newNumber, SetNumberOfSubmittedJobsCallback callback) {
Message message = new MutableMessage("avid.acs.attributes", "global", 3, "store");
message.parameters().put("name", "java.service.store");
message.parameters().put("value", Collections.singletonMap("numberOfSubmittedJobs", newNumber));
try {
MessageOptions options = new MessageOptions(2000);
busAccess.query(message, createSetNumberOfSubmittedJobsResponseHandler(callback, number, newNumber), options);
} catch (BusAccessException e) {
callback.handle(true, null, number, newNumber);
}
}
private ResponseHandler createGetNumberOfSubmittedJobsResponseHandler(final GetNumberOfSubmittedJobsCallback callback) {
return new ResponseHandler() {
public void onResponse( { Message response)
final Data value = response.results().get("value");
Integer number = null;
if (value != null && value instanceof JsonData) {
final JsonNode jsonNode = ((JsonData) value).get().get("numberOfSubmittedJobs");
if (jsonNode != null) {
number = jsonNode.asInt();
}
} else {
if (value == null) {
number = 0;
}
}
callback.handle(false, response.errors(), number);
}
public void onMessageProcessingError( { String error)
LOG.info("Error occurred");
callback.handle(true, null, null);
}
public void onTimeout() {
LOG.info("Timeout occurred");
callback.handle(true, null, null);
}
};
}
private ResponseHandler createSetNumberOfSubmittedJobsResponseHandler(final SetNumberOfSubmittedJobsCallback callback, final Integer number, final Integer newNumber) {
return new ResponseHandler() {
public void onResponse( { Message response)
callback.handle(false, response.errors(), number, newNumber);
}
public void onMessageProcessingError( { String error)
LOG.info("Error occurred");
callback.handle(true, null, number, newNumber);
}
public void onTimeout() {
LOG.info("Timeout occurred");
callback.handle(true, null, number, newNumber);
}
};
}
private interface GetNumberOfSubmittedJobsCallback {
void handle(boolean responseMissing, ErrorSet errors, Integer number);
}
private interface SetNumberOfSubmittedJobsCallback {
void handle(boolean responseMissing, ErrorSet errors, Integer oldNumber, Integer newNumber);
}
}Next, modify
src/main/java/com/avid/connector/tutorial/JavaServiceMain.java
, adding abus
instance to the constructor ofJavaServiceImpl
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47package com.avid.connector.tutorial;
import com.avid.acs.bus.*;
import com.avid.acs.bus.connection.ConnectEvent;
import com.avid.acs.bus.connection.ConnectionListener;
import com.avid.acs.bus.connection.DisconnectEvent;
import com.avid.acs.service.IpcBusAccessFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JavaServiceMain {
private static final Logger LOG = LoggerFactory.getLogger(JavaServiceMain.class);
private static BusAccess bus;
public static void main(String[] args) {
try {
bus = new IpcBusAccessFactory().createBusAccess(null, false);
bus.connect(new ConnectionListener() {
public void onConnect(ConnectEvent event) {
LOG.info("Connected to Avid Platform");
}
public void onDisconnect(DisconnectEvent event) {
LOG.info("Disconnected from Avid Platform");
}
});
JavaService javaService = new JavaServiceImpl(bus);
AsyncCallback<ServiceContext> asyncCallback = new AsyncCallback<ServiceContext>() {
public void onSuccess(ServiceContext serviceContext) {
LOG.info("Service registered with id={}", serviceContext.getServiceInfo().getId());
}
public void onError(CallbackError callbackError) {
LOG.info("Failed to register service: {}", callbackError.getMessage());
}
};
bus.registerService(javaService, null, asyncCallback);
} catch (BusAccessException e) {
LOG.error(e.getMessage(), e);
}
}
}Build the service:
1
gradle clean build installDist
Set environment variables to use a websocket connection with protobuf payload:
1
export ACS_GATEWAY_HOST=avid-platform-zone1
Run the application:
1
build/install/java-service/bin/java-service
You should see logs output in the shell.
Test that the service is working. Open ACS Monitor, and send several
submitJob
requests to your service. Each time you send a new request, you should see the number of submitted jobs increment in the response:1
2
3
4
5
6{
"resultSet": {
"jobStatus": "submitted",
"numberOfSubmittedJobs": 2
}
}Press
Ctrl+C
in the shell to terminate the service.
Congratulations! You have successfully sent a request to another service, and received a response from it.
Posting Events when the Job is Submitted And Completed
Edit the
src/main/java/com/avid/connector/tutorial/JavaServiceImpl.java
file. Here, we modify thesubmitJob
method and add newpostChannelMessages
method:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202package com.avid.connector.tutorial;
import com.avid.acs.bus.*;
import com.avid.acs.bus.channel.ChannelMessage;
import com.avid.acs.bus.channel.MutableChannelMessage;
import com.avid.acs.bus.error.BusError;
import com.avid.acs.bus.error.ErrorSet;
import com.avid.acs.bus.message.Message;
import com.avid.acs.bus.message.MessageOptions;
import com.avid.acs.bus.message.MutableMessage;
import com.avid.acs.bus.message.data.Data;
import com.avid.acs.bus.message.data.JsonData;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
public class JavaServiceImpl implements JavaService {
private static final Logger LOG = LoggerFactory.getLogger(JavaServiceImpl.class);
private final BusAccess busAccess;
public JavaServiceImpl(BusAccess busAccess) {
this.busAccess = busAccess;
}
public void submitJob(final Integer jobId, Integer execTime, final OperationContext<JavaServiceResponse> operationContext) {
if (jobId == null || jobId < 1) {
Map<String, String> params = Collections.singletonMap("jobId", jobId == null ? "null" : String.valueOf(jobId));
operationContext.error(new BusError("INVALID_JOB_ID", params, "Invalid jobId was provided. Must be integer > 0."));
} else if (execTime == null || execTime < 100) {
Map<String, String> params = Collections.singletonMap("execTime", execTime == null ? "null" : String.valueOf(execTime));
operationContext.error(new BusError("INVALID_EXECUTION_TIME", params, "Invalid execution time provided. Must be integer >= 100."));
} else {
postChannelMessages(jobId, execTime);
final SetNumberOfSubmittedJobsCallback setNumberOfSubmittedJobsCallback = new SetNumberOfSubmittedJobsCallback() {
public void handle(boolean responseMissing, ErrorSet errors, Integer oldNumber, Integer newNumber) {
JavaServiceResponse response;
if (responseMissing || errors.hasErrors()) {
response = new JavaServiceResponse("submitted", oldNumber);
} else {
response = new JavaServiceResponse("submitted", newNumber);
}
operationContext.respond(response);
}
};
final GetNumberOfSubmittedJobsCallback getNumberOfSubmittedJobsCallback = new GetNumberOfSubmittedJobsCallback() {
public void handle(boolean responseMissing, ErrorSet errors, Integer number) {
Integer newNumber;
if (!responseMissing && !errors.hasErrors()) {
newNumber = number + 1;
setNumberOfSubmittedJobs(number, newNumber, setNumberOfSubmittedJobsCallback);
} else if (errors != null && errors.hasErrors() && "avid.acs.attributes/NOT_FOUND".equals(errors.iterator().next().getCode())) {
newNumber = 1;
setNumberOfSubmittedJobs(number, newNumber, setNumberOfSubmittedJobsCallback);
} else {
JavaServiceResponse response = new JavaServiceResponse("submitted", null);
operationContext.respond(response);
}
}
};
getNumberOfSubmittedJobs(getNumberOfSubmittedJobsCallback);
}
}
private void postChannelMessages(final Integer jobId, Integer execTime) {
ChannelMessage message = new MutableChannelMessage("java.service.jobs", "submit");
message.data().put("jobId", jobId);
try {
busAccess.postToChannel(message, getPostToChannelAsyncCallback());
} catch (BusAccessException e) {
LOG.error("Failed to post channel message", e);
}
new Timer().schedule(new TimerTask() {
public void run() {
ChannelMessage message = new MutableChannelMessage("java.service.jobs", "complete");
message.data().put("jobId", jobId);
try {
busAccess.postToChannel(message, getPostToChannelAsyncCallback());
} catch (BusAccessException e) {
LOG.error("Failed to post channel message", e);
}
}
}, execTime);
}
private AsyncCallback<Void> getPostToChannelAsyncCallback() {
return new AsyncCallback<Void>() {
public void onSuccess(Void aVoid) {
LOG.info("Successfully posted to channel");
}
public void onError(CallbackError callbackError) {
LOG.info("Failed post to channel: {}", callbackError.getMessage());
}
};
}
private void getNumberOfSubmittedJobs(GetNumberOfSubmittedJobsCallback callback) {
Message message = new MutableMessage("avid.acs.attributes", "global", 3, "fetch");
message.parameters().put("name", "java.service.store");
try {
MessageOptions options = new MessageOptions(2000);
busAccess.query(message, options, createGetNumberOfSubmittedJobsCallback(callback));
} catch (BusAccessException e) {
callback.handle(true, null, null);
}
}
private void setNumberOfSubmittedJobs(Integer number, int newNumber, SetNumberOfSubmittedJobsCallback callback) {
Message message = new MutableMessage("avid.acs.attributes", "global", 3, "store");
message.parameters().put("name", "java.service.store");
message.parameters().put("value", Collections.singletonMap("numberOfSubmittedJobs", newNumber));
try {
MessageOptions options = new MessageOptions(2000);
busAccess.query(message, options, createSetNumberOfSubmittedJobsCallback(callback, number, newNumber));
} catch (BusAccessException e) {
callback.handle(true, null, number, newNumber);
}
}
private QueryAsyncCallback createGetNumberOfSubmittedJobsCallback(final GetNumberOfSubmittedJobsCallback callback) {
return new QueryAsyncCallback() {
public void onSuccess(Message response) {
final Data value = response.results().get("value");
Integer number = null;
if (value != null && value instanceof JsonData) {
final JsonNode jsonNode = ((JsonData) value).get().get("numberOfSubmittedJobs");
if (jsonNode != null) {
number = jsonNode.asInt();
}
}
callback.handle(false, response.errors(), number);
}
public void onError(CallbackError error) {
LOG.info("Error occurred");
callback.handle(true, null, null);
}
public void onTimeout() {
LOG.info("Timeout occurred");
callback.handle(true, null, null);
}
};
}
private QueryAsyncCallback createSetNumberOfSubmittedJobsCallback(final SetNumberOfSubmittedJobsCallback callback, final Integer number, final Integer newNumber) {
return new QueryAsyncCallback() {
public void onSuccess(Message response) {
callback.handle(false, response.errors(), number, newNumber);
}
public void onError(CallbackError error) {
LOG.info("Error occurred");
callback.handle(true, null, number, newNumber);
}
public void onTimeout() {
LOG.info("Timeout occurred");
callback.handle(true, null, number, newNumber);
}
};
}
private interface GetNumberOfSubmittedJobsCallback {
void handle(boolean responseMissing, ErrorSet errors, Integer number);
}
private interface SetNumberOfSubmittedJobsCallback {
void handle(boolean responseMissing, ErrorSet errors, Integer oldNumber, Integer newNumber);
}
}Build the service:
1
gradle clean build installDist
Set environment variables to use a websocket connection with a protobuf payload:
1
export ACS_GATEWAY_HOST=avid-platform-zone1
Run the application:
1
build/install/java-service/bin/java-service
You should see logs output in the shell.
Press
Ctrl+C
in the shell to terminate the service.
Congratulations! You have successfully published events to the Platform.
At this point the service behavior in the ACS Monitor is unchanged. Next, we create a consumer of our service events.
Subscribing to Service Events
Create a
src/main/java/com/avid/connector/tutorial/JavaClientMain.java
file:1
touch src/main/java/com/avid/connector/tutorial/JavaClientMain.java
Add the following content to the file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83package com.avid.connector.tutorial;
import com.avid.acs.bus.AsyncCallback;
import com.avid.acs.bus.BusAccess;
import com.avid.acs.bus.BusAccessException;
import com.avid.acs.bus.CallbackError;
import com.avid.acs.bus.channel.ChannelMessage;
import com.avid.acs.bus.channel.ChannelSubscriber;
import com.avid.acs.bus.connection.ConnectEvent;
import com.avid.acs.bus.connection.ConnectionListener;
import com.avid.acs.bus.connection.DisconnectEvent;
import com.avid.acs.service.IpcBusAccessFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.util.Collections;
public class JavaClientMain {
private static final Logger LOG = LoggerFactory.getLogger(JavaClientMain.class);
private static BusAccess bus;
public static void main(String[] args) {
try {
bus = new IpcBusAccessFactory().createBusAccess(null, false);
bus.connect(new ConnectionListener() {
public void onConnect(ConnectEvent event) {
LOG.info("Connected to Avid Platform");
}
public void onDisconnect(DisconnectEvent event) {
LOG.info("Disconnected from Avid Platform");
}
});
AsyncCallback<Void> asyncCallback = new AsyncCallback<Void>() {
public void onSuccess(Void aVoid) {
LOG.info("Successfully subscribed to channel");
}
public void onError(CallbackError callbackError) {
LOG.info("Subscribe to channel failed: {}", callbackError.getMessage());
}
};
bus.subscribeToChannel("java.service.jobs", Collections.singletonList("submit"), createSubmitSubscriber(), null, asyncCallback);
bus.subscribeToChannel("java.service.jobs", Collections.singletonList("complete"), createCompleteSubscriber(), null, asyncCallback);
} catch (BusAccessException e) {
LOG.error(e.getMessage(), e);
}
}
private static ChannelSubscriber createSubmitSubscriber() {
return new ChannelSubscriber() {
public void onChannelMessage( { ChannelMessage channelMessage)
LOG.info("Job {} is submitted", channelMessage.data().get("jobId").asInt());
}
public void onEndOfChannel( { String name)
LOG.info("Deprecated, not used method");
}
};
}
private static ChannelSubscriber createCompleteSubscriber() {
return new ChannelSubscriber() {
public void onChannelMessage( { ChannelMessage channelMessage)
LOG.info("Job {} is completed", channelMessage.data().get("jobId").asInt());
}
public void onEndOfChannel( { String name)
LOG.info("Deprecated, not used method");
}
};
}
}Modify the
build.gradle
file to support multiple main classes for the application plugin:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39apply plugin: 'java'
apply from: "${projectDir}/versions.gradle"
apply plugin: 'application'
group = 'com.avid.connector.tutorial'
version = '0.0.1-SNAPSHOT'
applicationName = 'java-service'
repositories {
mavenCentral()
flatDir {
dirs '../../../libs'
}
}
dependencies {
compile fileTree(dir: '../../../libs', include: '*.jar')
compile group: 'ch.qos.logback', name: 'logback-core', version: "${logbackVersion}"
compile group: 'ch.qos.logback', name: 'logback-classic', version: "${logbackVersion}"
compile group: 'com.google.code.findbugs', name: 'jsr305', version: "${jsr305}"
}
mainClassName = 'com.avid.connector.tutorial.JavaServiceMain'
task moreStartScripts(type: CreateStartScripts) {
mainClassName = "com.avid.connector.tutorial.JavaClientMain"
applicationName = "java-client"
outputDir = new File(project.buildDir, 'scripts')
classpath = jar.outputs.files + project.configurations.runtime
}
applicationDistribution.into("bin") {
from(moreStartScripts)
fileMode = 0755
}Build the service:
1
gradle clean build installDist
Set environment variables to use a websocket connection with a protobuf payload:
1
export ACS_GATEWAY_HOST=avid-platform-zone1
Run the service:
1
build/install/java-service/bin/java-service
Open a new shell in the same location. Set environment variables to use websocket connection with protobuf payload:
1
export ACS_GATEWAY_HOST=avid-platform-zone1
Run the client:
1
build/install/java-service/bin/java-client
Test that the service and client are working:
- Open ACS Monitor, and filter the services list by entering the
java\.service
regular expression in the box. You should now see only one service in the list:avid.tutorial.java.service
. - Send multiple
submitJob
messages to your service with different values. Examine the logs of your client. The logs ought to contain each “job submit” and “job complete” event.
- Open ACS Monitor, and filter the services list by entering the
Congratulations! You have created a client that subscribes to the events being posted by your service.
Sending REST Requests to Your Service
Log in to
avid-platform-zone1
with ssh (username: root; password: vagrant):1
ssh root@avid-platform-zone1
Send a POST request to your service using the
curl
command. Use the avidAccessToken generated previously:1
2
3curl -H "Content-Type: application/json" --data '{"execTime":15000}' "http://avid-platform-zone1:8080/apis/avid.tutorial.java.service;version=1/jobs/10?_avidAccessToken=ZWEwZDhlOTItYmJhZS00YTQ5LWFiN2QtNTAyYWMwZjhjZjJj"
#NOTE: The request should be routed via NGINX and use the HTTPS protocol: https://avid-platform-zone1/apis/...Given HTTP request will send following message to your service:
1
2
3
4
5
6
7
8
9
10{
"serviceType": "avid.tutorial.java.service",
"serviceRealm": "global",
"serviceVersion": 1,
"op": "submitJob",
"paramSet": {
"jobId": 10,
"execTime": 15000
}
}Press
Ctrl+C
in the shells to terminate both the service and client.
Delivering the Service as an RPM on CentOS 6.x/RedHat 6.x
For this tutorial and the next one, you need a CentOS 6.x/RedHat 6.x system. You may use your VM (created in the first steps of this tutorial) with the host name avid-platform-zone1
. The following steps assume you are using this VM to test the RPM.
Add a Gradle wrapper to your build. The wrapper allows the service to operate, even if Gradle is not in the PATH:
1
gradle wrapper
At this point your current directory should be
java-service/module
. Go up one level. Your current directory must bejava-service
:1
cd ..
Create a
java-service.spec
file:1
touch java-service.spec
Add the following content to the file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46%global debug_package %{nil}
Name: java-service
Version: 1
Release: 1
Summary: Tutorial service based on Avid Connector API for Java
License: Avid EULA
Vendor: Avid Technology Inc.
URL: http://avid.com
Source: %{name}-src.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
Prefix: /opt/avid
AutoReqProv: no
Requires: jre-1.7.0-avid-openjdk-x86_64
%description
Tutorial service based on Avid Connector API for Java
%build
if [ ! -f module/build/install/%{name}/bin/%{name} ]
then
echo "You have to call 'module/gradlew -p module clean installDist' first"
exit -1
fi
%prep
%setup -c -n %{name}
%install
mkdir -p "${RPM_BUILD_ROOT}/opt/avid/sbin"
mkdir -p "${RPM_BUILD_ROOT}/opt/avid/lib/java_modules/%{name}/bin"
mkdir -p "${RPM_BUILD_ROOT}/opt/avid/lib/java_modules/%{name}/lib"
mv module/build/install/java-service/bin/* "${RPM_BUILD_ROOT}/opt/avid/lib/java_modules/%{name}/bin/"
mv module/build/install/java-service/lib/* "${RPM_BUILD_ROOT}/opt/avid/lib/java_modules/%{name}/lib/"
ln -s /opt/avid/lib/java_modules/%{name}/bin/%{name} "${RPM_BUILD_ROOT}/opt/avid/sbin/%{name}"
# Exit clean
exit 0
%files
%attr(755, root, root) /opt/avid/lib/java_modules/%{name}/bin/%{name}
/opt/avid/sbin/%{name}
/opt/avid/lib/java_modules/%{name}Create a
create-rpm.sh
file and make it executable:1
2touch create-rpm.sh
chmod 755 create-rpm.shAdd the following content to the file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# Print usage if we have no args
if [ $# -eq 0 ]; then
echo 'Usage:'
echo ' create-rpm <spec-file>'
echo 'Description:'
echo ' Used by jenkins jobs to create rpms. Specify'
echo ' the spec file and this script will tar the'
echo ' directory containing that file and run rpmbuild'
echo ' in the parent dir. See source for details.'
echo 'Examples:'
echo ' ./create-rpm.sh controllers/registry/avid-acs-registry.spec'
echo ' Creates ./rpmbuild/RPMS/x86_64/<rpm-name>.rpm'
exit 0
fi
RPM_VERSION=${RPM_VERSION-"0.0.0_DEV"}
if [ -f ./VERSION ]; then
# file generated by jenkins job
RPM_VERSION="$(cat ./VERSION)"
fi
ORIGINAL_DIR="$(pwd)"
# The directory that we are going to tar
SOURCE_DIR="$(dirname $1)"
# The base name of the spec file. This name will be used
# for the tar file.
SPEC_NAME="$(basename $1 .spec)"
# Set up rpmbuild tree
mkdir -p rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
pushd "$SOURCE_DIR"
# Compress sources and move into rpmbuild tree
tar -czf "$SPEC_NAME-src.tgz" ./* --exclude=.acignore --exclude=node_modules --exclude=*.swp --exclude=*~
mv "$SPEC_NAME-src.tgz" "$ORIGINAL_DIR/rpmbuild/SOURCES/"
# Copy spec into rpmbuild tree
cp "$SPEC_NAME.spec" "$ORIGINAL_DIR/rpmbuild/SPECS/"
popd
# Build rpm
rpmbuild --define "_rpmversion $RPM_VERSION" --define "_topdir `pwd`/rpmbuild" -bb "rpmbuild/SPECS/$SPEC_NAME.spec"Build your project:
1
module/gradlew -p module clean installDist
Copy your project to
avid-platform-zone1
using ssh (username: root; password: vagrant). Your current directory should bejava-service
:1
scp -r ../java-service root@avid-platform-zone1:/root/
Log in to
avid-platform-zone1
using ssh (username: root; password: vagrant):1
ssh root@avid-platform-zone1
In the
ssh
shell change your current directory:1
cd /root/java-service
Install the needed RPM development tools:
1
yum install rpm-devel
Make a
create-rpm.sh
executable and create the RPM:1
2chmod 755 create-rpm.sh
./create-rpm.sh java-service.specInstall your RPM:
1
yum install rpmbuild/RPMS/x86_64/java-service-1-1.x86_64.rpm
Run the installed service:
1
/opt/avid/sbin/java-service
Test that the service is working in the ACS Monitor
Press
Ctrl+C
to terminate the service.
Congratulations! You have successfully created an RPM for your service, installed and started it.
Daemonizing the Service on CentOS 6.x/RedHat 6.x
In this section we daemonize the service, provide a default configuration for it, output logs into a file and add log rotation. If you are still in the ssh shell from the previous section, exit and return to your project directory. Your current directory must be java-service
.
Modify the
module/src/main/java/com/avid/connector/tutorial/JavaServiceImpl.java
file. Add theserviceIsRegistered
andreportServiceIsReady
methods:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233package com.avid.connector.tutorial;
import com.avid.acs.bus.*;
import com.avid.acs.bus.annotations.Subscribe;
import com.avid.acs.bus.channel.ChannelMessage;
import com.avid.acs.bus.channel.MutableChannelMessage;
import com.avid.acs.bus.error.BusError;
import com.avid.acs.bus.error.ErrorSet;
import com.avid.acs.bus.message.Message;
import com.avid.acs.bus.message.MessageOptions;
import com.avid.acs.bus.message.MutableMessage;
import com.avid.acs.bus.message.data.Data;
import com.avid.acs.bus.message.data.JsonData;
import com.avid.acs.bus.service.events.ServiceRegisteredEvent;
import com.fasterxml.jackson.databind.JsonNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
public class JavaServiceImpl implements JavaService {
private static final Logger LOG = LoggerFactory.getLogger(JavaServiceImpl.class);
private final BusAccess busAccess;
public JavaServiceImpl(BusAccess busAccess) {
this.busAccess = busAccess;
}
public void serviceIsRegistered(ServiceRegisteredEvent event) {
reportServiceIsReady();
}
public void submitJob(final Integer jobId, Integer execTime, final OperationContext<JavaServiceResponse> operationContext) {
if (jobId == null || jobId < 1) {
Map<String, String> params = Collections.singletonMap("jobId", jobId == null ? "null" : String.valueOf(jobId));
operationContext.error(new BusError("INVALID_JOB_ID", params, "Invalid jobId was provided. Must be integer > 0."));
} else if (execTime == null || execTime < 100) {
Map<String, String> params = Collections.singletonMap("execTime", execTime == null ? "null" : String.valueOf(execTime));
operationContext.error(new BusError("INVALID_EXECUTION_TIME", params, "Invalid execution time provided. Must be integer >= 100."));
} else {
postChannelMessages(jobId, execTime);
final SetNumberOfSubmittedJobsCallback setNumberOfSubmittedJobsCallback = new SetNumberOfSubmittedJobsCallback() {
public void handle(boolean responseMissing, ErrorSet errors, Integer oldNumber, Integer newNumber) {
JavaServiceResponse response;
if (responseMissing || errors.hasErrors()) {
response = new JavaServiceResponse("submitted", oldNumber);
} else {
response = new JavaServiceResponse("submitted", newNumber);
}
operationContext.respond(response);
}
};
final GetNumberOfSubmittedJobsCallback getNumberOfSubmittedJobsCallback = new GetNumberOfSubmittedJobsCallback() {
public void handle(boolean responseMissing, ErrorSet errors, Integer number) {
Integer newNumber;
if (!responseMissing && !errors.hasErrors()) {
newNumber = number + 1;
setNumberOfSubmittedJobs(number, newNumber, setNumberOfSubmittedJobsCallback);
} else if (errors != null && errors.hasErrors() && "avid.acs.attributes/NOT_FOUND".equals(errors.iterator().next().getCode())) {
newNumber = 1;
setNumberOfSubmittedJobs(number, newNumber, setNumberOfSubmittedJobsCallback);
} else {
JavaServiceResponse response = new JavaServiceResponse("submitted", null);
operationContext.respond(response);
}
}
};
getNumberOfSubmittedJobs(getNumberOfSubmittedJobsCallback);
}
}
private void postChannelMessages(final Integer jobId, Integer execTime) {
ChannelMessage message = new MutableChannelMessage("java.service.jobs", "submit");
message.data().put("jobId", jobId);
try {
busAccess.postToChannel(message, getPostToChannelAsyncCallback());
} catch (BusAccessException e) {
LOG.error("Failed to post channel message", e);
}
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
ChannelMessage message = new MutableChannelMessage("java.service.jobs", "complete");
message.data().put("jobId", jobId);
try {
busAccess.postToChannel(message, getPostToChannelAsyncCallback());
} catch (BusAccessException e) {
LOG.error("Failed to post channel message", e);
}
timer.cancel();
}
}, execTime);
}
private AsyncCallback<Void> getPostToChannelAsyncCallback() {
return new AsyncCallback<Void>() {
public void onSuccess(Void aVoid) {
LOG.info("Successfully posted to channel");
}
public void onError(CallbackError callbackError) {
LOG.info("Failed post to channel: {}", callbackError.getMessage());
}
};
}
private void getNumberOfSubmittedJobs(GetNumberOfSubmittedJobsCallback callback) {
Message message = new MutableMessage("avid.acs.attributes", "global", 3, "fetch");
message.parameters().put("name", "java.service.store");
try {
MessageOptions options = new MessageOptions(2000);
busAccess.query(message, createGetNumberOfSubmittedJobsResponseHandler(callback), options);
} catch (BusAccessException e) {
callback.handle(true, null, null);
}
}
private void setNumberOfSubmittedJobs(Integer number, int newNumber, SetNumberOfSubmittedJobsCallback callback) {
Message message = new MutableMessage("avid.acs.attributes", "global", 3, "store");
message.parameters().put("name", "java.service.store");
message.parameters().put("value", Collections.singletonMap("numberOfSubmittedJobs", newNumber));
try {
MessageOptions options = new MessageOptions(2000);
busAccess.query(message, createSetNumberOfSubmittedJobsResponseHandler(callback, number, newNumber), options);
} catch (BusAccessException e) {
callback.handle(true, null, number, newNumber);
}
}
private ResponseHandler createGetNumberOfSubmittedJobsResponseHandler(final GetNumberOfSubmittedJobsCallback callback) {
return new ResponseHandler() {
public void onResponse( { Message response)
final Data value = response.results().get("value");
Integer number = null;
if (value != null && value instanceof JsonData) {
final JsonNode jsonNode = ((JsonData) value).get().get("numberOfSubmittedJobs");
if (jsonNode != null) {
number = jsonNode.asInt();
}
}
callback.handle(false, response.errors(), number);
}
public void onMessageProcessingError( { String error)
LOG.info("Error occurred");
callback.handle(true, null, null);
}
public void onTimeout() {
LOG.info("Timeout occurred");
callback.handle(true, null, null);
}
};
}
private ResponseHandler createSetNumberOfSubmittedJobsResponseHandler(final SetNumberOfSubmittedJobsCallback callback, final Integer number, final Integer newNumber) {
return new ResponseHandler() {
public void onResponse( { Message response)
callback.handle(false, response.errors(), number, newNumber);
}
public void onMessageProcessingError( { String error)
LOG.info("Error occurred");
callback.handle(true, null, number, newNumber);
}
public void onTimeout() {
LOG.info("Timeout occurred");
callback.handle(true, null, number, newNumber);
}
};
}
private interface GetNumberOfSubmittedJobsCallback {
void handle(boolean responseMissing, ErrorSet errors, Integer number);
}
private interface SetNumberOfSubmittedJobsCallback {
void handle(boolean responseMissing, ErrorSet errors, Integer oldNumber, Integer newNumber);
}
private static void reportServiceIsReady() {
String lockFile = System.getenv("AVID_SERVICE_LOCK_FILE");
if (lockFile == null) {
LOG.info("AVID_SERVICE_LOCK_FILE is not provided");
return;
}
File f = new File(lockFile);
f.getParentFile().mkdirs();
try {
boolean createdNew = f.createNewFile();
if (!createdNew) {
LOG.warn("File [{}] was already created", lockFile);
}
} catch (IOException e) {
LOG.info("Failed to create file [{}]", lockFile);
}
}
}Create
etc
,etc/init.d
,etc/syconfig
andetc/logrotate.d
directories:1
2
3
4mkdir etc
mkdir etc/init.d
mkdir etc/sysconfig
mkdir etc/logrotate.dCreate a
java-service
file in each off theetc/init.d
,etc/syconfig
andetc/logrotate.d
directories:1
2
3touch etc/init.d/java-service
touch etc/sysconfig/java-service
touch etc/logrotate.d/java-serviceAdd the following content to
etc/sysconfig/java-service
:1
2
3
4#export ACS_GATEWAY_HOST=127.0.0.1
#export ACS_GATEWAY_PORT=9900
#export ACS_BUS_QUERY_TIMEOUT=10000
#export ACS_LOG_LEVEL=infoAdd the following content to
etc/logrotate.d/java-service
:1
2
3
4
5
6
7
8
9/var/log/avid/java-service/java-service.log {
size 100M
rotate 10
copytruncate
missingok
notifempty
compress
delaycompress
}Add the following content to
etc/init.d/java-service
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#
# java-service Tutorial Java service
#
# chkconfig: - 86 15
# description: Tutorial service based on Avid Connector API for Java
#
### BEGIN INIT INFO
# Provides: java-service
# Required-Start: $local_fs $network $remote_fs
# Required-Stop: $local_fs $network $remote_fs
# Short-Description: Tutorial Java service
# Description: Tutorial service based on Avid Connector API for Java
#
### END INIT INFO
AVID_SERVICE_NAME="java-service"
AVID_SERVICE_PID_FILE="/var/run/avid/${AVID_SERVICE_NAME}/${AVID_SERVICE_NAME}.pid"
AVID_SERVICE_START_CMD="/opt/avid/sbin/${AVID_SERVICE_NAME}"
AVID_SERVICE_STDOUT_FILE="/var/log/avid/${AVID_SERVICE_NAME}/${AVID_SERVICE_NAME}.log"
AVID_SERVICE_STDERR_FILE="/var/log/avid/${AVID_SERVICE_NAME}/${AVID_SERVICE_NAME}.err"
AVID_SERVICE_LOCK_FILE="/var/lock/avid/${AVID_SERVICE_NAME}/${AVID_SERVICE_NAME}.lock"
# Source service config file if it exists
[ -e /etc/sysconfig/$AVID_SERVICE_NAME ] && . /etc/sysconfig/$AVID_SERVICE_NAME
# Source function library.
. /opt/avid/share/avid-daemonizer/avid-daemonizer.sh
# Call into the generic case statement in acs-functions
avid_svc_ctrl "$@"Modify
java-service.spec
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68%global debug_package %{nil}
Name: java-service
Version: 2
Release: 1
Summary: Tutorial service based on Avid Connector API for Java
License: Avid EULA
Vendor: Avid Technology Inc.
URL: http://avid.com
Source: %{name}-src.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
Prefix: /opt/avid
AutoReqProv: no
Requires: jre-1.7.0-avid-openjdk-x86_64
%description
Tutorial service based on Avid Connector API for Java
%build
if [ ! -f module/build/install/%{name}/bin/%{name} ]
then
echo "You have to call 'module/gradlew -p module clean installDist' first"
exit -1
fi
%prep
%setup -c -n %{name}
%install
mkdir -p "${RPM_BUILD_ROOT}/opt/avid/sbin"
mkdir -p "${RPM_BUILD_ROOT}/opt/avid/lib/java_modules/%{name}/bin"
mkdir -p "${RPM_BUILD_ROOT}/opt/avid/lib/java_modules/%{name}/lib"
mv etc "${RPM_BUILD_ROOT}/"
mv module/build/install/java-service/bin/* "${RPM_BUILD_ROOT}/opt/avid/lib/java_modules/%{name}/bin/"
mv module/build/install/java-service/lib/* "${RPM_BUILD_ROOT}/opt/avid/lib/java_modules/%{name}/lib/"
ln -s /opt/avid/lib/java_modules/%{name}/bin/%{name} "${RPM_BUILD_ROOT}/opt/avid/sbin/%{name}"
%post
chkconfig --add %{name}
# Exit clean
exit 0
%preun
service %{name} stop ||:
# If uninstalling, remove chkconfig entry
if [ $1 -eq 0 ]; then
chkconfig --del %{name}
fi
%postun
# If upgrading, restart the service if it was already running
if [ $1 -eq 1 ]; then
chkconfig %{name} off
service %{name} condrestart ||:
fi
%files
%attr(755, root, root) /etc/init.d/%{name}
%attr(644, root, root) /etc/logrotate.d/%{name}
%attr(644, root, root) /etc/sysconfig/%{name}
%attr(755, root, root) /opt/avid/lib/java_modules/%{name}/bin/%{name}
/opt/avid/sbin/%{name}
/opt/avid/lib/java_modules/%{name}Build your project:
1
module/gradlew -p module clean installDist
Copy your project to
avid-platform-zone1
using ssh (username: root; password: vagrant). Your current directory should bejava-service
:1
scp -r ../java-service root@avid-platform-zone1:/root/
Log in to
avid-platform-zone1
with ssh (username: root; password: vagrant):1
ssh root@avid-platform-zone1
In the
ssh
shell change your current directory:1
cd /root/java-service
If you created files on Windows, make sure to use the LF line separator for bash scripts; if not, convert CRLF to LF with the following command:
1
2
3sed -i 's/\r//' create-rpm.sh
sed -i 's/\r//' etc/init.d/node-service
sed -i 's/\r//' module/bin/node-service.jsOr, you can use vi or vim editor to change the file format by opening the file in the editor and then use :set ff=unix and then save the file.
Create the RPM:
1
./create-rpm.sh java-service.spec
Install your RPM:
1
yum install rpmbuild/RPMS/x86_64/java-service-2-1.x86_64.rpm
Run the installed service:
1
service java-service start
Test that the service is working using the ACS Monitor as previously outlined.
Monitor the service logs:
1
2
3service java-service logs
#or
tail -f /var/log/avid/java-service/java-service.log
Congratulations! You have successfully completed the tutorial.