1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.fosstrak.epcis.captureclient;
22
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.io.OutputStreamWriter;
29 import java.io.StringWriter;
30 import java.net.Authenticator;
31 import java.net.HttpURLConnection;
32 import java.net.MalformedURLException;
33 import java.net.PasswordAuthentication;
34 import java.net.ProtocolException;
35 import java.net.URL;
36 import java.security.KeyStore;
37 import java.security.SecureRandom;
38 import java.security.cert.CertificateException;
39 import java.security.cert.X509Certificate;
40 import java.util.Properties;
41
42 import javax.net.ssl.HostnameVerifier;
43 import javax.net.ssl.HttpsURLConnection;
44 import javax.net.ssl.KeyManagerFactory;
45 import javax.net.ssl.SSLContext;
46 import javax.net.ssl.SSLSession;
47 import javax.net.ssl.TrustManager;
48 import javax.net.ssl.X509TrustManager;
49 import javax.xml.bind.JAXBContext;
50 import javax.xml.bind.JAXBElement;
51 import javax.xml.bind.JAXBException;
52 import javax.xml.bind.Marshaller;
53
54 import org.fosstrak.epcis.model.Document;
55 import org.fosstrak.epcis.model.EPCISDocumentType;
56 import org.fosstrak.epcis.model.EPCISMasterDataDocumentType;
57 import org.fosstrak.epcis.model.ObjectFactory;
58 import org.fosstrak.epcis.utils.AuthenticationType;
59
60
61
62
63
64
65
66
67
68 public class CaptureClient implements X509TrustManager, HostnameVerifier {
69
70 private static final String PROPERTY_FILE = "/captureclient.properties";
71 private static final String PROPERTY_CAPTURE_URL = "default.url";
72 private static final String DEFAULT_CAPTURE_URL = "http://demo.fosstrak.org/epcis/capture";
73
74
75
76
77 private String captureUrl;
78
79 private Object[] authOptions;
80
81
82
83
84 public CaptureClient() {
85 this(null, null);
86 }
87
88
89
90
91
92
93
94 public CaptureClient(String url) {
95 this(url, null);
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 public CaptureClient(final String url, Object[] authOptions) {
126
127 if (url != null) {
128 captureUrl = url;
129 } else {
130 Properties props = loadProperties();
131 if (props != null) {
132 captureUrl = props.getProperty(PROPERTY_CAPTURE_URL);
133 }
134 if (captureUrl == null) {
135 captureUrl = DEFAULT_CAPTURE_URL;
136 }
137 }
138 this.authOptions = authOptions;
139 }
140
141
142
143
144 private Properties loadProperties() {
145 Properties props = new Properties();
146 InputStream is = getClass().getResourceAsStream(PROPERTY_FILE);
147 if (is != null) {
148 try {
149 props.load(is);
150 is.close();
151 } catch (IOException e) {
152 System.out.println("Unable to load properties from " + PROPERTY_FILE + ". Using defaults.");
153 }
154 } else {
155 System.out.println("Unable to load properties from file " + PROPERTY_FILE + ". Using defaults.");
156 }
157 return props;
158 }
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173 public int capture(final InputStream xmlStream) throws CaptureClientException {
174 try {
175 return doPost(xmlStream, "text/xml");
176 } catch (IOException e) {
177 throw new CaptureClientException("error communicating with EPCIS cpature interface: " + e.getMessage(), e);
178 }
179 }
180
181
182
183
184
185
186
187
188
189
190
191
192 public int capture(final String eventXml) throws CaptureClientException {
193 try {
194 return doPost(eventXml, "text/xml");
195 } catch (IOException e) {
196 throw new CaptureClientException("error communicating with EPCIS cpature interface: " + e.getMessage(), e);
197 }
198 }
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213 public int capture(final Document epcisDoc) throws CaptureClientException {
214 StringWriter writer = new StringWriter();
215 ObjectFactory objectFactory = new ObjectFactory();
216 try {
217 JAXBContext context = JAXBContext.newInstance("org.fosstrak.epcis.model");
218 JAXBElement<? extends Document> item;
219 if (epcisDoc instanceof EPCISDocumentType) {
220 item = objectFactory.createEPCISDocument((EPCISDocumentType) epcisDoc);
221 } else {
222 item = objectFactory.createEPCISMasterDataDocument((EPCISMasterDataDocumentType) epcisDoc);
223 }
224 Marshaller marshaller = context.createMarshaller();
225 marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
226 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
227 marshaller.marshal(item, writer);
228 } catch (JAXBException e) {
229 throw new CaptureClientException("error serializing EPCIS Document: " + e.getMessage(), e);
230 }
231 return capture(writer.toString());
232 }
233
234
235
236
237
238
239
240
241
242
243
244 public int dbReset() throws CaptureClientException {
245 String formParam = "dbReset=true";
246 try {
247 return doPost(formParam, "application/x-www-form-urlencoded");
248 } catch (IOException e) {
249 throw new CaptureClientException("error communicating with EPCIS cpature interface: " + e.getMessage(), e);
250 }
251 }
252
253 private boolean isEmpty(String s) {
254 return s == null || "".equals(s);
255 }
256
257
258
259
260
261
262
263
264 private HttpURLConnection getConnection(final String contentType) throws CaptureClientException, IOException {
265 URL serviceUrl;
266 try {
267 serviceUrl = new URL(captureUrl);
268 } catch (MalformedURLException e) {
269 throw new CaptureClientException(captureUrl + " is not an URL", e);
270 }
271 HttpURLConnection connection;
272 SSLContext sslContext = null;
273
274 if (authOptions != null) {
275
276 if (AuthenticationType.BASIC.equals(authOptions[0])) {
277
278
279
280
281 final String username = (String) authOptions[1];
282 final String password = (String) authOptions[2];
283
284 if (isEmpty(username) || isEmpty(password)) {
285 throw new CaptureClientException("Authentication method " + authOptions[0]
286 + " requires a valid user name and password");
287 }
288
289 Authenticator.setDefault(new Authenticator() {
290 @Override
291 protected PasswordAuthentication getPasswordAuthentication() {
292 return new PasswordAuthentication(username, password.toCharArray());
293 }
294 });
295
296 } else if (AuthenticationType.HTTPS_WITH_CLIENT_CERT.equals(authOptions[0])) {
297
298
299
300
301 if (!"HTTPS".equalsIgnoreCase(serviceUrl.getProtocol())) {
302 throw new CaptureClientException("Authentication method " + authOptions[0]
303 + " requires the use of HTTPS");
304 }
305
306 String keyStoreFile = (String) authOptions[1];
307 String password = (String) authOptions[2];
308
309 if (isEmpty(keyStoreFile) || isEmpty(password)) {
310 throw new CaptureClientException("Authentication method " + authOptions[0]
311 + " requires a valid keystore (PKCS12 or JKS) and password");
312 }
313
314 try {
315 KeyStore keyStore = KeyStore.getInstance(keyStoreFile.endsWith(".p12") ? "PKCS12" : "JKS");
316 keyStore.load(new FileInputStream(new File(keyStoreFile)), password.toCharArray());
317
318 Authenticator.setDefault(null);
319 sslContext = getSSLContext(keyStore, password.toCharArray());
320 } catch (Throwable t) {
321 throw new CaptureClientException("unable to load keystore or set up SSL context", t);
322 }
323 } else {
324 Authenticator.setDefault(null);
325 }
326 } else {
327 Authenticator.setDefault(null);
328 }
329
330 connection = (HttpURLConnection) serviceUrl.openConnection();
331 if (sslContext != null && connection instanceof HttpsURLConnection) {
332 HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
333 httpsConnection.setHostnameVerifier(this);
334 httpsConnection.setSSLSocketFactory(sslContext.getSocketFactory());
335 }
336 connection.setRequestProperty("content-type", contentType);
337 try {
338 connection.setRequestMethod("POST");
339 } catch (ProtocolException e) {
340 throw new CaptureClientException("unable to set HTTP request method POST", e);
341 }
342 connection.setDoInput(true);
343 connection.setDoOutput(true);
344 return connection;
345 }
346
347
348
349
350
351
352
353
354
355
356
357 private int doPost(final String data, final String contentType) throws CaptureClientException, IOException {
358 HttpURLConnection connection = getConnection(contentType);
359
360 OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream());
361 wr.write(data);
362 wr.flush();
363 wr.close();
364
365 return connection.getResponseCode();
366 }
367
368
369
370
371
372
373
374
375
376
377
378
379 private int doPost(final InputStream data, final String contentType) throws IOException, CaptureClientException {
380 HttpURLConnection connection = getConnection(contentType);
381
382 OutputStream os = connection.getOutputStream();
383 int b;
384 while ((b = data.read()) != -1) {
385 os.write(b);
386 }
387 os.flush();
388 os.close();
389
390 return connection.getResponseCode();
391 }
392
393
394
395
396 public String getCaptureUrl() {
397 return captureUrl;
398 }
399
400 public Object[] getAuthOptions() {
401 return authOptions;
402 }
403
404
405
406
407
408 public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
409 }
410
411 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
412 }
413
414 public X509Certificate[] getAcceptedIssuers() {
415 return null;
416 }
417
418
419
420
421
422 public boolean verify(String arg0, SSLSession arg1) {
423 return true;
424 }
425
426 private SSLContext getSSLContext(KeyStore keyStore, char[] password) throws Exception {
427 KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
428 keyManagerFactory.init(keyStore, password);
429 SSLContext context = SSLContext.getInstance("TLS");
430 context.init(keyManagerFactory.getKeyManagers(), new TrustManager[] { this }, new SecureRandom());
431 return context;
432 }
433 }