Skip to content

Commit

Permalink
Merge pull request #33 from todvora/client-interface-changes
Browse files Browse the repository at this point in the history
Significant client interface changes, switch to 3.x version
  • Loading branch information
todvora authored May 13, 2017
2 parents 710ad03 + 3902cd2 commit a0955d6
Show file tree
Hide file tree
Showing 41 changed files with 849 additions and 462 deletions.
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ Implementer has to take care of:

## Usage

Demo project on [github.com/todvora/eet-client-demo](https://github.com/todvora/eet-client-demo).

```java
InputStream clientKey = getClass().getResourceAsStream("/keys/CZ683555118.p12");
InputStream rootCACertificate = getClass().getResourceAsStream("/keys/rca15_rsa.der");
InputStream subordinateCACertificate = getClass().getResourceAsStream("/keys/2qca16_rsa.der");
EETClient client = EETServiceFactory.getInstance(clientKey, "eet", rootCACertificate, subordinateCACertificate);
ClientKey clientKey = ClientKey.fromInputStream(getClass().getResourceAsStream("/keys/CZ683555118.p12"), "eet");
ServerKey serverKey = ServerKey.trustingEmbeddedCertificates();
EETClient client = EETServiceFactory.getInstance(clientKey, serverKey);

TrzbaDataType data = new TrzbaDataType()
.withDicPopl("CZ683555118")
Expand All @@ -38,7 +39,8 @@ TrzbaDataType data = new TrzbaDataType()
.withCelkTrzba(new BigDecimal("3264"));

try {
SubmitResult result = client.submitReceipt(data, CommunicationMode.REAL, EndpointType.PLAYGROUND, SubmissionType.FIRST_ATTEMPT);
TrzbaType request = eetService.prepareFirstRequest(data, CommunicationMode.REAL);
SubmitResult result = eetService.sendSync(request, EndpointType.PLAYGROUND);
// print codes on the receipt
System.out.println("FIK:" + result.getFik());
System.out.println("BKP:" + result.getBKP());
Expand All @@ -60,7 +62,8 @@ try {
Asynchronous call with a callback:

```java
client.submitReceipt(data, CommunicationMode.REAL, EndpointType.PLAYGROUND, SubmissionType.FIRST_ATTEMPT, new ResponseCallback() {
TrzbaType request = eetClient.prepareFirstRequest(data, CommunicationMode.REAL);
eetClient.sendAsync(request, EndpointType.PLAYGROUND, new ResponseCallback() {
@Override
public void onComplete(final SubmitResult result) {
System.out.println("FIK:" + result.getFik());
Expand Down
37 changes: 33 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>cz.tomasdvorak</groupId>
<artifactId>eet-client</artifactId>
<version>2.3.0</version>
<version>3.0.0-beta-9</version>

<description>
Client / Connector for the #EET - registration of sales is an advanced system of online communication between entrepreneurs and Financial Authority.
Expand All @@ -24,6 +24,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<cxf.version>3.0.10</cxf.version>
<junit.version>4.12</junit.version>
<slf4j.version>1.7.24</slf4j.version>
<log4j.version>2.3</log4j.version>
<maven-failsafe.version>2.19.1</maven-failsafe.version>
<jacoco.version>0.7.7.201606060606</jacoco.version>
Expand Down Expand Up @@ -74,24 +75,34 @@
<version>1.55</version>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>


<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
Expand Down Expand Up @@ -283,9 +294,6 @@
<goals>
<goal>jar</goal>
</goals>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
</execution>
</executions>
</plugin>
Expand Down Expand Up @@ -324,4 +332,25 @@
</plugins>
</reporting>

<profiles>
<profile>
<id>doclint-java8-disable</id>
<activation>
<jdk>[1.8,)</jdk>
</activation>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
3 changes: 1 addition & 2 deletions scripts/update-version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ elif [ $1 = "minor" ]; then
elif [ $1 = "patch" ]; then
mvn build-helper:parse-version versions:set -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}.\${parsedVersion.nextIncrementalVersion} versions:commit
else
echo "Unknown component increment: $1"
exit 1
mvn versions:set -DnewVersion=$1 versions:commit
fi

NEW_VERSION=$(mvn -B org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\[')
Expand Down
47 changes: 46 additions & 1 deletion src/main/java/cz/tomasdvorak/eet/client/EETClient.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cz.tomasdvorak.eet.client;

import cz.etrzby.xml.TrzbaDataType;
import cz.etrzby.xml.TrzbaType;
import cz.tomasdvorak.eet.client.config.CommunicationMode;
import cz.tomasdvorak.eet.client.config.EndpointType;
import cz.tomasdvorak.eet.client.config.SubmissionType;
Expand All @@ -15,10 +16,49 @@
* EET client implementation, handling computation of security codes, signing of requests, validation of responses.
*/
public interface EETClient {

/**
* Prepare request for <strong>first submit</strong> to EET Service.
* @param receiptData Receipt data like price, VAT or Tax ID.
* @param mode test or real communication (will be encoded in header
* @return complete request, including computed security codes, added random message UUID, current date.
* @throws DataSigningException if problem with signing occurs. Should be related to client key in that case
*/
TrzbaType prepareFirstRequest(final TrzbaDataType receiptData, final CommunicationMode mode) throws DataSigningException;

/**
* Prepare request for <strong>second or every other</strong> submit to EET service.
* @param request the original request, which has been already constructed and sent to EET servers.
* @return Updated request, re-generated message UUID, send date, set first-submission flag to false, recomputed
* security codes if they change meanwhile (new client certificate used for this submission)
* @throws DataSigningException if problem with signing occurs. Should be related to client key in that case
*/
TrzbaType prepareRepeatedRequest(final TrzbaType request) throws DataSigningException;

/**
* Submit synchronously a receipt to EET servers.
* @param request prepared by calling one of {@link #prepareFirstRequest(TrzbaDataType, CommunicationMode)} or {@link #prepareRepeatedRequest(TrzbaType)} method.
* @param endpointType real or test endpoint
* @return result provided by EET servers.
* @throws CommunicationException if something fails. It may be timeout, not accessible servers, invalid message, invalid signature of response.
*/
SubmitResult sendSync(final TrzbaType request, final EndpointType endpointType) throws CommunicationException;

/**
* Submit asynchronously a receipt to EET servers.
* @param request prepared by calling one of {@link #prepareFirstRequest(TrzbaDataType, CommunicationMode)} or {@link #prepareRepeatedRequest(TrzbaType)} method.
* @param endpointType real or test endpoint
* @param handler callback for response or failure of the call
* @return result future you can wait on
*/
Future<?> sendAsync(final TrzbaType request, final EndpointType endpointType, final ResponseCallback handler);

/**
* Central and only method of EET. Send the receipt data to selected endpoint, select if it's first or repeated
* Central method of EET. Send the receipt data to selected endpoint, select if it's first or repeated
* submission and if the communication is test only or real.
*
* This method is DEPRECATED. Prepare data using prepareFirstRequest first, then send them using one of send* methods.
*
* @param receipt Receipt data - price, date, tax numbers, ...
* @param mode real or test submission
* @param endpointType playground or real endpoint
Expand All @@ -27,7 +67,12 @@ public interface EETClient {
* @throws DataSigningException Failed to compute PKP or BKP
* @throws CommunicationException Failed to send or receive data from EET endpoint
*/
@Deprecated
SubmitResult submitReceipt(final TrzbaDataType receipt, final CommunicationMode mode, final EndpointType endpointType, final SubmissionType submissionType) throws DataSigningException, CommunicationException;

/**
* * This method is DEPRECATED. Prepare data using prepareFirstRequest first, then send them using one of send* methods.
*/
@Deprecated
Future<?> submitReceipt(final TrzbaDataType receipt, final CommunicationMode mode, final EndpointType endpointType, final SubmissionType submissionType, final ResponseCallback handler) throws DataSigningException;
}
79 changes: 58 additions & 21 deletions src/main/java/cz/tomasdvorak/eet/client/EETClientImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,47 @@
import cz.tomasdvorak.eet.client.dto.ResponseCallback;
import cz.tomasdvorak.eet.client.dto.SubmitResult;
import cz.tomasdvorak.eet.client.dto.WebserviceConfiguration;
import cz.tomasdvorak.eet.client.errors.EetErrorConverter;
import cz.tomasdvorak.eet.client.exceptions.*;
import cz.tomasdvorak.eet.client.security.ClientKey;
import cz.tomasdvorak.eet.client.security.SecureEETCommunication;
import cz.tomasdvorak.eet.client.security.SecurityCodesGenerator;
import cz.tomasdvorak.eet.client.security.ServerKey;
import cz.tomasdvorak.eet.client.utils.ExceptionUtils;
import org.apache.logging.log4j.Logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Response;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.Future;


class EETClientImpl extends SecureEETCommunication implements EETClient {

private static final Logger logger = org.apache.logging.log4j.LogManager.getLogger(SecureEETCommunication.class);
private static final Logger logger = LoggerFactory.getLogger(EETClientImpl.class);


EETClientImpl(final ClientKey clientKey, final ServerKey serverKey, final WebserviceConfiguration wsConfiguration) {
super(clientKey, serverKey, wsConfiguration);
}

@Deprecated
public SubmitResult submitReceipt(final TrzbaDataType receipt, final CommunicationMode mode, final EndpointType endpointType, final SubmissionType submissionType) throws DataSigningException, CommunicationException {
final TrzbaType request = prepareData(receipt, mode, submissionType);
final TrzbaType request = prepareRequest(receipt, mode, submissionType);
return sendSync(request, endpointType);
}

@Deprecated
public Future<?> submitReceipt(final TrzbaDataType receipt, final CommunicationMode mode, final EndpointType endpointType, final SubmissionType submissionType, final ResponseCallback handler) throws DataSigningException {
final TrzbaType request = prepareRequest(receipt, mode, submissionType);
return sendAsync(request, endpointType, handler);
}

public SubmitResult sendSync(final TrzbaType request, final EndpointType endpointType) throws CommunicationException {
final EET port;
try {
port = getPort(endpointType);
Expand All @@ -43,26 +57,18 @@ public SubmitResult submitReceipt(final TrzbaDataType receipt, final Communicati
} catch (DnsTimeoutException e) {
throw new CommunicationTimeoutException(request, e);
}

try {
final OdpovedType response = port.odeslaniTrzby(request);
if (response != null && !response.getVarovani().isEmpty()) {
for (final OdpovedVarovaniType warning : response.getVarovani()) {
logger.warn("Response warning: code=" + warning.getKodVarov() + "; message=" + warning.getContent());
}
}
return new SubmitResult(request, response);
return convertToSubmitResult(request, response);
} catch (final Exception e) {
if (ExceptionUtils.containsExceptionType(e, SocketTimeoutException.class)) {
if(ExceptionUtils.containsExceptionType(e, SocketTimeoutException.class)) {
throw new CommunicationTimeoutException(request, e);
}
throw new CommunicationException(request, e);
}
}


public Future<?> submitReceipt(final TrzbaDataType receipt, final CommunicationMode mode, final EndpointType endpointType, final SubmissionType submissionType, final ResponseCallback handler) throws DataSigningException {
final TrzbaType request = prepareData(receipt, mode, submissionType);
public Future<?> sendAsync(final TrzbaType request, final EndpointType endpointType, final ResponseCallback handler) {
final EET port;
try {
port = getPort(endpointType);
Expand All @@ -71,11 +77,10 @@ public Future<?> submitReceipt(final TrzbaDataType receipt, final CommunicationM
public void handleResponse(final Response<OdpovedType> res) {
try {
final OdpovedType response = res.get();
final SubmitResult submitResult = new SubmitResult(request, response);
final SubmitResult submitResult = convertToSubmitResult(request, response);
handler.onComplete(submitResult);

} catch (final Exception e) {
if (ExceptionUtils.containsExceptionType(e, SocketTimeoutException.class)) {
if(ExceptionUtils.containsExceptionType(e, SocketTimeoutException.class)) {
handler.onTimeout(new CommunicationTimeoutException(request, e));
} else {
handler.onError(new CommunicationException(request, e));
Expand All @@ -91,12 +96,44 @@ public void handleResponse(final Response<OdpovedType> res) {
return new CompletedFuture<Object>();
}

private TrzbaType prepareData(final TrzbaDataType data, final CommunicationMode mode, final SubmissionType submissionType) throws DataSigningException {
private SubmitResult convertToSubmitResult(final TrzbaType request, final OdpovedType response) throws ResponseWithErrorException {
if (response != null && !response.getVarovani().isEmpty()) {
for (final OdpovedVarovaniType warning : response.getVarovani()) {
logger.warn("Response warning: code=" + warning.getKodVarov() + "; message=" + warning.getContent());
}
}
if(response != null) {
final ResponseWithErrorException error = EetErrorConverter.getErrorType(response.getChyba());
if(error != null) {
throw error;
}
}
return new SubmitResult(request, response);
}

public TrzbaType prepareFirstRequest(final TrzbaDataType receiptData, final CommunicationMode mode) throws DataSigningException {
return prepareRequest(receiptData, mode, SubmissionType.FIRST_ATTEMPT);
}

private TrzbaType prepareRequest(final TrzbaDataType receiptData, final CommunicationMode mode, final SubmissionType submissionType) throws DataSigningException {
return new TrzbaType()
.withHlavicka(getHeader(mode, submissionType))
.withData(data)
.withKontrolniKody(getCheckCodes(data));
.withData(receiptData)
.withKontrolniKody(getCheckCodes(receiptData));
}

@Override
public TrzbaType prepareRepeatedRequest(final TrzbaType request) throws DataSigningException {
request.getHlavicka().setUuidZpravy(UUID.randomUUID().toString());
request.getHlavicka().setDatOdesl(new Date());
request.getHlavicka().setPrvniZaslani(false);

final TrzbaKontrolniKodyType newCheckCodes = getCheckCodes(request.getData());

if(!request.getKontrolniKody().getBkp().getValue().equals(newCheckCodes.getBkp().getValue()) || !Arrays.equals(request.getKontrolniKody().getPkp().getValue(), newCheckCodes.getPkp().getValue())) {
logger.warn("Check codes BKP and PKP from original request doesn't corespond with current computed. This can happen for example when client certificate is renewed.");
}
return request;
}

private TrzbaHlavickaType getHeader(final CommunicationMode mode, final SubmissionType submissionType) {
Expand Down
22 changes: 20 additions & 2 deletions src/main/java/cz/tomasdvorak/eet/client/EETServiceFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,40 @@ private EETServiceFactory() {} // utility class, no instance allowed

/**
* Produces EET service instance bound to a keystore and password.
* Deprecated - use {@link #getInstance(ClientKey, ServerKey)} method instead
* @param clientKeyStream data stream of the keystore. Expected is only pkcs12 type of keystore, containing only one alias
* @param password password to the keystore
* @param serverKeyStream data stream of the root CA of the EET certificate. Stream will be closed automatically.
*/
@Deprecated
public static EETClient getInstance(final InputStream clientKeyStream, final String password, final InputStream... serverKeyStream) throws InvalidKeystoreException {
return new EETClientImpl(new ClientKey(clientKeyStream, password), new ServerKey(serverKeyStream), WebserviceConfiguration.DEFAULT);
return new EETClientImpl(ClientKey.fromInputStream(clientKeyStream, password), ServerKey.fromInputStream(serverKeyStream), WebserviceConfiguration.DEFAULT);
}

/**
* Produces EET service instance bound to a keystore and password with additional WS configuration (like connection timeout)
* Deprecated - use {@link #getInstance(ClientKey, ServerKey, WebserviceConfiguration)} method instead
* @param wsConfiguration additional WS configuration (like timeout)
* @param clientKeyStream data stream of the keystore. Expected is only pkcs12 type of keystore, containing only one alias
* @param password password to the keystore
* @param serverKeyStream data stream of the root CA of the EET certificate. Stream will be closed automatically.
*/
@Deprecated
public static EETClient getInstance(final WebserviceConfiguration wsConfiguration, final InputStream clientKeyStream, final String password, final InputStream... serverKeyStream) throws InvalidKeystoreException {
return new EETClientImpl(new ClientKey(clientKeyStream, password), new ServerKey(serverKeyStream), wsConfiguration);
return new EETClientImpl(ClientKey.fromInputStream(clientKeyStream, password), ServerKey.fromInputStream(serverKeyStream), wsConfiguration);
}

/**
* @since 3.0
*/
public static EETClient getInstance(final ClientKey clientKey, final ServerKey serverKey) throws InvalidKeystoreException {
return new EETClientImpl(clientKey, serverKey, WebserviceConfiguration.DEFAULT);
}

/**
* @since 3.0
*/
public static EETClient getInstance(final ClientKey clientKey, final ServerKey serverKey, final WebserviceConfiguration wsConfiguration) throws InvalidKeystoreException {
return new EETClientImpl(clientKey, serverKey, wsConfiguration);
}
}
Loading

0 comments on commit a0955d6

Please sign in to comment.