/*
|
* 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 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 <CODE>ServletConfig</CODE> 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<BatchRequest> batchRequestElement =
|
unmarshaller.unmarshal(se, BatchRequest.class);
|
BatchRequest batchRequest = batchRequestElement.getValue();
|
BatchResponse batchResponse = objFactory.createBatchResponse();
|
|
List<JAXBElement<?>> batchResponses = batchResponse.getBatchResponses();
|
List<DsmlMessage> 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<SearchResponse> 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<LDAPResult> 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<ExtendedResponse> 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<LDAPResult> 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<LDAPResult> 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<LDAPResult> 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<LDAPResult> modResponseEl =
|
objFactory.createBatchResponseModifyResponse(modResponse);
|
batchResponses.add(modResponseEl);
|
} else {
|
String msg = "Invalid DSML request:" + nextElement;
|
throw new IOException(msg);
|
}
|
}
|
|
JAXBElement<BatchResponse> 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(new AtomicInteger(1));
|
}
|
}
|
}
|
|
/**
|
* 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();
|
}
|
}
|