/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2006-2007 Sun Microsystems, Inc. */ package org.opends.dsml.protocol; import org.opends.server.core.DirectoryServer; import org.opends.server.tools.LDAPConnection; import org.opends.server.tools.LDAPConnectionOptions; import org.opends.server.util.Base64; import org.w3c.dom.Document; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.soap.*; import java.io.IOException; import java.io.OutputStream; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import java.util.concurrent.atomic.AtomicInteger; /** * This class provides the entry point for the DSML request. * It parses the SOAP request, calls the appropriate class * which performs the LDAP operation, and returns the response * as a DSML response. */ public class DSMLServlet extends HttpServlet { private static final String PKG_NAME = "org.opends.dsml.protocol"; private static final String PORT = "ldap.port"; private static final String HOST = "ldap.host"; private static final long serialVersionUID = -3748022009593442973L; private static final AtomicInteger nextMessageID = new AtomicInteger(1); private Unmarshaller unmarshaller; private Marshaller marshaller; private ObjectFactory objFactory; private MessageFactory messageFactory; private DocumentBuilder db; private String hostName; private Integer port; /** * This method will be called by the Servlet Container when * this servlet is being placed into service. * * @param config - the ServletConfig object that * contains configutation information for this servlet. * @throws ServletException If an error occurs during processing. */ public void init(ServletConfig config) throws ServletException { try { hostName = config.getServletContext().getInitParameter(HOST); port = new Integer(config.getServletContext().getInitParameter(PORT)); JAXBContext jaxbContext = JAXBContext.newInstance(PKG_NAME); unmarshaller = jaxbContext.createUnmarshaller(); marshaller = jaxbContext.createMarshaller(); marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapperImpl()); objFactory = new ObjectFactory(); messageFactory = MessageFactory.newInstance(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); db = dbf.newDocumentBuilder(); DirectoryServer.bootstrapClient(); } catch (Exception je) { je.printStackTrace(); throw new ServletException(je.getMessage()); } } /** * The HTTP GET operation. * * @param req Information about the request received from the client. * @param res Information about the response to send to the client. * @throws ServletException If an error occurs during servlet processing. * @throws IOException If an error occurs while interacting with the client. */ public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { super.doGet(req, res); } /** * The HTTP POST operation. This servlet expects a SOAP message * with a DSML request payload. * * @param req Information about the request received from the client. * @param res Information about the response to send to the client. * @throws ServletException If an error occurs during servlet processing. * @throws IOException If an error occurs while interacting with the client. */ public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { SOAPMessage reply; LDAPConnectionOptions connOptions = new LDAPConnectionOptions(); LDAPConnection connection = null; try { MimeHeaders mimeHeaders = new MimeHeaders(); Enumeration en = req.getHeaderNames(); String bindDN = ""; String bindPassword = ""; while (en.hasMoreElements()) { String headerName = (String) en.nextElement(); String headerVal = req.getHeader(headerName); if (headerName.equalsIgnoreCase("authorization")) { if (headerVal.startsWith("Basic ")) { String authorization = headerVal.substring(6).trim(); // Decode and parse the authorization credentials String unencoded = new String(Base64.decode(authorization)); int colon = unencoded.indexOf(':'); if (colon < 0) continue; bindDN = unencoded.substring(0, colon).trim(); bindPassword = unencoded.substring(colon + 1); } } StringTokenizer tk = new StringTokenizer(headerVal, ","); while (tk.hasMoreTokens()) { mimeHeaders.addHeader(headerName, tk.nextToken().trim()); } } SOAPMessage message = messageFactory.createMessage(mimeHeaders, req.getInputStream()); message.writeTo(System.out); Document doc = db.newDocument(); SOAPBody body = message.getSOAPBody(); Iterator it = body.getChildElements(); while (it.hasNext()) { Object obj = it.next(); if (!(obj instanceof SOAPElement)) { continue; } SOAPElement se = (SOAPElement) obj; JAXBElement batchRequestElement = unmarshaller.unmarshal(se, BatchRequest.class); BatchRequest batchRequest = batchRequestElement.getValue(); BatchResponse batchResponse = objFactory.createBatchResponse(); List> batchResponses = batchResponse.getBatchResponses(); List list = batchRequest.getBatchRequests(); for (DsmlMessage nextElement : list) { if (nextElement instanceof SearchRequest) { // Process the search request. connection = new LDAPConnection(hostName, port, connOptions); connection.connectToHost(bindDN, bindPassword); SearchRequest sr = (SearchRequest) nextElement; DSMLSearchOperation ds = new DSMLSearchOperation(connection); SearchResponse searchResponse = ds.doSearch(objFactory, sr); JAXBElement searchResponseEl = objFactory.createBatchResponseSearchResponse(searchResponse); batchResponses.add(searchResponseEl); } else if (nextElement instanceof AddRequest) { // Process the add request. connection = new LDAPConnection(hostName, port, connOptions); connection.connectToHost(bindDN, bindPassword); AddRequest ar = (AddRequest) nextElement; DSMLAddOperation addOp = new DSMLAddOperation(connection); LDAPResult addResponse = addOp.doOperation(objFactory, ar); JAXBElement addResponseEl = objFactory.createBatchResponseAddResponse(addResponse); batchResponses.add(addResponseEl); } else if (nextElement instanceof AbandonRequest) { // Process the abandon request. connection = new LDAPConnection(hostName, port, connOptions); connection.connectToHost(bindDN, bindPassword); AbandonRequest ar = (AbandonRequest) nextElement; DSMLAbandonOperation ao = new DSMLAbandonOperation(connection); LDAPResult abandonResponse = ao.doOperation(objFactory, ar); } else if (nextElement instanceof ExtendedRequest) { // Process the extended request. connection = new LDAPConnection(hostName, port, connOptions); connection.connectToHost(bindDN, bindPassword); ExtendedRequest er = (ExtendedRequest) nextElement; DSMLExtendedOperation eo = new DSMLExtendedOperation(connection); ExtendedResponse extendedResponse = eo.doOperation(objFactory, er); JAXBElement extendedResponseEl = objFactory.createBatchResponseExtendedResponse(extendedResponse); batchResponses.add(extendedResponseEl); } else if (nextElement instanceof DelRequest) { // Process the delete request. connection = new LDAPConnection(hostName, port, connOptions); connection.connectToHost(bindDN, bindPassword); DelRequest dr = (DelRequest) nextElement; DSMLDeleteOperation delOp = new DSMLDeleteOperation(connection); LDAPResult delResponse = delOp.doOperation(objFactory, dr); JAXBElement delResponseEl = objFactory.createBatchResponseDelResponse(delResponse); batchResponses.add(delResponseEl); } else if (nextElement instanceof CompareRequest) { // Process the compare request. connection = new LDAPConnection(hostName, port, connOptions); connection.connectToHost(bindDN, bindPassword); CompareRequest cr = (CompareRequest) nextElement; DSMLCompareOperation compareOp = new DSMLCompareOperation(connection); LDAPResult compareResponse = compareOp.doOperation(objFactory, cr); JAXBElement compareResponseEl = objFactory.createBatchResponseCompareResponse(compareResponse); batchResponses.add(compareResponseEl); } else if (nextElement instanceof ModifyDNRequest) { // Process the Modify DN request. connection = new LDAPConnection(hostName, port, connOptions); connection.connectToHost(bindDN, bindPassword); ModifyDNRequest mr = (ModifyDNRequest) nextElement; DSMLModifyDNOperation moddnOp = new DSMLModifyDNOperation(connection); LDAPResult moddnResponse = moddnOp.doOperation(objFactory, mr); JAXBElement moddnResponseEl = objFactory.createBatchResponseModDNResponse(moddnResponse); batchResponses.add(moddnResponseEl); } else if (nextElement instanceof ModifyRequest) { // Process the Modify request. connection = new LDAPConnection(hostName, port, connOptions); connection.connectToHost(bindDN, bindPassword); ModifyRequest modr = (ModifyRequest) nextElement; DSMLModifyOperation modOp = new DSMLModifyOperation(connection); LDAPResult modResponse = modOp.doOperation(objFactory, modr); JAXBElement modResponseEl = objFactory.createBatchResponseModifyResponse(modResponse); batchResponses.add(modResponseEl); } else { String msg = "Invalid DSML request:" + nextElement; throw new IOException(msg); } } JAXBElement batchResponseElement = objFactory.createBatchResponse(batchResponse); marshaller.marshal(batchResponseElement, System.out); marshaller.marshal(batchResponseElement, doc); } // Send the DSML response back to the client. reply = messageFactory.createMessage(); sendResponse(doc, false, reply, res, null); } catch (Exception se) { se.printStackTrace(); // send SOAP fault try { reply = messageFactory.createMessage(); sendResponse(null, true, reply, res, se); } catch (Exception e) { } } finally { if (connection != null) { connection.close(nextMessageID); } } } /** * Send a response back to the client. This could be either a SOAP fault * or a correct DSML response. * * @param doc The document to include in the response. * @param error Indicates whether an error occurred. * @param reply The reply to send to the client. * @param res Information about the HTTP response to the client. * @param e Information about any exception that was thrown. * @throws IOException If an error occurs while interacting with the client. * @throws SOAPException If an encoding or decoding error occurs. */ private void sendResponse(Document doc, boolean error, SOAPMessage reply, HttpServletResponse res, Exception e) throws IOException, SOAPException { SOAPHeader header = reply.getSOAPHeader(); header.detachNode(); SOAPBody replyBody = reply.getSOAPBody(); res.setHeader("Content-Type", "text/xml"); if (error) { SOAPFault fault = replyBody.addFault(); Name faultName = SOAPFactory.newInstance().createName("Server", "", SOAPConstants.URI_NS_SOAP_ENVELOPE); fault.setFaultCode(faultName); fault.setFaultString("Server Error: " + e.getMessage()); // FIXME - Set correct fault actor fault.setFaultActor("http://localhost:8080"); } else { SOAPElement bodyElement = replyBody.addDocument(doc); } reply.saveChanges(); OutputStream os = res.getOutputStream(); reply.writeTo(os); os.flush(); } /** * Retrieves a message ID that may be used for the next LDAP message sent to * the Directory Server. * * @return A message ID that may be used for the next LDAP message sent to * the Directory Server. */ public static int nextMessageID() { int nextID = nextMessageID.getAndIncrement(); if (nextID == Integer.MAX_VALUE) { nextMessageID.set(1); } return nextID; } }