Я хочу использовать классы SOAPConnectionFactory и MessageFactory из SAAJ с несколькими потоками, но оказывается, что я не могу предположить, что они потокобезопасны. Некоторые связанные сообщения:
- экземпляр javax.xml.soap.MessageFactory является потокобезопасным?
- кеширование объекта jaxp для MessageFactory
Вот небольшое интересное доказательство того, что он может быть потокобезопасным: http://svn.apache.org/repos/asf/axis/axis2/java/core/tags/v1.5.6/modules/saaj/src/org/apache/axis2/saaj/SOAPConnectionImpl.java сказано
Хотя спецификации SAAJ явно не требуют безопасности потоков, похоже, что SOAPConnection в эталонной реализации Sun является потокобезопасной.
Но все же я не думаю, что этого достаточно, чтобы рассматривать классы SAAJ как потокобезопасные.
Итак, мой вопрос: верна ли идиома ниже? Я создаю ровно один объект SOAPConnection и MessageFactory, используя, возможно, не потокобезопасные фабрики внутри основного потока, а затем безопасно публикую этот объект в задаче исполнителя, используя гарантию «происходит до» интерфейса CompletionService. Я также использую это происходит перед гарантией для извлечения объекта результата HashMap.
По сути, я просто хочу проверить здравомыслие своих рассуждений.
public static void main(String args[]) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(10);
CompletionService<Map<String, String>> completionService = new ExecutorCompletionService<>(executorService);
//submitting 100 tasks
for (int i = 0; i < 100; i++) {
// there is no docs on if these classes are thread-safe or not, so creating them before submitting to the
// external thread. This seems to be safe, because we are relying on the happens-before guarantees of the
// CompletionService.
SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
SOAPConnection soapConnection = soapConnectionFactory.createConnection();
MessageFactory messageFactory = MessageFactory.newInstance();
int number = i;// we can't just use i, because it's not effectively final within the task below
completionService.submit(() -> {
// using messageFactory here!
SOAPMessage request = createSOAPRequest(messageFactory, number);
// using soapConnection here!
SOAPMessage soapResponse = soapConnection.call(request, "example.com");
soapConnection.close();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
soapResponse.writeTo(outputStream);
// HashMap is not thread-safe on its own, but we'll use the happens-before guarantee. See f.get() below.
Map<String, String> result = new HashMap<>();
result.put("soapResponse", new String(outputStream.toByteArray()));
return result;
});
}
// printing the responses as they arrive
for (int i = 0; i < 100; i++) {
Future<Map<String, String>> f = completionService.take();
Map<String, String> result = f.get();
System.out.println(result.get("soapResponse"));
}
executorService.shutdown();
}
/**
* Thread-safe static method
*/
private static SOAPMessage createSOAPRequest(MessageFactory messageFactory, int number) throws Exception {
SOAPMessage soapMessage = messageFactory.createMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
String serverURI = "example.com";
SOAPEnvelope envelope = soapPart.getEnvelope();
envelope.addNamespaceDeclaration("example", serverURI);
SOAPBody soapBody = envelope.getBody();
SOAPElement soapBodyElem = soapBody.addChildElement("number", "example");
soapBodyElem.addTextNode(String.valueOf(number));
soapMessage.saveChanges();
return soapMessage;
}