com.novell.ldap.util.DOMWriter Maven / Gradle / Ivy
/* **************************************************************************
* $OpenLDAP: pkg/jldap/com/novell/ldap/util/DOMWriter.java,v 1.14 2003/08/26 11:00:42 kkanil Exp $
*
* Copyright (C) 2002 - 2003 Novell, Inc. All Rights Reserved.
*
* THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
* TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
* TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
* AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
* IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
* OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
* PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
* THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
*/
package com.novell.ldap.util;
import com.novell.ldap.*;
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Iterator;
/**
* Writes LDAPMessages into a DOM structure as DSML batch requests and
* batch responses.
*
* @see DOMReader
* @see DSMLWriter
* @see LDAPMessage
*/
public class DOMWriter implements LDAPWriter
{
/* Document object used to create DOM nodes */
private Document doc;
/* Root node of either batchRequest or batchResponse.*/
private Element root;
private int state = NEW_BATCH;
private static final int NEW_BATCH = 0;
private static final int REQUEST_BATCH = 1;
private static final int RESPONSE_BATCH = 2;
private static final int SEARCH_RESPONSE = 3;
private Element searchNode;
/**
* Initializes the DOMWriter.
* @throws ParserConfigurationException
* Occurs if a parser could not be found or is misconfigured.
*/
public DOMWriter() throws ParserConfigurationException
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.newDocument();
return;
}
/**
* Retrieves the version of DSML being written, currently only 2.0 is
* supported.
* @return Version of DSML being used.
*/
public String getVersion()
{
return "2.0";
}
/**
* Used to identify if the root node is a batchRequest or not.
* @return true if the root node of the DOM is a batchRequest and false
* otherwise.
*/
public boolean isRequest()
{
return root.getNodeName().equals("batchRequest");
}
/**
* This method is not implemented and is silently ignored.
*/
public void writeComments(String comments) throws IOException
{
return;
}
/**
* Writes the LDAPMessage into the DOMStructure.
*
* @param message LDAPMessage to write
* @throws LDAPLocalException Occurs when a message is written out of
* sequence, i.e. a response is written into a batchRequest.
*/
public void writeMessage(LDAPMessage message)
throws LDAPLocalException, IOException
{
checkState(message);
if (message.getType() == LDAPMessage.SEARCH_RESPONSE ||
message.getType() == LDAPMessage.SEARCH_RESULT ||
message.getType() == LDAPMessage.SEARCH_RESULT_REFERENCE)
{
searchNode.appendChild(message2Element(message));
} else {
root.appendChild( message2Element(message) );
}
if (message.getType()== LDAPMessage.SEARCH_RESULT){
state = RESPONSE_BATCH;
searchNode = null;
}
return;
}
/**
* Write an LDAP record into LDIF file as LDAPContent data.
* An LDAPEntry is written as a DSML SearchResultEntry record.
*
* You are not allowed to mix request data and content data
*
* @param entry LDAPEntry object
*
* @throws LDAPLocalException if data and content are mixed.
*
* @throws LDAPLocalException if an I/O error occurs.
*
* @see com.novell.ldap.LDAPEntry
*/
public void writeEntry( LDAPEntry entry)
throws LDAPLocalException
{
checkState( entry);
myWriteEntry( entry, null);
return;
}
// Javadoc from interface
/**
* Write an LDAP record into LDIF file as LDAPContent data.
* An LDAPEntry is written as a DSML SearchResultEntry record.
*
* You are not allowed to mix request data and content data
*
* @param entry LDAPEntry object
*
* @param controls Controls that were returned with this entry
*
* @throws LDAPLocalException if data and content are mixed.
*
* @see com.novell.ldap.LDAPEntry
*/
public void writeEntry( LDAPEntry entry, LDAPControl[] controls)
throws LDAPLocalException
{
checkState( entry);
myWriteEntry( entry, controls);
return;
}
/**
* Write an LDAP record into LDIF file as LDAPContent data.
* An LDAPEntry is written as a DSML SearchResultEntry record.
*
* You are not allowed to mix request data and content data
*
* @param entry object
*
* @param controls Controls that were returned with this entry
*
* @param requestID the String that associates this response with the request
*
* @throws LDAPLocalException if data and content are mixed.
*
* @see com.novell.ldap.LDAPEntry
*/
public void writeEntry( LDAPEntry entry,
LDAPControl[] controls,
String requestID)
throws LDAPLocalException
{
checkState( entry);
Element e = myWriteEntry( entry, controls);
if( (requestID != null) && (requestID.length() != 0)) {
e.setAttribute("requestID", requestID);
}
return;
}
// Javadoc from interface
private Element myWriteEntry( LDAPEntry entry,
LDAPControl[] controls )
{
Element e = doc.createElement("searchResultEntry");
e.setAttribute("dn", entry.getDN());
LDAPAttributeSet set = entry.getAttributeSet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
LDAPAttribute attr = (LDAPAttribute)iterator.next();
Element attribute = doc.createElement("attr");
writeAttribute(attribute, attr);
e.appendChild(attribute);
}
if (controls != null) {
writeControls(e, controls);
}
searchNode.appendChild( e);
return e;
}
private void writeAttribute(Element attribute, LDAPAttribute attr)
{
attribute.setAttribute("name", attr.getName());
String values[] = attr.getStringValueArray();
byte bytevalues[][] = attr.getByteValueArray();
for(int i=0; i0){
e.setAttribute("newSuperior", temp);
}
break;
}
case LDAPMessage.COMPARE_REQUEST: {
e = doc.createElement("compareRequest");
LDAPCompareRequest comp = (LDAPCompareRequest) message;
e.setAttribute("dn", comp.getDN());
Element assertion = doc.createElement("assertion");
assertion.setAttribute("name", comp.getAttributeDescription());
e.appendChild(assertion);
Element value = doc.createElement("value");
assertion.appendChild(value);
byte[] compareValue = comp.getAssertionValue();
if (Base64.isValidUTF8(compareValue, false)){
try {
value.appendChild(doc.createTextNode(
new String(compareValue, "UTF-8")));
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("UTF-8 not supported by JVM:"
+ uee);
}
} else {
value.setNodeValue(Base64.encode(compareValue));
//value.appendChild(doc.createTextNode(
// Base64.encode(compareValue)));
}
break;
}
case LDAPMessage.ABANDON_REQUEST:
e = doc.createElement("abandonRequest");
e.setAttribute("abandonID",findRequestID(message));
break;
case LDAPMessage.EXTENDED_REQUEST:{
e = doc.createElement("extendedRequest");
LDAPExtendedOperation xreq =
((LDAPExtendedRequest)message).getExtendedOperation();
Element reqName = doc.createElement("requestName");
reqName.appendChild(doc.createTextNode(xreq.getID()));
e.appendChild(reqName);
byte value[] = xreq.getValue();
if (value != null){
Element reqValue = doc.createElement("requestValue");
reqValue.setAttribute("xsi:type", "xsd:base64Binary");
reqValue.appendChild(
doc.createTextNode(Base64.encode(xreq.getValue())));
e.appendChild(reqValue);
}
break;
}
//Responses:
case LDAPMessage.SEARCH_RESPONSE:
e = myWriteEntry( ((LDAPSearchResult)message).getEntry(),
message.getControls());
break;
case LDAPMessage.SEARCH_RESULT_REFERENCE:
e = doc.createElement("searchResultReference");
String[] refs = ((LDAPSearchResultReference)
message).getReferrals();
for(int i=0; i< refs.length; i++){
Element ref = doc.createElement("ref");
Text value = doc.createTextNode(refs[i]);
ref.appendChild(value);
e.appendChild(ref);
}
break;
case LDAPMessage.SEARCH_RESULT: //final search done message
e = doc.createElement("searchResultDone");
writeResult(e, (LDAPResponse)message);
break;
case LDAPMessage.MODIFY_RESPONSE:
e = doc.createElement("modifyResponse");
writeResult(e, (LDAPResponse)message);
break;
case LDAPMessage.ADD_RESPONSE:
e = doc.createElement("addResponse");
writeResult(e, (LDAPResponse)message);
break;
case LDAPMessage.DEL_RESPONSE:
e = doc.createElement("delResponse");
writeResult(e, (LDAPResponse)message);
break;
case LDAPMessage.MODIFY_RDN_RESPONSE:
e = doc.createElement("modDNResponse");
writeResult(e, (LDAPResponse)message);
break;
case LDAPMessage.COMPARE_RESPONSE:
e = doc.createElement("compareResponse");
writeResult(e, (LDAPResponse)message);
break;
case LDAPMessage.EXTENDED_RESPONSE:
LDAPExtendedResponse xResp = (LDAPExtendedResponse) message;
e = doc.createElement("extendedResponse");
writeResult(e, (LDAPResponse)message);
Element resp = doc.createElement("responseName");
Text text = doc.createTextNode(xResp.getID());
resp.appendChild(text);
e.appendChild(resp);
byte[] value = xResp.getValue();
if (value != null){
resp = doc.createElement("response");
resp.setAttribute("xsi:type", "base64Binary");
resp.appendChild(doc.createTextNode(Base64.encode(value)));
e.appendChild(resp);
}
break;
}
//if valid tag && write requestIDs is set.
String id = findRequestID(message);
if( (id != null) && (id.length() != 0)) {
e.setAttribute("requestID", id);
}
return e;
}
/**
* Common code for =, >=, <=, and ~=.
*/
private void writeMatching( Element newElement, Iterator itr)
{
newElement.setAttribute("name", (String)itr.next());
Element valueNode = doc.createElement("value");
newElement.appendChild(valueNode);
byte[] value = (byte[])itr.next();
String text = byte2String(value);
valueNode.appendChild(doc.createTextNode(text));
return;
}
/**
*
* @param e element to add a DSML search filter component (filter or a
* AND, OR or NOT )
* @param itr
*/
private void writeFilter(Element e, Iterator itr)
{
int op=-1;
Element newElement = null;
while (itr.hasNext()){
Object filterpart = itr.next();
if (filterpart instanceof Integer) {
op = ((Integer)filterpart).intValue();
switch (op){
case LDAPSearchRequest.AND:
newElement = doc.createElement("and");
break;
case LDAPSearchRequest.OR:
newElement = doc.createElement("or");
break;
case LDAPSearchRequest.NOT:
newElement = doc.createElement("not");
break;
case LDAPSearchRequest.EQUALITY_MATCH:
newElement = doc.createElement("equalityMatch");
writeMatching( newElement, itr);
break;
case LDAPSearchRequest.GREATER_OR_EQUAL:
newElement = doc.createElement("greaterOrEqual");
writeMatching( newElement, itr);
break;
case LDAPSearchRequest.LESS_OR_EQUAL:
newElement = doc.createElement("lessOrEqual");
writeMatching( newElement, itr);
break;
case LDAPSearchRequest.APPROX_MATCH:
newElement = doc.createElement("approxMatch");
writeMatching( newElement, itr);
break;
case LDAPSearchRequest.PRESENT:
newElement = doc.createElement("present");
newElement.setAttribute("name", (String)itr.next());
break;
case LDAPSearchRequest.EXTENSIBLE_MATCH:{
newElement = doc.createElement("extensibleMatch");
newElement.setAttribute("matchingRule",
(String)itr.next());
newElement.setAttribute("name",
(String)itr.next());
Element value = doc.createElement("value");
value.appendChild(doc.createTextNode((String)itr.next()));
newElement.appendChild(value);
//TODO DN matching
break;
}
case LDAPSearchRequest.SUBSTRINGS:{
newElement = doc.createElement("substrings");
newElement.setAttribute("name", (String)itr.next());
//loop through all substrings
while (itr.hasNext()){
op = ((Integer)itr.next()).intValue();
Element nextSubString = null;
switch(op){
case LDAPSearchRequest.INITIAL:
nextSubString = doc.createElement("initial");
break;
case LDAPSearchRequest.ANY:
nextSubString = doc.createElement("any");
break;
case LDAPSearchRequest.FINAL:
nextSubString = doc.createElement("final");
break;
}
String value = (String)itr.next();
nextSubString.appendChild(
doc.createTextNode(value));
newElement.appendChild(nextSubString);
}
break;
}
}
} else if (filterpart instanceof Iterator){
//This case will occur after AND, OR and NOT
writeFilter(newElement, (Iterator)filterpart);
}
}
e.appendChild(newElement);
return;
}
private String byte2String(byte[] value)
{
String text = null;
if (Base64.isValidUTF8(value, false)){
try {
text = new String(value, "UTF-8");
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException(
"UTF-8 not supported by JVM" + uee);
}
} else {
text = Base64.encode(value);
}
return text;
}
/**
* Writes the specified LDAPResponse into the specified element.
* Possible information written to the element is a Result code with a
* description, a server response, and a matched DN. Controls and referrals
* should also be written - and will be in the future.
* @param e Element to insert response info into.
* @param response Response message to write.
*/
private void writeResult(Element e, LDAPResponse response)
{
/* controls: */
LDAPControl controls[] = response.getControls();
if (controls != null){
writeControls(e, controls);
}
/* Referral */
String urls[] = response.getReferrals();
if (urls != null){
for (int i=0; i 0){
Element err = doc.createElement("errorMessage");
Text errorMessage = doc.createTextNode(temp);
err.appendChild(errorMessage);
e.appendChild(err);
}
/* MatchedDN */
temp = response.getMatchedDN();
if (temp != null && temp.length() > 0){
e.setAttribute("matchedDN", temp);
}
}
private void writeControls(Element e, LDAPControl[] controls)
{
for (int i=0; i< controls.length; i++){
Element el = doc.createElement("control");
el.setAttribute("NumericOID", controls[i].getID());
el.setAttribute("criticality", ""+controls[i].isCritical());
byte byteValue[] = controls[i].getValue();
if (byteValue!= null){
Element value = doc.createElement("controlValue");
value.setAttribute("xsi:type", "base64Binary");
Text text = doc.createTextNode( Base64.encode(byteValue));
value.appendChild(text);
el.appendChild(value);
}
e.appendChild( el );
}
}
/**
* Any Exception can be written in DSML with this method, via the
* tag. In general LDAPExceptions should be written to the
* errorResponse tag and other exception in a SOAP Fault.
* @param e LDAPException to be written in DSML.
*/
public void writeError(Exception e) throws IOException
{
//check if we are in a response, if not set the state and write DSML tag
Element error = doc.createElement("errorResponse");
if (e instanceof LDAPException){
switch (((LDAPException)e).getResultCode()){
case LDAPException.DECODING_ERROR:
error.setAttribute("type", "malformedRequest");
break;
case LDAPException.LOCAL_ERROR:
error.setAttribute("type", "gatewayInternalError");
break;
case LDAPException.INVALID_CREDENTIALS:
error.setAttribute("type", "authenticationFailed");
break;
default:
error.setAttribute("type", "other");
}
} else {
error.setAttribute("type", "other");
}
Element message = doc.createElement("message");
Text messageValue = doc.createTextNode(e.toString());
message.appendChild(messageValue);
error.appendChild(messageValue);
root.appendChild(error);
return;
}
/**
* Tests the current state with a new message that is either a response or
* request.
* If the state is NEW_BATCH, check_state will create the
* appropriate batch element and set it as root. If the state is
* SEARCH_RESPONSE then the new message is verified to be a search result,
* search response or search reference.
*
* @param message Message to be written
* @throws LDAPLocalException
*/
private void checkState(LDAPMessage message)
throws LDAPLocalException
{
boolean isResponse = !message.isRequest();
if (state == NEW_BATCH) {
if (isResponse) {
root = doc.createElement("batchResponse");
root.setAttribute("xmlns", "urn:oasis:names:tc:DSML:2:0:core");
root.setAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema");
root.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
state = RESPONSE_BATCH;
}
else{
root = doc.createElement("batchRequest");
root.setAttribute("xmlns", "urn:oasis:names:tc:DSML:2:0:core");
root.setAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema");
root.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
state = REQUEST_BATCH;
}
}
if (state != SEARCH_RESPONSE &&
(message.getType()==LDAPMessage.SEARCH_RESPONSE ||
message.getType()==LDAPMessage.SEARCH_RESULT ||
message.getType()==LDAPMessage.SEARCH_RESULT_REFERENCE))
{
searchNode = doc.createElement("searchResponse");
searchNode.setAttribute("requestID", ""+ findRequestID(message));
root.appendChild(searchNode);
state = SEARCH_RESPONSE;
}
else if ((state == REQUEST_BATCH) && (isResponse)) {
throw new LDAPLocalException(
"Attempted insertion of a response message in a request batch",
LDAPException.ENCODING_ERROR);
} else if ((state == RESPONSE_BATCH || state == SEARCH_RESPONSE)
&& (!isResponse))
{
throw new LDAPLocalException(
"Attempted insertion of a request message in a response batch",
LDAPException.ENCODING_ERROR);
} else if ( state == SEARCH_RESPONSE &&
(message.getType()!=LDAPMessage.SEARCH_RESPONSE &&
message.getType()!=LDAPMessage.SEARCH_RESULT &&
message.getType()!=LDAPMessage.SEARCH_RESULT_REFERENCE))
{
throw new LDAPLocalException(
"Attempted insertion of a non-search result into a searchResponse",
LDAPException.ENCODING_ERROR);
}
return;
}
/**
* Tests the current state with a new message that is either a response or
* request.
*
If the state is NEW_BATCH, check_state will create the
* appropriate batch element and set it as root. If the state is
* SEARCH_RESPONSE then the new message is verified to be a search result,
* search response or search reference.
*
* @param entry Message to be written
* @throws LDAPLocalException
*/
private void checkState(LDAPEntry entry)
throws LDAPLocalException
{
boolean isResponse = true;
if (state == NEW_BATCH) {
root = doc.createElement("batchResponse");
root.setAttribute("xmlns", "urn:oasis:names:tc:DSML:2:0:core");
root.setAttribute("xmlns:xsd","http://www.w3.org/2001/XMLSchema");
root.setAttribute("xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");
state = RESPONSE_BATCH;
}
if (state != SEARCH_RESPONSE) {
searchNode = doc.createElement("searchResponse");
root.appendChild(searchNode);
state = SEARCH_RESPONSE;
}
else if ((state == REQUEST_BATCH) && (isResponse)) {
throw new LDAPLocalException(
"Attempted insertion of a response message in a request batch",
LDAPException.ENCODING_ERROR);
}
return;
}
static String findRequestID(LDAPMessage message)
{
String tag = message.getTag();
if (tag == null){
tag = message.getMessageID() + "";
}
return tag;
}
/**
* Retrieves the batchRequest or batchResponse element populated with this
* writer.
* @return A batchRequest or batchResponse element.
*/
public Element getRootElement()
{
return root;
}
public void finish() throws IOException
{
doc.appendChild(root);
return;
}
}