List of usage examples for javax.servlet.http HttpServletRequest getContentLength
public int getContentLength();
From source file:org.sakaiproject.dav.DavServlet.java
/** * PROPPATCH Method./*from w w w. ja va 2 s . c o m*/ */ @SuppressWarnings("deprecation") protected void doProppatch(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Check that the resource is not locked if (isLocked(req)) { resp.sendError(SakaidavStatus.SC_LOCKED); } // we can't actually do this, but MS requires us to. Say we did. // I'm trying to be as close to valid here, so I generate an OK // for all the properties they tried to set. This is really hairy because // it gets into name spaces. But if we ever try to implement this for real, // we'll have to do this. So might as well start now. // During testing I found by mistake that it's actually OK to send // an empty multistatus return, so I don't actually need all of this stuff. // The big problem is that the properties are typically not in the dav namespace // we build a hash table of namespaces, with the prefix we're going to use // since D: is used for dav, we start with E:, actually D+1 DocumentBuilder documentBuilder = null; try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); factory.setFeature("http://xml.org/sax/features/external-general-entities", false); factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); documentBuilder = factory.newDocumentBuilder(); } catch (Exception e) { resp.sendError(SakaidavStatus.SC_METHOD_FAILURE); return; } int contentLength = req.getContentLength(); // a list of the properties with the new prefix List<String> props = new ArrayList<String>(); // hash of namespace, prefix Hashtable<String, String> spaces = new Hashtable<String, String>(); // read the xml document if (contentLength > MAX_XML_STREAM_LENGTH) { resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE); return; } else if (contentLength > 0) { byte[] byteContent = new byte[contentLength]; InputStream inputStream = req.getInputStream(); int lenRead = 0; try { while (lenRead < contentLength) { int read = inputStream.read(byteContent, lenRead, contentLength - lenRead); if (read <= 0) break; lenRead += read; } } catch (Exception ignore) { } // Parse the input XML to see what they really want if (lenRead > 0) try { // if we got here, "is" is the xml document InputStream is = new ByteArrayInputStream(byteContent, 0, lenRead); Document document = documentBuilder.parse(new InputSource(is)); // Get the root element of the document Element rootElement = document.getDocumentElement(); // find all the property nodes NodeList childList = rootElement.getElementsByTagNameNS("DAV:", "prop"); int nextChar = 1; for (int i = 0; i < childList.getLength(); i++) { // this should be a prop node Node currentNode = childList.item(i); // this should be the actual property NodeList names = currentNode.getChildNodes(); // this should be the name for (int j = 0; j < names.getLength(); j++) { String namespace = names.item(j).getNamespaceURI(); String prefix = spaces.get(namespace); // see if we know about this namespace. If not add it and // generate a prefix if (prefix == null) { prefix = "" + Character.toChars('D' + nextChar)[0]; spaces.put(namespace, prefix); } props.add(prefix + ":" + names.item(j).getLocalName()); } } } catch (Exception ignore) { } } resp.setStatus(SakaidavStatus.SC_MULTI_STATUS); resp.setContentType("text/xml; charset=UTF-8"); Writer writer = resp.getWriter(); writer.write("<D:multistatus xmlns:D=\"DAV:\""); // dump all the name spaces and their prefix for (String namespace : spaces.keySet()) writer.write(" xmlns:" + spaces.get(namespace) + "=\"" + namespace + "\""); writer.write("><D:response><D:href>" + javax.servlet.http.HttpUtils.getRequestURL(req) + "</D:href>"); // now output properties, claiming we did it for (String pname : props) { writer.write("<D:propstat><D:prop><" + pname + "/></D:prop><D:status>HTTP/1.1 201 OK</D:status></D:propstat>"); } writer.write("</D:response></D:multistatus>"); writer.close(); }
From source file:org.sakaiproject.dav.DavServlet.java
/** * PROPFIND Method.//from ww w . ja v a 2s .c o m */ protected void doPropfind(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String path = getRelativePathSAKAI(req); if (path.endsWith("/")) path = path.substring(0, path.length() - 1); if ((path.toUpperCase().startsWith("/WEB-INF")) || (path.toUpperCase().startsWith("/META-INF")) || prohibited(path)) { resp.sendError(SakaidavStatus.SC_FORBIDDEN); return; } // Properties which are to be displayed. Vector<String> properties = null; // Propfind depth int depth = INFINITY; // Propfind type int type = FIND_ALL_PROP; String depthStr = req.getHeader("Depth"); if (depthStr == null) { depth = INFINITY; } else { if (depthStr.equals("0")) { depth = 0; } else if (depthStr.equals("1")) { depth = 1; } else if (depthStr.equals("infinity")) { depth = INFINITY; } } Node propNode = null; DocumentBuilder documentBuilder = getDocumentBuilder(); // be careful how we get content, as we've had hangs in mod_jk // Rather than passing the XML parser a stream on the network // input, we read it into a buffer and pass them a stream // on the buffer. This is an experiment to see if it fixes // the hangs. // Note that getContentLength can return -1. As everyone seems // to use the content-length header, ignore that case for now // It is strongly discouraged by the spec. int contentLength = req.getContentLength(); if (contentLength > MAX_XML_STREAM_LENGTH) { resp.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE); return; } else if (contentLength > 0) { byte[] byteContent = new byte[contentLength]; InputStream inputStream = req.getInputStream(); int lenRead = 0; try { while (lenRead < contentLength) { int read = inputStream.read(byteContent, lenRead, contentLength - lenRead); if (read <= 0) break; lenRead += read; } } catch (Exception ignore) { } // if anything goes wrong, we treat it as find all props // Parse the input XML to see what they really want if (lenRead > 0) try { InputStream is = new ByteArrayInputStream(byteContent, 0, lenRead); // System.out.println("have bytes"); Document document = documentBuilder.parse(new InputSource(is)); // Get the root element of the document Element rootElement = document.getDocumentElement(); NodeList childList = rootElement.getChildNodes(); // System.out.println("have nodes " + childList.getLength()); for (int i = 0; i < childList.getLength(); i++) { Node currentNode = childList.item(i); // System.out.println("looking at node " + currentNode.getNodeName()); switch (currentNode.getNodeType()) { case Node.TEXT_NODE: break; case Node.ELEMENT_NODE: if (currentNode.getNodeName().endsWith("prop")) { type = FIND_BY_PROPERTY; propNode = currentNode; } if (currentNode.getNodeName().endsWith("propname")) { type = FIND_PROPERTY_NAMES; } if (currentNode.getNodeName().endsWith("allprop")) { type = FIND_ALL_PROP; } break; } } } catch (SAXParseException se) { resp.sendError(HttpServletResponse.SC_BAD_REQUEST); return; } catch (Exception e) { M_log.warn("Exception parsing DAV request", e); } // again, in case of exception, we'll have the default // FIND_ALL_PROP } // System.out.println("Find type " + type); if (type == FIND_BY_PROPERTY) { properties = new Vector<String>(); NodeList childList = propNode.getChildNodes(); for (int i = 0; i < childList.getLength(); i++) { Node currentNode = childList.item(i); switch (currentNode.getNodeType()) { case Node.TEXT_NODE: break; case Node.ELEMENT_NODE: String nodeName = currentNode.getNodeName(); String propertyName = null; if (nodeName.indexOf(':') != -1) { propertyName = nodeName.substring(nodeName.indexOf(':') + 1); } else { propertyName = nodeName; } // href is a live property which is handled differently properties.addElement(propertyName); break; } } } // Retrieve the resources DirContextSAKAI resources = getResourcesSAKAI(); if (resources == null) { resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return; } // Point the resource object at a particular path and catch the error if necessary. boolean exists = true; try { resources.lookup(path); } catch (NamingException e) { exists = false; int slash = path.lastIndexOf('/'); if (slash != -1) { String parentPath = path.substring(0, slash); Vector<String> currentLockNullResources = lockNullResources.get(parentPath); if (currentLockNullResources != null) { Enumeration<String> lockNullResourcesList = currentLockNullResources.elements(); while (lockNullResourcesList.hasMoreElements()) { String lockNullPath = lockNullResourcesList.nextElement(); if (lockNullPath.equals(path)) { resp.setStatus(SakaidavStatus.SC_MULTI_STATUS); resp.setContentType("text/xml; charset=UTF-8"); // Create multistatus object XMLWriter generatedXML = new XMLWriter(resp.getWriter()); generatedXML.writeXMLHeader(); generatedXML.writeElement("D", "multistatus" + generateNamespaceDeclarations(), XMLWriter.OPENING); parseLockNullProperties(req, generatedXML, lockNullPath, type, properties); generatedXML.writeElement("D", "multistatus", XMLWriter.CLOSING); generatedXML.sendData(); return; } } } } } if (!exists) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "/dav" + path); return; } resp.setStatus(SakaidavStatus.SC_MULTI_STATUS); resp.setContentType("text/xml; charset=UTF-8"); // Create multistatus object XMLWriter generatedXML = new XMLWriter(resp.getWriter()); generatedXML.writeXMLHeader(); generatedXML.writeElement("D", "multistatus" + generateNamespaceDeclarations(), XMLWriter.OPENING); if (depth == 0) { parseProperties(req, resources, generatedXML, path, type, properties); } else { // The stack always contains the object of the current level Stack<String> stack = new Stack<String>(); stack.push(path); // Stack of the objects one level below Stack<String> stackBelow = new Stack<String>(); while ((!stack.isEmpty()) && (depth >= 0)) { String currentPath = (String) stack.pop(); try { // if (M_log.isDebugEnabled()) M_log.debug("Lookup currentPath="+currentPath); resources.lookup(currentPath); } catch (NamingException e) { continue; } parseProperties(req, resources, generatedXML, currentPath, type, properties); if ((resources.isCollection) && (depth > 0)) { Iterator<ContentEntity> it = resources.list(currentPath); while (it.hasNext()) { Entity mbr = it.next(); String resourceName = getResourceNameSAKAI(mbr); String newPath = currentPath; if (!(newPath.endsWith("/"))) newPath += "/"; newPath += resourceName; if (!(newPath.toLowerCase().indexOf("/protected") >= 0 && !contentHostingService.allowAddCollection(newPath))) stackBelow.push(newPath); // if (M_log.isDebugEnabled()) M_log.debug("SAKAI found resource " + newPath); } // Displaying the lock-null resources present in that // collection String lockPath = currentPath; if (lockPath.endsWith("/")) lockPath = lockPath.substring(0, lockPath.length() - 1); Vector<String> currentLockNullResources = lockNullResources.get(lockPath); if (currentLockNullResources != null) { Enumeration<String> lockNullResourcesList = currentLockNullResources.elements(); while (lockNullResourcesList.hasMoreElements()) { String lockNullPath = (String) lockNullResourcesList.nextElement(); parseLockNullProperties(req, generatedXML, lockNullPath, type, properties); } } } if (stack.isEmpty()) { depth--; stack = stackBelow; stackBelow = new Stack<String>(); } // if (M_log.isDebugEnabled()) M_log.debug("SAKAIDAV.propfind() " + generatedXML.toString()); generatedXML.sendData(); } } generatedXML.writeElement("D", "multistatus", XMLWriter.CLOSING); // if (M_log.isDebugEnabled()) M_log.debug("SAKAIDAV.propfind() at end:" + generatedXML.toString()); generatedXML.sendData(); }
From source file:de.innovationgate.wgpublisher.WGPDispatcher.java
private void dispatchTmlRequest(WGPRequestPath path, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Date startDate) throws java.lang.Exception { WGContent content = path.getContent(); WGDatabase database = path.getDatabase(); if (database == null) { throw new HttpErrorException(404, "No database of key " + path.getDatabaseKey(), null); }/*from w ww .j a va2 s. com*/ WGARequestInformation info = (WGARequestInformation) request .getAttribute(WGARequestInformation.REQUEST_ATTRIBUTENAME); if (info != null) { info.setDatabase(database); info.setContent(content); if (content != null) { info.setType(WGARequestInformation.TYPE_CONTENT); } else { info.setType(WGARequestInformation.TYPE_TML); } } boolean ajax = false; HttpSession session = request.getSession(); // Determine requested mime type and type key String mediaKey = path.getMediaKey(); if (mediaKey == null) { mediaKey = database.getAttribute(WGACore.DBATTRIB_DEFAULT_MEDIAKEY).toString(); } // Ensure existing SO registry for this session/database SessionScopeResolver.getRegistry(request.getSession(), database.getDbReference(), true); // Look if a session cookie has to be set _core.setSessionCookie(request, response, database); // Context request, if content key filled or we have a title path if (content != null) { if (!content.mayBePublished( isBrowserInterface(session) || isAuthoringMode(database.getDbReference(), session), WGContent.DISPLAYTYPE_NONE)) { sendNoContentNotification(path, request, response, database); return; } if (content.isVirtual()) { if (isBrowserInterface(session)) { if (!content.getStatus().equals(WGContent.STATUS_DRAFT) && request.getParameter("forceVLink") == null) { String url = getVirtualContentURL(request, database, path, content); sendRedirect(request, response, url); return; } } else { String vLink = buildVirtualLink(WGA.get(request, response, getCore()), content, path.getMediaKey(), path.getLayoutKey()); if (vLink != null) { sendRedirect(request, response, vLink); return; } else { throw new HttpErrorException(HttpServletResponse.SC_NOT_FOUND, "Unresolveable virtual link on content " + content.getContentKey().toString(), path.getDatabaseKey()); } } } } // Contextless request. We use a dummy content else { content = database.getDummyContent(path.getRequestLanguage()); } // Test browsability of content if (!content.isDummy() && getBrowsingSecurity(database) <= BrowsingSecurity.NO_BROWSING) { throw new HttpErrorException(java.net.HttpURLConnection.HTTP_FORBIDDEN, "Browsing not allowed in database '" + path.getDatabaseKey() + "'", path.getDatabaseKey()); } // Drop cache if requested by url param if (request.getQueryString() != null && request.getQueryString().toLowerCase().indexOf("dropcache") != -1 && isAdminLoggedIn(request)) { content.dropCache(); } // Preparse TMLForm data into multipart form data TMLForm.MultipartFormData formData = null; String ajaxFormdataSessionKey = request.getParameter("$ajaxformkey"); if (ajaxFormdataSessionKey != null && !ajaxFormdataSessionKey.trim().equals("")) { formData = (TMLForm.MultipartFormData) request.getSession().getAttribute(ajaxFormdataSessionKey); if (formData != null && !formData.isValid()) { formData = null; } request.getSession().removeAttribute(ajaxFormdataSessionKey); } else if (ServletFileUpload.isMultipartContent(request) && request.getContentLength() != -1) { try { formData = new TMLForm.MultipartFormData(request, getCore()); } catch (IOException e) { getCore().getLog().warn( "Could not parse multipart form data because of IO exception: " + WGUtils.getRootCause(e)); } catch (Exception e) { getCore().getLog().error("Exception parsing multipart form data", e); } } // Read ajax information, to be able to determine AJAX requests String encAjaxInfo = request.getParameter("$ajaxInfo"); boolean isAjax = (encAjaxInfo != null); TMLUserProfile tmlUserProfile = null; try { tmlUserProfile = getCore().getPersManager().prepareUserProfileForRequest(request, response, content, database, formData, isAjax); if (info != null && tmlUserProfile != null) { info.setProfile(tmlUserProfile); } } catch (Exception e) { _log.error("Unable to personalize WebTML request " + path.getCompleteURL(), e); } // Set some basic attributes for WebTML processing request.setAttribute(WGACore.ATTRIB_WGPPATH, path.getPublisherURL()); request.setAttribute(WGACore.ATTRIB_TAGIDS, new ConcurrentHashMap<String, BaseTagStatus>()); request.setAttribute(WGACore.ATTRIB_REQUESTURL, path.getCompleteURL()); request.setAttribute(WGACore.ATTRIB_REQUESTTYPE, REQUESTTYPE_TML); request.setAttribute(WGACore.ATTRIB_URI_HASH, WGUtils.createMD5HEX(request.getRequestURI().getBytes("UTF-8"))); request.setAttribute(WGACore.ATTRIB_FORMDATA, formData); request.setAttribute(WGACore.ATTRIB_COOKIES, fetchHttpCookies(request)); // Determine tml design for this request WGA wga = WGA.get(request, response, getCore()); Design outerLayout = null; if (path.getLayoutKey() != null) { outerLayout = wga.design(database).resolve(path.getLayoutKey()); WGTMLModule tmlLib = outerLayout.getTMLModule(mediaKey); if (tmlLib != null && tmlLib.isDirectAccessAllowed() == false) { throw new HttpErrorException(java.net.HttpURLConnection.HTTP_FORBIDDEN, "This design is not allowed for direct access: " + tmlLib.getName() + " (" + tmlLib.getMediaKey() + ")", path.getDatabaseKey()); } } else { WGStructEntry entry = content.getStructEntry(); if (entry == null) { throw new HttpErrorException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Content " + content.getContentKey().toString() + " has no struct entry", path.getDatabaseKey()); } outerLayout = wga.design(database).resolve(entry.getContentType().getOuterLayoutName()); } request.setAttribute(WGACore.ATTRIB_OUTER_DESIGN, outerLayout.getBaseReference().getResourceName()); // If AJAX read the AJAX data and use WebTML module from there AjaxInfo ajaxInfo = null; if (encAjaxInfo != null) { ajaxInfo = readAjaxInformation(request, database, encAjaxInfo); ajax = true; WGTMLModule tmlLib = getAjaxTMLModule(request, database, ajaxInfo); outerLayout = wga.design(tmlLib.getDatabase()).resolve(tmlLib.getName()); mediaKey = tmlLib.getMediaKey(); } // Set these here for once, so the following scripts can fetch them. Might be changed by the renderer. MediaKey mediaKeyObj = _core.getMediaKey(mediaKey); request.setAttribute(WGACore.ATTRIB_MIMETYPE, mediaKeyObj.getMimeType()); request.setAttribute(WGACore.ATTRIB_MEDIAKEY, mediaKeyObj.getKey()); // Update usage statistics getCore().getUsageStatistics().addRequestStatistic(request, session, database, tmlUserProfile); // Prepare WebTML environment TMLContext mainContext = new WebTMLEnvironmentBuilder(getCore(), content, request, response, tmlUserProfile, ajaxInfo).prepareWebTmlEnvironment(); // Dispatch try { rootDispatch(wga, outerLayout, mainContext, mediaKey); // Eventually do redirect if (request.getAttribute(WGACore.ATTRIB_REDIRECT) != null) { if (!ajax) { // On AJAX requests the redirect is performed by Root.tmlEndTag() if (!response.isCommitted()) { if (!session.isNew()) { // on new sessions we must not reset the response (#00000147) response.reset(); } response.sendRedirect(String.valueOf(request.getAttribute(WGACore.ATTRIB_REDIRECT))); } else { // Out of luck for redirect. We only can log that we could not redirect getCore().getLog() .warn("Unable to perform redirect to '" + String.valueOf(request.getAttribute(WGACore.ATTRIB_REDIRECT)) + "' because response was already committed"); } } } } catch (Exception t) { throw t; } finally { if (info != null) { info.setMimeType((String) request.getAttribute(WGACore.ATTRIB_MIMETYPE)); info.setPath(path); info.setDesign(outerLayout.getTMLModule((String) request.getAttribute(WGACore.ATTRIB_MEDIAKEY))); info.setAjax(ajax); } TMLContext.clearThreadMainContext(); } }
From source file:com.portfolio.data.provider.MysqlAdminProvider.java
@Override public Object postPortfolioZip(MimeType mimeType, MimeType mimeType2, HttpServletRequest httpServletRequest, int userId, int groupId, String modelId) throws FileNotFoundException, IOException { if (!credential.isAdmin(userId)) throw new RestWebApplicationException(Status.FORBIDDEN, "No admin right"); DataInputStream inZip = new DataInputStream(httpServletRequest.getInputStream()); ByteArrayOutputStream byte2 = new ByteArrayOutputStream(); StringBuffer outTrace = new StringBuffer(); String portfolioId = null;/*from ww w .j av a 2s . c o m*/ portfolioId = httpServletRequest.getParameter("portfolio"); String foldersfiles = null; String filename; String[] xmlFiles; String[] allFiles; String[] ImgFiles; int formDataLength = httpServletRequest.getContentLength(); byte[] buff = new byte[formDataLength]; // Recuperation de l'heure laquelle le zip est cr //Calendar cal = Calendar.getInstance(); //SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss_S"); //String now = sdf.format(cal.getTime()); this.genererPortfolioUuidPreliminaire(); javax.servlet.http.HttpSession session = httpServletRequest.getSession(true); String ppath = session.getServletContext().getRealPath(File.separator); String outsideDir = ppath.substring(0, ppath.lastIndexOf(File.separator)) + "_files" + File.separator; File outsideDirectoryFile = new File(outsideDir); System.out.println(outsideDir); // if the directory does not exist, create it if (!outsideDirectoryFile.exists()) { outsideDirectoryFile.mkdir(); } //Creation du zip filename = outsideDir + "xml_" + this.portfolioUuidPreliminaire + ".zip"; FileOutputStream outZip = new FileOutputStream(filename); int len; while ((len = inZip.read(buff)) != -1) { outZip.write(buff, 0, len); } inZip.close(); outZip.close(); //-- unzip -- foldersfiles = unzip(filename, outsideDir + this.portfolioUuidPreliminaire + File.separator); //TODO Attention si plusieurs XML dans le fichier xmlFiles = findFiles(outsideDir + this.portfolioUuidPreliminaire + File.separator, "xml"); allFiles = findFiles(outsideDir + this.portfolioUuidPreliminaire + File.separator, null); for (int i = 0; i < allFiles.length; i++) { portfolioRessourcesImportPath.add(allFiles[i]); String tmpFileName = allFiles[i].substring(allFiles[i].lastIndexOf(File.separator) + 1); String uuid = tmpFileName.substring(0, tmpFileName.indexOf("_")); portfolioRessourcesImportUuid.add(uuid); tmpFileName = allFiles[i].substring(allFiles[i].lastIndexOf(File.separator) + 1); String lang; try { int tmpPos = tmpFileName.indexOf("_"); lang = tmpFileName.substring(tmpPos + 1, tmpPos + 3); } catch (Exception ex) { lang = ""; } InputStream is = new FileInputStream(allFiles[i]); byte b[] = new byte[is.available()]; is.read(b); String extension; try { extension = tmpFileName.substring(tmpFileName.lastIndexOf(".") + 1); } catch (Exception ex) { extension = null; } // trop long //String tmpMimeType = FileUtils.getMimeType("file://"+allFiles[i]); String tmpMimeType = FileUtils.getMimeTypeFromExtension(extension); // Attention on initialise la ligne file // avec l'UUID d'origine de l'asmContext parent // Il sera mis jour avec l'UUID asmContext final dans writeNode try { UUID tmpUuid = UUID.fromString(uuid); if (tmpUuid.toString().equals(uuid)) this.putFile(uuid, lang, tmpFileName, outsideDir, tmpMimeType, extension, b.length, b, userId); } catch (Exception ex) { // Le nom du fichier ne commence pas par un UUID, // ce n'est donc pas une ressource } } //TODO Supprimer le zip quand a fonctionnera bien //--- Read xml fileL ---- for (int i = 0; i < xmlFiles.length; i++) { BufferedReader br = new BufferedReader(new FileReader(new File(xmlFiles[i]))); String line; StringBuilder sb = new StringBuilder(); while ((line = br.readLine()) != null) { sb.append(line.trim()); } String xml = "?"; xml = sb.toString(); if (xml.contains("<portfolio id=")) { try { Object returnValue = postPortfolio(new MimeType("text/xml"), new MimeType("text/xml"), xml, userId, groupId, null); return returnValue; } catch (MimeTypeParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return false; }
From source file:org.metis.pull.WdsResourceBean.java
/** * This method gets called by the WdsRdbMapper bean to handle a HTTP * request. This method must be multi-thread capable. Note that since we're * not using Views, this method must return null. * // w w w .j a va 2 s. c o m * @param request * the http request that is being serviced * @param response * the response that will be sent back to the service consumer * @return must return null since we're not using a view * @throws Exception */ @SuppressWarnings("unchecked") protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception { LOG.debug(getBeanName() + ": handleRequestInternal - **** new request ****"); // dump the request if trace is on if (LOG.isTraceEnabled()) { LOG.trace(getBeanName() + ":handleRequestInternal - method = " + request.getMethod()); LOG.trace(getBeanName() + ":handleRequestInternal - uri = " + request.getRequestURI()); LOG.trace(getBeanName() + ":handleRequestInternal - protocol = " + request.getProtocol()); LOG.trace(getBeanName() + ":handleRequestInternal - secure = " + request.isSecure()); // dump all the http headers and their values Enumeration<String> headerNames = request.getHeaderNames(); if (headerNames != null) { while (headerNames.hasMoreElements()) { String headerName = headerNames.nextElement(); LOG.trace(getBeanName() + ":handleRequestInternal - " + headerName + " = " + request.getHeader(headerName)); } } if (request.getQueryString() != null) { LOG.trace(getBeanName() + ":handleRequestInternal - queryString = " + request.getQueryString()); } } long currentTime = System.currentTimeMillis(); // give the response a Date header with the current time response.setDateHeader(DATE_HDR, currentTime); // assign the Server header this container's info response.setHeader(SERVER_HDR, getServerInfo()); // determine the HTTP protocol version being used by the client // default version will be 0 int protocolVersion = 0; try { protocolVersion = Integer .parseInt(request.getProtocol().split(FORWARD_SLASH_STR)[1].split(ESC_DOT_STR)[1]); } catch (Exception exc) { LOG.warn(getBeanName() + ": handleRequestInternal - unable to get http protocol " + "version, stack trace follows: "); LOG.error(getBeanName() + ": exception stack trace follows:"); dumpStackTrace(exc.getStackTrace()); } LOG.trace(getBeanName() + ":handleRequestInternal - using this " + "protocol version: " + protocolVersion); /* * Ok, the request first needs to run the security gauntlet * * We do not want to send any error messages back to the client that * would give it a hint that we're invoking SQL statements. This is a * countermeasure for SQL injection probes. */ // see if this RDB is restricting user agents and if so, validate user // agent if ((getAllowedAgents() != null && !getAllowedAgents().isEmpty()) || (getNotAllowedAgents() != null && !getNotAllowedAgents().isEmpty())) { String userAgent = request.getHeader(USER_AGENT_HDR); if (userAgent != null && userAgent.length() > 0) { LOG.debug( getBeanName() + ": handleRequestInternal - validating this " + "user agent: " + userAgent); // Convert to lower case as allowed agents have been // converted to lower case as well userAgent = userAgent.toLowerCase(); boolean allow = false; if (getAllowedAgents() != null && !getAllowedAgents().isEmpty()) { for (String agent : getAllowedAgents()) { LOG.trace(getBeanName() + ": handleRequestInternal - comparing to this " + "allowed agent : " + agent); if (userAgent.indexOf(agent) >= 0) { LOG.trace(getBeanName() + ": handleRequestInternal - this allowed agent " + "was found: " + agent); allow = true; break; } } } else { allow = true; for (String agent : getNotAllowedAgents()) { LOG.trace(getBeanName() + ": handleRequestInternal - comparing to this " + "non-allowed agent : " + agent); if (userAgent.indexOf(agent) >= 0) { LOG.trace(getBeanName() + ": handleRequestInternal - this non-allowed " + "agent was found: " + agent); allow = false; break; } } } if (!allow) { response.sendError(SC_UNAUTHORIZED, "ERROR, user agent " + "is not authorized"); LOG.error(getBeanName() + ": handleRequestInternal - ERROR, user agent is " + "not authorized"); return null; } } else { response.sendError(SC_UNAUTHORIZED, "ERROR, user agent info " + "was not received and is required!"); LOG.error(getBeanName() + ": handleRequestInternal - ERROR, user agent header " + "is required but was not provided by the client"); return null; } } // we do not support chunked transfer encoding, which is a http // 1.1 feature. if (request.getHeader(TRANSFER_ENCODING_HDR) != null && request.getHeader(TRANSFER_ENCODING_HDR).equalsIgnoreCase(CHUNKED)) { response.sendError(SC_BAD_REQUEST, "Chunked tranfer encoding is not " + "supported"); return null; } /* * isSecure returns a boolean indicating whether this request was made * using a secure channel, such as HTTPS. so, if the channel must be * secure, but it is not, then throw an exception and return an error. */ if (isSecure() && !request.isSecure()) { response.sendError(SC_UNAUTHORIZED, "ERROR, channel is not secure"); LOG.error(getBeanName() + ": handleRequestInternal - ERROR, channel is not secure"); return null; } /* * getUserPrincipal() returns a java.security.Principal containing the * name of the user making this request, else it returns null if the * user has not been authenticated. so, if it is mandated that the user * be authenticated, but has not been authenticated, then throw an * exception and return an error */ if (isAuthenticated() && request.getUserPrincipal() == null) { response.sendError(SC_UNAUTHORIZED, "ERROR, user is not authenticated"); LOG.error(getBeanName() + ": handleRequestInternal - ERROR, user is not authenticated"); return null; } /* * Check for valid method - the only supported http methods are GET, * POST, PUT, and DELETE. Here are some good descriptions regarding the * methods and their use with respect to this servlet. * * The GET method is used for projecting data from the DB. So it maps to * a select statement. * * The PUT and POST methods are used for inserting or updating an entity * in the DB. So they map to either an update or insert. * * The DELETE is used for removing one or more entities from the DB. So * it maps to a delete. * * The bean must be assigned at least one of the methods to service */ Method method = null; try { method = Enum.valueOf(Method.class, request.getMethod().toUpperCase()); LOG.debug(getBeanName() + ": handleRequestInternal - processing this method: " + method.toString()); } catch (IllegalArgumentException e) { LOG.error(getBeanName() + ":handleRequestInternal - This method is not allowed [" + request.getMethod() + "]"); response.setHeader("Allow", allowedMethodsRsp); response.sendError(SC_METHOD_NOT_ALLOWED, "This method is not allowed [" + request.getMethod() + "]"); return null; } // do some more method validation; i.e., make sure requested method has // been assigned a SQL statement // // TODO: we may be able to remove this block of code String s1 = null; if (method.isGet() && sqlStmnts4Get == null || method.isPost() && sqlStmnts4Post == null || method.isPut() && sqlStmnts4Put == null || method.isDelete() && sqlStmnts4Delete == null) { response.setHeader("Allow", allowedMethodsRsp); s1 = "HTTP method [" + method + "] is not supported"; response.sendError(SC_METHOD_NOT_ALLOWED, s1); LOG.error(getBeanName() + ":handleRequestInternal - " + s1); return null; } // If the client has specified an 'Accept' header field, then determine // if it is willing or capable of accepting JSON or anything (*/*) // // TODO: what about the client accepting urlencoded strings?? s1 = request.getHeader(ACCEPT_HDR); if (s1 != null && s1.length() > 0) { LOG.debug(getBeanName() + ":handleRequestInternal - client-specified media " + "type in accept header = " + s1); // parse the accept header's content String[] mediaTypes = s1.trim().split(COMMA_STR); boolean match = false; for (String mediaType : mediaTypes) { mediaType = mediaType.trim().toLowerCase(); if (mediaType.startsWith(anyContentType) || mediaType.startsWith(jsonContentType)) { match = true; break; } } if (!match) { LOG.error(getBeanName() + ":handleRequestInternal - client-specified media type of '" + s1 + "' does not include '" + "'" + jsonContentType); response.sendError(SC_NOT_ACCEPTABLE, "client-specified media " + "type of '" + s1 + "' does not include '" + "'" + jsonContentType); return null; } } // pick up the corresponding list of SQL statements for this request List<SqlStmnt> sqlStmnts = null; switch (method) { case GET: sqlStmnts = getSqlStmnts4Get(); break; case DELETE: sqlStmnts = getSqlStmnts4Delete(); break; case PUT: sqlStmnts = getSqlStmnts4Put(); break; case POST: sqlStmnts = getSqlStmnts4Post(); break; default: response.sendError(SC_METHOD_NOT_ALLOWED, "ERROR, unsupported method type: " + method); LOG.error(getBeanName() + ": handleRequestInternal - ERROR, encountered unknown " + "method type: " + method); return null; } // ~~~~~~ EXTRACT PARAMERTERS, IF ANY ~~~~~~~~~~~ // GETs with entity bodies are illegal if (method.isGet() && request.getContentLength() > 0) { response.sendError(SC_BAD_REQUEST, "Client has issued a malformed or illegal request; " + "GET cannot include entity body"); return null; } // the DELETE method also cannot include an entity body; however, the // servlet containers already ignore them. so no need to check for that // see if json object arrived boolean jsonObjectPresent = (method.isPost() || method.isPut()) && (request.getContentLength() > 0 && request.getContentType().equalsIgnoreCase(jsonContentType)); LOG.debug(getBeanName() + ": jsonObjectPresent = " + jsonObjectPresent); // see if this is a PUT with entity. we've learned that for PUTs, // getParameterMap does not work the same across all servlet containers. // so we need take care of this ourselves boolean putWithBodyPresent = (method.isPut()) && (request.getContentLength() > 0 && request.getContentType().equalsIgnoreCase(urlEncodedContentType)); LOG.debug(getBeanName() + ": putWithBodyPresent = " + putWithBodyPresent); // collect incoming parameters and place them in a common bucket // // ~~~~ ALL PARAMETER KEY NAMES MUST BE FORCED TO LOWER CASE ~~~ // List<Map<String, String>> cParams = new ArrayList<Map<String, String>>(); // first, get the incoming query or form parameters (if any); we will // assume that each key has only one parameter. in other words, // we're not dealing with drop-down boxes or things similar if (!putWithBodyPresent && !jsonObjectPresent) { Map<String, String[]> qParams = request.getParameterMap(); if (qParams != null && !qParams.isEmpty()) { Map<String, String> qMap = new HashMap<String, String>(); for (String key : qParams.keySet()) { qMap.put(key.toLowerCase(), qParams.get(key)[0]); } if (!qMap.isEmpty()) { cParams.add(qMap); LOG.debug(getBeanName() + ": query params = " + qMap.toString()); } } } // a put with entity body arrived, so get the parameters from the // body and place them in the common bucket else if (putWithBodyPresent) { try { Map<String, String> putParams = null; // parseUrlEncoded will force keys to lower case putParams = Utils.parseUrlEncoded(request.getInputStream()); if (putParams != null && !putParams.isEmpty()) { cParams.add(putParams); } } catch (Exception exc) { LOG.error(getBeanName() + ": ERROR, caught this " + "exception while parsing urlencoded string: " + exc.toString()); LOG.error(getBeanName() + ": exception stack trace follows:"); dumpStackTrace(exc.getStackTrace()); if (exc.getCause() != null) { LOG.error(getBeanName() + ": Caused by " + exc.getCause().toString()); LOG.error(getBeanName() + ": causing exception stack trace follows:"); dumpStackTrace(exc.getCause().getStackTrace()); } response.sendError(SC_BAD_REQUEST, "urlencoded string parsing error: " + exc.getMessage()); return null; } } // ok, a json object arrived, so get parameters defined in that object // and place them in the common bucket else { // its a json object, so parse it to extract params from it try { List<Map<String, String>> jParams = null; // parseJson will ensure that all passed-in JSON objects have // the same set of identical keys jParams = Utils.parseJson(request.getInputStream()); if (jParams != null && !jParams.isEmpty()) { // if we also got query params then ensure they have the // same set of keys as the json params. why anyone would // ever do this is beyond me, but I'll leave it in for now if (!cParams.isEmpty()) { Map<String, String> cMap = cParams.get(0); Map<String, String> jMap = jParams.get(0); for (String key : cMap.keySet()) { if (jMap.get(key) == null) { String eStr = getBeanName() + ": ERROR, json " + "object key set does not match query " + "param key set"; LOG.error(eStr); response.sendError(SC_BAD_REQUEST, eStr); return null; } } // place the passed in query params in the jParams // bucket jParams.add(cMap); } // assign the jParams bucket to the common bucket cParams = jParams; } } catch (Exception exc) { LOG.error(getBeanName() + ": ERROR, caught this " + "exception while parsing json object: " + exc.toString()); LOG.error(getBeanName() + ": exception stack trace follows:"); dumpStackTrace(exc.getStackTrace()); if (exc.getCause() != null) { LOG.error(getBeanName() + ": Caused by " + exc.getCause().toString()); LOG.error(getBeanName() + ": causing exception stack trace follows:"); dumpStackTrace(exc.getCause().getStackTrace()); } response.sendError(SC_BAD_REQUEST, "json parsing error: " + exc.getMessage()); return null; } } // if trace is on, dump the params (if any) to the log if (LOG.isDebugEnabled()) { if (!cParams.isEmpty()) { for (int i = 0; i < cParams.size(); i++) { LOG.debug(getBeanName() + ": handleRequestInternal - received these params: " + cParams.get(i).toString()); } } else { LOG.debug(getBeanName() + ": handleRequestInternal - did not receive any params"); } } // ensure none of the params' values have been black listed if (!cParams.isEmpty() && getBlackList().length() > 0) { char[] bl = getBlackList().toCharArray(); for (int i = 0; i < cParams.size(); i++) { for (String value : cParams.get(i).values()) { if (Utils.isOnBlackList(value, bl)) { response.sendError(SC_BAD_REQUEST, "encountered black listed character in this param " + "value: " + value); LOG.error(getBeanName() + "handleRequestInternal - encountered black listed " + "character in this param value: " + value); return null; } } } } // find the proper SQL statement based on the incoming parameters' (if // any) keys SqlStmnt sqlStmnt = null; try { // getMatch will try and find a match, even if no params were // provided. // @formatter:off sqlStmnt = (cParams.isEmpty()) ? SqlStmnt.getMatch(sqlStmnts, null) : SqlStmnt.getMatch(sqlStmnts, cParams.get(0).keySet()); // @formatter:on if (sqlStmnt == null && !cParams.isEmpty()) { LOG.error(getBeanName() + ":ERROR, unable to find sql " + "statement with this incoming param set: " + cParams.toString()); response.sendError(SC_INTERNAL_SERVER_ERROR, "internal server error: mapping error"); return null; } else if (sqlStmnt == null) { LOG.warn(getBeanName() + ": warning, unable to find sql " + "statement on first pass, will use extra path info"); } else { LOG.debug(getBeanName() + ": handleRequestInternal - matching sql stmt = " + sqlStmnt.toString()); } } catch (Exception exc) { LOG.error(getBeanName() + ":ERROR, caught this exception " + "while mapping sql to params: " + exc.toString()); LOG.error(getBeanName() + ": exception stack trace follows:"); dumpStackTrace(exc.getStackTrace()); if (exc.getCause() != null) { LOG.error(getBeanName() + ": Caused by " + exc.getCause().toString()); LOG.error(getBeanName() + ": causing exception stack trace follows:"); dumpStackTrace(exc.getCause().getStackTrace()); } response.sendError(SC_INTERNAL_SERVER_ERROR, "mapping error"); return null; } // if getMatch could not find a match - perhaps input params were not // provided - then use the URI's 'extended path' information as an input // param if (sqlStmnt == null) { LOG.debug(getBeanName() + ": invoking getExtraPathInfo"); String[] xtraPathInfo = Utils.getExtraPathInfo(request.getPathInfo()); if (xtraPathInfo != null && xtraPathInfo.length >= 2) { LOG.debug(getBeanName() + ": extra path key:value = " + xtraPathInfo[0] + ":" + xtraPathInfo[1]); } else { LOG.error(getBeanName() + ":ERROR, getExtraPathInfo failed to find info"); response.sendError(SC_INTERNAL_SERVER_ERROR, "internal server error: mapping error"); return null; } // put the xtra path info in the common param bucket and try again cParams.clear(); Map<String, String> xMap = new HashMap<String, String>(); xMap.put(xtraPathInfo[0], xtraPathInfo[1]); cParams.add(xMap); // try again with the extra path info sqlStmnt = SqlStmnt.getMatch(sqlStmnts, xMap.keySet()); if (sqlStmnt == null) { LOG.error(getBeanName() + ":ERROR, unable to find sql " + "statement with this xtra path info: " + cParams.toString()); response.sendError(SC_NOT_FOUND, "internal server error: mapping error"); return null; } } // if we've gotten this far, we've gotten past the security gauntlet and // we have a SQL statement to work with. SqlResult sqlResult = null; try { // get the output stream OutputStream os = response.getOutputStream(); // FIRE IN THE DB HOLE :) if ((sqlResult = sqlStmnt.execute(cParams)) == null) { // execute will have logged the necessary debug/error info response.sendError(SC_INTERNAL_SERVER_ERROR); return null; } // execute went through ok, lets see how to respond switch (method) { case GET: // if a resultset was returned, then set the content type, // convert it to json, and write it out List<Map<String, Object>> listMap = sqlResult.getResultSet(); if (listMap != null) { // tell the client the content type response.setContentType(rspJsonContentType); String jsonOutput = Utils.generateJson(sqlResult.getResultSet()); LOG.trace(getBeanName() + ": returning this payload - " + jsonOutput); os.write(jsonOutput.getBytes()); // ensure that only the client can cache the data and tell // the client how long the data can remain active response.setHeader(CACHE_CNTRL_HDR, (getCacheControl() != null) ? getCacheControl() : DFLT_CACHE_CNTRL_STR); response.setHeader(PRAGMA_HDR, PRAGMA_NO_CACHE_STR); response.setDateHeader(EXPIRES_HDR, currentTime + (getExpires() * 1000)); } else { LOG.debug(getBeanName() + ": NOT returning json message"); } response.setStatus(SC_OK); break; case DELETE: // a DELETE should not send back an entity body response.setStatus(SC_NO_CONTENT); break; case PUT: /* * PUTs are idempotent; therefore, they must provide ALL the * properties that pertain to the resource/entity that they are * creating or updating. Updates cannot be partial updates; they * must be full updates. A PUT is issued by a client that knows * the identifier (in our case, primary key) of the * resource/entity. Therefore, we do not have to send back a * Location header in response to a PUT that has created a * resource. */ if (sqlStmnt.isInsert()) { response.setStatus(SC_CREATED); } else { response.setStatus(SC_OK); } break; case POST: /* * A POST is not idempotent; therefore, it can be used to * perform a 'partial' update, as well as a full create. When * creating a resource via POST, the client does not know the * primary key, and it assumes it will be auto-generated; * therefore, a Location header with auto-generated key must be * returned to client. */ if (sqlStmnt.isInsert()) { response.setStatus(SC_CREATED); // we need to return the new key, but only if it was not a // batch insert. the new key should be returned via the // location header // check if a key holder exists; if not, then table was not // configured with auto-generated key. String locationPath = request.getRequestURL().toString(); if (sqlResult.getKeyHolder() != null) { // key holder exists, check and see if a key is // present if (sqlResult.getKeyHolder().getKey() != null) { String id = sqlResult.getKeyHolder().getKey().toString(); LOG.debug(getBeanName() + ": getKey() returns " + id); locationPath += ("/" + id); LOG.debug(getBeanName() + ": locationPath = " + locationPath); response.setHeader(LOCATION_HDR, locationPath); } // no key, check for multiple keys // TODO: should we send back all keys? else if (sqlResult.getKeyHolder().getKeys() != null) { Map<String, Object> keyMap = sqlResult.getKeyHolder().getKeys(); LOG.debug(getBeanName() + ": getKeys() returns " + keyMap); } // maybe map of keys? // TODO: should we send back all keys? else if (sqlResult.getKeyHolder().getKeyList() != null) { for (Map<String, Object> map : sqlResult.getKeyHolder().getKeyList()) { LOG.debug(getBeanName() + ": Map from getKeyList(): " + map); } } } else { // if it was not an insert, then it was an update. LOG.debug(getBeanName() + ": key holder was not returned for the insert"); } } else { // it was not an insert, so just send back an OK for the // update response.setStatus(SC_OK); } break; default: response.setStatus(SC_OK); break; } } catch (JsonProcessingException exc) { LOG.error(getBeanName() + ":ERROR, caught this " + "JsonProcessingException while trying to gen json " + "message: " + exc.toString()); LOG.error(getBeanName() + ": exception stack trace follows:"); dumpStackTrace(exc.getStackTrace()); if (exc.getCause() != null) { LOG.error(getBeanName() + ": Caused by " + exc.getCause().toString()); LOG.error(getBeanName() + ": causing exception stack trace follows:"); dumpStackTrace(exc.getCause().getStackTrace()); } response.sendError(SC_INTERNAL_SERVER_ERROR, "parsing error"); return null; } catch (Exception exc) { LOG.error(getBeanName() + ":ERROR, caught this " + "Exception while trying to gen json " + "message: " + exc.toString()); LOG.error(getBeanName() + ": exception stack trace follows:"); dumpStackTrace(exc.getStackTrace()); if (exc.getCause() != null) { LOG.error(getBeanName() + ": Caused by " + exc.getCause().toString()); LOG.error(getBeanName() + ": causing exception stack trace follows:"); dumpStackTrace(exc.getCause().getStackTrace()); } response.sendError(SC_INTERNAL_SERVER_ERROR, "parsing error"); return null; } finally { if (sqlResult != null) { SqlResult.enqueue(sqlResult); } } // must return null, because we're not using views! return null; }