package org.talend.esb.policy.schemavalidate.interceptors;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Collection;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.apache.cxf.common.classloader.ClassLoaderUtils;
import org.apache.cxf.common.xmlschema.LSInputImpl;
import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageUtils;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.ws.policy.AssertionInfo;
import org.apache.cxf.ws.policy.AssertionInfoMap;
import org.talend.esb.policy.schemavalidate.SchemaValidationPolicy;
import org.talend.esb.policy.schemavalidate.SchemaValidationPolicyBuilder;
import org.talend.esb.policy.schemavalidate.SchemaValidationPolicy.AppliesToType;
import org.talend.esb.policy.schemavalidate.SchemaValidationPolicy.MessageType;
import org.talend.esb.policy.schemavalidate.SchemaValidationPolicy.ValidationType;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.SAXException;
public abstract class SchemaValidationPolicyAbstractInterceptor extends
AbstractPhaseInterceptor<Message> {
protected SchemaValidationPolicy policy = null;
public SchemaValidationPolicyAbstractInterceptor(String phase) {
super(phase);
}
public SchemaValidationPolicyAbstractInterceptor(String phase,
SchemaValidationPolicy policy) {
super(phase);
this.policy = policy;
}
@Override
public void handleMessage(Message message) throws Fault {
if (policy == null) {
handleMessageWithAssertionInfo(message);
} else {
handleMessageWithoutAssertionInfo(message);
}
}
protected void handleMessageWithAssertionInfo(Message message) throws Fault {
AssertionInfoMap aim = message.get(AssertionInfoMap.class);
if (null == aim) {
return;
}
Collection<AssertionInfo> ais = aim
.get(SchemaValidationPolicyBuilder.SCHEMA_VALIDATION);
if (null == ais) {
return;
}
for (AssertionInfo ai : ais) {
if (ai.getAssertion() instanceof SchemaValidationPolicy) {
SchemaValidationPolicy vPolicy = (SchemaValidationPolicy) ai
.getAssertion();
ValidationType vldType = vPolicy.getValidationType();
AppliesToType appliesToType = vPolicy.getApplyToType();
MessageType msgType = vPolicy.getMessageType();
String customSchemaPath = vPolicy.getCustomSchemaPath();
if (vldType != ValidationType.WSDLSchema) {
ai.setAsserted(true);
}
if (shouldSchemaValidate(message, msgType, appliesToType)) {
if (vldType == ValidationType.CustomSchema) {
// load custom schema from external source
try{
loadCustomSchema(message, customSchemaPath, this.getClass());
}catch(IOException ex){
throw new RuntimeException("Can not load custom schema", ex);
}
}
// do schema validation by setting value to
// "schema-validation-enabled" property
validateBySettingProperty(message);
}
ai.setAsserted(true);
}
ai.setAsserted(true);
}
}
protected void handleMessageWithoutAssertionInfo(Message message)
throws Fault {
ValidationType vldType = policy.getValidationType();
AppliesToType appliesToType = policy.getApplyToType();
MessageType msgType = policy.getMessageType();
String customSchemaPath = policy.getCustomSchemaPath();
if (shouldSchemaValidate(message, msgType, appliesToType)) {
if (vldType == ValidationType.CustomSchema) {
// load custom schema from external source
try{
loadCustomSchema(message, customSchemaPath, this.getClass());
}catch(IOException ex){
throw new RuntimeException("Can not load custom schema", ex);
}
}
// do schema validation by setting value to
// "schema-validation-enabled" property
validateBySettingProperty(message);
}
}
protected void loadCustomSchema(Message message, String customSchemaPath,
@SuppressWarnings("rawtypes") Class c) throws IOException{
if (customSchemaPath == null || customSchemaPath.trim().isEmpty()) {
throw new IllegalArgumentException(
"Path to custom schema is not set or empty");
}
String absoluteSchemaPath = null;
CachedOutputStream cos = new CachedOutputStream();
absoluteSchemaPath = loadResource(customSchemaPath, cos);
InputStream customSchemaStream = cos.getInputStream();
if (customSchemaStream == null) {
throw new IllegalArgumentException(
"Cannot load custom schema from path: " + customSchemaPath);
}
SchemaFactory factory = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
factory.setResourceResolver(new SchemaResourceResolver(absoluteSchemaPath, customSchemaPath));
Source src = new StreamSource(customSchemaStream);
Schema customSchema;
try {
customSchema = factory.newSchema(src);
} catch (SAXException e) {
throw new IllegalArgumentException(
"Cannot create custom schema from path: "
+ customSchemaPath
+ "\n" + e.getMessage(), e);
}
message.getExchange().getService().getServiceInfos().get(0)
.setProperty(Schema.class.getName(), customSchema);
}
@SuppressWarnings("resource")
private String loadResource(String path, OutputStream output)
throws IOException{
InputStream resource = null;
String absolutePath = null;
// try to load resource from file system
try {
resource = new FileInputStream(path);
if(resource!=null){
absolutePath = path;
}
} catch (FileNotFoundException e) {
resource = null;
}
if (resource == null) {
// try to load resource from class loader root
resource = ClassLoaderUtils.getResourceAsStream(path,
this.getClass());
if(resource!=null){
URL url = ClassLoaderUtils.getResource(path, this.getClass());
if(url!= null){
absolutePath = url.getPath();
}
}
}
if (resource == null) {
// try to load schema as web resource
try {
URL url = new URL(path);
resource = url.openStream();
} catch (Exception e) {
}
}
if(resource!=null){
IOUtils.copyAndCloseInput(resource, output);
return absolutePath;
}
return null;
}
class SchemaResourceResolver implements LSResourceResolver {
String parentSchemaAbsolutePath = null;
String parentSchemaProvidedPath = null;
public SchemaResourceResolver(String parentSchemaAbsolutePath,
String parentSchemaProvidedPath){
this.parentSchemaAbsolutePath = parentSchemaAbsolutePath;
this.parentSchemaProvidedPath = parentSchemaProvidedPath;
}
public LSInput resolveResource(String type, String namespaceURI,
String publicId, String systemId, String baseURI) {
boolean isRemoteLocation = (systemId != null &&
(systemId.startsWith("http://") || systemId.startsWith("https://")));
//Try to find path to parent schema directory
String parentSchemaDir = "";
if(parentSchemaAbsolutePath!=null && !isRemoteLocation){
File file = new File(parentSchemaAbsolutePath);
if(file.exists()){
parentSchemaDir = file.getParentFile().getAbsolutePath();
}
}
// Try to resolve path to imported schema
// using provided resource properties
String resURL = null;
if (systemId != null) {
String schemaLocation = "";
if (baseURI != null) {
schemaLocation = baseURI.substring(0,
baseURI.lastIndexOf("/") + 1);
}
if (!isRemoteLocation) {
resURL = schemaLocation + systemId;
} else {
resURL = systemId;
}
} else if (namespaceURI != null) {
// Schema location is not set
// lets try to use namespace as
// possible schema location
resURL = namespaceURI;
isRemoteLocation = true;
}
CachedOutputStream cache = new CachedOutputStream();
InputStream resourceStream = null;
String actualSchemaURL = null;
try{
// Try to load schema using absolute path
actualSchemaURL = resURL;
loadResource(actualSchemaURL, cache);
if(cache.size()==0 && parentSchemaDir!=null && !parentSchemaDir.isEmpty() && !isRemoteLocation){
// Schema is not found
// Try to load schema using path to basic schema directory
// (which is referenced in policy) as root
actualSchemaURL = parentSchemaDir+File.separator + resURL;
loadResource(actualSchemaURL, cache);
}
resourceStream = cache.getInputStream();
}catch (IOException ex){
return null;
}
if (cache.size() != 0) {
LSInput resource = new LSInputImpl();
resource.setSystemId(actualSchemaURL);
resource.setPublicId(publicId);
resource.setByteStream(resourceStream);
return resource;
}else{
StringBuilder message = new StringBuilder();
message.append("Schema validation: can not load internal schema with path {");
if(systemId==null){
message.append(resURL);
}else{
message.append(systemId);
}
message.append("}");
// if(baseURI!=null && !baseURI.isEmpty()){
// message.append(" which is referenced from schema {");
// message.append(baseURI);
// message.append("}");
// }else if(parentSchemaProvidedPath!=null && !parentSchemaProvidedPath.isEmpty()){
// message.append(" which is referenced from schema {");
// message.append(parentSchemaProvidedPath);
// message.append("}");
// }
throw new RuntimeException(message.toString());
}
}
}
protected boolean shouldSchemaValidate(Message message,
MessageType msgType, AppliesToType appliesToType) {
if (MessageUtils.isRequestor(message)) {
if (MessageUtils.isOutbound(message)) { // REQ_OUT
return ((appliesToType == AppliesToType.consumer || appliesToType == AppliesToType.always) && (msgType == MessageType.request || msgType == MessageType.all));
} else { // RESP_IN
return ((appliesToType == AppliesToType.consumer || appliesToType == AppliesToType.always) && (msgType == MessageType.response || msgType == MessageType.all));
}
} else {
if (MessageUtils.isOutbound(message)) { // RESP_OUT
return ((appliesToType == AppliesToType.provider || appliesToType == AppliesToType.always) && (msgType == MessageType.response || msgType == MessageType.all));
} else { // REQ_IN
return ((appliesToType == AppliesToType.provider || appliesToType == AppliesToType.always) && (msgType == MessageType.request || msgType == MessageType.all));
}
}
}
protected abstract void validateBySettingProperty(Message message);
}