com.predic8.membrane.core.interceptor.server.WSDLPublisherInterceptor Maven / Gradle / Ivy
/* Copyright 2012 predic8 GmbH, www.predic8.com
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. */
package com.predic8.membrane.core.interceptor.server;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import javax.annotation.concurrent.GuardedBy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.predic8.membrane.annot.MCAttribute;
import com.predic8.membrane.annot.MCElement;
import com.predic8.membrane.core.exchange.Exchange;
import com.predic8.membrane.core.http.MimeType;
import com.predic8.membrane.core.http.Response;
import com.predic8.membrane.core.interceptor.AbstractInterceptor;
import com.predic8.membrane.core.interceptor.Outcome;
import com.predic8.membrane.core.interceptor.WSDLInterceptor;
import com.predic8.membrane.core.resolver.ResolverMap;
import com.predic8.membrane.core.resolver.ResourceRetrievalException;
import com.predic8.membrane.core.rules.Rule;
import com.predic8.membrane.core.rules.SOAPProxy;
import com.predic8.membrane.core.util.HttpUtil;
import com.predic8.membrane.core.util.URLParamUtil;
import com.predic8.membrane.core.util.URLUtil;
import com.predic8.membrane.core.ws.relocator.Relocator.PathRewriter;
import static com.predic8.membrane.core.interceptor.Outcome.*;
/**
* @description
* The wsdlPublisher can be used to serve WSDL files (and attached XML Schema Documents), if your
* backend service does not already do so.
*
* @topic 8. SOAP based Web Services
*/
@MCElement(name="wsdlPublisher")
public class WSDLPublisherInterceptor extends AbstractInterceptor {
private static final Logger log = LoggerFactory.getLogger(WSDLPublisherInterceptor.class);
public WSDLPublisherInterceptor() {
name = "WSDL Publisher";
}
/**
* Note that this class fulfills two purposes:
*
* * During the initial processDocuments() run, the XSDs are enumerated.
*
* * During later runs (as well as the initial run, but that's result is discarded),
* the documents are rewritten.
*/
private final class RelativePathRewriter implements PathRewriter {
private final Exchange exc;
private final String resource;
private RelativePathRewriter(Exchange exc, String resource) {
this.exc = exc;
this.resource = resource;
}
@Override
public String rewrite(String path) {
try {
if (!path.contains("://") && !path.startsWith("/")) {
path = ResolverMap.combine(resource, path);
}
synchronized(paths) {
if (paths_reverse.containsKey(path)) {
path = paths_reverse.get(path).toString();
} else {
int n = paths.size() + 1;
paths.put(n, path);
paths_reverse.put(path, n);
documents_to_process.add(path);
path = Integer.toString(n);
}
}
path = "./" + URLUtil.getName(router.getUriFactory(), exc.getDestinations().get(0)) + "?xsd=" + path;
} catch (Exception e) {
throw new RuntimeException(e);
}
return path;
}
}
@GuardedBy("paths")
private final HashMap paths = new HashMap<>();
@GuardedBy("paths")
private final HashMap paths_reverse = new HashMap<>();
@GuardedBy("paths")
private final Queue documents_to_process = new LinkedList<>();
private void processDocuments(Exchange exc) throws Exception {
// exc.response is only temporarily used so we can call the WSDLInterceptor
// exc.response is set to garbage and should be discarded after this method
synchronized (paths) {
try {
while (true) {
String doc = documents_to_process.poll();
if (doc == null)
break;
log.debug("WSDLPublisherInterceptor: processing " + doc);
exc.setResponse(WebServerInterceptor.createResponse(router.getResolverMap(), doc));
WSDLInterceptor wi = new WSDLInterceptor();
wi.setRewriteEndpoint(false);
wi.setPathRewriter(new RelativePathRewriter(exc, doc));
wi.handleResponse(exc);
}
} catch (ResourceRetrievalException e) {
log.error("Could not recursively load all documents referenced by '"+wsdl+"'.", e);
}
}
}
private String wsdl;
public String getWsdl() {
return wsdl;
}
/**
* @description The WSDL (URL or file).
* @example /WEB-INF/wsdl/ArticleService.wsdl
*/
@MCAttribute
public void setWsdl(String wsdl) {
this.wsdl = wsdl;
synchronized(paths) {
paths.clear();
paths_reverse.clear();
documents_to_process.clear();
documents_to_process.add(wsdl);
}
}
@Override
public void init() throws Exception {
// inherit wsdl="..." from SoapProxy
if (wsdl == null) {
Rule parent = router.getParentProxy(this);
if (parent instanceof SOAPProxy) {
wsdl = ((SOAPProxy)parent).getWsdl();
setWsdl(wsdl);
}
}
}
@Override
public Outcome handleRequest(final Exchange exc) throws Exception {
if (!"GET".equals(exc.getRequest().getMethod()))
return CONTINUE;
try {
String resource = null;
if (exc.getRequestURI().endsWith("?wsdl") || exc.getRequestURI().endsWith("?WSDL")) {
processDocuments(exc);
exc.setResponse(WebServerInterceptor.createResponse(router.getResolverMap(), resource = wsdl));
exc.getResponse().getHeader().setContentType(MimeType.TEXT_XML);
}
if (exc.getRequestURI().contains("?xsd=")) {
Map params = URLParamUtil.getParams(router.getUriFactory(), exc, URLParamUtil.DuplicateKeyOrInvalidFormStrategy.ERROR);
if (params.containsKey("xsd")) {
int n = Integer.parseInt(params.get("xsd"));
String path;
processDocuments(exc);
synchronized(paths) {
if (!paths.containsKey(n)) {
exc.setResponse(Response.forbidden("Unknown parameter. You may only retrieve documents referenced by the WSDL.").build());
return ABORT;
}
path = paths.get(n);
}
exc.setResponse(WebServerInterceptor.createResponse(router.getResolverMap(), resource = path));
exc.getResponse().getHeader().setContentType(MimeType.TEXT_XML);
}
}
if (resource != null) {
WSDLInterceptor wi = new WSDLInterceptor();
wi.setRewriteEndpoint(false);
wi.setPathRewriter(new RelativePathRewriter(exc, resource));
wi.handleResponse(exc);
return RETURN;
}
} catch (NumberFormatException e) {
exc.setResponse(HttpUtil.setHTMLErrorResponse(Response.internalServerError(), "Bad parameter format.", ""));
return ABORT;
} catch (ResourceRetrievalException e) {
exc.setResponse(Response.notFound().build());
return ABORT;
}
return CONTINUE;
}
@Override
public String getShortDescription() {
return "Publishes the WSDL at " + wsdl + " under \"?wsdl\" (as well as its dependent schemas under similar URLs).";
}
}