From 8de776b0b92f819c81cd82afa7819c698e0bf0ca Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Wed, 20 Apr 2016 18:51:37 +0100 Subject: [PATCH 01/28] updating poms for 2.9.3-SNAPSHOT development --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d76f9ae..6492a06 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ com.ceridwen.circulation ceridwen-standard-interchange-protocol-library - 2.9.2-SNAPSHOT + 2.9.3-SNAPSHOT jar ceridwen-standard-interchange-protocol-library From f9ba03c8c019eee7c509c7dafd1aaabd82882a73 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Wed, 20 Apr 2016 19:01:07 +0100 Subject: [PATCH 02/28] updating develop poms to master versions to avoid merge conflicts --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6492a06..99d55e9 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ com.ceridwen.circulation ceridwen-standard-interchange-protocol-library - 2.9.3-SNAPSHOT + 2.9.2 jar ceridwen-standard-interchange-protocol-library From 417090ffea25077de02c963e138f6ed138369d0c Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Wed, 20 Apr 2016 19:01:10 +0100 Subject: [PATCH 03/28] Updating develop poms back to pre merge state --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 99d55e9..6492a06 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ com.ceridwen.circulation ceridwen-standard-interchange-protocol-library - 2.9.2 + 2.9.3-SNAPSHOT jar ceridwen-standard-interchange-protocol-library From a3eb721163743c9b12c5a105c3536f670ac09bb7 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Sat, 23 Apr 2016 21:54:59 +0100 Subject: [PATCH 04/28] Update Ceridwen repositories --- pom.xml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 6492a06..f52670d 100644 --- a/pom.xml +++ b/pom.xml @@ -62,14 +62,27 @@ ceridwen-libs-release Ceridwen Release Repository - https://software.ceridwen.com/artifactory/libs-release-local + https://software.ceridwen.com/artifactory/libs-release ceridwen-libs-snapshot Ceridwen Snapshot Repository - https://software.ceridwen.com/artifactory/libs-snapshot-local + https://software.ceridwen.com/artifactory/libs-snapshot + + + + ceridwen-plugins-release + Ceridwen Release Plugin Repository + https://software.ceridwen.com/artifactory/plugins-release + + + ceridwen-plugins-snapshot + Ceridwen Snapshot Plugin Repository + https://software.ceridwen.com/artifactory/plugins-snapshot + + From dd485140a9f1f1963af855e3f46227091e46df9b Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Sat, 23 Apr 2016 22:58:28 +0100 Subject: [PATCH 05/28] Don't close the XMLEncoder\Decoder in Message.xmlDecode\xmlDecode. This closes the stream passed as a parameter, and it is the callers reponsibility to close this stream (as the caller may still need to use it) --- .../java/com/ceridwen/circulation/SIP/messages/Message.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/messages/Message.java b/src/main/java/com/ceridwen/circulation/SIP/messages/Message.java index 0a2a8f2..202ef8b 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/messages/Message.java +++ b/src/main/java/com/ceridwen/circulation/SIP/messages/Message.java @@ -684,13 +684,11 @@ public void xmlEncode(OutputStream strm) { XMLEncoder out = new XMLEncoder(strm); out.writeObject(this); out.flush(); - out.close(); } public static Message xmlDecode(InputStream strm) { XMLDecoder in = new XMLDecoder(strm); Message msg = (Message) in.readObject(); - in.close(); return msg; } From 2a5c3171aefdbd9b3bab821a5ef1a21e1322ba19 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Sat, 23 Apr 2016 22:59:29 +0100 Subject: [PATCH 06/28] Added a simple dummy driver for the netty server. It merely returns the minimum response to any SIP2 request. --- .../SIP/samples/netty/DummyDriver.java | 150 ++++++++++++++++++ .../SIP/samples/netty/DummyDriverFactory.java | 22 +++ 2 files changed, 172 insertions(+) create mode 100644 src/main/java/com/ceridwen/circulation/SIP/samples/netty/DummyDriver.java create mode 100644 src/main/java/com/ceridwen/circulation/SIP/samples/netty/DummyDriverFactory.java diff --git a/src/main/java/com/ceridwen/circulation/SIP/samples/netty/DummyDriver.java b/src/main/java/com/ceridwen/circulation/SIP/samples/netty/DummyDriver.java new file mode 100644 index 0000000..c32385b --- /dev/null +++ b/src/main/java/com/ceridwen/circulation/SIP/samples/netty/DummyDriver.java @@ -0,0 +1,150 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ceridwen.circulation.SIP.samples.netty; + +import com.ceridwen.circulation.SIP.messages.ACSStatus; +import com.ceridwen.circulation.SIP.messages.CheckInResponse; +import com.ceridwen.circulation.SIP.messages.CheckOutResponse; +import com.ceridwen.circulation.SIP.messages.EndSessionResponse; +import com.ceridwen.circulation.SIP.messages.FeePaidResponse; +import com.ceridwen.circulation.SIP.messages.HoldResponse; +import com.ceridwen.circulation.SIP.messages.ItemInformationResponse; +import com.ceridwen.circulation.SIP.messages.ItemStatusUpdateResponse; +import com.ceridwen.circulation.SIP.messages.LoginResponse; +import com.ceridwen.circulation.SIP.messages.PatronEnableResponse; +import com.ceridwen.circulation.SIP.messages.PatronInformationResponse; +import com.ceridwen.circulation.SIP.messages.PatronStatusRequest; +import com.ceridwen.circulation.SIP.messages.PatronStatusResponse; +import com.ceridwen.circulation.SIP.messages.RenewAllResponse; +import com.ceridwen.circulation.SIP.messages.RenewResponse; +import com.ceridwen.circulation.SIP.messages.SCStatus; +import com.ceridwen.circulation.SIP.netty.server.driver.AbstractDriver; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.BlockPatronOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.CheckInOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.CheckOutOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.EndPatronSessionOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.FeePaidOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.HoldOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.ItemInformationOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.ItemStatusUpdateOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.LoginOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.PatronEnableOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.PatronInformationOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.PatronStatusOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.RenewAllOperation; +import com.ceridwen.circulation.SIP.netty.server.driver.operation.RenewOperation; + +/** + * + * @author Matthew + */ +public class DummyDriver extends AbstractDriver + implements BlockPatronOperation, + CheckInOperation, + CheckOutOperation, + EndPatronSessionOperation, + FeePaidOperation, + HoldOperation, + ItemInformationOperation, + ItemStatusUpdateOperation, + LoginOperation, + PatronEnableOperation, + PatronInformationOperation, + PatronStatusOperation, + RenewAllOperation, + RenewOperation +{ + + @Override + public ACSStatus Status(ACSStatus status, SCStatus msg) { + status.setACSRenewalPolicy(false); + status.setCheckInOk(true); + status.setCheckOutOk(true); + status.setOfflineOk(false); + status.setStatusUpdateOk(true); + return status; + } + + @Override + public PatronStatusResponse BlockPatron( + com.ceridwen.circulation.SIP.messages.BlockPatron msg) { + return new PatronStatusResponse(); + } + + @Override + public CheckInResponse CheckIn( + com.ceridwen.circulation.SIP.messages.CheckIn msg) { + return new CheckInResponse(); + } + + @Override + public CheckOutResponse CheckOut( + com.ceridwen.circulation.SIP.messages.CheckOut msg) { + return new CheckOutResponse(); + } + + @Override + public EndSessionResponse EndPatronSession( + com.ceridwen.circulation.SIP.messages.EndPatronSession msg) { + return new EndSessionResponse(); + } + + @Override + public FeePaidResponse FeePaid( + com.ceridwen.circulation.SIP.messages.FeePaid msg) { + return new FeePaidResponse(); + } + + @Override + public HoldResponse Hold(com.ceridwen.circulation.SIP.messages.Hold msg) { + return new HoldResponse(); + } + + @Override + public ItemInformationResponse ItemInformation( + com.ceridwen.circulation.SIP.messages.ItemInformation msg) { + return new ItemInformationResponse(); + } + + @Override + public ItemStatusUpdateResponse ItemStatusUpdate( + com.ceridwen.circulation.SIP.messages.ItemStatusUpdate msg) { + return new ItemStatusUpdateResponse(); + } + + @Override + public LoginResponse Login(com.ceridwen.circulation.SIP.messages.Login msg) { + return new LoginResponse(); + } + + @Override + public PatronEnableResponse PatronEnable( + com.ceridwen.circulation.SIP.messages.PatronEnable msg) { + return new PatronEnableResponse(); + } + + @Override + public PatronInformationResponse PatronInformation( + com.ceridwen.circulation.SIP.messages.PatronInformation msg) { + return new PatronInformationResponse(); + } + + @Override + public PatronStatusResponse PatronStatus(PatronStatusRequest msg) { + return new PatronStatusResponse(); + } + + @Override + public RenewResponse Renew(com.ceridwen.circulation.SIP.messages.Renew msg) { + return new RenewResponse(); + } + + @Override + public RenewAllResponse RenewAll( + com.ceridwen.circulation.SIP.messages.RenewAll msg) { + return new RenewAllResponse(); + } +} diff --git a/src/main/java/com/ceridwen/circulation/SIP/samples/netty/DummyDriverFactory.java b/src/main/java/com/ceridwen/circulation/SIP/samples/netty/DummyDriverFactory.java new file mode 100644 index 0000000..2b9bd37 --- /dev/null +++ b/src/main/java/com/ceridwen/circulation/SIP/samples/netty/DummyDriverFactory.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ceridwen.circulation.SIP.samples.netty; + +import com.ceridwen.circulation.SIP.netty.server.driver.Driver; +import com.ceridwen.circulation.SIP.netty.server.driver.DriverFactory; + +/** + * + * @author Matthew + */ +public class DummyDriverFactory implements DriverFactory { + + @Override + public Driver getDriver() { + return new DummyDriver(); + } + +} From e5e69cf1622adbdf29ae25c1b7f95913921c867e Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Sat, 23 Apr 2016 23:10:12 +0100 Subject: [PATCH 07/28] Updated samples/Sample.java to use Netty server and general code tidying --- .../circulation/SIP/samples/Sample.java | 279 +++++++----------- 1 file changed, 112 insertions(+), 167 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java b/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java index e342f95..15ec3b6 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java +++ b/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java @@ -1,21 +1,23 @@ -/******************************************************************************* +/** + * ***************************************************************************** * Copyright (c) 2010 Matthew J. Dovey (www.ceridwen.com). * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at + * which accompanies this distribution, and is available at * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Contributors: - * Matthew J. Dovey (www.ceridwen.com) - initial API and implementation - ******************************************************************************/ + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * Contributors: Matthew J. Dovey (www.ceridwen.com) - initial API and + * implementation + * **************************************************************************** + */ package com.ceridwen.circulation.SIP.samples; import java.util.Date; @@ -32,174 +34,117 @@ import com.ceridwen.circulation.SIP.messages.CheckOutResponse; import com.ceridwen.circulation.SIP.messages.Message; import com.ceridwen.circulation.SIP.messages.SCStatus; -import com.ceridwen.circulation.SIP.server.MessageHandlerDummyImpl; -import com.ceridwen.circulation.SIP.server.SocketDaemon; -import com.ceridwen.circulation.SIP.transport.Connection; +import com.ceridwen.circulation.SIP.netty.server.SIPDaemon; +import com.ceridwen.circulation.SIP.samples.netty.DummyDriverFactory; import com.ceridwen.circulation.SIP.transport.SocketConnection; import com.ceridwen.circulation.SIP.types.enumerations.ProtocolVersion; import com.ceridwen.circulation.SIP.types.flagfields.SupportedMessages; +import java.util.logging.Level; +import java.util.logging.Logger; -/** - *

Title:

- * - *

Description:

- * - *

Copyright: Copyright (c) 2004

- * - *

Company:

- * - * @author not attributable - * @version 1.0 - */ public class Sample { - static SocketDaemon thread; - - public static void startServer() { - /** - * Run simple socket server - */ + public static void main(String[] args) { + try { + System.setProperty("com.ceridwen.circulation.SIP.charset", "ISO8859_1"); - Sample.thread = new SocketDaemon("localhost", 12345, new MessageHandlerDummyImpl()); - Sample.thread.setStrictChecksumChecking(true); - Sample.thread.start(); + SIPDaemon server; + + // Run netty server + server = new SIPDaemon("Sample", "localhost", 12345, false, new DummyDriverFactory(), true); + server.start(); + + // Do sample checkout + Sample.checkOut(); + + // Shut down netty server + server.stop(); + } catch (Exception ex) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public static void checkOut() { + /** + * Now try basic client commands + */ + SocketConnection connection; + + connection = new SocketConnection(); + connection.setHost("localhost"); + connection.setPort(12345); + connection.setConnectionTimeout(30000); + connection.setIdleTimeout(30000); + connection.setRetryAttempts(2); + connection.setRetryWait(500); + + try { + connection.connect(); + } catch (Exception ex) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); + return; } - public static void checkOut() { - /** - * Now try basic client commands - */ - Connection connection; - Message request, response; - - connection = new SocketConnection(); - ((SocketConnection) connection).setHost("localhost"); - ((SocketConnection) connection).setPort(12345); - ((SocketConnection) connection).setConnectionTimeout(30000); - ((SocketConnection) connection).setIdleTimeout(30000); - ((SocketConnection) connection).setRetryAttempts(2); - ((SocketConnection) connection).setRetryWait(500); - - try { - connection.connect(); - } catch (Exception e1) { - e1.printStackTrace(); - return; - } - - /** - * It is necessary to send a SC Status with protocol version 2.0 else - * server will assume 1.0) - */ - - request = new SCStatus(); - ((SCStatus) request).setProtocolVersion(ProtocolVersion.VERSION_2_00); - - try { - response = connection.send(request); - } catch (RetriesExceeded e) { - e.printStackTrace(); - return; - } catch (ConnectionFailure e) { - e.printStackTrace(); - return; - } catch (MessageNotUnderstood e) { - e.printStackTrace(); - return; - } catch (ChecksumError e) { - e.printStackTrace(); - return; - } catch (SequenceError e) { - e.printStackTrace(); - return; - } catch (MandatoryFieldOmitted e) { - e.printStackTrace(); - return; - } catch (InvalidFieldLength e) { - e.printStackTrace(); - return; - } - - if (!(response instanceof ACSStatus)) { - System.err.println("Error - Status Request did not return valid response from server."); - return; - } - - /** - * For debugging XML handling code (but could be useful in Cocoon) - */ - response.xmlEncode(System.out); - - /** - * Check if the server can support checkout - */ - if (!((ACSStatus) response).getSupportedMessages().isSet(SupportedMessages.CHECK_OUT)) { - System.out.println("Check out not supported"); - return; - } - - request = new CheckOut(); - - /** - * The code below would be the normal way of creating the request - */ - - ((CheckOut) request).setPatronIdentifier("2000000"); - ((CheckOut) request).setItemIdentifier("300000000"); - ((CheckOut) request).setSCRenewalPolicy(Boolean.TRUE); - ((CheckOut) request).setTransactionDate(new Date()); - - try { - response = connection.send(request); - } catch (RetriesExceeded e) { - e.printStackTrace(); - return; - } catch (ConnectionFailure e) { - e.printStackTrace(); - return; - } catch (MessageNotUnderstood e) { - e.printStackTrace(); - return; - } catch (ChecksumError e) { - e.printStackTrace(); - return; - } catch (SequenceError e) { - e.printStackTrace(); - return; - } catch (MandatoryFieldOmitted e) { - e.printStackTrace(); - return; - } catch (InvalidFieldLength e) { - e.printStackTrace(); - return; - } - - if (!(response instanceof CheckOutResponse)) { - System.err.println("Error - CheckOut Request did not return valid response from server"); - return; - } - response.xmlEncode(System.out); - - // System.out.println(((PatronInformationResponse)response).getPersonalName()); - // System.out.println(((PatronInformationResponse)response).getEMailAddress()); - - connection.disconnect(); + /** + * It is necessary to send a SC Status with protocol version 2.0 else server + * will assume 1.0) + */ + SCStatus scStatusRequest = new SCStatus(); + scStatusRequest.setProtocolVersion(ProtocolVersion.VERSION_2_00); + + Message scStatusResponse; + + try { + scStatusResponse = connection.send(scStatusRequest); + } catch (RetriesExceeded | ConnectionFailure | MessageNotUnderstood | ChecksumError | SequenceError | MandatoryFieldOmitted | InvalidFieldLength ex) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); + return; } - public static void stopServer() { - /** - * Stop simple socket server - */ + if (!(scStatusResponse instanceof ACSStatus)) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, "Error - Status Request did not return valid response from server."); + return; + } - Sample.thread.shutdown(); + /** + * For debugging XML handling code (but could be useful in Cocoon) + */ + scStatusResponse.xmlEncode(System.out); + + /** + * Check if the server can support checkout + */ + if (!((ACSStatus) scStatusResponse).getSupportedMessages().isSet(SupportedMessages.CHECK_OUT)) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, "Check out not supported"); + return; } - public static void main(String[] args) { - System.setProperty("com.ceridwen.circulation.SIP.charset", "ISO8859_1"); + CheckOut checkOutRequest = new CheckOut(); + + /** + * Now try a checkout request + */ + checkOutRequest.setPatronIdentifier("2000000"); + checkOutRequest.setItemIdentifier("300000000"); + checkOutRequest.setSCRenewalPolicy(Boolean.TRUE); + checkOutRequest.setTransactionDate(new Date()); - Sample.startServer(); - Sample.checkOut(); - Sample.stopServer(); + Message checkOutResponse; + try { + checkOutResponse = connection.send(checkOutRequest); + } catch (RetriesExceeded | ConnectionFailure | MessageNotUnderstood | ChecksumError | SequenceError | MandatoryFieldOmitted | InvalidFieldLength ex) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); + return; } + + if (!(checkOutResponse instanceof CheckOutResponse)) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, "Error - CheckOut Request did not return valid response from server"); + return; + } + checkOutResponse.xmlEncode(System.out); + + connection.disconnect(); + } + } From 2c00328cca2471680b242b5091901987ef87a038 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Sat, 23 Apr 2016 23:13:46 +0100 Subject: [PATCH 08/28] Update README.md to include general details and sample code --- README.md | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d89ee74..2e13694 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,126 @@ -## ceridwen-standard-interchange-protocol-library +## Ceridwen 3M SIP Circulation Library for Java -Java Implementation of the 3M SIP2 and NISO SIP3 Protocols for library circulation and self-check facilities. \ No newline at end of file +3M SIP is an industry standard protocol by 3M to allow automatec check out terminals communicate with library systems. + +The Ceridwen 3M SIP Circulation Library for Java is an Open Source (GPL v3) implementation of the 3M SIP version 2 protocol (also known as SIP2). It includes both client and server implementations and support both telnet and socket based communications. + +### Problems and Issues + +For general problems please contact [development@ceridwen.com](mailto:development@ceridwen.com). For bugs and feature requests, please use our [online issue tracker](). + +### License + +The Ceridwen Self Issue Client is available as open source under [GPL v3](http://www.gnu.org/licenses/gpl.html). Contact [development@ceridwen.com](mailto:development@ceridwen.com) for other licensing options. + +### Sample Application + +The following hava code demonstrates the use of the library. It starts up a simple server implementation, and then uses the client API to check out a book. + +```java +public class Sample { + + public static void main(String[] args) { + try { + System.setProperty("com.ceridwen.circulation.SIP.charset", "ISO8859_1"); + + SIPDaemon server; + + // Run netty server + server = new SIPDaemon("Sample", "localhost", 12345, false, new DummyDriverFactory(), true); + server.start(); + + // Do sample checkout + Sample.checkOut(); + + // Shut down netty server + server.stop(); + } catch (Exception ex) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); + } + } + + public static void checkOut() { + /** + * Now try basic client commands + */ + SocketConnection connection; + + connection = new SocketConnection(); + connection.setHost("localhost"); + connection.setPort(12345); + connection.setConnectionTimeout(30000); + connection.setIdleTimeout(30000); + connection.setRetryAttempts(2); + connection.setRetryWait(500); + + try { + connection.connect(); + } catch (Exception ex) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); + return; + } + + /** + * It is necessary to send a SC Status with protocol version 2.0 else server + * will assume 1.0) + */ + SCStatus scStatusRequest = new SCStatus(); + scStatusRequest.setProtocolVersion(ProtocolVersion.VERSION_2_00); + + Message scStatusResponse; + + try { + scStatusResponse = connection.send(scStatusRequest); + } catch (RetriesExceeded | ConnectionFailure | MessageNotUnderstood | ChecksumError | SequenceError | MandatoryFieldOmitted | InvalidFieldLength ex) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); + return; + } + + if (!(scStatusResponse instanceof ACSStatus)) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, "Error - Status Request did not return valid response from server."); + return; + } + + /** + * For debugging XML handling code (but could be useful in Cocoon) + */ + scStatusResponse.xmlEncode(System.out); + + /** + * Check if the server can support checkout + */ + if (!((ACSStatus) scStatusResponse).getSupportedMessages().isSet(SupportedMessages.CHECK_OUT)) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, "Check out not supported"); + return; + } + + CheckOut checkOutRequest = new CheckOut(); + + /** + * Now try a checkout request + */ + checkOutRequest.setPatronIdentifier("2000000"); + checkOutRequest.setItemIdentifier("300000000"); + checkOutRequest.setSCRenewalPolicy(Boolean.TRUE); + checkOutRequest.setTransactionDate(new Date()); + + Message checkOutResponse; + + try { + checkOutResponse = connection.send(checkOutRequest); + } catch (RetriesExceeded | ConnectionFailure | MessageNotUnderstood | ChecksumError | SequenceError | MandatoryFieldOmitted | InvalidFieldLength ex) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); + return; + } + + if (!(checkOutResponse instanceof CheckOutResponse)) { + Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, "Error - CheckOut Request did not return valid response from server"); + return; + } + checkOutResponse.xmlEncode(System.out); + + connection.disconnect(); + } + +} +``` From e621bd6a905fd54866cab6f3df7d25a2fcfecdad Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Tue, 6 Sep 2016 12:59:31 +0100 Subject: [PATCH 09/28] Update pom.xml to execute tests in situ from src/main/java --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index f52670d..da8b2f5 100644 --- a/pom.xml +++ b/pom.xml @@ -162,6 +162,11 @@ org.apache.maven.plugins maven-surefire-plugin 2.19.1 + + ${basedir}/src/main/java/ + ${project.build.directory}/classes/ + Message# + org.apache.maven.plugins From 87f7ea631f757b0514a7535d12781408047954d3 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Tue, 6 Sep 2016 13:03:28 +0100 Subject: [PATCH 10/28] Update ItemInformationResponse tests to accommodate changes to holdQueueLength (to meet 3M emulator behaviour) --- .../circulation/SIP/messages/ItemInformationResponse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/messages/ItemInformationResponse.java b/src/main/java/com/ceridwen/circulation/SIP/messages/ItemInformationResponse.java index 4bbe25a..acb3c7a 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/messages/ItemInformationResponse.java +++ b/src/main/java/com/ceridwen/circulation/SIP/messages/ItemInformationResponse.java @@ -42,8 +42,8 @@ import com.ceridwen.circulation.SIP.types.enumerations.SecurityMarker; @Command("18") -@TestCaseDefault("1801000119700101 010000AB|AJ|") -@TestCasePopulated("1813030919700101 010000ABitemIdentifier|AFscreenMessage|AGprintLine|AHdueDate|AJtitleIdentifier|APcurrentLocation|AQpermanentLocation|BGowner|BHGBP|BVfeeAmount|CF123456789|CHitemProperties|CJ19700101 010000|CK010|CM19700101 010000|") +@TestCaseDefault("1801000119700101 010000AB|AJ|CF00000|") +@TestCasePopulated("1813030919700101 010000ABitemIdentifier|AFscreenMessage|AGprintLine|AHdueDate|AJtitleIdentifier|APcurrentLocation|AQpermanentLocation|BGowner|BHGBP|BVfeeAmount|CF12345|CHitemProperties|CJ19700101 010000|CK010|CM19700101 010000|") public class ItemInformationResponse extends Message { private static final long serialVersionUID = 6408854778106704492L; @PositionedField(start = 2, end = 3) From 313b225fd82da251cd6ed4acae4f51882e1a5cb4 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Tue, 6 Sep 2016 17:49:58 +0100 Subject: [PATCH 11/28] Update dependencies for netty and commons-net. Update maven plugins --- pom.xml | 23 ++- .../SIP/netty/server/SIPDaemon.java | 144 +++++++++--------- 2 files changed, 81 insertions(+), 86 deletions(-) diff --git a/pom.xml b/pom.xml index da8b2f5..0ab2fcb 100644 --- a/pom.xml +++ b/pom.xml @@ -103,18 +103,13 @@ commons-net commons-net - 3.4 + 3.5 commons-logging commons-logging 1.2 - - commons-collections - commons-collections - 3.2.2 - commons-beanutils commons-beanutils @@ -123,12 +118,12 @@ io.netty netty-transport - 4.0.36.Final + 4.1.5.Final io.netty netty-handler - 4.0.36.Final + 4.1.5.Final
@@ -156,7 +151,7 @@ org.apache.maven.plugins maven-resources-plugin - 2.7 + 3.0.1 org.apache.maven.plugins @@ -176,7 +171,7 @@ org.apache.maven.plugins maven-jar-plugin - 2.6 + 3.0.2 @@ -198,17 +193,17 @@ org.apache.maven.plugins maven-war-plugin - 2.6 + 3.0.0 org.apache.maven.plugins maven-javadoc-plugin - 2.10.3 + 2.10.4 org.apache.maven.plugins maven-source-plugin - 3.0.0 + 3.0.1 org.apache.maven.plugins @@ -233,7 +228,7 @@ org.codehaus.mojo versions-maven-plugin - 2.2 + 2.3 diff --git a/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java b/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java index e3349d2..15273a1 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java +++ b/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java @@ -15,88 +15,88 @@ import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.SelfSignedCertificate; -import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; -public class SIPDaemon implements GenericFutureListener { - private static Log log = LogFactory.getLog(SIPDaemon.class); +public class SIPDaemon implements GenericFutureListener { + private static final Log log = LogFactory.getLog(SIPDaemon.class); - private String name; - private String ip; - private int port; - private boolean ssl; - private DriverFactory driverFactory; - private boolean strictChecksumChecking; + private final String name; + private final String ip; + private final int port; + private final boolean ssl; + private final DriverFactory driverFactory; + private final boolean strictChecksumChecking; - private ChannelFuture f; - private EventLoopGroup bossGroup; - private EventLoopGroup workerGroup; + private ChannelFuture f; + private EventLoopGroup bossGroup; + private EventLoopGroup workerGroup; - public SIPDaemon(String name, String ip, int port, boolean ssl, DriverFactory driverFactory, boolean strictChecksumChecking) { - this.name = name; - this.ip = ip; - this.port = port; - this.ssl = ssl; - this.driverFactory = driverFactory; - this.strictChecksumChecking = strictChecksumChecking; - } - - public void start() throws Exception { - // Configure SSL. - log.info("Server " + name + " on " + ip + " " + port + " starting..."); - - final SslContext sslCtx; - if (ssl) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey()); - } else { - sslCtx = null; - } - - bossGroup = new NioEventLoopGroup(1); - workerGroup = new NioEventLoopGroup(); - - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(new SIPServerInitializer(driverFactory, strictChecksumChecking, sslCtx)) - .option(ChannelOption.SO_BACKLOG, 128) - .childOption(ChannelOption.SO_KEEPALIVE, true); - - // Bind and start to accept incoming connections. - f = b.bind(ip, port).sync(); // (7) - - // Wait until the server socket is closed. - // In this example, this does not happen, but you can do that to gracefully - // shut down your server. - f.channel().closeFuture().addListener(this); - - - log.info("Server " + name + " on " + ip + " " + port + " startup complete."); - } catch (Exception ex) { - workerGroup.shutdownGracefully(); - bossGroup.shutdownGracefully(); - } - } - - public void stop() - { - log.info("Server " + name + " on " + ip + " " + port + " shutting down..."); - if (f != null) { - f.channel().close(); - } - workerGroup.shutdownGracefully(); - bossGroup.shutdownGracefully(); - log.info("Server " + name + " on " + ip + " " + port + " shuttdown complete."); - } + public SIPDaemon(String name, String ip, int port, boolean ssl, DriverFactory driverFactory, boolean strictChecksumChecking) { + this.name = name; + this.ip = ip; + this.port = port; + this.ssl = ssl; + this.driverFactory = driverFactory; + this.strictChecksumChecking = strictChecksumChecking; + } + + public void start() throws Exception { + // Configure SSL. + log.info("Server " + name + " on " + ip + " " + port + " starting..."); + + final SslContext sslCtx; + if (ssl) { + SelfSignedCertificate ssc = new SelfSignedCertificate(); + sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); + } else { + sslCtx = null; + } + + bossGroup = new NioEventLoopGroup(1); + workerGroup = new NioEventLoopGroup(); + + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(new SIPServerInitializer(driverFactory, strictChecksumChecking, sslCtx)) + .option(ChannelOption.SO_BACKLOG, 128) + .childOption(ChannelOption.SO_KEEPALIVE, true); + + // Bind and start to accept incoming connections. + f = b.bind(ip, port).sync(); // (7) + + // Wait until the server socket is closed. + // In this example, this does not happen, but you can do that to gracefully + // shut down your server. + f.channel().closeFuture().addListener(this); + + + log.info("Server " + name + " on " + ip + " " + port + " startup complete."); + } catch (Exception ex) { + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + } + } + + public void stop() + { + log.info("Server " + name + " on " + ip + " " + port + " shutting down..."); + if (f != null) { + f.channel().close(); + } + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + log.info("Server " + name + " on " + ip + " " + port + " shuttdown complete."); + } @Override - public void operationComplete(Future arg0) throws Exception { + public void operationComplete(ChannelFuture arg0) throws Exception { log.info("Server " + name + " on " + ip + " " + port + " shutting down..."); workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); From 78dbd09d7a91f9e353cd395b6df684a05b666e35 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Tue, 6 Sep 2016 12:41:04 +0100 Subject: [PATCH 12/28] Extract SocketConnection to AbstractSocketConnection, create SocketConnection and SSLSocketConnection classes --- CHANGELOG.txt | 11 ++ .../transport/AbstractSocketConnection.java | 110 ++++++++++++++++ .../circulation/SIP/transport/Connection.java | 9 -- .../SIP/transport/SSLSocketConnection.java | 22 ++++ .../SIP/transport/SocketConnection.java | 119 +++--------------- 5 files changed, 159 insertions(+), 112 deletions(-) create mode 100644 src/main/java/com/ceridwen/circulation/SIP/transport/AbstractSocketConnection.java create mode 100644 src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 815f873..858cd15 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,14 @@ +Version 2.9.3: 06/09/2016 +Update sample to use netty server +Added SSL Socket client connection +Dependencies: ceridwen utilitiy 1.6.2 + apache commons beanutils 1.9.2 + apache commons lang 3.4 + apache commons logging 1.2 + apache commons net 3.4 + io.netty transport 4.0.36.Final + io.netty handler 4.0.36.Final + Version 2.9.2: 20/04/2016 Added netty server implementation Added field ordering options (alphabetic or specification listed) diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/AbstractSocketConnection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/AbstractSocketConnection.java new file mode 100644 index 0000000..3e55890 --- /dev/null +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/AbstractSocketConnection.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2010 Matthew J. Dovey (www.ceridwen.com). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Contributors: + * Matthew J. Dovey (www.ceridwen.com) - initial API and implementation + ******************************************************************************/ +package com.ceridwen.circulation.SIP.transport; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.InetSocketAddress; +import java.net.Socket; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.ceridwen.circulation.SIP.exceptions.ConnectionFailure; +import com.ceridwen.circulation.SIP.messages.Message; + +public abstract class AbstractSocketConnection extends Connection { + private static Log log = LogFactory.getLog(AbstractSocketConnection.class); + + private Socket socket; + private BufferedReader in; + private BufferedWriter out; + + abstract Socket getSocket() throws Exception; + + @Override + protected void connect(int retryAttempts) throws Exception { + try { + this.socket = this.getSocket(); + this.socket.connect(new InetSocketAddress(this.getHost(), this.getPort()), this.getConnectionTimeout()); + this.socket.setSoTimeout(this.getIdleTimeout()); + this.out = new BufferedWriter(new OutputStreamWriter(this.socket.getOutputStream(), Message.getCharsetEncoding())); + this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream(), Message.getCharsetEncoding())); + } catch (Exception ex) { + if (retryAttempts > 0) { + try { + Thread.sleep(this.getRetryWait()); + } catch (Exception ex1) { + AbstractSocketConnection.log.debug("Thread sleep error", ex1); + } + this.connect(retryAttempts - 1); + } else { + throw ex; + } + } + } + + @Override + public boolean isConnected() { + return this.socket.isConnected(); + } + + @Override + public void disconnect() { + try { + this.socket.close(); + } catch (Exception ex) { + + } + } + + @Override + protected void internalSend(String cmd) throws ConnectionFailure { + try { + this.out.write(cmd); + this.out.write('\r'); + this.out.flush(); + } catch (Exception ex) { + throw new ConnectionFailure(ex); + } + } + + @Override + protected String internalWaitfor(String match) throws ConnectionFailure { + StringBuffer message = new StringBuffer(); + char buffer[] = new char[2048]; + int len; + + try { + do { + len = this.in.read(buffer); + message.append(new String(buffer, 0, len)); + } while ((message.toString()).lastIndexOf(match) < 0); + } catch (Exception ex) { + throw new ConnectionFailure(ex); + } + + String msg = message.toString(); + int cutoff = msg.lastIndexOf(match); + String ret = msg.substring(0, cutoff); + return ret; + } +} diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/Connection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/Connection.java index 077c0f3..27ca005 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/Connection.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/Connection.java @@ -18,15 +18,6 @@ ******************************************************************************/ package com.ceridwen.circulation.SIP.transport; -/** - *

Title: RTSI

- *

Description: Real Time Self Issue

- *

Copyright:

- - * @author Matthew J. Dovey - * @version 1.0 - */ - import java.util.Timer; import java.util.TimerTask; diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java new file mode 100644 index 0000000..409e389 --- /dev/null +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java @@ -0,0 +1,22 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ceridwen.circulation.SIP.transport; + +import java.net.Socket; +import javax.net.ssl.SSLSocketFactory; + +/** + * + * @author Matthew.Dovey + */ +public class SSLSocketConnection extends AbstractSocketConnection { + + @Override + Socket getSocket() throws Exception { + return SSLSocketFactory.getDefault().createSocket(); + } + +} diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java index 3060f48..aa7376a 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java @@ -1,108 +1,21 @@ -/******************************************************************************* - * Copyright (c) 2010 Matthew J. Dovey (www.ceridwen.com). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Contributors: - * Matthew J. Dovey (www.ceridwen.com) - initial API and implementation - ******************************************************************************/ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ package com.ceridwen.circulation.SIP.transport; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.InetSocketAddress; import java.net.Socket; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.ceridwen.circulation.SIP.exceptions.ConnectionFailure; -import com.ceridwen.circulation.SIP.messages.Message; - -public class SocketConnection extends Connection { - private static Log log = LogFactory.getLog(SocketConnection.class); - - private Socket socket; - private BufferedReader in; - private BufferedWriter out; - - @Override - protected void connect(int retryAttempts) throws Exception { - try { - this.socket = new java.net.Socket(); - this.socket.connect(new InetSocketAddress(this.getHost(), this.getPort()), this.getConnectionTimeout()); - this.socket.setSoTimeout(this.getIdleTimeout()); - this.out = new BufferedWriter(new OutputStreamWriter(this.socket.getOutputStream(), Message.getCharsetEncoding())); - this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream(), Message.getCharsetEncoding())); - } catch (Exception ex) { - if (retryAttempts > 0) { - try { - Thread.sleep(this.getRetryWait()); - } catch (Exception ex1) { - SocketConnection.log.debug("Thread sleep error", ex1); - } - this.connect(retryAttempts - 1); - } else { - throw ex; - } - } - } - - @Override - public boolean isConnected() { - return this.socket.isConnected(); - } - - @Override - public void disconnect() { - try { - this.socket.close(); - } catch (Exception ex) { - - } - } - - @Override - protected void internalSend(String cmd) throws ConnectionFailure { - try { - this.out.write(cmd); - this.out.write('\r'); - this.out.flush(); - } catch (Exception ex) { - throw new ConnectionFailure(ex); - } - } - - @Override - protected String internalWaitfor(String match) throws ConnectionFailure { - StringBuffer message = new StringBuffer(); - char buffer[] = new char[2048]; - int len; - - try { - do { - len = this.in.read(buffer); - message.append(new String(buffer, 0, len)); - } while ((message.toString()).lastIndexOf(match) < 0); - } catch (Exception ex) { - throw new ConnectionFailure(ex); - } - - String msg = message.toString(); - int cutoff = msg.lastIndexOf(match); - String ret = msg.substring(0, cutoff); - return ret; - } +/** + * + * @author Matthew.Dovey + */ +public class SocketConnection extends AbstractSocketConnection { + + @Override + Socket getSocket() throws Exception { + return new java.net.Socket(); + } + } From 91729fd129d9455cb4dd6732a53313863ebd08cc Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Wed, 7 Sep 2016 22:45:10 +0100 Subject: [PATCH 13/28] Add certificate configuration to netty SIPDaemon Add certificate configuration to SSLSocketConnection Refactor SSLSocketConnection to be subclass of SocketConnection Add TestSSLSocketTranport jUnit test --- .../SIP/netty/server/SIPDaemon.java | 36 +++- .../transport/AbstractSocketConnection.java | 110 ----------- .../SIP/transport/SSLSocketConnection.java | 119 ++++++++++- .../SIP/transport/SocketConnection.java | 123 ++++++++++-- .../SIP/transport/TestSSLSocketTransport.java | 186 ++++++++++++++++++ .../SIP/transport/TestSocketTransport.java | 22 +-- 6 files changed, 442 insertions(+), 154 deletions(-) delete mode 100644 src/main/java/com/ceridwen/circulation/SIP/transport/AbstractSocketConnection.java create mode 100644 src/main/java/com/ceridwen/circulation/SIP/transport/TestSSLSocketTransport.java diff --git a/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java b/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java index 15273a1..0ea3760 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java +++ b/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java @@ -16,17 +16,19 @@ import io.netty.handler.logging.LoggingHandler; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.util.concurrent.GenericFutureListener; +import io.netty.util.internal.StringUtil; +import java.io.File; public class SIPDaemon implements GenericFutureListener { private static final Log log = LogFactory.getLog(SIPDaemon.class); - private final String name; private final String ip; private final int port; - private final boolean ssl; + private final File keyCertChainFile; + private final File keyFile; + private final String keyPassword; private final DriverFactory driverFactory; private final boolean strictChecksumChecking; @@ -34,26 +36,40 @@ public class SIPDaemon implements GenericFutureListener { private EventLoopGroup bossGroup; private EventLoopGroup workerGroup; + public SIPDaemon(String name, String ip, int port, File keyCertChainFile, File keyFile, DriverFactory driverFactory, boolean strictChecksumChecking) { + this(name, ip, port, keyCertChainFile, keyFile, null, driverFactory, strictChecksumChecking); + } - public SIPDaemon(String name, String ip, int port, boolean ssl, DriverFactory driverFactory, boolean strictChecksumChecking) { + public SIPDaemon(String name, String ip, int port, DriverFactory driverFactory, boolean strictChecksumChecking) { + this(name, ip, port, null, null, null, driverFactory, strictChecksumChecking); + } + + public SIPDaemon(String name, String ip, int port, File keyCertChainFile, File keyFile, String keyPassword, DriverFactory driverFactory, boolean strictChecksumChecking) { this.name = name; this.ip = ip; this.port = port; - this.ssl = ssl; + this.keyCertChainFile = keyCertChainFile; + this.keyFile = keyFile; + this.keyPassword = keyPassword; this.driverFactory = driverFactory; this.strictChecksumChecking = strictChecksumChecking; } + public void start() throws Exception { // Configure SSL. log.info("Server " + name + " on " + ip + " " + port + " starting..."); - final SslContext sslCtx; - if (ssl) { - SelfSignedCertificate ssc = new SelfSignedCertificate(); - sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); + final SslContext sslCtx; + + if (keyCertChainFile == null || keyFile == null) { + sslCtx = null; } else { - sslCtx = null; + if (StringUtil.isNullOrEmpty(keyPassword)) { + sslCtx = SslContextBuilder.forServer(keyCertChainFile, keyFile).build(); + } else { + sslCtx = SslContextBuilder.forServer(keyCertChainFile, keyFile, keyPassword).build(); + } } bossGroup = new NioEventLoopGroup(1); diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/AbstractSocketConnection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/AbstractSocketConnection.java deleted file mode 100644 index 3e55890..0000000 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/AbstractSocketConnection.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010 Matthew J. Dovey (www.ceridwen.com). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * Contributors: - * Matthew J. Dovey (www.ceridwen.com) - initial API and implementation - ******************************************************************************/ -package com.ceridwen.circulation.SIP.transport; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.InetSocketAddress; -import java.net.Socket; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.ceridwen.circulation.SIP.exceptions.ConnectionFailure; -import com.ceridwen.circulation.SIP.messages.Message; - -public abstract class AbstractSocketConnection extends Connection { - private static Log log = LogFactory.getLog(AbstractSocketConnection.class); - - private Socket socket; - private BufferedReader in; - private BufferedWriter out; - - abstract Socket getSocket() throws Exception; - - @Override - protected void connect(int retryAttempts) throws Exception { - try { - this.socket = this.getSocket(); - this.socket.connect(new InetSocketAddress(this.getHost(), this.getPort()), this.getConnectionTimeout()); - this.socket.setSoTimeout(this.getIdleTimeout()); - this.out = new BufferedWriter(new OutputStreamWriter(this.socket.getOutputStream(), Message.getCharsetEncoding())); - this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream(), Message.getCharsetEncoding())); - } catch (Exception ex) { - if (retryAttempts > 0) { - try { - Thread.sleep(this.getRetryWait()); - } catch (Exception ex1) { - AbstractSocketConnection.log.debug("Thread sleep error", ex1); - } - this.connect(retryAttempts - 1); - } else { - throw ex; - } - } - } - - @Override - public boolean isConnected() { - return this.socket.isConnected(); - } - - @Override - public void disconnect() { - try { - this.socket.close(); - } catch (Exception ex) { - - } - } - - @Override - protected void internalSend(String cmd) throws ConnectionFailure { - try { - this.out.write(cmd); - this.out.write('\r'); - this.out.flush(); - } catch (Exception ex) { - throw new ConnectionFailure(ex); - } - } - - @Override - protected String internalWaitfor(String match) throws ConnectionFailure { - StringBuffer message = new StringBuffer(); - char buffer[] = new char[2048]; - int len; - - try { - do { - len = this.in.read(buffer); - message.append(new String(buffer, 0, len)); - } while ((message.toString()).lastIndexOf(match) < 0); - } catch (Exception ex) { - throw new ConnectionFailure(ex); - } - - String msg = message.toString(); - int cutoff = msg.lastIndexOf(match); - String ret = msg.substring(0, cutoff); - return ret; - } -} diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java index 409e389..63cc991 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java @@ -5,18 +5,131 @@ */ package com.ceridwen.circulation.SIP.transport; + +import java.io.File; +import java.io.FileInputStream; import java.net.Socket; +import java.nio.file.Files; +import java.security.KeyFactory; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.security.cert.CertificateFactory; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; +import java.security.cert.Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; /** * * @author Matthew.Dovey */ -public class SSLSocketConnection extends AbstractSocketConnection { +public class SSLSocketConnection extends SocketConnection { + + + private File clientCertificate; + private File clientPrivateKey; + private String clientPrivateKeyPassword; + private File serverCertificateCA; + + /** + * Get the value of ServerCertificateCA + * + * @return the value of ServerCertificateCA + */ + public File getServerCertificateCA() { + return serverCertificateCA; + } + + /** + * Set the value of ServerCertificateCA + * + * @param ServerCertificateCA new value of ServerCertificateCA + */ + public void setServerCertificateCA(File ServerCertificateCA) { + this.serverCertificateCA = ServerCertificateCA; + } + + + /** + * Get the value of clientCertificate + * + * @return the value of clientCertificate + */ + public File getClientCertificate() { + return clientCertificate; + } + + /** + * Set the value of clientCertificate + * + * @param clientCertificate new value of clientCertificate + */ + public void setClientCertificate(File clientCertificate) { + this.clientCertificate = clientCertificate; + } + + + /** + * Get the value of clientPrivateKey + * + * @return the value of clientPrivateKey + */ + public File getClientPrivateKey() { + return clientPrivateKey; + } + + /** + * Set the value of clientPrivateKey + * + * @param clientPrivateKey new value of clientPrivateKey + */ + public void setClientPrivateKey(File clientPrivateKey) { + this.clientPrivateKey = clientPrivateKey; + } + + public String getClientPrivateKeyPassword() { + return clientPrivateKeyPassword; + } + + public void setClientPrivateKeyPassword(String clientPrivateKeyPassword) { + this.clientPrivateKeyPassword = clientPrivateKeyPassword; + } + @Override - Socket getSocket() throws Exception { - return SSLSocketFactory.getDefault().createSocket(); + protected Socket getSocket() throws Exception { + KeyManagerFactory keyManagerFactory = null; + TrustManagerFactory trustManagerFactory = null; + KeyStore keyStore = KeyStore.getInstance("PKCS12"); + KeyStore trustStore = KeyStore.getInstance("PKCS12"); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + KeyFactory kf = KeyFactory.getInstance("RSA"); + + if (clientPrivateKey != null && clientCertificate != null) { + keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); + keyStore.load(null); + if (clientPrivateKeyPassword == null) { + keyStore.setKeyEntry("client", kf.generatePrivate(new PKCS8EncodedKeySpec(Files.readAllBytes(clientPrivateKey.toPath()))), null, cf.generateCertificates(new FileInputStream(clientCertificate)).toArray(new Certificate[]{})); + } else { + keyStore.setKeyEntry("client", kf.generatePrivate(new PKCS8EncodedKeySpec(Files.readAllBytes(clientPrivateKey.toPath()))), clientPrivateKeyPassword.toCharArray(), cf.generateCertificates(new FileInputStream(clientCertificate)).toArray(new Certificate[]{})); + } + keyManagerFactory.init(keyStore, "changeit".toCharArray()); + } + + if (serverCertificateCA != null) { + trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustStore.load(null); + trustStore.setCertificateEntry("ca", cf.generateCertificate(new FileInputStream(serverCertificateCA))); + trustManagerFactory.init(trustStore); + } + + SSLContext context = SSLContext.getInstance("TLS"); + context.init(keyManagerFactory == null?null:keyManagerFactory.getKeyManagers(), trustManagerFactory == null?null:trustManagerFactory.getTrustManagers(), new SecureRandom()); + SSLSocketFactory sockFact = context.getSocketFactory(); + return sockFact.createSocket(); } } diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java index aa7376a..3037c92 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java @@ -1,21 +1,112 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ +/******************************************************************************* + * Copyright (c) 2010 Matthew J. Dovey (www.ceridwen.com). + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Contributors: + * Matthew J. Dovey (www.ceridwen.com) - initial API and implementation + ******************************************************************************/ package com.ceridwen.circulation.SIP.transport; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.InetSocketAddress; import java.net.Socket; -/** - * - * @author Matthew.Dovey - */ -public class SocketConnection extends AbstractSocketConnection { - - @Override - Socket getSocket() throws Exception { - return new java.net.Socket(); - } - +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import com.ceridwen.circulation.SIP.exceptions.ConnectionFailure; +import com.ceridwen.circulation.SIP.messages.Message; + +public class SocketConnection extends Connection { + private static Log log = LogFactory.getLog(SocketConnection.class); + + private Socket socket; + private BufferedReader in; + private BufferedWriter out; + + protected Socket getSocket() throws Exception { + return new java.net.Socket(); + } + + @Override + protected void connect(int retryAttempts) throws Exception { + try { + this.socket = this.getSocket(); + this.socket.connect(new InetSocketAddress(this.getHost(), this.getPort()), this.getConnectionTimeout()); + this.socket.setSoTimeout(this.getIdleTimeout()); + this.out = new BufferedWriter(new OutputStreamWriter(this.socket.getOutputStream(), Message.getCharsetEncoding())); + this.in = new BufferedReader(new InputStreamReader(this.socket.getInputStream(), Message.getCharsetEncoding())); + } catch (Exception ex) { + if (retryAttempts > 0) { + try { + Thread.sleep(this.getRetryWait()); + } catch (Exception ex1) { + SocketConnection.log.debug("Thread sleep error", ex1); + } + this.connect(retryAttempts - 1); + } else { + throw ex; + } + } + } + + @Override + public boolean isConnected() { + return this.socket.isConnected(); + } + + @Override + public void disconnect() { + try { + this.socket.close(); + } catch (Exception ex) { + + } + } + + @Override + protected void internalSend(String cmd) throws ConnectionFailure { + try { + this.out.write(cmd); + this.out.write('\r'); + this.out.flush(); + } catch (Exception ex) { + throw new ConnectionFailure(ex); + } + } + + @Override + protected String internalWaitfor(String match) throws ConnectionFailure { + StringBuffer message = new StringBuffer(); + char buffer[] = new char[2048]; + int len; + + try { + do { + len = this.in.read(buffer); + message.append(new String(buffer, 0, len)); + } while ((message.toString()).lastIndexOf(match) < 0); + } catch (Exception ex) { + throw new ConnectionFailure(ex); + } + + String msg = message.toString(); + int cutoff = msg.lastIndexOf(match); + String ret = msg.substring(0, cutoff); + return ret; + } } diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/TestSSLSocketTransport.java b/src/main/java/com/ceridwen/circulation/SIP/transport/TestSSLSocketTransport.java new file mode 100644 index 0000000..17d4125 --- /dev/null +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/TestSSLSocketTransport.java @@ -0,0 +1,186 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ceridwen.circulation.SIP.transport; + +import com.ceridwen.circulation.SIP.exceptions.ChecksumError; +import com.ceridwen.circulation.SIP.exceptions.ConnectionFailure; +import com.ceridwen.circulation.SIP.exceptions.InvalidFieldLength; +import com.ceridwen.circulation.SIP.exceptions.MandatoryFieldOmitted; +import com.ceridwen.circulation.SIP.exceptions.MessageNotUnderstood; +import com.ceridwen.circulation.SIP.exceptions.RetriesExceeded; +import com.ceridwen.circulation.SIP.exceptions.SequenceError; +import com.ceridwen.circulation.SIP.messages.ACSStatus; +import com.ceridwen.circulation.SIP.messages.CheckOut; +import com.ceridwen.circulation.SIP.messages.CheckOutResponse; +import com.ceridwen.circulation.SIP.messages.Message; +import com.ceridwen.circulation.SIP.messages.SCStatus; +import com.ceridwen.circulation.SIP.netty.server.SIPDaemon; +import com.ceridwen.circulation.SIP.samples.netty.DummyDriverFactory; +import com.ceridwen.circulation.SIP.types.enumerations.ProtocolVersion; +import com.ceridwen.circulation.SIP.types.flagfields.SupportedMessages; +import io.netty.handler.ssl.util.SelfSignedCertificate; +import java.util.Date; +import junit.framework.Assert; +import org.junit.After; +import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Test; + +/** + * + * @author Matthew.Dovey + */ +public class TestSSLSocketTransport { + static SIPDaemon server; + static SelfSignedCertificate ssc; + + @Before + public void setUp() throws Exception { + // Run netty server + ssc = new SelfSignedCertificate(); + server = new SIPDaemon("Sample", "localhost", 12345, ssc.certificate(), ssc.privateKey(), new DummyDriverFactory(), true); + + server.start(); + } + + @After + public void tearDown() throws Exception { + server.stop(); + } + + @Test + public void test() { + /** + * Now try basic client commands + */ + Connection connection; + Message request, response; + + connection = new SSLSocketConnection(); + ((SSLSocketConnection) connection).setServerCertificateCA(ssc.certificate()); + ((SocketConnection) connection).setHost("localhost"); + ((SocketConnection) connection).setPort(12345); + ((SocketConnection) connection).setConnectionTimeout(30000); + ((SocketConnection) connection).setIdleTimeout(30000); + ((SocketConnection) connection).setRetryAttempts(2); + ((SocketConnection) connection).setRetryWait(500); + + try { + connection.connect(); + } catch (Exception e1) { + fail("Connection failed: " + e1.getMessage()); + return; + } + + /** + * It is necessary to send a SC Status with protocol version 2.0 else + * server will assume 1.0) + */ + + request = new SCStatus(); + ((SCStatus) request).setProtocolVersion(ProtocolVersion.VERSION_2_00); + + try { + response = connection.send(request); + } catch (RetriesExceeded e) { + Assert.fail("Retries exceeded: " + e.getMessage()); + return; + } catch (ConnectionFailure e) { + Assert.fail("Connection failure: " + e.getMessage()); + return; + } catch (MessageNotUnderstood e) { + Assert.fail("Message not understood: " + e.getMessage()); + return; + } catch (ChecksumError e) { + Assert.fail("Checksum error: " + e.getMessage()); + return; + } catch (SequenceError e) { + Assert.fail("Sequence error: " + e.getMessage()); + return; + } catch (MandatoryFieldOmitted e) { + Assert.fail("Mandatory Field Omitted: " + e.getMessage()); + return; + } catch (InvalidFieldLength e) { + Assert.fail("Invalid field length: " + e.getMessage()); + return; + } + + if (!(response instanceof ACSStatus)) { + fail("Status Request did not return valid response from server."); + return; + } + + + /** + * Check if the server can support checkout + */ + if (!((ACSStatus) response).getSupportedMessages().isSet(SupportedMessages.CHECK_OUT)) { + fail("Check out not supported"); + return; + } + + request = new CheckOut(); + + /** + * The code below would be the normal way of creating the request + */ + + ((CheckOut) request).setPatronIdentifier("2000000"); + ((CheckOut) request).setItemIdentifier("300000000"); + ((CheckOut) request).setSCRenewalPolicy(Boolean.TRUE); + ((CheckOut) request).setTransactionDate(new Date()); + + try { + response = connection.send(request); + } catch (RetriesExceeded e) { + fail("Retries exceeded: " + e.getMessage()); + return; + } catch (ConnectionFailure e) { + fail("Connection failure: " + e.getMessage()); + return; + } catch (MessageNotUnderstood e) { + fail("Message not understood: " + e.getMessage()); + return; + } catch (ChecksumError e) { + fail("Checksum error: " + e.getMessage()); + return; + } catch (SequenceError e) { + fail("Sequence error: " + e.getMessage()); + return; + } catch (MandatoryFieldOmitted e) { + fail("Mandatory Field Omitted: " + e.getMessage()); + return; + } catch (InvalidFieldLength e) { + fail("Invalid field length: " + e.getMessage()); + return; + } + + if (!(response instanceof CheckOutResponse)) { + fail("Error - CheckOut Request did not return valid response from server"); + return; + } + + try { + String testCase = response.encode('1'); + Assert.assertTrue(testCase.startsWith("120NUN") && testCase.contains("AA|AB|AH|AJ|AO|AY1AZ")); // strip out components which may change (transaction date and checksum) + } catch (MessageNotUnderstood e) { + fail("Message not understood: " + e.getMessage()); + return; + } catch (MandatoryFieldOmitted e) { + fail("Mandatory Field Omitted: " + e.getMessage()); + return; + } catch (InvalidFieldLength e) { + fail("Invalid field length: " + e.getMessage()); + return; + } + } +} + + + + + + diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/TestSocketTransport.java b/src/main/java/com/ceridwen/circulation/SIP/transport/TestSocketTransport.java index d25b4ee..fddbdf8 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/TestSocketTransport.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/TestSocketTransport.java @@ -22,33 +22,25 @@ import com.ceridwen.circulation.SIP.messages.CheckOutResponse; import com.ceridwen.circulation.SIP.messages.Message; import com.ceridwen.circulation.SIP.messages.SCStatus; -import com.ceridwen.circulation.SIP.server.MessageHandlerDummyImpl; -import com.ceridwen.circulation.SIP.server.SocketDaemon; +import com.ceridwen.circulation.SIP.netty.server.SIPDaemon; +import com.ceridwen.circulation.SIP.samples.netty.DummyDriverFactory; import com.ceridwen.circulation.SIP.types.enumerations.ProtocolVersion; import com.ceridwen.circulation.SIP.types.flagfields.SupportedMessages; public class TestSocketTransport { - static SocketDaemon thread; - + static SIPDaemon server; @Before public void setUp() throws Exception { - /** - * Run simple socket server - */ + // Run netty server + server = new SIPDaemon("Sample", "localhost", 12345, new DummyDriverFactory(), true); - TestSocketTransport.thread = new SocketDaemon("localhost", 12345, new MessageHandlerDummyImpl()); - TestSocketTransport.thread.setStrictChecksumChecking(true); - TestSocketTransport.thread.start(); + server.start(); } @After public void tearDown() throws Exception { - /** - * Stop simple socket server - */ - - TestSocketTransport.thread.shutdown(); + server.stop(); } @Test From 0b79f27a3780e2e38e7bc1b98bc7b703e2de0624 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Wed, 7 Sep 2016 22:45:51 +0100 Subject: [PATCH 14/28] Deprecate old server implementation --- .../com/ceridwen/circulation/SIP/server/MessageBroker.java | 1 + .../ceridwen/circulation/SIP/server/MessageHandler.java | 1 + .../circulation/SIP/server/MessageHandlerDummyImpl.java | 1 + .../com/ceridwen/circulation/SIP/server/SocketDaemon.java | 1 + .../com/ceridwen/circulation/SIP/server/SocketServer.java | 3 ++- .../com/ceridwen/circulation/SIP/server/package-info.java | 7 +++++++ 6 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/ceridwen/circulation/SIP/server/package-info.java diff --git a/src/main/java/com/ceridwen/circulation/SIP/server/MessageBroker.java b/src/main/java/com/ceridwen/circulation/SIP/server/MessageBroker.java index fded984..7ca9e81 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/server/MessageBroker.java +++ b/src/main/java/com/ceridwen/circulation/SIP/server/MessageBroker.java @@ -29,6 +29,7 @@ import com.ceridwen.circulation.SIP.messages.Message; import com.ceridwen.circulation.SIP.messages.SCResend; +@Deprecated public class MessageBroker { private static Log logger = LogFactory.getLog(MessageBroker.class); diff --git a/src/main/java/com/ceridwen/circulation/SIP/server/MessageHandler.java b/src/main/java/com/ceridwen/circulation/SIP/server/MessageHandler.java index d6044b6..ea33569 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/server/MessageHandler.java +++ b/src/main/java/com/ceridwen/circulation/SIP/server/MessageHandler.java @@ -48,6 +48,7 @@ import com.ceridwen.circulation.SIP.messages.RenewResponse; import com.ceridwen.circulation.SIP.messages.SCStatus; +@Deprecated public interface MessageHandler { public ACSStatus Status(SCStatus msg); diff --git a/src/main/java/com/ceridwen/circulation/SIP/server/MessageHandlerDummyImpl.java b/src/main/java/com/ceridwen/circulation/SIP/server/MessageHandlerDummyImpl.java index f4d62a9..f8aa3f1 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/server/MessageHandlerDummyImpl.java +++ b/src/main/java/com/ceridwen/circulation/SIP/server/MessageHandlerDummyImpl.java @@ -36,6 +36,7 @@ import com.ceridwen.circulation.SIP.messages.SCStatus; import com.ceridwen.circulation.SIP.types.flagfields.SupportedMessages; +@Deprecated public class MessageHandlerDummyImpl implements MessageHandler { @Override diff --git a/src/main/java/com/ceridwen/circulation/SIP/server/SocketDaemon.java b/src/main/java/com/ceridwen/circulation/SIP/server/SocketDaemon.java index 4c903c1..c3ad12c 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/server/SocketDaemon.java +++ b/src/main/java/com/ceridwen/circulation/SIP/server/SocketDaemon.java @@ -31,6 +31,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +@Deprecated public class SocketDaemon extends Thread { private static Log logger = LogFactory.getLog(SocketDaemon.class); diff --git a/src/main/java/com/ceridwen/circulation/SIP/server/SocketServer.java b/src/main/java/com/ceridwen/circulation/SIP/server/SocketServer.java index 5756b11..410b8c6 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/server/SocketServer.java +++ b/src/main/java/com/ceridwen/circulation/SIP/server/SocketServer.java @@ -18,12 +18,13 @@ ******************************************************************************/ package com.ceridwen.circulation.SIP.server; +@Deprecated public class SocketServer { /** * @param args */ - public static void main(String[] args) { + public static void main(String[] args) { SocketDaemon thread = new SocketDaemon("localhost", 12345, new MessageHandlerDummyImpl()); thread.start(); } diff --git a/src/main/java/com/ceridwen/circulation/SIP/server/package-info.java b/src/main/java/com/ceridwen/circulation/SIP/server/package-info.java new file mode 100644 index 0000000..4e885d7 --- /dev/null +++ b/src/main/java/com/ceridwen/circulation/SIP/server/package-info.java @@ -0,0 +1,7 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +@Deprecated +package com.ceridwen.circulation.SIP.server; From 51a4af181b8ff75854c16c5ac15db4950350aba0 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Wed, 7 Sep 2016 23:15:29 +0100 Subject: [PATCH 15/28] Fix Private Key loading code --- .../SIP/transport/SSLSocketConnection.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java index 63cc991..89349f7 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java @@ -21,6 +21,7 @@ import java.security.cert.Certificate; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; /** * @@ -111,12 +112,17 @@ protected Socket getSocket() throws Exception { if (clientPrivateKey != null && clientCertificate != null) { keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyStore.load(null); + String data = new String(Files.readAllBytes(clientPrivateKey.toPath())); + data = data.replace("-----BEGIN PRIVATE KEY-----\n", ""); + data = data.replace("-----END PRIVATE KEY-----", ""); + data = data.replaceAll("\\s", ""); if (clientPrivateKeyPassword == null) { - keyStore.setKeyEntry("client", kf.generatePrivate(new PKCS8EncodedKeySpec(Files.readAllBytes(clientPrivateKey.toPath()))), null, cf.generateCertificates(new FileInputStream(clientCertificate)).toArray(new Certificate[]{})); + keyStore.setKeyEntry("client", kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(data))), null, cf.generateCertificates(new FileInputStream(clientCertificate)).toArray(new Certificate[]{})); + keyManagerFactory.init(keyStore, null); } else { - keyStore.setKeyEntry("client", kf.generatePrivate(new PKCS8EncodedKeySpec(Files.readAllBytes(clientPrivateKey.toPath()))), clientPrivateKeyPassword.toCharArray(), cf.generateCertificates(new FileInputStream(clientCertificate)).toArray(new Certificate[]{})); + keyStore.setKeyEntry("client", kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(data))), clientPrivateKeyPassword.toCharArray(), cf.generateCertificates(new FileInputStream(clientCertificate)).toArray(new Certificate[]{})); + keyManagerFactory.init(keyStore, clientPrivateKeyPassword.toCharArray()); } - keyManagerFactory.init(keyStore, "changeit".toCharArray()); } if (serverCertificateCA != null) { From 8f68c9e44e6916a6d7fd0392c4e7444372cc8563 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Thu, 8 Sep 2016 20:51:05 +0100 Subject: [PATCH 16/28] Load default SSLContext when SSL parameters not set --- .../SIP/transport/SSLSocketConnection.java | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java index 89349f7..786cf61 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/SSLSocketConnection.java @@ -102,38 +102,43 @@ public void setClientPrivateKeyPassword(String clientPrivateKeyPassword) { @Override protected Socket getSocket() throws Exception { - KeyManagerFactory keyManagerFactory = null; - TrustManagerFactory trustManagerFactory = null; + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");; + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); KeyStore keyStore = KeyStore.getInstance("PKCS12"); KeyStore trustStore = KeyStore.getInstance("PKCS12"); CertificateFactory cf = CertificateFactory.getInstance("X.509"); KeyFactory kf = KeyFactory.getInstance("RSA"); if (clientPrivateKey != null && clientCertificate != null) { - keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyStore.load(null); - String data = new String(Files.readAllBytes(clientPrivateKey.toPath())); - data = data.replace("-----BEGIN PRIVATE KEY-----\n", ""); - data = data.replace("-----END PRIVATE KEY-----", ""); - data = data.replaceAll("\\s", ""); - if (clientPrivateKeyPassword == null) { - keyStore.setKeyEntry("client", kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(data))), null, cf.generateCertificates(new FileInputStream(clientCertificate)).toArray(new Certificate[]{})); - keyManagerFactory.init(keyStore, null); - } else { - keyStore.setKeyEntry("client", kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(data))), clientPrivateKeyPassword.toCharArray(), cf.generateCertificates(new FileInputStream(clientCertificate)).toArray(new Certificate[]{})); - keyManagerFactory.init(keyStore, clientPrivateKeyPassword.toCharArray()); - } + String data = new String(Files.readAllBytes(clientPrivateKey.toPath())); + data = data.replace("-----BEGIN PRIVATE KEY-----\n", ""); + data = data.replace("-----END PRIVATE KEY-----", ""); + data = data.replaceAll("\\s", ""); + if (clientPrivateKeyPassword == null) { + keyStore.setKeyEntry("client", kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(data))), null, cf.generateCertificates(new FileInputStream(clientCertificate)).toArray(new Certificate[]{})); + keyManagerFactory.init(keyStore, null); + } else { + keyStore.setKeyEntry("client", kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(data))), clientPrivateKeyPassword.toCharArray(), cf.generateCertificates(new FileInputStream(clientCertificate)).toArray(new Certificate[]{})); + keyManagerFactory.init(keyStore, clientPrivateKeyPassword.toCharArray()); + } + } else { + keyManagerFactory = null; } if (serverCertificateCA != null) { - trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustStore.load(null); trustStore.setCertificateEntry("ca", cf.generateCertificate(new FileInputStream(serverCertificateCA))); trustManagerFactory.init(trustStore); + } else { + if (keyManagerFactory == null) { + return SSLContext.getDefault().getSocketFactory().createSocket(); + } else { + trustManagerFactory.init((KeyStore)null); + } } - SSLContext context = SSLContext.getInstance("TLS"); - context.init(keyManagerFactory == null?null:keyManagerFactory.getKeyManagers(), trustManagerFactory == null?null:trustManagerFactory.getTrustManagers(), new SecureRandom()); + context.init(keyManagerFactory == null?null:keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); SSLSocketFactory sockFact = context.getSocketFactory(); return sockFact.createSocket(); } From ae7eaff698724a1502076cd6e6706be24e76e332 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Thu, 8 Sep 2016 20:56:13 +0100 Subject: [PATCH 17/28] Sample to include ssl support --- .../circulation/SIP/samples/Sample.java | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java b/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java index 15ec3b6..1c40445 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java +++ b/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java @@ -36,14 +36,19 @@ import com.ceridwen.circulation.SIP.messages.SCStatus; import com.ceridwen.circulation.SIP.netty.server.SIPDaemon; import com.ceridwen.circulation.SIP.samples.netty.DummyDriverFactory; +import com.ceridwen.circulation.SIP.transport.SSLSocketConnection; import com.ceridwen.circulation.SIP.transport.SocketConnection; import com.ceridwen.circulation.SIP.types.enumerations.ProtocolVersion; import com.ceridwen.circulation.SIP.types.flagfields.SupportedMessages; +import io.netty.handler.ssl.util.SelfSignedCertificate; import java.util.logging.Level; import java.util.logging.Logger; public class Sample { - + + private static final boolean SSL = false; + private static SelfSignedCertificate ssc; + public static void main(String[] args) { try { System.setProperty("com.ceridwen.circulation.SIP.charset", "ISO8859_1"); @@ -51,7 +56,12 @@ public static void main(String[] args) { SIPDaemon server; // Run netty server - server = new SIPDaemon("Sample", "localhost", 12345, false, new DummyDriverFactory(), true); + if (SSL) { + ssc = new SelfSignedCertificate(); + server = new SIPDaemon("Sample", "localhost", 12345, ssc.certificate(), ssc.privateKey(), new DummyDriverFactory(), true); + } else { + server = new SIPDaemon("Sample", "localhost", 12345, new DummyDriverFactory(), true); + } server.start(); // Do sample checkout @@ -70,7 +80,12 @@ public static void checkOut() { */ SocketConnection connection; - connection = new SocketConnection(); + if (SSL) { + connection = new SSLSocketConnection(); + ((SSLSocketConnection) connection).setServerCertificateCA(ssc.certificate()); + } else { + connection = new SocketConnection(); + } connection.setHost("localhost"); connection.setPort(12345); connection.setConnectionTimeout(30000); From eb105580e74906db5d1609b35de0b7f05acdca01 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Fri, 9 Sep 2016 21:14:48 +0100 Subject: [PATCH 18/28] Remove redundant code --- .../SIP/netty/server/channel/SIPChannelHandler.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/netty/server/channel/SIPChannelHandler.java b/src/main/java/com/ceridwen/circulation/SIP/netty/server/channel/SIPChannelHandler.java index 2b601f8..0e1f73c 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/netty/server/channel/SIPChannelHandler.java +++ b/src/main/java/com/ceridwen/circulation/SIP/netty/server/channel/SIPChannelHandler.java @@ -67,12 +67,7 @@ public void channelRead0(ChannelHandlerContext ctx, Message request) throws Exce response.setSequenceCharacter(request.getSequenceCharacter()); - boolean close = false; - ChannelFuture future = ctx.write(response); - if (close) { - future.addListener(ChannelFutureListener.CLOSE); - } } @Override From d39fe4e5f733b547346185f395d673cae91ec84b Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Fri, 9 Sep 2016 21:15:28 +0100 Subject: [PATCH 19/28] Add informational methods --- .../circulation/SIP/netty/server/SIPDaemon.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java b/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java index 0ea3760..f32ae9e 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java +++ b/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPDaemon.java @@ -35,7 +35,7 @@ public class SIPDaemon implements GenericFutureListener { private ChannelFuture f; private EventLoopGroup bossGroup; private EventLoopGroup workerGroup; - + public SIPDaemon(String name, String ip, int port, File keyCertChainFile, File keyFile, DriverFactory driverFactory, boolean strictChecksumChecking) { this(name, ip, port, keyCertChainFile, keyFile, null, driverFactory, strictChecksumChecking); } @@ -55,6 +55,21 @@ public SIPDaemon(String name, String ip, int port, File keyCertChainFile, File k this.strictChecksumChecking = strictChecksumChecking; } + public String getName() { + return name; + } + + public String getIp() { + return ip; + } + + public int getPort() { + return port; + } + + public boolean isStrictChecksumChecking() { + return strictChecksumChecking; + } public void start() throws Exception { // Configure SSL. From 15b736a89b2ced1ff55a3430fa38ba4f9eb05dda Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Fri, 9 Sep 2016 21:19:35 +0100 Subject: [PATCH 20/28] Remove static declarations --- .../SIP/netty/server/SIPServerInitializer.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPServerInitializer.java b/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPServerInitializer.java index 76ad0f4..e4f9a2b 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPServerInitializer.java +++ b/src/main/java/com/ceridwen/circulation/SIP/netty/server/SIPServerInitializer.java @@ -20,12 +20,12 @@ public class SIPServerInitializer extends ChannelInitializer { - private static final StringDecoder DECODER = new StringDecoder(); - private static final StringEncoder ENCODER = new StringEncoder(); - private static SIPMessageDecoder SIPDECODER; - private static final SIPMessageEncoder SIPENCODER = new SIPMessageEncoder(); + private final StringDecoder DECODER = new StringDecoder(); + private final StringEncoder ENCODER = new StringEncoder(); + private SIPMessageDecoder SIPDECODER; + private final SIPMessageEncoder SIPENCODER = new SIPMessageEncoder(); - private static SIPChannelHandler SERVER_HANDLER; + private SIPChannelHandler SERVER_HANDLER; private final SslContext sslCtx; @@ -45,8 +45,8 @@ public SIPServerInitializer(DriverFactory driverFactory, boolean strictChecksumC public SIPServerInitializer(DriverFactory driverFactory, boolean strictChecksumChecking) { this.sslCtx = null; - SIPDECODER = new SIPMessageDecoder(strictChecksumChecking); - SERVER_HANDLER = new SIPChannelHandler(driverFactory); + SIPDECODER = new SIPMessageDecoder(strictChecksumChecking); + SERVER_HANDLER = new SIPChannelHandler(driverFactory); } @Override @@ -62,7 +62,7 @@ public void initChannel(SocketChannel ch) throws Exception { // Add the text line codec combination first, pipeline.addLast(new DelimiterBasedFrameDecoder(8192, getDelimiters())); - // the encoder and decoder are static as these are sharable + // Add encoders and decoders pipeline.addLast(DECODER); pipeline.addLast(ENCODER); pipeline.addLast(SIPDECODER); From f62a5bbf0797ef5bce00a59139dab7a389f59f1a Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Fri, 9 Sep 2016 21:20:26 +0100 Subject: [PATCH 21/28] Use more efficient Integer constructors --- .../com/ceridwen/circulation/SIP/messages/Message.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/messages/Message.java b/src/main/java/com/ceridwen/circulation/SIP/messages/Message.java index 202ef8b..c7b7cab 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/messages/Message.java +++ b/src/main/java/com/ceridwen/circulation/SIP/messages/Message.java @@ -327,7 +327,7 @@ private String encode(Character sequence, boolean autoPop) throws MandatoryField desc.getPropertyType().getName()); } } - if (fixed.containsKey(new Integer(field.start))) { + if (fixed.containsKey(Integer.valueOf(field.start))) { throw new java.lang.AssertionError("Positioning error inserting field at " + field.start + " for class " + this.getClass().getName()); } fixed.put(new Integer(field.start), this.pad(value[0], field)); @@ -411,7 +411,7 @@ private void setProp(PropertyDescriptor desc, String value) { if (desc.getPropertyType() == Integer.class) { if (!value.trim().isEmpty()) { desc.getWriteMethod().invoke(this, - new Object[] { new Integer(value.trim()) }); + new Object[] {Integer.valueOf(value.trim()) }); } return; } @@ -806,7 +806,7 @@ private Message getDefaultMessage() { if (field.getType() == Integer.class) { Method method = desc.getWriteMethod(); if (method != null) { - method.invoke(msg, new Object[]{new Integer(0)}); + method.invoke(msg, new Object[]{Integer.valueOf(0)}); } } if (field.getType() == Boolean.class) { @@ -876,7 +876,7 @@ private Message getPopulatedMessage() { if (length != 0) { value = value.substring(0, length); } - method.invoke(msg, new Object[]{new Integer(value)}); + method.invoke(msg, new Object[]{Integer.valueOf(value)}); } if (type == String.class) { String value = field.getName(); From 3019f7c13c8b03bfb9df0f5b26eeef0e67f6bf45 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Fri, 9 Sep 2016 21:21:54 +0100 Subject: [PATCH 22/28] Remove redundant code --- .../com/ceridwen/circulation/SIP/fields/Fields.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/fields/Fields.java b/src/main/java/com/ceridwen/circulation/SIP/fields/Fields.java index 9e0c11c..851102a 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/fields/Fields.java +++ b/src/main/java/com/ceridwen/circulation/SIP/fields/Fields.java @@ -247,9 +247,9 @@ static public PositionedFieldDefinition getPositionedFieldDefinition(String mess } catch (Exception ex) { throw new java.lang.AssertionError(messageName + " - Positioned FieldDescriptor not defined: " + fieldName); } - if (fld == null) { - throw new java.lang.AssertionError(messageName + " - Positioned FieldDescriptor not defined: " + fieldName); - } +// if (fld == null) { +// throw new java.lang.AssertionError(messageName + " - Positioned FieldDescriptor not defined: " + fieldName); +// } Field fldann = fld.getAnnotation(Field.class); if (fldann == null) { throw new java.lang.AssertionError(messageName + " - Positioned FieldDescriptor not defined: " + fieldName); @@ -273,9 +273,9 @@ static public TaggedFieldDefinition getTaggedFieldDefinition(String messageName, } catch (Exception ex) { throw new java.lang.AssertionError(messageName + " - Tagged FieldDescriptor not defined: " + fieldName); } - if (fld == null) { - throw new java.lang.AssertionError(messageName + " - Tagged FieldDescriptor not defined: " + fieldName); - } +// if (fld == null) { +// throw new java.lang.AssertionError(messageName + " - Tagged FieldDescriptor not defined: " + fieldName); +// } Field fldann = fld.getAnnotation(Field.class); if (fldann == null) { throw new java.lang.AssertionError(messageName + " - Tagged FieldDescriptor not defined: " + fieldName); From ac4b219e679515e7a63ee3d541eebee2e0fb1984 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Fri, 9 Sep 2016 21:22:39 +0100 Subject: [PATCH 23/28] Improve retry handling logic and exception raising --- .../SIP/exceptions/MessageNotUnderstood.java | 5 +++ .../SIP/exceptions/RetriesExceeded.java | 5 +++ .../circulation/SIP/samples/Sample.java | 4 +-- .../circulation/SIP/transport/Connection.java | 25 +++++++------- .../SIP/transport/SocketConnection.java | 3 +- .../SIP/transport/TelnetConnection.java | 5 +-- .../SIP/transport/TestSSLSocketTransport.java | 33 ++++++------------ .../SIP/transport/TestSocketTransport.java | 34 ++++++------------- 8 files changed, 51 insertions(+), 63 deletions(-) diff --git a/src/main/java/com/ceridwen/circulation/SIP/exceptions/MessageNotUnderstood.java b/src/main/java/com/ceridwen/circulation/SIP/exceptions/MessageNotUnderstood.java index b0b15f7..5929227 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/exceptions/MessageNotUnderstood.java +++ b/src/main/java/com/ceridwen/circulation/SIP/exceptions/MessageNotUnderstood.java @@ -25,5 +25,10 @@ public class MessageNotUnderstood extends Exception { private static final long serialVersionUID = 1857825095575274480L; public MessageNotUnderstood() { + super(); + } + + public MessageNotUnderstood(Throwable ex) { + super(ex); } } diff --git a/src/main/java/com/ceridwen/circulation/SIP/exceptions/RetriesExceeded.java b/src/main/java/com/ceridwen/circulation/SIP/exceptions/RetriesExceeded.java index 4fbb2ed..d60a98e 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/exceptions/RetriesExceeded.java +++ b/src/main/java/com/ceridwen/circulation/SIP/exceptions/RetriesExceeded.java @@ -34,5 +34,10 @@ public class RetriesExceeded extends Exception { private static final long serialVersionUID = 1416841113916472161L; public RetriesExceeded() { + super(); + } + + public RetriesExceeded(Throwable ex) { + super(ex); } } diff --git a/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java b/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java index 1c40445..2ca64ef 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java +++ b/src/main/java/com/ceridwen/circulation/SIP/samples/Sample.java @@ -111,7 +111,7 @@ public static void checkOut() { try { scStatusResponse = connection.send(scStatusRequest); - } catch (RetriesExceeded | ConnectionFailure | MessageNotUnderstood | ChecksumError | SequenceError | MandatoryFieldOmitted | InvalidFieldLength ex) { + } catch (RetriesExceeded | MessageNotUnderstood | ChecksumError | SequenceError | MandatoryFieldOmitted | InvalidFieldLength ex) { Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); return; } @@ -148,7 +148,7 @@ public static void checkOut() { try { checkOutResponse = connection.send(checkOutRequest); - } catch (RetriesExceeded | ConnectionFailure | MessageNotUnderstood | ChecksumError | SequenceError | MandatoryFieldOmitted | InvalidFieldLength ex) { + } catch (RetriesExceeded | MessageNotUnderstood | ChecksumError | SequenceError | MandatoryFieldOmitted | InvalidFieldLength ex) { Logger.getLogger(Sample.class.getName()).log(Level.SEVERE, null, ex); return; } diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/Connection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/Connection.java index 27ca005..a6dc36f 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/Connection.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/Connection.java @@ -198,7 +198,7 @@ public String waitfor(String match) throws ConnectionFailure { return ret; } - public synchronized Message send(Message msg) throws ConnectionFailure, RetriesExceeded, ChecksumError, SequenceError, MessageNotUnderstood, + public synchronized Message send(Message msg) throws RetriesExceeded, ChecksumError, SequenceError, MessageNotUnderstood, MandatoryFieldOmitted, InvalidFieldLength { String request, response = null; Message responseMessage = null; @@ -208,13 +208,12 @@ public synchronized Message send(Message msg) throws ConnectionFailure, RetriesE } try { boolean retry; - boolean understood = false; int retries = 0; do { retry = false; try { if (this.getAddSequenceAndChecksum()) { - request = msg.encode(new Character(this.getNextSequence())); + request = msg.encode(Character.valueOf(this.getNextSequence())); } else { request = msg.encode(null); } @@ -229,28 +228,28 @@ public synchronized Message send(Message msg) throws ConnectionFailure, RetriesE responseMessage = Message.decode(response, null, this.getStrictChecksumChecking()); } if (responseMessage instanceof SCResend) { - throw new ConnectionFailure(); + throw new MessageNotUnderstood(); } - understood = true; - } catch (ConnectionFailure ex) { + } catch (ConnectionFailure | MessageNotUnderstood ex) { try { this.wait(this.getRetryWait()); } catch (Exception ex1) { Connection.log.debug("Thread sleep error", ex1); } - retry = true; retries++; if (retries > this.getRetryAttempts()) { - if (understood) { - throw new RetriesExceeded(); - } else { - throw new MessageNotUnderstood(); - } + if (ex instanceof MessageNotUnderstood) { + throw (MessageNotUnderstood)ex; + } else { + throw new RetriesExceeded(ex); + } + } else { + retry = true; } } } while (retry); if (responseMessage == null) { - throw new ConnectionFailure(); + throw new MessageNotUnderstood(); } return responseMessage; } catch (RetriesExceeded e) { diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java index 3037c92..4f12240 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/SocketConnection.java @@ -29,6 +29,7 @@ import org.apache.commons.logging.LogFactory; import com.ceridwen.circulation.SIP.exceptions.ConnectionFailure; +import com.ceridwen.circulation.SIP.exceptions.RetriesExceeded; import com.ceridwen.circulation.SIP.messages.Message; public class SocketConnection extends Connection { @@ -59,7 +60,7 @@ protected void connect(int retryAttempts) throws Exception { } this.connect(retryAttempts - 1); } else { - throw ex; + throw new RetriesExceeded(ex); } } } diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/TelnetConnection.java b/src/main/java/com/ceridwen/circulation/SIP/transport/TelnetConnection.java index 399de83..ac26257 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/TelnetConnection.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/TelnetConnection.java @@ -28,6 +28,7 @@ import org.apache.commons.net.telnet.TelnetClient; import com.ceridwen.circulation.SIP.exceptions.ConnectionFailure; +import com.ceridwen.circulation.SIP.exceptions.RetriesExceeded; import com.ceridwen.circulation.SIP.messages.Message; import com.ceridwen.util.net.TimeoutSocketFactory; @@ -94,7 +95,7 @@ protected void connect(int retry) throws Exception { } this.connect(retry - 1); } else { - throw e; + throw new RetriesExceeded(e); } } try { @@ -110,7 +111,7 @@ protected void connect(int retry) throws Exception { } this.connect(retry - 1); } else { - throw e; + throw new RetriesExceeded(e); } } } diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/TestSSLSocketTransport.java b/src/main/java/com/ceridwen/circulation/SIP/transport/TestSSLSocketTransport.java index 17d4125..42c3702 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/TestSSLSocketTransport.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/TestSSLSocketTransport.java @@ -6,7 +6,6 @@ package com.ceridwen.circulation.SIP.transport; import com.ceridwen.circulation.SIP.exceptions.ChecksumError; -import com.ceridwen.circulation.SIP.exceptions.ConnectionFailure; import com.ceridwen.circulation.SIP.exceptions.InvalidFieldLength; import com.ceridwen.circulation.SIP.exceptions.MandatoryFieldOmitted; import com.ceridwen.circulation.SIP.exceptions.MessageNotUnderstood; @@ -23,7 +22,6 @@ import com.ceridwen.circulation.SIP.types.flagfields.SupportedMessages; import io.netty.handler.ssl.util.SelfSignedCertificate; import java.util.Date; -import junit.framework.Assert; import org.junit.After; import static org.junit.Assert.fail; import org.junit.Before; @@ -34,8 +32,8 @@ * @author Matthew.Dovey */ public class TestSSLSocketTransport { - static SIPDaemon server; - static SelfSignedCertificate ssc; + SIPDaemon server; + SelfSignedCertificate ssc; @Before public void setUp() throws Exception { @@ -86,25 +84,22 @@ public void test() { try { response = connection.send(request); } catch (RetriesExceeded e) { - Assert.fail("Retries exceeded: " + e.getMessage()); - return; - } catch (ConnectionFailure e) { - Assert.fail("Connection failure: " + e.getMessage()); + fail("Retries exceeded: " + e.getMessage()); return; } catch (MessageNotUnderstood e) { - Assert.fail("Message not understood: " + e.getMessage()); + fail("Message not understood: " + e.getMessage()); return; } catch (ChecksumError e) { - Assert.fail("Checksum error: " + e.getMessage()); + fail("Checksum error: " + e.getMessage()); return; } catch (SequenceError e) { - Assert.fail("Sequence error: " + e.getMessage()); + fail("Sequence error: " + e.getMessage()); return; } catch (MandatoryFieldOmitted e) { - Assert.fail("Mandatory Field Omitted: " + e.getMessage()); + fail("Mandatory Field Omitted: " + e.getMessage()); return; } catch (InvalidFieldLength e) { - Assert.fail("Invalid field length: " + e.getMessage()); + fail("Invalid field length: " + e.getMessage()); return; } @@ -138,9 +133,6 @@ public void test() { } catch (RetriesExceeded e) { fail("Retries exceeded: " + e.getMessage()); return; - } catch (ConnectionFailure e) { - fail("Connection failure: " + e.getMessage()); - return; } catch (MessageNotUnderstood e) { fail("Message not understood: " + e.getMessage()); return; @@ -165,16 +157,13 @@ public void test() { try { String testCase = response.encode('1'); - Assert.assertTrue(testCase.startsWith("120NUN") && testCase.contains("AA|AB|AH|AJ|AO|AY1AZ")); // strip out components which may change (transaction date and checksum) + assert(testCase.startsWith("120NUN") && testCase.contains("AA|AB|AH|AJ|AO|AY1AZ")); // strip out components which may change (transaction date and checksum) } catch (MessageNotUnderstood e) { fail("Message not understood: " + e.getMessage()); - return; - } catch (MandatoryFieldOmitted e) { - fail("Mandatory Field Omitted: " + e.getMessage()); - return; + } catch (MandatoryFieldOmitted e) { + fail("Mandatory Field Omitted: " + e.getMessage()); } catch (InvalidFieldLength e) { fail("Invalid field length: " + e.getMessage()); - return; } } } diff --git a/src/main/java/com/ceridwen/circulation/SIP/transport/TestSocketTransport.java b/src/main/java/com/ceridwen/circulation/SIP/transport/TestSocketTransport.java index fddbdf8..3534f6e 100644 --- a/src/main/java/com/ceridwen/circulation/SIP/transport/TestSocketTransport.java +++ b/src/main/java/com/ceridwen/circulation/SIP/transport/TestSocketTransport.java @@ -4,14 +4,11 @@ import java.util.Date; -import junit.framework.Assert; - import org.junit.After; import org.junit.Before; import org.junit.Test; import com.ceridwen.circulation.SIP.exceptions.ChecksumError; -import com.ceridwen.circulation.SIP.exceptions.ConnectionFailure; import com.ceridwen.circulation.SIP.exceptions.InvalidFieldLength; import com.ceridwen.circulation.SIP.exceptions.MandatoryFieldOmitted; import com.ceridwen.circulation.SIP.exceptions.MessageNotUnderstood; @@ -28,7 +25,7 @@ import com.ceridwen.circulation.SIP.types.flagfields.SupportedMessages; public class TestSocketTransport { - static SIPDaemon server; + SIPDaemon server; @Before public void setUp() throws Exception { @@ -77,30 +74,27 @@ public void test() { try { response = connection.send(request); } catch (RetriesExceeded e) { - Assert.fail("Retries exceeded: " + e.getMessage()); - return; - } catch (ConnectionFailure e) { - Assert.fail("Connection failure: " + e.getMessage()); + fail("Retries exceeded: " + e.getMessage()); return; } catch (MessageNotUnderstood e) { - Assert.fail("Message not understood: " + e.getMessage()); + fail("Message not understood: " + e.getMessage()); return; } catch (ChecksumError e) { - Assert.fail("Checksum error: " + e.getMessage()); + fail("Checksum error: " + e.getMessage()); return; } catch (SequenceError e) { - Assert.fail("Sequence error: " + e.getMessage()); + fail("Sequence error: " + e.getMessage()); return; } catch (MandatoryFieldOmitted e) { - Assert.fail("Mandatory Field Omitted: " + e.getMessage()); + fail("Mandatory Field Omitted: " + e.getMessage()); return; } catch (InvalidFieldLength e) { - Assert.fail("Invalid field length: " + e.getMessage()); + fail("Invalid field length: " + e.getMessage()); return; } if (!(response instanceof ACSStatus)) { - fail("Status Request did not return valid response from server."); + fail("Status Request did not return valid response from server."); return; } @@ -129,9 +123,6 @@ public void test() { } catch (RetriesExceeded e) { fail("Retries exceeded: " + e.getMessage()); return; - } catch (ConnectionFailure e) { - fail("Connection failure: " + e.getMessage()); - return; } catch (MessageNotUnderstood e) { fail("Message not understood: " + e.getMessage()); return; @@ -156,16 +147,13 @@ public void test() { try { String testCase = response.encode('1'); - Assert.assertTrue(testCase.startsWith("120NUN") && testCase.contains("AA|AB|AH|AJ|AO|AY1AZ")); // strip out components which may change (transaction date and checksum) + assert(testCase.startsWith("120NUN") && testCase.contains("AA|AB|AH|AJ|AO|AY1AZ")); // strip out components which may change (transaction date and checksum) } catch (MessageNotUnderstood e) { fail("Message not understood: " + e.getMessage()); - return; - } catch (MandatoryFieldOmitted e) { - fail("Mandatory Field Omitted: " + e.getMessage()); - return; + } catch (MandatoryFieldOmitted e) { + fail("Mandatory Field Omitted: " + e.getMessage()); } catch (InvalidFieldLength e) { fail("Invalid field length: " + e.getMessage()); - return; } } } From eb4c627aef0ff81c45432f717680df5530777878 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Fri, 9 Sep 2016 21:26:25 +0100 Subject: [PATCH 24/28] Added performance and stress test --- .../SIP/performance/PerformanceTest.java | 246 ++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 src/test/java/com/ceridwen/circulation/SIP/performance/PerformanceTest.java diff --git a/src/test/java/com/ceridwen/circulation/SIP/performance/PerformanceTest.java b/src/test/java/com/ceridwen/circulation/SIP/performance/PerformanceTest.java new file mode 100644 index 0000000..8f1ac5c --- /dev/null +++ b/src/test/java/com/ceridwen/circulation/SIP/performance/PerformanceTest.java @@ -0,0 +1,246 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.ceridwen.circulation.SIP.performance; + +import com.ceridwen.circulation.SIP.exceptions.ChecksumError; +import com.ceridwen.circulation.SIP.exceptions.InvalidFieldLength; +import com.ceridwen.circulation.SIP.exceptions.MandatoryFieldOmitted; +import com.ceridwen.circulation.SIP.exceptions.MessageNotUnderstood; +import com.ceridwen.circulation.SIP.exceptions.RetriesExceeded; +import com.ceridwen.circulation.SIP.exceptions.SequenceError; +import com.ceridwen.circulation.SIP.messages.ACSStatus; +import com.ceridwen.circulation.SIP.messages.BlockPatron; +import com.ceridwen.circulation.SIP.messages.CheckIn; +import com.ceridwen.circulation.SIP.messages.CheckOut; +import com.ceridwen.circulation.SIP.messages.EndPatronSession; +import com.ceridwen.circulation.SIP.messages.FeePaid; +import com.ceridwen.circulation.SIP.messages.Hold; +import com.ceridwen.circulation.SIP.messages.ItemInformation; +import com.ceridwen.circulation.SIP.messages.ItemStatusUpdate; +import com.ceridwen.circulation.SIP.messages.Login; +import com.ceridwen.circulation.SIP.messages.Message; +import com.ceridwen.circulation.SIP.messages.PatronEnable; +import com.ceridwen.circulation.SIP.messages.PatronInformation; +import com.ceridwen.circulation.SIP.messages.PatronStatusRequest; +import com.ceridwen.circulation.SIP.messages.Renew; +import com.ceridwen.circulation.SIP.messages.RenewAll; +import com.ceridwen.circulation.SIP.messages.SCStatus; +import com.ceridwen.circulation.SIP.netty.server.SIPDaemon; +import com.ceridwen.circulation.SIP.samples.netty.DummyDriverFactory; +import com.ceridwen.circulation.SIP.transport.SSLSocketConnection; +import com.ceridwen.circulation.SIP.transport.SocketConnection; +import com.ceridwen.circulation.SIP.types.enumerations.ProtocolVersion; +import io.netty.handler.ssl.util.SelfSignedCertificate; +import java.io.File; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; + +class ThreadCounter { + private int count; + private int error; + private int retries; + + ThreadCounter() { + this.count = 0; + this.error = 0; + this.retries = 0; + } + + synchronized void Enter() { + count++; + } + + synchronized void Leave() { + count--; + } + + synchronized void Error(Exception ex, String type) { + error++; + this.Leave(); + } + + synchronized void Retried() { + retries++; + this.Leave(); + } + + int getCount() { + return count; + } + + int getError() { + return error; + } + + int getRetries() { + return retries; + } +} + +class ClientRunner extends Thread { + File cert = null; + Message msg; + ThreadCounter counter; + + public ClientRunner(Message msg, File cert, ThreadCounter counter) { + this.cert = cert; + this.msg = msg; + this.counter = counter; + } + + @Override + public void run() { + counter.Enter(); + SocketConnection connection; + + if (cert != null) { + connection = new SSLSocketConnection(); + ((SSLSocketConnection) connection).setServerCertificateCA(cert); + } else { + connection = new SocketConnection(); + } + connection.setHost("localhost"); + connection.setPort(12345); + connection.setConnectionTimeout(30000); + connection.setIdleTimeout(30000); + connection.setRetryAttempts(25); + connection.setRetryWait(500); + + try { + connection.connect(); + } catch (RetriesExceeded ex) { + counter.Retried(); + return; + } catch (Exception ex) { + counter.Error(ex, "CONNECT"); + return; + } + + /** + * It is necessary to send a SC Status with protocol version 2.0 else server + * will assume 1.0) + */ + SCStatus scStatusRequest = new SCStatus(); + scStatusRequest.setProtocolVersion(ProtocolVersion.VERSION_2_00); + + Message scStatusResponse; + + try { + scStatusResponse = connection.send(scStatusRequest); + } catch (RetriesExceeded ex) { + counter.Retried(); + return; + } catch (MessageNotUnderstood | ChecksumError | SequenceError | MandatoryFieldOmitted | InvalidFieldLength ex1) { + counter.Error(ex1, "STATUS"); + return; + } + + if (!(scStatusResponse instanceof ACSStatus)) { + counter.Error(new Exception("Invalid Status Response"), "STATUSCHECK"); + return; + } + + Message response; + + try { + response = connection.send(msg); + } catch (RetriesExceeded ex) { + counter.Retried(); + return; + } catch (MessageNotUnderstood | ChecksumError | SequenceError | MandatoryFieldOmitted | InvalidFieldLength ex) { + counter.Error(ex, "MESSAGE"); + return; + } + + connection.disconnect(); + counter.Leave(); + } + + + + +} + + +/** + * + * @author Matthew.Dovey + */ +public class PerformanceTest { + private static final boolean SSL = true; + private static SelfSignedCertificate ssc; + + public static void main(String[] args) { + try { + System.setProperty("com.ceridwen.circulation.SIP.charset", "ISO8859_1"); + + SIPDaemon server; + + // Run netty server + if (SSL) { + ssc = new SelfSignedCertificate(); + server = new SIPDaemon("Sample", "localhost", 12345, ssc.certificate(), ssc.privateKey(), new DummyDriverFactory(), true); + } else { + server = new SIPDaemon("Sample", "localhost", 12345, new DummyDriverFactory(), true); + } + server.start(); + + Scanner sc = new Scanner(System.in); + + while (true) { + System.gc(); + + System.out.println("***READY"); + + boolean pause = true; + while (sc.hasNextLine() && pause) { + if (sc.next().startsWith("go")) { + pause = false; + } + } + + System.out.println("***STARTED"); + + ThreadCounter count = new ThreadCounter(); + + for (int i = 0; i <2000; i++) { + System.out.println("***CLIENT TEST INTERATION: " + i + " - ACTIVE THREADS: " + count.getCount()); + new ClientRunner(new BlockPatron(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new CheckIn(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new CheckOut(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new EndPatronSession(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new FeePaid(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new Hold(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new ItemInformation(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new ItemStatusUpdate(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new Login(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new PatronEnable(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new PatronInformation(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new PatronStatusRequest(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new Renew(), ssc == null?null:ssc.certificate(), count).start(); + new ClientRunner(new RenewAll(), ssc == null?null:ssc.certificate(), count).start(); + } + + System.out.println("***STOPPING"); + while (count.getCount() > 0) { + System.out.println("***ACTIVE THREADS: " + count.getCount()); + Thread.sleep(1); + } + System.out.println("***STOPPED"); + System.out.println("*** THREADS ERRORED: " + count.getError()); + System.out.println("*** THREADS RETRIED: " + count.getRetries()); + } + +// server.stop(); + + + } catch (Exception ex) { + Logger.getLogger(PerformanceTest.class.getName()).log(Level.SEVERE, null, ex); + } + } + +} From d7771e081242dfffc56d085f298db2256edeaa90 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Wed, 21 Sep 2016 14:50:05 +0100 Subject: [PATCH 25/28] Update to local snapshot version of jgitflow --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0ab2fcb..540db07 100644 --- a/pom.xml +++ b/pom.xml @@ -236,7 +236,7 @@ external.atlassian.jgitflow jgitflow-maven-plugin - 1.0-m5.1 + 1.0.0-mjf_276-SNAPSHOT master From e801b662b7173dfd393dfb0089dbd7d4413a0b81 Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Wed, 12 Oct 2016 18:24:59 +0100 Subject: [PATCH 26/28] Update CHANGELOG.txt --- CHANGELOG.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 858cd15..4b71ef3 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,7 @@ -Version 2.9.3: 06/09/2016 +Version 2.9.3: 10/10/2016 Update sample to use netty server Added SSL Socket client connection +Updated Netty SSL server to load certificates Dependencies: ceridwen utilitiy 1.6.2 apache commons beanutils 1.9.2 apache commons lang 3.4 From 3b75865c93d80b66f4a9e67026c03bd90787649a Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Wed, 12 Oct 2016 19:30:48 +0100 Subject: [PATCH 27/28] Update CHANGELOG.txt Update beanutils dependency to 1.9.3 --- CHANGELOG.txt | 8 ++++---- pom.xml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 4b71ef3..8a2cb3c 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -3,12 +3,12 @@ Update sample to use netty server Added SSL Socket client connection Updated Netty SSL server to load certificates Dependencies: ceridwen utilitiy 1.6.2 - apache commons beanutils 1.9.2 + apache commons beanutils 1.9.3 apache commons lang 3.4 apache commons logging 1.2 - apache commons net 3.4 - io.netty transport 4.0.36.Final - io.netty handler 4.0.36.Final + apache commons net 3.5 + io.netty transport 4.1.5.Final + io.netty handler 4.1.5.Final Version 2.9.2: 20/04/2016 Added netty server implementation diff --git a/pom.xml b/pom.xml index 540db07..698ed2e 100644 --- a/pom.xml +++ b/pom.xml @@ -113,7 +113,7 @@ commons-beanutils commons-beanutils - 1.9.2 + 1.9.3 io.netty From d89a35373ba22f2387ff7a54d6846fd15ee3de2a Mon Sep 17 00:00:00 2001 From: "Matthew J. Dovey" Date: Wed, 12 Oct 2016 19:42:15 +0100 Subject: [PATCH 28/28] updating poms for branch'release/ceridwen-standard-interchange-protocol-library-2.9.3' with non-snapshot versions --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 698ed2e..39902cb 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ com.ceridwen.circulation ceridwen-standard-interchange-protocol-library - 2.9.3-SNAPSHOT + 2.9.3 jar ceridwen-standard-interchange-protocol-library