/*
* Copyright (C) 2014 Jan Pokorsky
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package cz.cas.lib.proarc.desa;
import cz.cas.lib.proarc.desa.nomenclature.Nomenclatures;
import cz.cas.lib.proarc.desa.soap.AuthenticateUserFault;
import cz.cas.lib.proarc.desa.soap.AuthenticateUserRequest;
import cz.cas.lib.proarc.desa.soap.AuthenticateUserResponse;
import cz.cas.lib.proarc.desa.soap.FileHashAlg;
import cz.cas.lib.proarc.desa.soap.NomenclatureListType;
import cz.cas.lib.proarc.desa.soap.SIPSubmission;
import cz.cas.lib.proarc.desa.soap.SIPSubmissionFault;
import cz.cas.lib.proarc.desa.soap.SIPSubmissionService;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.WebServiceException;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
import org.glassfish.jersey.logging.LoggingFeature;
/**
* The client for DESA SOAP and REST WS interface.
*
* @author Jan Pokorsky
*/
public final class DesaClient {
private static final Logger LOG = Logger.getLogger(DesaClient.class.getName());
private static JAXBContext NOMEN_JAXB;
private final String soapUrl;
private final String restUrl;
private final String user;
private final String passwd;
private SIPSubmission sipSubmission;
private Client httpClient;
private DatatypeFactory xmlTypeFactory;
public DesaClient(String soapUrl, String restUrl, String user, String passwd) {
this.soapUrl = soapUrl;
this.restUrl = restUrl;
this.user = user;
this.passwd = passwd;
}
/**
* Gets SIP transporter.
*
* @param operator operator user name
* @param producerCode producer code
* @return the transporter
*/
public SIP2DESATransporter getSipTransporter(String operator, String producerCode) {
return new SIP2DESATransporter(this, operator, producerCode);
}
/**
* Gets nomenclatures from the remote registry.
* @param operator operator login name
* @param nomenclatureAcronyms acronyms to query
* @return nomenclatures
*/
public Nomenclatures getNomenclatures(String operator, String producerCode, List<String> nomenclatureAcronyms) {
try {
Source src = getNomenclaturesSource(operator, producerCode, nomenclatureAcronyms);
return getNomenUnmarshaller().unmarshal(src, Nomenclatures.class).getValue();
} catch (JAXBException ex) {
String msg = String.format("producer: %s, acronyms: %s", producerCode, nomenclatureAcronyms);
throw new IllegalStateException(msg, ex);
}
}
/**
* Gets nomenclatures.
*
* @param operator operator login name
* @param nomenclatureAcronyms acronyms to query
* @return nomenclature list as XML
*/
public Source getNomenclaturesSource(String operator, String producerCode, List<String> nomenclatureAcronyms) {
XMLGregorianCalendar currentDate = getXmlTypes().newXMLGregorianCalendar(new GregorianCalendar());
NomenclatureListType nsType = new NomenclatureListType();
nsType.getNomenclatureAcronyme().addAll(nomenclatureAcronyms);
try {
byte[] result = getSoapClient().getNomenclatures(null, producerCode, operator, nsType, currentDate);
ByteArrayInputStream bis = new ByteArrayInputStream(result);
return new StreamSource(bis);
} catch (SIPSubmissionFault e) {
String msg = String.format("producer: %s, acronyms: %s", producerCode, nomenclatureAcronyms);
throw new IllegalStateException(msg, e);
}
}
/**
* Submits SIP via HTTP API.
*
* @param file package contents
* @param operator user name who sends the package
* @param producerCode producer code (not ID)
* @param producerSipId package ID
* @param fileHashAlg optional hash algorithm code
* @param fileHash optional package hash
* @param lang optional language of error messages
* @return AIP Version ID assigned by DESA
*/
public String submitPackage(File file, String operator, String producerCode,
String producerSipId, FileHashAlg fileHashAlg, String fileHash, String lang) {
Response response = resource()
.path("submitpackage")
.queryParam("userName", operator)
.queryParam("producerCode", producerCode)
.queryParam("producerSipId", producerSipId)
.queryParam("fileHashAlg", fileHashAlg == null ? null : fileHashAlg.value())
.queryParam("fileHash", fileHash)
.request()
.acceptLanguage(lang)
.post(Entity.entity(file, MediaType.APPLICATION_OCTET_STREAM_TYPE));
try {
if (response.getStatus() > 400) {
String error = response.readEntity(String.class);
throw new IllegalStateException(String.format("HTTP %s, Error: %s", response.getStatus(), error));
}
String aipVersionId = response.getStringHeaders().getFirst("X-DEA-AipVersionId");
return aipVersionId;
} finally {
response.close();
}
}
/**
* Authenticates an operator.
*
* @param operator operator user name
* @param passwd operator password
* @param producerCode producer code (not ID)
* @return the authenticated operator
* @throws AuthenticateUserFault authentication failure
* @throws WebServiceException soap failure
*/
public AuthenticateUserResponse authenticateUser(
String operator, String passwd, String producerCode)
throws AuthenticateUserFault, WebServiceException {
AuthenticateUserRequest request = new AuthenticateUserRequest();
request.setLogin(operator);
request.setPassword(passwd);
request.setProducerCode(producerCode);
AuthenticateUserResponse response = getSoapClient().authenticateUser(request);
if ("OK".equals(response.getStatus())) {
return response;
} else {
String msg = String.format("operator: %s, producer: %s, status: %s",
operator, producerCode, response.getStatus());
throw new AuthenticateUserFault(msg, null);
}
}
public boolean isDebug() {
return LOG.isLoggable(Level.FINEST);
}
public void setDebug(boolean debug) {
LOG.setLevel(debug ? Level.FINEST : null);
}
DatatypeFactory getXmlTypes() {
if (xmlTypeFactory == null) {
try {
xmlTypeFactory = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException ex) {
throw new IllegalStateException(ex);
}
}
return xmlTypeFactory;
}
SIPSubmission getSoapClient() {
if (sipSubmission == null) {
SIPSubmissionService service = new SIPSubmissionService();
sipSubmission = service.getSIPSubmissionSOAP();
Map<String, Object> context = ((BindingProvider) sipSubmission).getRequestContext();
context.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, soapUrl);
context.put(BindingProvider.USERNAME_PROPERTY, user);
context.put(BindingProvider.PASSWORD_PROPERTY, passwd);
}
return sipSubmission;
}
Client getHttpClient() {
if (httpClient == null) {
httpClient = ClientBuilder.newBuilder()
.register(HttpAuthenticationFeature.basic(user, passwd))
.property(ClientProperties.FOLLOW_REDIRECTS, true)
.property(ClientProperties.CONNECT_TIMEOUT, 2 * 60 * 1000) // 2 min
.build();
}
return httpClient;
}
private WebTarget resource() {
WebTarget resource = getHttpClient().target(restUrl);
if (isDebug()) {
resource.register(new LoggingFeature(LOG));
}
return resource;
}
/**
* Initialize JAXB transformers for Nomenclatures
*/
private Unmarshaller getNomenUnmarshaller() throws JAXBException {
return getNomenJaxbContext().createUnmarshaller();
}
/**
* Gets the cached thread safe JAXB context.
*/
private static JAXBContext getNomenJaxbContext() throws JAXBException {
if (NOMEN_JAXB == null) {
NOMEN_JAXB = JAXBContext.newInstance(Nomenclatures.class);
}
return NOMEN_JAXB;
}
}