/**
* Copyright (c) Codice Foundation
*
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or 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
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
*
**/
package org.codice.ddf.spatial.ogc.csw.catalog.source;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.xml.namespace.QName;
import org.apache.commons.lang.StringUtils;
import org.apache.cxf.Bus;
import org.apache.cxf.jaxrs.client.Client;
import org.apache.cxf.jaxrs.client.ClientConfiguration;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.ws.security.tokenstore.SecurityToken;
import org.apache.cxf.ws.security.trust.STSClient;
import org.codice.ddf.security.common.jaxrs.RestSecurity;
import org.codice.ddf.spatial.ogc.catalog.common.TrustedRemoteSource;
import org.codice.ddf.spatial.ogc.csw.catalog.common.Csw;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswConstants;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswException;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswJAXBElementProvider;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswRecordCollection;
import org.codice.ddf.spatial.ogc.csw.catalog.common.CswSourceConfiguration;
import org.codice.ddf.spatial.ogc.csw.catalog.common.DescribeRecordRequest;
import org.codice.ddf.spatial.ogc.csw.catalog.common.GetCapabilitiesRequest;
import org.codice.ddf.spatial.ogc.csw.catalog.common.GetRecordByIdRequest;
import org.codice.ddf.spatial.ogc.csw.catalog.common.GetRecordsRequest;
import org.codice.ddf.spatial.ogc.csw.catalog.source.reader.GetRecordsMessageBodyReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.thoughtworks.xstream.converters.Converter;
import ddf.security.Subject;
import ddf.security.service.SecurityManager;
import ddf.security.sts.client.configuration.STSClientConfiguration;
import net.opengis.cat.csw.v_2_0_2.CapabilitiesType;
import net.opengis.cat.csw.v_2_0_2.DescribeRecordResponseType;
import net.opengis.cat.csw.v_2_0_2.DescribeRecordType;
import net.opengis.cat.csw.v_2_0_2.GetCapabilitiesType;
import net.opengis.cat.csw.v_2_0_2.GetRecordByIdType;
import net.opengis.cat.csw.v_2_0_2.GetRecordsResponseType;
import net.opengis.cat.csw.v_2_0_2.GetRecordsType;
import net.opengis.cat.csw.v_2_0_2.TransactionResponseType;
import net.opengis.cat.csw.v_2_0_2.TransactionType;
/**
* A client to a CSW 2.0.2 Service. This class uses the {@link Csw} interface to create a client
* proxy from the {@link org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean}.
*/
public class RemoteCsw extends TrustedRemoteSource implements Csw {
private static final Logger LOGGER = LoggerFactory.getLogger(RemoteCsw.class.getName());
protected Csw csw;
private CswJAXBElementProvider<GetRecordsType> getRecordsTypeProvider;
private List<String> jaxbElementClassNames = new ArrayList<String>();
private Map<String, String> jaxbElementClassMap = new HashMap<String, String>();
/**
* Instantiates a new RemoteCsw
*
* @param cswTransformProvider The reference to the the CSW Transform Provider
* @param cswSourceConfiguration The Csw Source Configuration
*/
public RemoteCsw(Converter cswTransformProvider,
CswSourceConfiguration cswSourceConfiguration) {
csw = createClientBean(Csw.class, cswSourceConfiguration.getCswUrl(),
cswSourceConfiguration.getUsername(), cswSourceConfiguration.getPassword(),
cswSourceConfiguration.getDisableCnCheck(),
initProviders(cswTransformProvider, cswSourceConfiguration),
getClass().getClassLoader());
}
/**
* Sets the TLS Parameters on the current client
*/
protected void setTlsParameters() {
Client client = WebClient.fromClientObject(csw);
setTlsParameters(client);
}
/**
* Sets the timeouts to use for connection and receive requests.
* @param connectionTimeout Time in milliseconds to allow a connection.
* @param receiveTimeout Time in milliseconds to allow a receive.
*/
public void setTimeouts(Integer connectionTimeout, Integer receiveTimeout) {
this.configureTimeouts(WebClient.fromClientObject(csw), connectionTimeout, receiveTimeout);
}
public void setSubject(Subject subject) {
Client client = WebClient.fromClientObject(csw);
client.reset();
RestSecurity.setSubjectOnClient(subject, client);
}
public void setSAMLAssertion(STSClientConfiguration stsClientConfig) {
Client client = WebClient.fromClientObject(csw);
LOGGER.debug("ENTERING: setSAMLAssertion()");
if (stsClientConfig == null || StringUtils.isBlank(stsClientConfig.getAddress())) {
LOGGER.debug(
"STSClientConfiguration is either null or its address is blank - assuming no STS Client is configured, so no SAML assertion will get generated.");
return;
}
ClientConfiguration clientConfig = WebClient.getConfig(client);
Bus bus = clientConfig.getBus();
STSClient stsClient = configureSTSClient(bus, stsClientConfig);
stsClient.setTokenType(stsClientConfig.getAssertionType());
stsClient.setKeyType(stsClientConfig.getKeyType());
stsClient.setKeySize(Integer.valueOf(stsClientConfig.getKeySize()));
try {
SecurityToken securityToken = stsClient
.requestSecurityToken(stsClientConfig.getAddress());
org.w3c.dom.Element samlToken = securityToken.getToken();
if (samlToken != null) {
Subject subject = securityManager.getSubject(securityToken);
RestSecurity.setSubjectOnClient(subject, client);
} else {
LOGGER.debug(
"Attempt to retrieve SAML token resulted in null token - could not add token to request");
}
} catch (Exception e) {
LOGGER.warn("Exception trying to get SAML assertion", e);
}
LOGGER.debug("EXITING: setSAMLAssertion()");
}
protected List<? extends Object> initProviders(Converter cswTransformProvider,
CswSourceConfiguration cswSourceConfiguration) {
getRecordsTypeProvider = new CswJAXBElementProvider<GetRecordsType>();
getRecordsTypeProvider.setMarshallAsJaxbElement(true);
// Adding class names that need to be marshalled/unmarshalled to
// jaxbElementClassNames list
jaxbElementClassNames.add(GetRecordsType.class.getName());
jaxbElementClassNames.add(CapabilitiesType.class.getName());
jaxbElementClassNames.add(GetCapabilitiesType.class.getName());
jaxbElementClassNames.add(GetRecordsResponseType.class.getName());
getRecordsTypeProvider.setJaxbElementClassNames(jaxbElementClassNames);
// Adding map entry of <Class Name>,<Qualified Name> to
// jaxbElementClassMap
String expandedName = new QName(CswConstants.CSW_OUTPUT_SCHEMA, CswConstants.GET_RECORDS)
.toString();
LOGGER.debug("{} expanded name: {}", CswConstants.GET_RECORDS, expandedName);
jaxbElementClassMap.put(GetRecordsType.class.getName(), expandedName);
String getCapsEpandedName = new QName(CswConstants.CSW_OUTPUT_SCHEMA,
CswConstants.GET_CAPABILITIES).toString();
LOGGER.debug("{} expanded name: {}", CswConstants.GET_CAPABILITIES, expandedName);
jaxbElementClassMap.put(GetCapabilitiesType.class.getName(), getCapsEpandedName);
String capsExpandedName = new QName(CswConstants.CSW_OUTPUT_SCHEMA,
CswConstants.CAPABILITIES).toString();
LOGGER.debug("{} expanded name: {}", CswConstants.CAPABILITIES, capsExpandedName);
jaxbElementClassMap.put(CapabilitiesType.class.getName(), capsExpandedName);
String caps201ExpandedName = new QName("http://www.opengis.net/cat/csw",
CswConstants.CAPABILITIES).toString();
LOGGER.debug("{} expanded name: {}", CswConstants.CAPABILITIES, caps201ExpandedName);
jaxbElementClassMap.put(CapabilitiesType.class.getName(), caps201ExpandedName);
getRecordsTypeProvider.setJaxbElementClassMap(jaxbElementClassMap);
GetRecordsMessageBodyReader grmbr = new GetRecordsMessageBodyReader(cswTransformProvider,
cswSourceConfiguration);
return Arrays.asList(getRecordsTypeProvider, new CswResponseExceptionMapper(), grmbr);
}
@Override
@GET
@Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public DescribeRecordResponseType describeRecord(DescribeRecordRequest request) throws
CswException {
return csw.describeRecord(request);
}
@Override
@POST
@Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public DescribeRecordResponseType describeRecord(DescribeRecordType request) throws
CswException {
return csw.describeRecord(request);
}
@Override
@GET
@Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public CapabilitiesType getCapabilities(GetCapabilitiesRequest request) throws CswException {
return csw.getCapabilities(request);
}
@Override
@POST
@Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public CapabilitiesType getCapabilities(GetCapabilitiesType request) throws CswException {
return csw.getCapabilities(request);
}
@Override
@GET
@Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public CswRecordCollection getRecordById(GetRecordByIdRequest request) throws CswException {
return csw.getRecordById(request);
}
@Override
@POST
@Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public CswRecordCollection getRecordById(GetRecordByIdType request) throws CswException {
return csw.getRecordById(request);
}
@Override
@GET
@Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public CswRecordCollection getRecords(GetRecordsRequest request) throws CswException {
return csw.getRecords(request);
}
@Override
@POST
@Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public CswRecordCollection getRecords(GetRecordsType request) throws CswException {
return csw.getRecords(request);
}
@Override
@POST
@Consumes({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
@Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
public TransactionResponseType transaction(TransactionType request) throws CswException {
return csw.transaction(request);
}
/*
* Set the version to CSW 2.0.1. The schemas don't vary much between 2.0.2 and 2.0.1. The
* largest difference is the namespace itself. This method tells CXF JAX-RS to transform
* outgoing messages CSW namespaces to 2.0.1.
*/
public void setCsw201() {
Map<String, String> outTransformElements = new HashMap<String, String>();
outTransformElements.put("{" + CswConstants.CSW_OUTPUT_SCHEMA + "}*",
"{http://www.opengis.net/cat/csw}*");
getRecordsTypeProvider.setOutTransformElements(outTransformElements);
}
public void setSecurityManager(SecurityManager securityManager) {
this.securityManager = securityManager;
}
}