pjs/directory/java-sdk/ldapsp/com/netscape/jndi/ldap/ObjectMapper.java

494 строки
18 KiB
Java

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1999 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
package com.netscape.jndi.ldap;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.spi.*;
import netscape.ldap.*;
import netscape.ldap.util.*;
import java.io.*;
import java.util.*;
import com.netscape.jndi.ldap.common.Debug;
/**
* Class used to map Java objects to ldap entries back and forth
*/
public class ObjectMapper {
/**
* Schema object classes for mapping java objects to ldap entries
*/
static final String OC_JAVAOBJECT = "javaObject"; // abstract oc
static final String OC_SERIALOBJ = "javaSerializedObject";//aux oc
static final String OC_MARSHALOBJ = "javaMarshalledObject";//aux oc
static final String OC_REFERENCE = "javaNamingReference"; //aux oc
//static final String OC_REMOTEOBJ = "javaRemoteObject"; //aux oc, depricated
static final String OC_CONTAINER = "javaContainer"; //structural oc
/**
* Schema attributes for mapping java objects to ldap entries
*/
static final String AT_CLASSNAME = "javaClassName"; //required
static final String AT_CLASSNAMES = "javaClassNames"; //optional
static final String AT_DESCRIPTION = "description"; //optional
static final String AT_JAVADOC = "javaDoc"; //optional
static final String AT_CODEBASE = "javaCodeBase"; //optional
static final String AT_SERIALDATA = "javaSerializedData"; //required
static final String AT_REFADDR = "javaReferenceAddress"; //optional
static final String AT_OBJFACTORY = "javaFactory"; //optional
//static final String AT_REMOTELOC = "javaRemoteLocation"; //required, depricated
//Default Object class for NameClassPair
static final String DEFAULT_OBJCLASS = "javax.naming.directory.DirContext";
static Object entryToObject(LDAPEntry entry, LdapContextImpl ctx) throws NamingException {
Object obj = entryToObject(entry);
if (obj == null) {
obj = new LdapContextImpl(entry.getDN(), ctx);
}
// SP is required to contact the DirectoryManager first to obtain an object
try {
String relName = LdapNameParser.getRelativeName(ctx.m_ctxDN, entry.getDN());
Name nameObj = LdapNameParser.getParser().parse(relName);
Attributes attrs = new AttributesImpl(entry.getAttributeSet(),
ctx.m_ctxEnv.getUserDefBinaryAttrs());
obj = DirectoryManager.getObjectInstance(obj, nameObj, ctx, ctx.getEnvironment(), attrs);
}
catch (Exception ex) {
if (ex instanceof NamingException) {
throw (NamingException)ex;
}
NamingException nameEx = new NamingException("NamingManager.getObjectInstance failed");
nameEx.setRootCause(ex);
throw nameEx;
}
return obj;
}
/**
* Convert a Ldap entry into a java object
*/
static Object entryToObject(LDAPEntry entry) throws NamingException {
try {
LDAPAttributeSet attrs = entry.getAttributeSet();
// TODO javaCodeBase to be processed
LDAPAttribute attr = null;
if ((attr = attrs.getAttribute(AT_SERIALDATA)) != null) {
byte[] serdata = (byte[])attr.getByteValues().nextElement();
return deserializeObject(serdata);
}
// Reference
else if ((attr = attrs.getAttribute(AT_REFADDR)) != null) {
return decodeRefObj(attrs);
}
return null;
}
catch (Exception ex) {
if (ex instanceof NamingException) {
throw (NamingException)ex;
}
NamingException nameEx = new NamingException("NamingManager.getStateToStore failed");
nameEx.setRootCause(ex);
throw nameEx;
}
}
/**
* Get the className for NameClassPair from an entry
*/
static String getClassName(LDAPEntry entry) {
LDAPAttributeSet attrs = entry.getAttributeSet();
LDAPAttribute attrClass = attrs.getAttribute(AT_CLASSNAME);
if (attrClass != null) {
String className = (String)attrClass.getStringValues().nextElement();
return className;
}
return DEFAULT_OBJCLASS;
}
/**
* Convert a java object with an optional set of attributes into a LDAP entry
*/
static LDAPAttributeSet objectToAttrSet(Object obj, String name, LdapContextImpl ctx, Attributes attrs) throws NamingException {
// SP is required to contact the DirectoryManager first to obtain a state object
try {
Name nameObj = LdapNameParser.getParser().parse(name);
DirStateFactory.Result stb = DirectoryManager.getStateToBind(obj, nameObj, ctx, ctx.getEnvironment(), attrs);
obj = stb.getObject();
attrs = stb.getAttributes();
}
catch (Exception ex) {
if (ex instanceof NamingException) {
throw (NamingException)ex;
}
NamingException nameEx = new NamingException("NamingManager.getStateToStore failed");
nameEx.setRootCause(ex);
throw nameEx;
}
if (obj == null) {
return AttributesImpl.jndiAttrsToLdapAttrSet(attrs);
}
else if (attrs == null) {
attrs = new BasicAttributes(/*ignoreCase=*/true);
}
Attribute objectclass = attrs.get("objectClass");
if (objectclass == null) {
objectclass = attrs.get("objectclass"); // try lower case
}
if (objectclass == null) {
objectclass = new BasicAttribute("objectClass", "top");
objectclass.add(OC_CONTAINER);
attrs.put(objectclass);
}
// Root object class
objectclass.add(OC_JAVAOBJECT);
if (obj instanceof Reference) {
objectclass.add(OC_REFERENCE);
char delimChar = ctx.m_ctxEnv.getRefSeparator();
attrs = encodeRefObj(delimChar, (Reference)obj, attrs);
}
else if (obj instanceof Referenceable) {
objectclass.add(OC_REFERENCE);
char delimChar = ctx.m_ctxEnv.getRefSeparator();
attrs = encodeRefObj(delimChar, ((Referenceable)obj).getReference(), attrs);
}
else if (obj instanceof Serializable) {
if (objectclass.contains(OC_MARSHALOBJ)) {
; // do nothing
}
else if (objectclass.contains(OC_MARSHALOBJ.toLowerCase())) {
; // do nothing
}
else {
objectclass.add(OC_SERIALOBJ);
}
attrs = encodeSerialObj((Serializable)obj , attrs);
}
else if (obj instanceof DirContext) {
attrs = encodeDirCtxObj((DirContext)obj , attrs);
}
else {
throw new NamingException("Can not bind object of type " +
obj.getClass().getName());
}
return AttributesImpl.jndiAttrsToLdapAttrSet(attrs);
}
/**
* Deserialized object, create object from a stream of bytes
*/
private static Object deserializeObject(byte[] byteBuf) throws NamingException {
ByteArrayInputStream bis = null;
ObjectInputStream objis = null;
try {
bis = new ByteArrayInputStream(byteBuf);
objis = new ObjectInputStream(bis);
return objis.readObject();
}
catch(Exception ex) {
NamingException nameEx = new NamingException("Failed to deserialize object");
nameEx.setRootCause(ex);
throw nameEx;
}
finally {
try {
if (objis != null) {
objis.close();
}
if (bis != null) {
bis.close();
}
}
catch (Exception ex) {}
}
}
/**
* Serialize object, convert object to a stream of bytes
*/
private static byte[] serializeObject(Object obj) throws NamingException {
ByteArrayOutputStream bos = null;
ObjectOutputStream objos = null;
try {
bos = new ByteArrayOutputStream();
objos = new ObjectOutputStream(bos);
objos.writeObject(obj);
objos.flush();
return bos.toByteArray();
}
catch(Exception ex) {
NamingException nameEx = new NamingException("Failed to serialize object");
nameEx.setRootCause(ex);
throw nameEx;
}
finally {
try {
if (objos != null) {
objos.close();
}
if (bos != null) {
bos.close();
}
}
catch (Exception ex) {}
}
}
/**
* Decode Jndi Reference Object
*/
private static Reference decodeRefObj(LDAPAttributeSet attrs) throws NamingException {
try {
LDAPAttribute attr = null;
String className=null, factory = null, factoryLoc = null;
if ((attr = attrs.getAttribute(AT_CLASSNAME)) == null ) {
throw new NamingException("Bad Reference encoding, no attribute " + AT_CLASSNAME);
}
className = (String)attr.getStringValues().nextElement();
if ((attr = attrs.getAttribute(AT_OBJFACTORY)) != null ) {
factory = (String)attr.getStringValues().nextElement();
}
if ((attr = attrs.getAttribute(AT_CODEBASE)) != null ) {
factoryLoc = (String)attr.getStringValues().nextElement();
}
Reference ref = new Reference(className, factory, factoryLoc);
if ((attr = attrs.getAttribute(AT_REFADDR)) == null ) {
return ref; // no refAddr
}
for (Enumeration e = attr.getStringValues(); e.hasMoreElements();) {
decodeRefAddr((String)e.nextElement(), ref);
}
return ref;
}
catch (Exception ex) {
if (ex instanceof NamingException) {
throw (NamingException)ex;
}
NamingException nameEx = new NamingException("NamingManager.getStateToStore failed");
nameEx.setRootCause(ex);
throw nameEx;
}
}
/**
* Decode RefAddr according to the <draft-ryan-java-schema-01.rev.txt>
* StringRefAddr are encoded as #posNo#refType#refValue where posNo is the
* refAddr position (index) inside a rerference. BynaryRefAddr is encoded
* as #posNo#refType##data where data is the base64 encoding of the serialized
* form of the entire BinaryRefAddr instance.
*/
private static void decodeRefAddr(String enc, Reference ref) throws NamingException{
if (enc.length() == 0) {
throw new NamingException("Bad Reference encoding, empty refAddr");
}
String delimChar = enc.substring(0,1);
StringTokenizer tok = new StringTokenizer(enc, delimChar);
int tokCount = tok.countTokens();
if (tokCount != 3 && tokCount != 4) {
Debug.println(3, "enc=" + enc + " delimChar="+delimChar + " tokCount="+tokCount);
throw new NamingException("Bad Reference encoding");
}
String type = null;
int posn = -1;
for (int i = 0; i < tokCount; i++) {
String s = tok.nextToken();
if (i == 0) { // position
try {
posn = Integer.parseInt(s);
}
catch (Exception e) {
throw new NamingException("Bad Reference encoding, refAddr position not an initger");
}
}
else if (i == 1) { // type
if (s.length() == 0) {
throw new NamingException("Bad Reference encoding, empty refAddr type");
}
type = s;
}
else if (i == 2) { // value for StringRefAddr, empty for BinaryRefAddr
if (s.length() == 0 && tokCount != 4) { // should be empty for binary refs
throw new NamingException("Bad Reference encoding, empty refAddr string value");
}
ref.add(posn, new StringRefAddr(type, s));
}
else { // base64 encoding of the serialized BinaryRefAddr
if (s.length() == 0) {
throw new NamingException("Bad Reference encoding, empty refAddr binary value");
}
MimeBase64Decoder base64Dec = new MimeBase64Decoder();
ByteBuf in = new ByteBuf(s), out = new ByteBuf();
base64Dec.translate(in, out);
base64Dec.eof(out);
ref.add(posn, (RefAddr) deserializeObject(out.toBytes()));
}
}
}
/**
* Encode Jndi Reference Object
*/
private static Attributes encodeRefObj(char delimChar, Reference ref, Attributes attrs) throws NamingException {
if (ref.getClassName() != null) {
attrs.put(new BasicAttribute(AT_CLASSNAME, ref.getClassName()));
}
if (ref.getFactoryClassName() != null) {
attrs.put(new BasicAttribute(AT_OBJFACTORY, ref.getFactoryClassName()));
}
if (ref.getFactoryClassLocation() != null) {
attrs.put(new BasicAttribute(AT_CODEBASE, ref.getFactoryClassLocation()));
}
if(ref.size() > 0) {
BasicAttribute refAttr = new BasicAttribute(AT_REFADDR);
for(int i = 0; i < ref.size(); i++) {
refAttr.add(encodeRefAddr(delimChar, i, ref.get(i)));
}
attrs.put(refAttr);
}
return attrs;
}
/**
* Encode RefAddr according to the <draft-ryan-java-schema-01.rev.txt>
* StringRefAddr are encoded as #posNo#refType#refValue where posNo is the
* refAddr position (index) inside a rerference. BynaryRefAddr is encoded
* as #posNo#refType##data where data is the base64 encoding of the serialized
* form of the entire BinaryRefAddr instance.
*/
private static String encodeRefAddr(char delimChar, int idx, RefAddr refAddr) throws NamingException{
if(refAddr instanceof StringRefAddr) {
String content = (String) refAddr.getContent();
if (content != null && content.length() > 0 && content.charAt(0) == delimChar) {
throw new NamingException(
"Can not encode StringRefAddr, value starts with the delimiter character " + delimChar);
}
return delimChar + idx +
delimChar + refAddr.getType() +
delimChar + content;
}
else { // BinaryRefAdd
byte[] serialRefAddr = serializeObject(refAddr);
MimeBase64Encoder base64 = new MimeBase64Encoder();
ByteBuf in = new ByteBuf(), out = new ByteBuf();
in.append(serialRefAddr);
base64.translate(in, out);
base64.eof(out);
return delimChar + idx +
delimChar + refAddr.getType() +
delimChar + delimChar + out.toString();
}
}
/**
* Encode Serializable object
*/
static Attributes encodeSerialObj(Serializable obj, Attributes attrs) throws NamingException{
if (attrs.get(AT_CLASSNAME) == null) {
attrs.put(new BasicAttribute(AT_CLASSNAME, obj.getClass().getName()));
}
attrs.put(new BasicAttribute(AT_SERIALDATA, serializeObject(obj)));
return attrs;
}
/**
* Encode DirContext object (merege attributes)
*/
static Attributes encodeDirCtxObj(DirContext obj, Attributes attrs) throws NamingException{
Attributes ctxAttrs = obj.getAttributes("");
for (NamingEnumeration enum = ctxAttrs.getAll(); enum.hasMore();) {
attrs.put((Attribute)enum.next());
}
return attrs;
}
public static void main(String[] args) {
byte[] serialRefAddr = { (byte)'a', (byte)'0', (byte)'A', (byte)0x10, (byte)0x7f, (byte)0xaa };
MimeBase64Encoder base64 = new MimeBase64Encoder();
MimeBase64Decoder base64Dec = new MimeBase64Decoder();
ByteBuf in = new ByteBuf(), out = new ByteBuf();
in.append(serialRefAddr);
base64.translate(in, out);
base64.eof(out);
System.err.println("in="+in);
System.err.println("out="+out);
in = new ByteBuf(out.toString());
out = new ByteBuf();
base64Dec.translate(in, out);
base64Dec.eof(out);
System.err.println("in="+in);
System.err.println("out="+out);
}
}