-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathPod.java
More file actions
288 lines (257 loc) · 10.9 KB
/
Pod.java
File metadata and controls
288 lines (257 loc) · 10.9 KB
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
package solid;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import cartago.Artifact;
import cartago.OPERATION;
import cartago.OpFeedbackParam;
/**
* A CArtAgO artifact that agent can use to interact with LDP containers in a
* Solid pod.
*/
public class Pod extends Artifact {
private String podURL; // the location of the Solid pod
/**
* Method called by CArtAgO to initialize the artifact.
*
* @param podURL The location of a Solid pod
*/
public void init(String podURL) {
this.podURL = podURL;
log("Pod artifact initialized for: " + this.podURL);
}
/**
* Return true if container exists in the Solid pod, false otherwise.
*
* @param containerName The name of the container to check
* @return true if the HEAD request returns 200, false otherwise
*/
private boolean containerExists(String containerName) {
ProcessBuilder processBuilder = new ProcessBuilder(
"curl",
"-s", // Silent mode
"-o", "/dev/null", // Discard output
"-w", "%{http_code}", // Write only the HTTP status code
"-I", // HEAD request
podURL + containerName + "/");
try {
Process process = processBuilder.start();
// Read the HTTP status code - should be a single line
String statusCode;
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
statusCode = reader.readLine();
}
process.waitFor();
// Check if status code is 200
return "200".equals(statusCode.toString().trim());
} catch (IOException | InterruptedException e) {
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
return false; // Assume container doesn't exist if there's an error
}
}
/**
* CArtAgO operation for creating a Linked Data Platform container in the Solid
* pod
*
* @param containerName The name of the container to be created
*
*/
@OPERATION
public void createContainer(String containerName) {
// Check whether container already exists
if (containerExists(containerName)) {
System.out.println("Container already exists: " + containerName);
return;
}
ProcessBuilder processBuilder = new ProcessBuilder(
"curl",
"-X", "PUT",
"-H", "Content-Type: text/turtle",
"-d", "",
podURL + containerName + "/");
// Set error redirection to capture both stdout and stderr in one stream
processBuilder.redirectErrorStream(true);
try {
Process process = processBuilder.start();
// Handle timeout of request
if (!process.waitFor(30, TimeUnit.SECONDS)) {
process.destroyForcibly();
throw new TimeoutException("Container creation timed out: " + containerName);
}
// Handle process exit
int exitCode = process.exitValue();
if (exitCode != 0) {
System.err.println("Container creation failed with exit code: " + exitCode);
}
} catch (IOException | InterruptedException | TimeoutException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Failed to create container: " + containerName, e);
}
}
/**
* CArtAgO operation for publishing data within a .txt file in a Linked Data
* Platform container of the Solid pod
*
* @param containerName The name of the container where the .txt file resource
* will be created
* @param fileName The name of the .txt file resource to be created in the
* container
* @param data An array of Object data that will be stored in the .txt
* file
*/
@OPERATION
public void publishData(String containerName, String fileName, Object[] data) {
ProcessBuilder processBuilder = new ProcessBuilder(
"curl",
"-X", "PUT",
"-H", "Content-Type: text/plain",
"-d", createStringFromArray(data),
podURL + containerName + "/" + fileName);
// Set error redirection to capture both stdout and stderr in one stream
processBuilder.redirectErrorStream(true);
try {
Process process = processBuilder.start();
// Handle timeout of request
if (!process.waitFor(30, TimeUnit.SECONDS)) {
process.destroyForcibly();
throw new TimeoutException("Data publish timed out: " + containerName);
}
// Handle process exit
int exitCode = process.exitValue();
if (exitCode != 0) {
System.err.println("Data publish failed with exit code: " + exitCode);
}
} catch (IOException | InterruptedException | TimeoutException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Failed to publish data: " + containerName, e);
}
}
/**
* CArtAgO operation for reading data of a .txt file in a Linked Data Platform
* container of the Solid pod
*
* @param containerName The name of the container where the .txt file resource
* is located
* @param fileName The name of the .txt file resource that holds the data
* to be read
* @param data An array whose elements are the data read from the .txt
* file
*/
@OPERATION
public void readData(String containerName, String fileName, OpFeedbackParam<Object[]> data) {
data.set(readData(containerName, fileName));
}
/**
* Method for reading data of a .txt file in a Linked Data Platform container of
* the Solid pod
*
* @param containerName The name of the container where the .txt file resource
* is located
* @param fileName The name of the .txt file resource that holds the data
* to be read
* @return An array whose elements are the data read from the .txt file
*/
/**
* Reads data from a file in a Linked Data Platform container
*
* @param containerName The name of the container
* @param fileName The name of the file to read
* @return Object array containing the parsed data, or empty array on error
*/
public Object[] readData(String containerName, String fileName) {
ProcessBuilder processBuilder = new ProcessBuilder(
"curl",
"-s", // Silent mode
"--fail", // Fail silently on server errors
"-H", "Accept: text/plain",
"-m", "30", // Set a timeout of 30 seconds
podURL + containerName + "/" + fileName);
try {
Process process = processBuilder.start();
// Read data more efficiently using InputStreamReader
StringBuilder data = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) {
char[] buffer = new char[8192]; // 8K buffer for efficiency
int bytesRead;
while ((bytesRead = reader.read(buffer)) != -1) {
data.append(buffer, 0, bytesRead);
}
}
// Handle timeout of request
if (!process.waitFor(30, TimeUnit.SECONDS)) {
process.destroyForcibly();
System.err.println("Read operation timed out for: " + containerName + "/" + fileName);
return new Object[0];
}
// Handle process exit
int exitCode = process.exitValue();
if (exitCode != 0) {
System.err.println("Failed to read data with exit code: " + exitCode);
return new Object[0];
}
return createArrayFromString(data.toString());
} catch (IOException | InterruptedException e) {
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt(); // Preserve interrupted status
}
System.err.println("Error reading data from " + containerName + "/" + fileName + ": " + e.getMessage());
return new Object[0];
}
}
/**
* Method that converts an array of Object instances to a string,
* e.g. the array ["one", 2, true] is converted to the string "one\n2\ntrue\n"
*
* @param array The array to be converted to a string
* @return A string consisting of the string values of the array elements
* separated by "\n"
*/
public static String createStringFromArray(Object[] array) {
StringBuilder sb = new StringBuilder();
for (Object obj : array) {
sb.append(obj.toString()).append("\n");
}
return sb.toString();
}
/**
* Method that converts a string to an array of Object instances computed by
* splitting the given string with delimiter "\n"
* e.g. the string "one\n2\ntrue\n" is converted to the array ["one", "2",
* "true"]
*
* @param str The string to be converted to an array
* @return An array consisting of string values that occur by splitting the
* string around "\n"
*/
public static Object[] createArrayFromString(String str) {
return str.split("\n");
}
/**
* CArtAgO operation for updating data of a .txt file in a Linked Data Platform
* container of the Solid pod
* The method reads the data currently stored in the .txt file and publishes in
* the file the old data along with new data
*
* @param containerName The name of the container where the .txt file resource
* is located
* @param fileName The name of the .txt file resource that holds the data
* to be updated
* @param data An array whose elements are the new data to be added in
* the .txt file
*/
@OPERATION
public void updateData(String containerName, String fileName, Object[] data) {
Object[] oldData = readData(containerName, fileName);
Object[] allData = new Object[oldData.length + data.length];
System.arraycopy(oldData, 0, allData, 0, oldData.length);
System.arraycopy(data, 0, allData, oldData.length, data.length);
publishData(containerName, fileName, allData);
}
}