package org.openstack.atlas.api.filters; import javax.servlet.ServletResponse; import javax.servlet.ServletRequest; import javax.servlet.FilterChain; import javax.servlet.Filter; import javax.servlet.ServletException; import javax.servlet.FilterConfig; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.io.StringWriter; import java.io.ByteArrayInputStream; import java.io.PrintWriter; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import org.openstack.atlas.docs.loadbalancers.api.v1.faults.BadRequest; import org.openstack.atlas.docs.loadbalancers.api.v1.faults.ValidationErrors; import org.openstack.atlas.api.filters.helpers.MediaType; import org.openstack.atlas.api.filters.helpers.XmlValidationExceptionHandler; import org.openstack.atlas.api.filters.wrappers.BufferedRequestWrapper; import org.openstack.atlas.api.helpers.JsonObjectMapper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.io.ClassPathResource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.stream.XMLStreamReader; import static org.openstack.atlas.api.filters.helpers.StringUtilities.logId; public class ValidationFilter implements Filter { private final Log LOG = LogFactory.getLog(ValidationFilter.class); protected static final String XML = "application/xml"; protected static final String JSON = "application/json"; protected static final String VFAIL = "Validation Failure"; protected static final int PAGESIZE = 4096; protected FilterConfig config = null; protected String pPkg; protected String pXSD; protected String fPkg; protected String fXSD; protected JAXBContext pCtx; protected JAXBContext fCtx; protected Schema pSchema; protected Schema fSchema; protected JsonObjectMapper mapper; public static final int BUFFSIZE = 16384; public static final int BADREQ = 400; public static final String dashline = "--------------------------------------\n"; public static final Pattern jsonUriPattern = Pattern.compile(".*\\.json$", Pattern.CASE_INSENSITIVE); public static final Pattern xmlUriPattern = Pattern.compile(".*\\.xml$", Pattern.CASE_INSENSITIVE); @Override public void init(FilterConfig config) throws ServletException { this.setConfig(config); } @Override public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException { throw new UnsupportedOperationException("Not supported yet."); } @Override public void destroy() { } private void deleteme(ServletRequest sreq, ServletResponse sresp) throws IOException { HttpServletRequest hreq = (HttpServletRequest) sreq; HttpServletResponse hresp = (HttpServletResponse) sresp; String accept = hreq.getHeader("Accept"); BufferedRequestWrapper breq = new BufferedRequestWrapper(hreq); String body = readFromInputStream(breq.getInputStream()); String method = hreq.getMethod(); MediaType contentMedia = MediaType.newInstance(hreq.getContentType()); String overideAccept; } public boolean isHeaderTrue(HttpServletRequest hreq, String name) { boolean out = false; if (hreq.getHeader(name) == null) { return out; } if (hreq.getHeader(name).equalsIgnoreCase("true")) { out = true; } return out; } protected void sendXMLErrorResponse(HttpServletRequest req, HttpServletResponse resp, int status, List<String> errList) throws IOException { ValidationErrors vFault = new ValidationErrors(); BadRequest badRequest = new BadRequest(); badRequest.setValidationErrors(vFault); badRequest.setCode(BADREQ); badRequest.setMessage(VFAIL); PrintWriter writer = resp.getWriter(); String result; try { vFault.getMessages().addAll(errList); resp.setStatus(status); resp.setContentType("application/xml; charset=UTF-8"); writer = resp.getWriter(); result = pojo2xml(badRequest, fCtx, fSchema); resp.setContentLength(result.length()); writer.write(result); writer.flush(); } catch (JAXBException ex) { String errMsg = jaxbParseExceptionToString(ex, ""); LOG.error(errMsg); resp.sendError(BADREQ, "Bad Request"); } } protected void sendJSONErrorResponse(HttpServletRequest req, HttpServletResponse resp, int status, List<String> errList) throws IOException { ValidationErrors vFault = new ValidationErrors(); BadRequest badRequest = new BadRequest(); badRequest.setValidationErrors(vFault); badRequest.setCode(BADREQ); badRequest.setMessage(VFAIL); PrintWriter writer = resp.getWriter(); String result; vFault.getMessages().addAll(errList); resp.setStatus(status); resp.setContentType("application/json; charset=UTF-8"); writer = resp.getWriter(); result = mapper.writeValueAsString(vFault); resp.setContentLength(result.length()); writer.write(result); writer.flush(); } protected void sendJSONErrorResponse(HttpServletRequest req, HttpServletResponse resp, int status, String err) throws IOException { List<String> errList = new ArrayList<String>(); errList.add(err); sendJSONErrorResponse(req, resp, status, errList); } protected void sendXMLErrorResponse(HttpServletRequest req, HttpServletResponse resp, int status, String err) throws IOException { List<String> errList = new ArrayList<String>(); errList.add(err); sendXMLErrorResponse(req, resp, status, errList); } protected void startConfig() throws MalformedURLException, SAXException, JAXBException, IOException { logId("startConfig() ", this); this.pCtx = JAXBContext.newInstance(pPkg); this.fCtx = JAXBContext.newInstance(fPkg); SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI); pSchema = sf.newSchema((new ClassPathResource(pXSD)).getURL()); fSchema = sf.newSchema((new ClassPathResource(fXSD)).getURL()); mapper = new JsonObjectMapper(); mapper.init(); } public static Object xml2pojo(String xml, JAXBContext ctx, Schema schema, XmlValidationExceptionHandler errHandler) throws JAXBException, UnsupportedEncodingException, IOException { Object out = null; XMLInputFactory xif = XMLInputFactory.newFactory(); xif.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); xif.setProperty(XMLInputFactory.SUPPORT_DTD, false); XMLStreamReader xsr; ByteArrayInputStream bytes = new ByteArrayInputStream(xml.getBytes("UTF-8")); try { xsr = xif.createXMLStreamReader(bytes); } catch (Exception ex) { Logger.getLogger(ValidationFilter.class.getName()).log(Level.SEVERE, null, ex); throw new IOException("Could not create XMLStreamReader", ex); } Unmarshaller u = ctx.createUnmarshaller(); u.setSchema(schema); if (errHandler != null) { u.setEventHandler(errHandler); } out = u.unmarshal(xsr); return out; } public static String pojo2xml(Object pojo, JAXBContext ctx, Schema schema) throws JAXBException { String result; StringWriter sw = new StringWriter(); Marshaller m = ctx.createMarshaller(); m.setSchema(schema); m.marshal(pojo, sw); result = sw.toString(); return result; } protected String readFromInputStream(InputStream is) throws IOException { String out; StringBuilder sb = new StringBuilder(PAGESIZE); int nread; byte[] buf; while (true) { buf = new byte[BUFFSIZE]; nread = is.read(buf); if (nread <= 0) { break; } String sbStr = new String(buf); sb.append(sbStr); } out = sb.toString().trim(); return out; } protected static boolean containsMethod(String method, String... methods) { if (method == null) { return false; } for (int i = 0; i < methods.length; i++) { if (methods[i].toLowerCase().equals(method.toLowerCase())) { return true; } } return false; } public static List<String> lineSplit(String strIn) { StringBuilder sb = new StringBuilder(); int i; List<String> lines = new ArrayList<String>(); char[] chrs = {' '}; for (i = 0; i < strIn.length(); i++) { String singleCharString; chrs[0] = strIn.charAt(i); singleCharString = new String(chrs); if (singleCharString.equals("\n")) { lines.add(sb.toString()); sb = new StringBuilder(); } else { sb.append(singleCharString); } } lines.add(sb.toString()); return lines; } // Grabs the text where the Error begins protected static String nearString(String strIn, int lineNum, int colNum) { String out = ""; StringBuilder sb = new StringBuilder(); String line; String lineOut; List<String> lines; int i; int last_i; lines = lineSplit(strIn); last_i = lines.size() - 1; for (i = lineNum; i <= last_i; i++) { lineOut = lines.get(i); if (i == lineNum) { if (colNum > lineOut.length()) { continue; } lineOut = lineOut.substring(colNum); } if (i != last_i) { lineOut = String.format("%s\n", lineOut); } sb.append(lineOut); } out = sb.toString(); return out; } protected static String jaxbParseExceptionToString(Exception ex, String xml) { String out; StringBuilder sb = new StringBuilder(); if (ex instanceof JAXBException) { JAXBException je = (JAXBException) ex; sb.append("JAXBException\n"); sb.append(dashline); sb.append(String.format("%s\n", getExtendedStackTrace(je))); sb.append(dashline); sb.append("\n"); if (je.getCause() != null && je.getCause() instanceof SAXParseException) { SAXParseException se = (SAXParseException) je.getCause(); sb.append(String.format("%s", saxParseExceptionToString(se, xml))); } } if (ex instanceof SAXParseException) { return saxParseExceptionToString(ex, xml); } return sb.toString(); } protected static String saxParseExceptionToString(Exception ex, String xml) { String out; StringBuilder sb = new StringBuilder(); if (ex instanceof SAXParseException) { int lineNum; int colNum; SAXParseException se = (SAXParseException) ex; lineNum = se.getLineNumber(); colNum = se.getColumnNumber(); sb.append(String.format("SAXParseException\n")); sb.append(dashline); sb.append(String.format("Error: %s\n", (se.getMessage() == null) ? "null" : se.getMessage())); sb.append(String.format("Line number: %d\n", lineNum - 1)); sb.append(String.format("Colum number: %d\n", colNum - 1)); sb.append(String.format("Near %s\n", nearString(xml, lineNum, colNum))); sb.append(dashline); sb.append("\n"); } out = sb.toString(); return out; } protected String overideAcceptType(String uri) { String out = null; Matcher m; m = xmlUriPattern.matcher(uri); if (m.find()) { return XML; } m = jsonUriPattern.matcher(uri); if (m.find()) { return JSON; } return out; } public static String getExtendedStackTrace(Throwable th) { Throwable t; StringBuilder sb = new StringBuilder(PAGESIZE); Throwable currThrowable; String msg; t = th; while (t != null) { if (t instanceof Throwable) { currThrowable = (Throwable) t; sb.append(String.format("\"%s\":\"%s\"\n", currThrowable.getClass().getName(), currThrowable.getMessage())); for (StackTraceElement se : currThrowable.getStackTrace()) { sb.append(String.format("%s\n", se.toString())); } sb.append("\n"); t = t.getCause(); } } return sb.toString(); } public FilterConfig getConfig() { return config; } public void setConfig(FilterConfig config) { this.config = config; } public String getpPkg() { return pPkg; } public void setpPkg(String pPkg) { this.pPkg = pPkg; } public String getpXSD() { return pXSD; } public void setpXSD(String pXSD) { this.pXSD = pXSD; } public String getfPkg() { return fPkg; } public void setfPkg(String fPkg) { this.fPkg = fPkg; } public String getfXSD() { return fXSD; } public void setfXSD(String fXSD) { this.fXSD = fXSD; } public JAXBContext getpCtx() { return pCtx; } public void setpCtx(JAXBContext pCtx) { this.pCtx = pCtx; } public JAXBContext getfCtx() { return fCtx; } public void setfCtx(JAXBContext fCtx) { this.fCtx = fCtx; } public Schema getpSchema() { return pSchema; } public void setpSchema(Schema pSchema) { this.pSchema = pSchema; } public Schema getfSchema() { return fSchema; } public void setfSchema(Schema fSchema) { this.fSchema = fSchema; } }