/**
* 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.catalog.common;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map.Entry;
import javax.ws.rs.core.MultivaluedMap;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.jaxrs.ext.ResourceComparator;
import org.apache.cxf.jaxrs.ext.xml.XMLSource;
import org.apache.cxf.jaxrs.model.ClassResourceInfo;
import org.apache.cxf.jaxrs.model.OperationResourceInfo;
import org.apache.cxf.jaxrs.model.OperationResourceInfoComparator;
import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import org.apache.cxf.message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Custom {@link ResourceComparator} to determine which method to call on the {@link WfsEndpoint}
* and {@link CswEndpoint} based on the incoming request. By default JAX-RS only uses the URI path,
* HTTP method, Consumes Media Type, and Produces Media Type to determine which method to execute.
* The {@link WfsEndpoint} and {@link CswEndpoint} require the decision to made based on the
* "request" query parameter or the type of XML document.
*
*/
public class EndpointOperationInfoResourceComparator extends OperationResourceInfoComparator
implements ResourceComparator {
public static final String HTTP_GET = "GET";
public static final String HTTP_POST = "POST";
public static final String QUERY_PARAM_DELIMITER = "&";
public static final String REQUEST_PARAM = "request";
public static final String SERVICE_PARAM = "service";
public static final String UNKNOWN_SERVICE = "unknownService";
public static final String UNKNOWN_OPERATION = "unknownOperation";
private static final Logger LOGGER = LoggerFactory
.getLogger(EndpointOperationInfoResourceComparator.class);
private String serviceName;
public EndpointOperationInfoResourceComparator() {
super(null, null);
this.serviceName = null;
}
public EndpointOperationInfoResourceComparator(String serviceName) {
super(null, null);
this.serviceName = serviceName;
}
@Override
public int compare(ClassResourceInfo cri1, ClassResourceInfo cri2, Message message) {
// Leave Class selection to CXF
return 0;
}
@Override
public int compare(OperationResourceInfo oper1, OperationResourceInfo oper2, Message message) {
if (null == oper1 || null == oper2 || null == message) {
LOGGER.warn("Found NULL parameters in the compare method.");
return 0;
}
String httpMethod = (String) message.get(Message.HTTP_REQUEST_METHOD);
LOGGER.debug("HTTP METHOD = {}", httpMethod);
String requestName = null;
String requestedService = null;
if (HTTP_GET.equalsIgnoreCase(httpMethod)) {
String queryString = (String) message.get(Message.QUERY_STRING);
if (StringUtils.isNotEmpty(queryString)) {
MultivaluedMap<String, String> allQueryParams = JAXRSUtils
.getStructuredParams(queryString, QUERY_PARAM_DELIMITER, false, false);
// Loop through the keys and do a case insensitive check
for (Entry<String, List<String>> queryParam : allQueryParams.entrySet()) {
if ((REQUEST_PARAM.equalsIgnoreCase(queryParam.getKey())) && (!queryParam
.getValue().isEmpty()) && requestName == null) {
// We should never have more than one "request" query
// param so ignore them if we do
requestName = queryParam.getValue().get(0);
LOGGER.debug("Request Query Param = {}", requestName);
}
if ((SERVICE_PARAM.equalsIgnoreCase(queryParam.getKey())) && (!queryParam
.getValue().isEmpty()) && requestedService == null) {
// We should never have more than one "service" query
// param so ignore them if we do
requestedService = queryParam.getValue().get(0);
LOGGER.debug("Service Query Param = {}", requestedService);
}
}
}
} else if (HTTP_POST.equalsIgnoreCase(httpMethod)) {
// Get the payload
try (InputStream is = message.getContent(InputStream.class)) {
if (is != null) {
CachedOutputStream bos = new CachedOutputStream();
// We need to make a copy and put it back for later
// processing
IOUtils.copy(is, bos);
bos.flush();
is.close();
message.setContent(InputStream.class, bos.getInputStream());
XMLSource xml = new XMLSource(bos.getInputStream());
xml.setBuffering();
// The request name will be the root node name
requestName = xml.getValue("local-name(/*)");
requestedService = xml.getValue("/*/@service");
LOGGER.debug("ROOT NODE = {}", requestName);
LOGGER.debug("Service Name = {}", requestedService);
bos.close();
}
} catch (IOException ioe) {
LOGGER.warn("Unable to read message contents", ioe);
}
} else {
LOGGER.warn("Got unknown HTTP Method {}", httpMethod);
return 0;
}
if (StringUtils.isEmpty(requestName)) {
LOGGER.warn("Unable to determine the request name");
return 0;
}
int op1Rank = getOperationRank(oper1, requestName, requestedService);
int op2Rank = getOperationRank(oper2, requestName, requestedService);
return (op1Rank == op2Rank) ? 0 : (op1Rank < op2Rank) ? 1 : -1;
}
private int getOperationRank(OperationResourceInfo operation, String requestName,
String requestedService) {
if (serviceName != null && (!serviceName.equalsIgnoreCase(requestedService)) && operation
.getMethodToInvoke().getName().equalsIgnoreCase(UNKNOWN_SERVICE)) {
return 3;
}
return operation.getMethodToInvoke().getName().equalsIgnoreCase(requestName) ?
1 :
(operation.getMethodToInvoke().getName().equalsIgnoreCase(UNKNOWN_OPERATION) ?
0 :
-1);
}
}