0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.harmony.jndi.provider.ldap;
0019:
0020: import java.io.ByteArrayInputStream;
0021: import java.io.ByteArrayOutputStream;
0022: import java.io.IOException;
0023: import java.io.ObjectInputStream;
0024: import java.io.ObjectOutputStream;
0025: import java.io.Serializable;
0026: import java.util.ArrayList;
0027: import java.util.Enumeration;
0028: import java.util.EventObject;
0029: import java.util.HashMap;
0030: import java.util.HashSet;
0031: import java.util.Hashtable;
0032: import java.util.Iterator;
0033: import java.util.List;
0034: import java.util.Map;
0035: import java.util.Set;
0036: import java.util.StringTokenizer;
0037: import java.util.TreeMap;
0038:
0039: import javax.naming.AuthenticationNotSupportedException;
0040: import javax.naming.Binding;
0041: import javax.naming.CannotProceedException;
0042: import javax.naming.CommunicationException;
0043: import javax.naming.CompositeName;
0044: import javax.naming.ConfigurationException;
0045: import javax.naming.Context;
0046: import javax.naming.InvalidNameException;
0047: import javax.naming.LimitExceededException;
0048: import javax.naming.Name;
0049: import javax.naming.NameClassPair;
0050: import javax.naming.NameNotFoundException;
0051: import javax.naming.NameParser;
0052: import javax.naming.NamingEnumeration;
0053: import javax.naming.NamingException;
0054: import javax.naming.PartialResultException;
0055: import javax.naming.RefAddr;
0056: import javax.naming.Reference;
0057: import javax.naming.Referenceable;
0058: import javax.naming.ReferralException;
0059: import javax.naming.StringRefAddr;
0060: import javax.naming.directory.Attribute;
0061: import javax.naming.directory.Attributes;
0062: import javax.naming.directory.BasicAttribute;
0063: import javax.naming.directory.BasicAttributes;
0064: import javax.naming.directory.DirContext;
0065: import javax.naming.directory.InvalidSearchFilterException;
0066: import javax.naming.directory.ModificationItem;
0067: import javax.naming.directory.SearchControls;
0068: import javax.naming.directory.SearchResult;
0069: import javax.naming.event.EventContext;
0070: import javax.naming.event.EventDirContext;
0071: import javax.naming.event.NamespaceChangeListener;
0072: import javax.naming.event.NamingEvent;
0073: import javax.naming.event.NamingExceptionEvent;
0074: import javax.naming.event.NamingListener;
0075: import javax.naming.event.ObjectChangeListener;
0076: import javax.naming.ldap.Control;
0077: import javax.naming.ldap.ControlFactory;
0078: import javax.naming.ldap.ExtendedRequest;
0079: import javax.naming.ldap.ExtendedResponse;
0080: import javax.naming.ldap.LdapContext;
0081: import javax.naming.ldap.LdapName;
0082: import javax.naming.ldap.ManageReferralControl;
0083: import javax.naming.ldap.Rdn;
0084: import javax.naming.ldap.UnsolicitedNotificationEvent;
0085: import javax.naming.ldap.UnsolicitedNotificationListener;
0086: import javax.naming.spi.DirectoryManager;
0087: import javax.naming.spi.NamingManager;
0088: import javax.naming.spi.DirStateFactory.Result;
0089:
0090: import org.apache.harmony.jndi.internal.nls.Messages;
0091: import org.apache.harmony.jndi.internal.parser.AttributeTypeAndValuePair;
0092: import org.apache.harmony.jndi.internal.parser.LdapNameParser;
0093: import org.apache.harmony.jndi.provider.ldap.asn1.Utils;
0094: import org.apache.harmony.jndi.provider.ldap.event.ECNotificationControl;
0095: import org.apache.harmony.jndi.provider.ldap.event.PersistentSearchResult;
0096: import org.apache.harmony.jndi.provider.ldap.ext.StartTlsResponseImpl;
0097: import org.apache.harmony.jndi.provider.ldap.parser.FilterParser;
0098: import org.apache.harmony.jndi.provider.ldap.parser.LdapUrlParser;
0099: import org.apache.harmony.jndi.provider.ldap.parser.ParseException;
0100: import org.apache.harmony.jndi.provider.ldap.sasl.SaslBind;
0101:
0102: /**
0103: * This context implements LdapContext, it's main entry point of all JNDI ldap
0104: * operations.
0105: */
0106: public class LdapContextImpl implements LdapContext, EventDirContext {
0107:
0108: /**
0109: * ldap connection
0110: */
0111: private LdapClient client;
0112:
0113: private boolean isClosed;
0114:
0115: /**
0116: * name of the context
0117: */
0118: protected Name contextDn;
0119:
0120: private Control[] requestControls;
0121:
0122: private Control[] responseControls;
0123:
0124: /**
0125: * environment properties for this context
0126: */
0127: protected Hashtable<Object, Object> env;
0128:
0129: /**
0130: * name parser for this context
0131: */
0132: private NameParser parser;
0133:
0134: /**
0135: * connection controls for this context
0136: */
0137: private Control[] connCtls;
0138:
0139: private HashMap<NamingListener, List<Integer>> listeners;
0140:
0141: private List<UnsolicitedNotificationListener> unls;
0142:
0143: private static final Control NON_CRITICAL_MANAGE_REF_CONTROL = new ManageReferralControl(
0144: Control.NONCRITICAL);
0145:
0146: private static final String LDAP_DELETE_RDN = "java.naming.ldap.deleteRDN"; //$NON-NLS-1$
0147:
0148: private static final String LDAP_DEREF_ALIASES = "java.naming.ldap.derefAliases"; //$NON-NLS-1$
0149:
0150: private static final String LDAP_CONTROL_CONNECT = "java.naming.ldap.control.connect"; //$NON-NLS-1$
0151:
0152: private static final String LDAP_TYPES_ONLY = "java.naming.ldap.typesOnly"; //$NON-NLS-1$
0153:
0154: /**
0155: * Some properties, such as 'java.naming.security.authentication', changed
0156: * by <code>Context.addToEnvironment</code> or
0157: * <code>Context.removeFromEnvironment</code> may affect connection with
0158: * LDAP server. This variable contains all such properties, which need
0159: * re-communication with LDAP server after changing.
0160: */
0161: private static final HashSet<String> connectionProperties = new HashSet<String>();
0162:
0163: static {
0164: connectionProperties.add(Context.SECURITY_AUTHENTICATION);
0165: connectionProperties.add(Context.SECURITY_CREDENTIALS);
0166: connectionProperties.add(Context.SECURITY_PRINCIPAL);
0167: connectionProperties.add(Context.SECURITY_PROTOCOL);
0168: connectionProperties.add("java.naming.ldap.factory.socket"); //$NON-NLS-1$
0169: }
0170:
0171: /**
0172: * construct a new inherit <code>LdapContextImpl</code>
0173: *
0174: * @param context
0175: * @param environment
0176: * @param dn
0177: * @throws InvalidNameException
0178: */
0179: public LdapContextImpl(LdapContextImpl context,
0180: Hashtable<Object, Object> environment, String dn)
0181: throws InvalidNameException {
0182: initial(context.client, environment, dn);
0183:
0184: // connection request controls are inheritied
0185: connCtls = context.connCtls;
0186: // request controls are not inheritied
0187: }
0188:
0189: /**
0190: * construct a new <code>LdapContextImpl</code> with a fresh
0191: * <code>LdapClient</code> which didn't do ldap bind operation yet.
0192: *
0193: * @param client
0194: * @param environment
0195: * @param dn
0196: * @throws NamingException
0197: */
0198: public LdapContextImpl(LdapClient client,
0199: Hashtable<Object, Object> environment, String dn)
0200: throws NamingException {
0201: initial(client, environment, dn);
0202:
0203: try {
0204: doBindOperation(connCtls);
0205: } catch (IOException e) {
0206: CommunicationException ex = new CommunicationException();
0207: ex.setRootCause(e);
0208: throw ex;
0209: }
0210: }
0211:
0212: private void initial(LdapClient ldapClient,
0213: Hashtable<Object, Object> environment, String dn)
0214: throws InvalidNameException {
0215: this .client = ldapClient;
0216: if (environment == null) {
0217: this .env = new Hashtable<Object, Object>();
0218: } else {
0219: this .env = (Hashtable<Object, Object>) environment.clone();
0220: }
0221:
0222: contextDn = new LdapName(dn);
0223: parser = new LdapNameParser(dn);
0224:
0225: if (env.get(Context.REFERRAL) == null
0226: || env.get(Context.REFERRAL).equals("ignore")) { //$NON-NLS-1$
0227: requestControls = new Control[] { NON_CRITICAL_MANAGE_REF_CONTROL };
0228: }
0229:
0230: connCtls = (Control[]) env.get(LDAP_CONTROL_CONNECT);
0231: }
0232:
0233: /**
0234: * Perform a LDAP Bind operation.
0235: *
0236: * @param env
0237: * @throws IOException
0238: * @throws IOException
0239: * @throws NamingException
0240: * @throws ParseException
0241: */
0242: private void doBindOperation(Control[] connCtsl)
0243: throws IOException, NamingException {
0244:
0245: SaslBind saslBind = new SaslBind();
0246: LdapResult result = null;
0247:
0248: SaslBind.AuthMech authMech = saslBind.valueAuthMech(env);
0249: if (authMech == SaslBind.AuthMech.None) {
0250: BindOp bind = new BindOp("", "", null, null);
0251: client.doOperation(bind, connCtsl);
0252: result = bind.getResult();
0253: } else if (authMech == SaslBind.AuthMech.Simple) {
0254: String principal = (String) env
0255: .get(Context.SECURITY_PRINCIPAL);
0256: String credential = Utils.getString(env
0257: .get(Context.SECURITY_CREDENTIALS));
0258: BindOp bind = new BindOp(principal, credential, null, null);
0259: client.doOperation(bind, connCtsl);
0260: result = bind.getResult();
0261: } else if (authMech == SaslBind.AuthMech.SASL) {
0262: result = saslBind
0263: .doSaslBindOperation(env, client, connCtsl);
0264: }
0265:
0266: if (LdapUtils.getExceptionFromResult(result) != null) {
0267: throw LdapUtils.getExceptionFromResult(result);
0268: }
0269: }
0270:
0271: public ExtendedResponse extendedOperation(ExtendedRequest request)
0272: throws NamingException {
0273: ExtendedOp op = new ExtendedOp(request);
0274: try {
0275: doBasicOperation(op);
0276: } catch (ReferralException e) {
0277: if (isFollowReferral(e)) {
0278: LdapContext referralContext = (LdapContext) getReferralContext(e);
0279: return referralContext.extendedOperation(request);
0280: }
0281: throw e;
0282: }
0283: ExtendedResponse response = op.getExtendedResponse();
0284: // set existing underlying socket to startTls extended response
0285: if (response instanceof StartTlsResponseImpl) {
0286: ((StartTlsResponseImpl) response).setSocket(client
0287: .getSocket());
0288: }
0289: return response;
0290: }
0291:
0292: public Control[] getConnectControls() throws NamingException {
0293: return copyControls(connCtls);
0294: }
0295:
0296: public Control[] getRequestControls() throws NamingException {
0297: return copyControls(requestControls);
0298: }
0299:
0300: public Control[] getResponseControls() throws NamingException {
0301: return copyControls(responseControls);
0302: }
0303:
0304: private Control[] copyControls(Control[] controls) {
0305: if (controls == null) {
0306: return null;
0307: }
0308:
0309: Control[] rtValue = new Control[controls.length];
0310: System.arraycopy(controls, 0, rtValue, 0, controls.length);
0311: return rtValue;
0312: }
0313:
0314: public LdapContext newInstance(Control[] reqCtrls)
0315: throws NamingException {
0316: LdapContextImpl instance = new LdapContextImpl(this , env,
0317: contextDn.toString());
0318: instance.setRequestControls(reqCtrls);
0319: return instance;
0320: }
0321:
0322: public void reconnect(Control[] ac) throws NamingException {
0323: connCtls = ac;
0324: try {
0325: doBindOperation(connCtls);
0326: } catch (IOException e) {
0327: CommunicationException ex = new CommunicationException();
0328: ex.setRootCause(e);
0329: throw ex;
0330: }
0331: }
0332:
0333: public void setRequestControls(Control[] controls)
0334: throws NamingException {
0335: boolean hasManageDsaITConntrol = false;
0336:
0337: if (env.get(Context.REFERRAL) == null
0338: || env.get(Context.REFERRAL).equals("ignore")) {
0339: hasManageDsaITConntrol = true;
0340: }
0341:
0342: if (controls == null) {
0343: if (hasManageDsaITConntrol) {
0344: requestControls = new Control[] { NON_CRITICAL_MANAGE_REF_CONTROL };
0345: } else {
0346: requestControls = null;
0347: }
0348: return;
0349: }
0350:
0351: if (hasManageDsaITConntrol) {
0352: requestControls = new Control[controls.length + 1];
0353: System.arraycopy(controls, 0, requestControls, 0,
0354: controls.length);
0355: requestControls[controls.length] = NON_CRITICAL_MANAGE_REF_CONTROL;
0356: } else {
0357: requestControls = new Control[controls.length];
0358: System.arraycopy(controls, 0, requestControls, 0,
0359: controls.length);
0360: }
0361: }
0362:
0363: public void bind(Name name, Object obj, Attributes attributes)
0364: throws NamingException {
0365: checkName(name);
0366:
0367: if (name instanceof CompositeName && name.size() > 1) {
0368: /*
0369: * multi ns, find next ns context, delegate operation to the next
0370: * contex
0371: */
0372: DirContext nns = (DirContext) findNnsContext(name);
0373: Name remainingName = name.getSuffix(1);
0374: nns.bind(remainingName, attributes);
0375: return;
0376: }
0377:
0378: /*
0379: * there is only one ldap ns
0380: */
0381: if (obj == null && attributes == null) {
0382: // ldap.2E=cannot bind null object without attributes
0383: throw new IllegalArgumentException(Messages
0384: .getString("ldap.2E")); //$NON-NLS-1$
0385: }
0386:
0387: if (obj == null) {
0388: createSubcontext(name, attributes);
0389: return;
0390: }
0391:
0392: Result result = DirectoryManager.getStateToBind(obj, name,
0393: this , env, attributes);
0394: Object o = result.getObject();
0395:
0396: Attributes attrs = null;
0397:
0398: if (o instanceof Reference) {
0399: attrs = convertRefToAttribute((Reference) o);
0400: } else if (o instanceof Referenceable) {
0401: attrs = convertRefToAttribute(((Referenceable) o)
0402: .getReference());
0403: } else if (o instanceof Serializable) {
0404: attrs = convertSerialToAttribute((Serializable) o);
0405: } else if (o instanceof DirContext) {
0406: DirContext cxt = (DirContext) o;
0407: attrs = cxt.getAttributes("");
0408: } else {
0409: throw new IllegalArgumentException(Messages
0410: .getString("ldap.24")); //$NON-NLS-1$
0411: }
0412:
0413: NamingEnumeration<? extends Attribute> enu = attrs.getAll();
0414: if (result.getAttributes() != null) {
0415: Attributes resultAttributes = result.getAttributes();
0416:
0417: while (enu.hasMore()) {
0418: Attribute element = enu.next();
0419: if (element.getID().equalsIgnoreCase("objectClass")) {
0420: element = mergeAttribute(resultAttributes
0421: .get("objectClass"), element);
0422: if (resultAttributes.get("objectClass") != null) {
0423: element.remove("javaContainer");
0424: }
0425: resultAttributes.put(element);
0426: } else if (resultAttributes.get(element.getID()) == null) {
0427: resultAttributes.put(element);
0428: }
0429: }
0430:
0431: createSubcontext(name, resultAttributes);
0432: } else {
0433: createSubcontext(name, attrs);
0434: }
0435: }
0436:
0437: private Attributes convertSerialToAttribute(
0438: Serializable serializable) throws NamingException {
0439: Attributes attrs = new BasicAttributes();
0440:
0441: Attribute objectClass = new BasicAttribute("objectClass");
0442: objectClass.add("top");
0443: objectClass.add("javaContainer");
0444: objectClass.add("javaObject");
0445: objectClass.add("javaSerializedObject");
0446: attrs.put(objectClass);
0447:
0448: Attribute javaClassNames = new BasicAttribute("javaClassNames");
0449: javaClassNames.add(serializable.getClass().getName());
0450: javaClassNames.add(Object.class.getName());
0451:
0452: Class[] cs = serializable.getClass().getInterfaces();
0453: for (Class c : cs) {
0454: javaClassNames.add(c.getName());
0455: }
0456:
0457: // add all ancestors class
0458: Class sup = serializable.getClass().getSuperclass();
0459: while (sup != null
0460: && !sup.getName().equals(Object.class.getName())) {
0461: javaClassNames.add(sup.getName());
0462: sup = sup.getSuperclass();
0463: }
0464: attrs.put(javaClassNames);
0465:
0466: attrs.put("javaClassName", serializable.getClass().getName());
0467:
0468: ByteArrayOutputStream bout = new ByteArrayOutputStream();
0469:
0470: try {
0471: ObjectOutputStream out = new ObjectOutputStream(bout);
0472: out.writeObject(serializable);
0473: out.close();
0474: } catch (IOException e) {
0475: // TODO need add more detail messages?
0476: NamingException ex = new NamingException();
0477: ex.setRootCause(e);
0478: throw ex;
0479: }
0480:
0481: byte[] bytes = bout.toByteArray();
0482: attrs.put("javaSerializedData", bytes);
0483:
0484: return attrs;
0485: }
0486:
0487: private Attributes convertRefToAttribute(Reference ref) {
0488: Attributes attrs = new BasicAttributes();
0489:
0490: Attribute objectClass = new BasicAttribute("objectClass");
0491: objectClass.add("top");
0492: objectClass.add("javaContainer");
0493: objectClass.add("javaObject");
0494: objectClass.add("javaNamingReference");
0495: attrs.put(objectClass);
0496:
0497: Attribute className = new BasicAttribute("javaClassName");
0498: className.add(ref.getClassName());
0499: attrs.put(className);
0500:
0501: Attribute address = new BasicAttribute("javaReferenceAddress");
0502: Enumeration<RefAddr> enu = ref.getAll();
0503: int index = 0;
0504: String separator = (String) env
0505: .get("java.naming.ldap.ref.separator");
0506: if (separator == null) {
0507: // use default separator '#'
0508: separator = "#";
0509: }
0510: while (enu.hasMoreElements()) {
0511: RefAddr addr = enu.nextElement();
0512: StringBuilder builder = new StringBuilder();
0513: builder.append(separator + index);
0514: builder.append(separator + addr.getType());
0515: builder.append(separator + addr.getContent());
0516: address.add(builder.toString());
0517: index++;
0518: }
0519: attrs.put(address);
0520:
0521: return attrs;
0522: }
0523:
0524: public void bind(String s, Object obj, Attributes attributes)
0525: throws NamingException {
0526: bind(convertFromStringToName(s), obj, attributes);
0527: }
0528:
0529: public DirContext createSubcontext(Name name, Attributes attributes)
0530: throws NamingException {
0531: checkName(name);
0532:
0533: if (name instanceof CompositeName && name.size() > 1) {
0534: /*
0535: * multi ns, find next ns context, delegate operation to the next
0536: * context
0537: */
0538: DirContext nns = (DirContext) findNnsContext(name);
0539: Name remainingName = name.getSuffix(1);
0540: return nns.createSubcontext(remainingName, attributes);
0541: }
0542:
0543: /*
0544: * there is only one ldap ns
0545: */
0546:
0547: if (attributes == null) {
0548: attributes = new BasicAttributes();
0549: Attribute attr = new LdapAttribute("objectClass", this );
0550: attr.add("top");
0551: attr.add("javaContainer");
0552: attributes.put(attr);
0553: }
0554:
0555: // get absolute dn name
0556: String targetDN = getTargetDN(name, contextDn);
0557: // merge attributes from dn and args
0558: Attributes attrs = mergeAttributes(attributes,
0559: getAttributesFromDN(name));
0560:
0561: // convert to LdapAttribute
0562: List<LdapAttribute> la = new ArrayList<LdapAttribute>(attrs
0563: .size());
0564: NamingEnumeration<? extends Attribute> enu = attrs.getAll();
0565: while (enu.hasMore()) {
0566: la.add(new LdapAttribute(enu.next(), this ));
0567: }
0568:
0569: // do add operation
0570: AddOp op = new AddOp(targetDN, la);
0571: try {
0572: doBasicOperation(op);
0573: } catch (ReferralException e) {
0574: if (isFollowReferral(e)) {
0575: DirContext referralContext = getReferralContext(e);
0576: return referralContext.createSubcontext(name,
0577: attributes);
0578: }
0579: throw e;
0580: }
0581:
0582: LdapResult result = op.getResult();
0583: return new LdapContextImpl(this , env, result.getMachedDN());
0584: }
0585:
0586: private DirContext getReferralContext(ReferralException e)
0587: throws LimitExceededException, NamingException {
0588: int limit = 0;
0589: if (env.get("java.naming.ldap.referral.limit") != null) {
0590: limit = Integer
0591: .valueOf(
0592: (String) env
0593: .get("java.naming.ldap.referral.limit"))
0594: .intValue();
0595: }
0596:
0597: if (limit == -1) {
0598: throw new LimitExceededException(Messages
0599: .getString("ldap.25")); //$NON-NLS-1$
0600: }
0601:
0602: if (limit == 1) {
0603: limit = -1;
0604: } else if (limit != 0) {
0605: limit -= 1;
0606: }
0607:
0608: Hashtable<Object, Object> newEnv = (Hashtable<Object, Object>) env
0609: .clone();
0610: newEnv.put("java.naming.ldap.referral.limit", String
0611: .valueOf(limit));
0612: DirContext referralContext = null;
0613:
0614: while (true) {
0615: try {
0616: referralContext = (DirContext) e
0617: .getReferralContext(newEnv);
0618: break;
0619: } catch (NamingException ex) {
0620: if (e.skipReferral()) {
0621: continue;
0622: }
0623: throw ex;
0624: }
0625: }
0626:
0627: return referralContext;
0628: }
0629:
0630: /**
0631: * merge two instanceof <code>Attributes</code> to one
0632: *
0633: * @param first
0634: * @param second
0635: * @return
0636: * @throws NamingException
0637: */
0638: private Attributes mergeAttributes(Attributes first,
0639: Attributes second) throws NamingException {
0640: if (first == null) {
0641: return second;
0642: }
0643:
0644: if (second == null) {
0645: return first;
0646: }
0647:
0648: BasicAttributes attrs = new BasicAttributes();
0649: NamingEnumeration<? extends Attribute> enu = first.getAll();
0650: while (enu.hasMore()) {
0651: attrs.put(enu.next());
0652: }
0653:
0654: enu = second.getAll();
0655: while (enu.hasMore()) {
0656: Attribute element = enu.next();
0657: element = mergeAttribute(element, attrs
0658: .get(element.getID()));
0659: attrs.put(element);
0660: }
0661:
0662: return attrs;
0663: }
0664:
0665: /**
0666: * merge two instance of <code>Attribute</code> to one
0667: *
0668: * @param first
0669: * @param second
0670: * @return
0671: * @throws NamingException
0672: */
0673: private Attribute mergeAttribute(Attribute first, Attribute second)
0674: throws NamingException {
0675: if (first == null) {
0676: return second;
0677: }
0678:
0679: if (second == null) {
0680: return first;
0681: }
0682:
0683: Attribute attr = new LdapAttribute(first.getID(), this );
0684: NamingEnumeration<?> enu = first.getAll();
0685: while (enu.hasMore()) {
0686: attr.add(enu.next());
0687: }
0688:
0689: enu = second.getAll();
0690: while (enu.hasMore()) {
0691: attr.add(enu.next());
0692: }
0693:
0694: return attr;
0695: }
0696:
0697: public DirContext createSubcontext(String s, Attributes attributes)
0698: throws NamingException {
0699: return createSubcontext(convertFromStringToName(s), attributes);
0700: }
0701:
0702: public Attributes getAttributes(Name name) throws NamingException {
0703: return getAttributes(name, null);
0704: }
0705:
0706: public Attributes getAttributes(Name name, String[] as)
0707: throws NamingException {
0708: checkName(name);
0709:
0710: if (name instanceof CompositeName && name.size() > 1) {
0711: if (!(name.getPrefix(0) instanceof LdapName)) {
0712: throw new InvalidNameException(Messages
0713: .getString("ldap.26")); //$NON-NLS-1$
0714: }
0715: /*
0716: * multi ns, find next ns context, delegate operation to the next
0717: * context
0718: */
0719: DirContext nns = (DirContext) findNnsContext(name);
0720: Name remainingName = name.getSuffix(1);
0721: return nns.getAttributes(remainingName, as);
0722: }
0723:
0724: /*
0725: * there is only one ldap ns
0726: */
0727: // absolute dn name to list
0728: String targetDN = getTargetDN(name, contextDn);
0729:
0730: // construct one-level search using filter "(objectclass=*)"
0731: SearchControls controls = new SearchControls();
0732: controls.setSearchScope(SearchControls.OBJECT_SCOPE);
0733:
0734: /*
0735: * none should be retrieved, send OID "1.1" to server, according to RFC
0736: * 2251, 4.5.1
0737: */
0738: if (as != null && as.length == 0) {
0739: // "1.1" means no attributes should return
0740: as = new String[] { "1.1" }; //$NON-NLS-1$
0741: }
0742: controls.setReturningAttributes(as);
0743:
0744: Filter filter = new Filter(Filter.PRESENT_FILTER);
0745: filter.setValue("objectClass");
0746:
0747: LdapSearchResult result = doSearch(targetDN, filter, controls);
0748: Iterator<Attributes> it = result.getEntries().values()
0749: .iterator();
0750: if (it.hasNext()) {
0751: Attributes attributes = it.next();
0752: NamingEnumeration<String> ids = attributes.getIDs();
0753: while (ids.hasMore()) {
0754: LdapAttribute attribute = (LdapAttribute) attributes
0755: .get(ids.next());
0756: attribute.setContext(this );
0757: }
0758:
0759: // FIXME: there must be only one Attributes?
0760: return attributes;
0761: } else if (result.getException() != null) {
0762: throw result.getException();
0763: }
0764:
0765: // no attribute retrieved from server, return a empty Attributes
0766: return new BasicAttributes();
0767: }
0768:
0769: public Attributes getAttributes(String s) throws NamingException {
0770: return getAttributes(convertFromStringToName(s));
0771: }
0772:
0773: public Attributes getAttributes(String s, String[] as)
0774: throws NamingException {
0775: return getAttributes(convertFromStringToName(s), as);
0776: }
0777:
0778: public static Hashtable<String, Hashtable<String, Hashtable<String, Object>>> schemaTree = new Hashtable<String, Hashtable<String, Hashtable<String, Object>>>();
0779:
0780: private LdapSchemaContextImpl ldapSchemaCtx = null;
0781:
0782: protected String subschemasubentry = null;
0783:
0784: public DirContext getSchema(Name name) throws NamingException {
0785: checkName(name);
0786: if (null != ldapSchemaCtx)
0787: return ldapSchemaCtx;
0788:
0789: SearchControls searchControls = new SearchControls();
0790: SearchOp search = null;
0791: Filter filter = null;
0792: FilterParser filterParser = null;
0793: LdapSearchResult sre = null;
0794: Map<String, Attributes> names = null;
0795: Set<String> keyset = null;
0796:
0797: if (name.size() != 0) {
0798: subschemasubentry = name.toString() + ","
0799: + contextDn.toString();
0800: }
0801: if (null == subschemasubentry) {
0802: filterParser = new FilterParser("(objectClass=*)");
0803: try {
0804: filter = filterParser.parse();
0805: } catch (ParseException e) {
0806: InvalidSearchFilterException ex = new InvalidSearchFilterException(
0807: Messages.getString("ldap.29")); //$NON-NLS-1$
0808: ex.setRootCause(e);
0809: throw ex;
0810: }
0811:
0812: searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
0813: searchControls
0814: .setReturningAttributes(new String[] {
0815: "namingContexts", "subschemaSubentry",
0816: "altServer", });
0817: search = new SearchOp("", searchControls, filter);
0818:
0819: try {
0820: client.doOperation(search, requestControls);
0821: } catch (IOException e) {
0822: CommunicationException ex = new CommunicationException(
0823: e.getMessage());
0824: ex.setRootCause(e);
0825: if (search.getSearchResult().isEmpty()) {
0826: throw ex;
0827: }
0828: search.getSearchResult().setException(ex);
0829: }
0830:
0831: sre = search.getSearchResult();
0832: names = sre.getEntries();
0833:
0834: keyset = names.keySet();
0835: schemaRoot: for (Iterator<String> iterator = keyset
0836: .iterator(); iterator.hasNext();) {
0837: String key = iterator.next();
0838: Attributes as = names.get(key);
0839: NamingEnumeration<String> ids = as.getIDs();
0840: while (ids.hasMore()) {
0841: String id = ids.next();
0842: if (id.equalsIgnoreCase("subschemasubentry")) {
0843: subschemasubentry = (String) as.get(id).get();
0844: break schemaRoot;
0845: }
0846: }
0847: }
0848: }
0849:
0850: searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
0851: searchControls.setReturningAttributes(new String[] {
0852: "objectclasses", "attributetypes", "matchingrules",
0853: "ldapsyntaxes" });
0854: searchControls.setReturningObjFlag(true);
0855: filterParser = new FilterParser("(objectClass=subschema)");
0856: try {
0857: filter = filterParser.parse();
0858: } catch (ParseException e) {
0859: InvalidSearchFilterException ex = new InvalidSearchFilterException(
0860: Messages.getString("ldap.29")); //$NON-NLS-1$
0861: ex.setRootCause(e);
0862: throw ex;
0863: }
0864: search = new SearchOp(subschemasubentry, searchControls, filter);
0865:
0866: try {
0867: client.doOperation(search, requestControls);
0868: } catch (IOException e) {
0869: CommunicationException ex = new CommunicationException(e
0870: .getMessage());
0871: ex.setRootCause(e);
0872: if (search.getSearchResult().isEmpty()) {
0873: throw ex;
0874: }
0875: search.getSearchResult().setException(ex);
0876: }
0877: if (search.getResult().getResultCode() == LdapResult.INVALID_DN_SYNTAX) {
0878: throw new InvalidNameException(Messages
0879: .getString("ldap.34"));
0880: }
0881: sre = search.getSearchResult();
0882: names = sre.getEntries();
0883:
0884: keyset = names.keySet();
0885: for (Iterator<String> iterator = keyset.iterator(); iterator
0886: .hasNext();) {
0887: String key = iterator.next();
0888: Attributes as = names.get(key);
0889: NamingEnumeration<String> ids = as.getIDs();
0890:
0891: while (ids.hasMoreElements()) {
0892: String schemaType = ids.nextElement();
0893: if (!schemaTree.contains(schemaType.toLowerCase())) {
0894: schemaTree
0895: .put(
0896: schemaType.toLowerCase(),
0897: new Hashtable<String, Hashtable<String, Object>>());
0898: }
0899: Hashtable<String, Hashtable<String, Object>> schemaDefs = schemaTree
0900: .get(schemaType.toLowerCase());
0901: LdapAttribute attribute = (LdapAttribute) as
0902: .get(schemaType);
0903: for (int i = 0; i < attribute.size(); i++) {
0904: String value = (String) attribute.get(i);
0905: parseValue(schemaType, value.toLowerCase(),
0906: schemaDefs);
0907: }
0908: }
0909: }
0910: ldapSchemaCtx = new LdapSchemaContextImpl(this , env, name);
0911: return ldapSchemaCtx;
0912: }
0913:
0914: Hashtable<String, Object> findSchemaDefInfo(String schemaType,
0915: String className) {
0916: Hashtable<String, Hashtable<String, Object>> schemaDefs = schemaTree
0917: .get(schemaType);
0918: Hashtable<String, Object> schemaDef = schemaDefs.get(className);
0919: return schemaDef;
0920: }
0921:
0922: /*
0923: * Sample schema value from Openldap server is ( 2.5.13.8 NAME
0924: * 'numericStringMatch' SYNTAX 1.3.6.1.4.1.1466.115.121.1.36 ) TODO check
0925: * with RFC to see whether all the schema definition has been catered for
0926: */
0927: private static void parseValue(String schemaType, String value,
0928: Hashtable<String, Hashtable<String, Object>> schemaDefs) {
0929: StringTokenizer st = new StringTokenizer(value);
0930: // Skip (
0931: st.nextToken();
0932:
0933: String oid = st.nextToken();
0934:
0935: Hashtable<String, Object> schemaDef = new Hashtable<String, Object>();
0936: schemaDef.put("orig", value);
0937: schemaDef.put("numericoid", oid);
0938: String token = null;
0939: ArrayList<String> values = null;
0940: StringBuilder desc = new StringBuilder();
0941: while (st.hasMoreTokens()) {
0942: String attrName = st.nextToken();
0943: if (attrName.startsWith("x-")) {
0944: token = st.nextToken();
0945: // remove the ending ' symbol
0946: token = token.substring(0, token.length() - 1);
0947: schemaDef.put(attrName, token);
0948: }
0949: if (attrName.equals("usage") || attrName.equals("equality")
0950: || attrName.equals("syntax")
0951: || attrName.equals("ordering")
0952: || attrName.equals("substr")) {
0953: token = st.nextToken();
0954: schemaDef.put(attrName, token);
0955: }
0956: if (attrName.equals("desc")) {
0957: token = st.nextToken();
0958:
0959: // remove the leading ' symbol
0960: if (token.startsWith("'"))
0961: token = token.substring(1);
0962: while (!token.endsWith("'")) {
0963: desc.append(token).append(" ");
0964: token = st.nextToken();
0965: }
0966:
0967: // remove the ending ' symbol
0968: desc.append(token.substring(0, token.length() - 1));
0969: schemaDef.put(attrName, desc.toString());
0970: desc.delete(0, desc.length());
0971: }
0972: if (attrName.equals("name")) {
0973: token = st.nextToken();
0974: values = new ArrayList<String>();
0975: // Name has multiple values
0976: if (token.startsWith("(")) {
0977: token = st.nextToken();
0978: while (!token.equals(")")) {
0979: // remove the leading ' symbol
0980: if (token.startsWith("'"))
0981: token = token.substring(1);
0982: while (!token.endsWith("'")) {
0983: desc.append(token).append(" ");
0984: token = st.nextToken();
0985: }
0986:
0987: // remove the ending ' symbol
0988: desc.append(token.substring(0,
0989: token.length() - 1));
0990: values.add(desc.toString());
0991: desc.delete(0, desc.length());
0992:
0993: token = st.nextToken();
0994: }
0995: } else {
0996: // remove the leading ' symbol
0997: if (token.startsWith("'"))
0998: token = token.substring(1);
0999: while (!token.endsWith("'")) {
1000: desc.append(token).append(" ");
1001: token = st.nextToken();
1002: }
1003:
1004: // remove the ending ' symbol
1005: desc.append(token.substring(0, token.length() - 1));
1006: values.add(desc.toString());
1007: desc.delete(0, desc.length());
1008: }
1009: schemaDef.put(attrName, values);
1010: if (schemaType
1011: .equalsIgnoreCase(LdapSchemaContextImpl.LDAP_SYNTAXES)) {
1012: schemaDefs.put(oid, schemaDef);
1013: } else {
1014: schemaDefs.put(values.get(0), schemaDef);
1015: }
1016: }
1017: if (attrName.equals("must") || attrName.equals("sup")
1018: || attrName.equals("may")) {
1019: token = st.nextToken();
1020: values = new ArrayList<String>();
1021: // has multiple values
1022: if (token.startsWith("(")) {
1023: token = st.nextToken();
1024: while (!token.equals(")")) {
1025: if (!token.equals("$"))
1026: values.add(token);
1027: token = st.nextToken();
1028: }
1029: } else {
1030: values.add(token);
1031: }
1032: schemaDef.put(attrName, values);
1033: }
1034: if (attrName.equals("abstract")
1035: || attrName.equals("structual")
1036: || attrName.equals("auxiliary")
1037: || attrName.equals("single-value")
1038: || attrName.equals("no-user-modification")) {
1039: schemaDef.put(attrName, "true");
1040: }
1041: }
1042: if (!schemaDef.keySet().contains("name")) {
1043: schemaDefs.put(oid, schemaDef);
1044: }
1045: }
1046:
1047: public DirContext getSchema(String s) throws NamingException {
1048: return getSchema(new CompositeName(s));
1049: }
1050:
1051: DirContext getSchemaAttributeDefinition(String name)
1052: throws NamingException {
1053: if (null == ldapSchemaCtx) {
1054: getSchema("");
1055: }
1056: Hashtable<String, Object> attrDef = findSchemaDefInfo(
1057: LdapSchemaContextImpl.ATTRIBUTE_TYPES, name);
1058:
1059: return new LdapSchemaAttrDefContextImpl(
1060: new CompositeName(name), env, attrDef, this );
1061: }
1062:
1063: public DirContext getSchemaClassDefinition(Name name)
1064: throws NamingException {
1065: if (null == ldapSchemaCtx) {
1066: getSchema("");
1067: }
1068:
1069: Hashtable<String, ArrayList<String>> classTree = new Hashtable<String, ArrayList<String>>();
1070:
1071: SearchControls searchControls = new SearchControls();
1072: searchControls.setSearchScope(SearchControls.OBJECT_SCOPE);
1073: searchControls
1074: .setReturningAttributes(new String[] { "objectClass", });
1075: searchControls.setReturningObjFlag(false);
1076: FilterParser parser = new FilterParser("(objectClass=*)");
1077: Filter filter = null;
1078: try {
1079: filter = parser.parse();
1080: } catch (ParseException e1) {
1081: // Should not throw this excption
1082: }
1083: String targetDN = getTargetDN(name, contextDn);
1084: SearchOp search = new SearchOp(targetDN, searchControls, filter);
1085:
1086: try {
1087: client.doOperation(search, requestControls);
1088: } catch (IOException e) {
1089: CommunicationException ex = new CommunicationException(e
1090: .getMessage());
1091: ex.setRootCause(e);
1092: if (search.getSearchResult().isEmpty()) {
1093: throw ex;
1094: }
1095: search.getSearchResult().setException(ex);
1096: }
1097: LdapSearchResult sre = search.getSearchResult();
1098: Map<String, Attributes> names = sre.getEntries();
1099:
1100: Set<String> keyset = names.keySet();
1101: for (Iterator<String> iterator = keyset.iterator(); iterator
1102: .hasNext();) {
1103: String key = iterator.next();
1104: Attributes as = names.get(key);
1105: NamingEnumeration<String> ids = as.getIDs();
1106:
1107: while (ids.hasMoreElements()) {
1108: String schemaType = ids.nextElement();
1109: if (!classTree.contains(schemaType)) {
1110: classTree.put(schemaType, new ArrayList());
1111: }
1112: ArrayList<String> classDefs = classTree.get(schemaType);
1113: LdapAttribute attribute = (LdapAttribute) as
1114: .get(schemaType);
1115: for (int i = 0; i < attribute.size(); i++) {
1116: String value = (String) attribute.get(i);
1117: classDefs.add(value);
1118: }
1119: }
1120: }
1121:
1122: return new LdapSchemaClassDefContextImpl(new CompositeName(
1123: targetDN), env, classTree, this );
1124: }
1125:
1126: public DirContext getSchemaClassDefinition(String s)
1127: throws NamingException {
1128: return getSchemaClassDefinition(convertFromStringToName(s));
1129: }
1130:
1131: public void modifyAttributes(Name name, int i, Attributes attributes)
1132: throws NamingException {
1133: checkName(name);
1134: if (attributes == null) {
1135: // jndi.13=Non-null attribute is required for modification
1136: throw new NullPointerException(Messages
1137: .getString("jndi.13")); //$NON-NLS-1$
1138: }
1139:
1140: if (i != DirContext.ADD_ATTRIBUTE
1141: && i != DirContext.REMOVE_ATTRIBUTE
1142: && i != DirContext.REPLACE_ATTRIBUTE) {
1143: /*
1144: * jndi.14=Modification code {0} must be one of
1145: * DirContext.ADD_ATTRIBUTE, DirContext.REPLACE_ATTRIBUTE and
1146: * DirContext.REMOVE_ATTRIBUTE
1147: */
1148: throw new IllegalArgumentException(Messages.getString(
1149: "jndi.14", i)); //$NON-NLS-1$
1150: }
1151:
1152: NamingEnumeration<? extends Attribute> enu = attributes
1153: .getAll();
1154: ModificationItem[] items = new ModificationItem[attributes
1155: .size()];
1156: int index = 0;
1157: while (enu.hasMore()) {
1158: items[index++] = new ModificationItem(i, enu.next());
1159: }
1160:
1161: modifyAttributes(name, items);
1162: }
1163:
1164: public void modifyAttributes(Name name,
1165: ModificationItem[] modificationItems)
1166: throws NamingException {
1167: checkName(name);
1168:
1169: if (modificationItems == null) {
1170: // FIXME: spec say ModificationItem may not be null, but ri
1171: // silence in this case
1172: throw new NullPointerException(Messages
1173: .getString("ldap.27")); //$NON-NLS-1$
1174: }
1175:
1176: if (name instanceof CompositeName && name.size() > 1) {
1177: /*
1178: * multi ns, find next ns context, delegate operation to the next
1179: * context
1180: */
1181: DirContext nns = (DirContext) findNnsContext(name);
1182: Name remainingName = name.getSuffix(1);
1183: nns.modifyAttributes(remainingName, modificationItems);
1184: return;
1185: }
1186:
1187: /*
1188: * there is only one ldap ns
1189: */
1190: // get absolute dn name
1191: String targetDN = getTargetDN(name, contextDn);
1192: ModifyOp op = new ModifyOp(targetDN);
1193: for (ModificationItem item : modificationItems) {
1194: switch (item.getModificationOp()) {
1195: case DirContext.ADD_ATTRIBUTE:
1196: op.addModification(0, new LdapAttribute(item
1197: .getAttribute(), this ));
1198: break;
1199: case DirContext.REMOVE_ATTRIBUTE:
1200: op.addModification(1, new LdapAttribute(item
1201: .getAttribute(), this ));
1202: break;
1203: case DirContext.REPLACE_ATTRIBUTE:
1204: op.addModification(2, new LdapAttribute(item
1205: .getAttribute(), this ));
1206: break;
1207: default:
1208: throw new IllegalArgumentException(Messages.getString(
1209: "jndi.14", item.getModificationOp())); //$NON-NLS-1$
1210: }
1211: }
1212:
1213: try {
1214: doBasicOperation(op);
1215: } catch (ReferralException e) {
1216: if (isFollowReferral(e)) {
1217: DirContext referralContext = getReferralContext(e);
1218: referralContext.modifyAttributes(name,
1219: modificationItems);
1220: return;
1221: }
1222: throw e;
1223: }
1224: }
1225:
1226: public void modifyAttributes(String s, int i, Attributes attributes)
1227: throws NamingException {
1228: modifyAttributes(convertFromStringToName(s), i, attributes);
1229: }
1230:
1231: public void modifyAttributes(String s,
1232: ModificationItem[] modificationItems)
1233: throws NamingException {
1234: modifyAttributes(convertFromStringToName(s), modificationItems);
1235: }
1236:
1237: public void rebind(Name name, Object obj, Attributes attributes)
1238: throws NamingException {
1239: Attributes attrs = null;
1240: try {
1241: attrs = getAttributes(name);
1242: } catch (NameNotFoundException e) {
1243: // entry does not exist, just do bind
1244: bind(name, obj, attributes);
1245: return;
1246: }
1247:
1248: if (attributes == null && obj instanceof DirContext) {
1249: attributes = ((DirContext) obj).getAttributes("");
1250: if (attributes == null) {
1251: attributes = attrs;
1252: }
1253: }
1254:
1255: destroySubcontext(name);
1256:
1257: bind(name, obj, attributes);
1258: }
1259:
1260: public void rebind(String s, Object obj, Attributes attributes)
1261: throws NamingException {
1262: rebind(convertFromStringToName(s), obj, attributes);
1263: }
1264:
1265: public NamingEnumeration<SearchResult> search(Name name,
1266: Attributes attributes) throws NamingException {
1267: return search(name, attributes, null);
1268: }
1269:
1270: public NamingEnumeration<SearchResult> search(Name name,
1271: Attributes attributes, String[] as) throws NamingException {
1272: checkName(name);
1273:
1274: if (name instanceof CompositeName && name.size() > 1) {
1275: /*
1276: * multi ns, find next ns context, delegate operation to the next
1277: * context
1278: */
1279: DirContext nns = (DirContext) findNnsContext(name);
1280: Name remainingName = name.getSuffix(1);
1281: return nns.search(remainingName, attributes, as);
1282: }
1283:
1284: /*
1285: * there is only one ldap ns
1286: */
1287: // get absolute dn name
1288: String targetDN = getTargetDN(name, contextDn);
1289: Filter filter = null;
1290:
1291: // construct filter
1292: if (attributes == null || attributes.size() == 0) {
1293: filter = new Filter(Filter.PRESENT_FILTER);
1294: filter.setValue("objectClass");
1295: } else {
1296: NamingEnumeration<? extends Attribute> attrs = attributes
1297: .getAll();
1298: filter = new Filter(Filter.AND_FILTER);
1299: while (attrs.hasMore()) {
1300: Attribute attr = attrs.next();
1301: String type = attr.getID();
1302: NamingEnumeration<?> enuValues = attr.getAll();
1303: while (enuValues.hasMore()) {
1304: Object value = enuValues.next();
1305: Filter child = new Filter(
1306: Filter.EQUALITY_MATCH_FILTER);
1307: child.setValue(new AttributeTypeAndValuePair(type,
1308: value));
1309: filter.addChild(child);
1310: }
1311: }
1312: }
1313:
1314: SearchControls controls = new SearchControls();
1315: controls.setReturningAttributes(as);
1316: LdapSearchResult result = doSearch(targetDN, filter, controls);
1317:
1318: List<SearchResult> list = new ArrayList<SearchResult>();
1319: Map<String, Attributes> entries = result.getEntries();
1320: Name tempName = new LdapName(contextDn.toString());
1321: tempName.addAll(name);
1322: String baseDN = tempName.toString();
1323: for (String dn : entries.keySet()) {
1324: SearchResult sr = null;
1325: if (dn.startsWith("ldap://")) {
1326: sr = new SearchResult(dn, null, entries.get(dn), false);
1327: int index = dn.indexOf("/", 7);
1328: sr.setNameInNamespace(dn.substring(index + 1, dn
1329: .length()));
1330: list.add(sr);
1331: } else {
1332: String relativeName = convertToRelativeName(dn, baseDN);
1333: sr = new SearchResult(relativeName, null, entries
1334: .get(dn));
1335: sr.setNameInNamespace(dn);
1336: }
1337: list.add(sr);
1338: }
1339:
1340: if (list.size() == 0 && result.getException() != null) {
1341: throw result.getException();
1342: }
1343:
1344: return new LdapNamingEnumeration<SearchResult>(list, result
1345: .getException());
1346: }
1347:
1348: public NamingEnumeration<SearchResult> search(Name name,
1349: String filter, Object[] objs, SearchControls searchControls)
1350: throws NamingException {
1351: checkName(name);
1352:
1353: if (name instanceof CompositeName && name.size() > 1) {
1354: /*
1355: * multi ns, find next ns context, delegate operation to the next
1356: * context
1357: */
1358: DirContext nns = (DirContext) findNnsContext(name);
1359: Name remainingName = name.getSuffix(1);
1360: return nns.search(remainingName, filter, objs,
1361: searchControls);
1362: }
1363:
1364: /*
1365: * there is only one ldap ns
1366: */
1367:
1368: if (searchControls == null) {
1369: searchControls = new SearchControls();
1370: }
1371:
1372: // get absolute dn name
1373: String targetDN = getTargetDN(name, contextDn);
1374:
1375: Filter f = LdapUtils.parseFilter(filter, objs);
1376:
1377: LdapSearchResult result = doSearch(targetDN, f, searchControls);
1378:
1379: List<SearchResult> list = new ArrayList<SearchResult>();
1380: Map<String, Attributes> entries = result.getEntries();
1381: Name tempName = new LdapName(contextDn.toString());
1382: tempName.addAll(name);
1383: String baseDN = tempName.toString();
1384: for (String dn : entries.keySet()) {
1385: SearchResult sr = null;
1386: if (dn.startsWith("ldap://")) {
1387: sr = new SearchResult(dn, null, entries.get(dn), false);
1388: int index = dn.indexOf("/", 7);
1389: sr.setNameInNamespace(dn.substring(index + 1, dn
1390: .length()));
1391: list.add(sr);
1392: } else {
1393: String relativeName = convertToRelativeName(dn, baseDN);
1394: sr = new SearchResult(relativeName, null, entries
1395: .get(dn));
1396: sr.setNameInNamespace(dn);
1397: }
1398: list.add(sr);
1399: }
1400:
1401: if (list.size() == 0 && result.getException() != null) {
1402: throw result.getException();
1403: }
1404:
1405: return new LdapNamingEnumeration<SearchResult>(list, result
1406: .getException());
1407: }
1408:
1409: public NamingEnumeration<SearchResult> search(Name name,
1410: String filter, SearchControls searchControls)
1411: throws NamingException {
1412: return search(name, filter, new Object[0], searchControls);
1413: }
1414:
1415: public NamingEnumeration<SearchResult> search(String name,
1416: Attributes attributes) throws NamingException {
1417: return search(convertFromStringToName(name), attributes);
1418: }
1419:
1420: public NamingEnumeration<SearchResult> search(String name,
1421: Attributes attributes, String[] as) throws NamingException {
1422: return search(convertFromStringToName(name), attributes, as);
1423: }
1424:
1425: public NamingEnumeration<SearchResult> search(String name,
1426: String filter, Object[] objs, SearchControls searchControls)
1427: throws NamingException {
1428: return search(convertFromStringToName(name), filter, objs,
1429: searchControls);
1430: }
1431:
1432: public NamingEnumeration<SearchResult> search(String name,
1433: String filter, SearchControls searchControls)
1434: throws NamingException {
1435: return search(convertFromStringToName(name), filter,
1436: searchControls);
1437: }
1438:
1439: LdapSearchResult doSearch(SearchOp op) throws NamingException {
1440: if (env.get(LDAP_DEREF_ALIASES) != null) {
1441: String derefAliases = (String) env.get(LDAP_DEREF_ALIASES);
1442: if (derefAliases.equals("always")) {
1443: op.setDerefAliases(0);
1444: } else if (derefAliases.equals("never")) {
1445: op.setDerefAliases(1);
1446: } else if (derefAliases.equals("finding")) {
1447: op.setDerefAliases(2);
1448: } else if (derefAliases.equals("searching")) {
1449: op.setDerefAliases(3);
1450: } else {
1451: throw new IllegalArgumentException(
1452: Messages
1453: .getString(
1454: "ldap.30", new Object[] { env.get(LDAP_DEREF_ALIASES), //$NON-NLS-1$
1455: LDAP_DEREF_ALIASES }));
1456: }
1457:
1458: }
1459:
1460: if (env.containsKey(LDAP_TYPES_ONLY)) {
1461: String typesOnly = (String) env.get(LDAP_TYPES_ONLY);
1462: if ("true".equals(typesOnly)) {
1463: op.setTypesOnly(true);
1464: } else if ("false".equals(typesOnly)) {
1465: op.setTypesOnly(false);
1466: } else {
1467: throw new IllegalArgumentException(
1468: Messages
1469: .getString(
1470: "ldap.30", new Object[] { env.get(LDAP_TYPES_ONLY), //$NON-NLS-1$
1471: LDAP_TYPES_ONLY }));
1472: }
1473: }
1474:
1475: LdapMessage message = null;
1476: try {
1477: message = client.doOperation(op, requestControls);
1478: } catch (IOException e) {
1479: CommunicationException ex = new CommunicationException(e
1480: .getMessage());
1481: ex.setRootCause(e);
1482: if (op.getSearchResult() == null
1483: || op.getSearchResult().isEmpty()) {
1484: throw ex;
1485: }
1486: op.getSearchResult().setException(ex);
1487: // occurs errors, just return, doesn't deal with referral and
1488: // controls
1489: return op.getSearchResult();
1490: }
1491:
1492: // TODO: assume response controls returned in last message, it may
1493: // be not correct
1494: Control[] rawControls = message.getControls();
1495: responseControls = narrowingControls(rawControls);
1496:
1497: LdapResult result = op.getResult();
1498:
1499: op.getSearchResult().setException(
1500: LdapUtils.getExceptionFromResult(result));
1501:
1502: // has error, not deal with referrals
1503: if (result.getResultCode() != LdapResult.REFERRAL
1504: && op.getSearchResult().getException() != null) {
1505: return op.getSearchResult();
1506: }
1507:
1508: // baseObject is not located at the server
1509: if (result.getResultCode() == LdapResult.REFERRAL) {
1510: ReferralException ex = new ReferralExceptionImpl(contextDn
1511: .toString(), result.getReferrals(), env);
1512: try {
1513: if (isFollowReferral(ex)) {
1514: LdapContextImpl ctx = (LdapContextImpl) getReferralContext(ex);
1515: return ctx.doSearch(op);
1516: } else {
1517: op.getSearchResult().setException(ex);
1518: return op.getSearchResult();
1519: }
1520: } catch (PartialResultException e) {
1521: op.getSearchResult().setException(e);
1522: return op.getSearchResult();
1523: }
1524: }
1525:
1526: // there are SearchResultReference in search result
1527: if (op.getSearchResult().getRefURLs() != null
1528: && op.getSearchResult().getRefURLs().size() != 0) {
1529: ReferralException ex = new ReferralExceptionImpl(contextDn
1530: .toString(), op.getSearchResult().getRefURLs()
1531: .toArray(new String[0]), env);
1532: try {
1533: if (isFollowReferral(ex)) {
1534: processSearchRef(op, ex);
1535: } else {
1536: op.getSearchResult().setException(ex);
1537: return op.getSearchResult();
1538: }
1539: } catch (PartialResultException e) {
1540: op.getSearchResult().setException(e);
1541: return op.getSearchResult();
1542: }
1543: }
1544:
1545: return op.getSearchResult();
1546: }
1547:
1548: /**
1549: * Follow referrals in SearchResultReference. Referrals in
1550: * SearchResultReference is different with LDAPResult, which may contians
1551: * filter parts. Filter and dn part of url will overwrite filter and
1552: * baseObject of last search operation.
1553: *
1554: * @param op
1555: * last search operation
1556: * @param ex
1557: */
1558: private void processSearchRef(SearchOp op, ReferralException ex) {
1559: LdapSearchResult result = op.getSearchResult();
1560: List<String> urls = result.getRefURLs();
1561:
1562: // clean referrals
1563: result.setRefURLs(null);
1564:
1565: try {
1566: for (String url : urls) {
1567:
1568: LdapUrlParser urlParser = LdapUtils
1569: .parserURL(url, true);
1570: // if url has dn part overwrite baseObject of last search
1571: // operation
1572: if (!urlParser.getBaseObject().equals("")) {
1573: op.setBaseObject(urlParser.getBaseObject());
1574: }
1575: // if url has filter part overwrite filter of last search
1576: // operation
1577: if (urlParser.hasFilter()) {
1578: op.setFilter(urlParser.getFilter());
1579: }
1580: LdapContextImpl ctx = (LdapContextImpl) getReferralContext(ex);
1581: result.setAddress("ldap://" + urlParser.getHost() + ":"
1582: + urlParser.getPort() + "/");
1583: ctx.doSearch(op);
1584: result.setAddress(null);
1585: }
1586: } catch (NamingException e) {
1587: /*
1588: * occrus exception, set to search result and return, not continue
1589: * to follow referral
1590: *
1591: * TODO test the behavior of ri
1592: *
1593: */
1594: result.setException(e);
1595: return;
1596: }
1597: }
1598:
1599: LdapSearchResult doSearch(String dn, Filter filter,
1600: SearchControls controls) throws NamingException {
1601: SearchOp op = new SearchOp(dn, controls, filter);
1602: return doSearch(op);
1603: }
1604:
1605: public Object addToEnvironment(String s, Object o)
1606: throws NamingException {
1607: Object preValue = env.put(s, o);
1608:
1609: // if preValue equals o, do nothing
1610: if ((preValue != null && preValue.equals(o))
1611: || (preValue == null && o == null)) {
1612: return preValue;
1613: }
1614:
1615: updateEnvironment(s);
1616:
1617: return preValue;
1618: }
1619:
1620: public void bind(Name n, Object o) throws NamingException {
1621: bind(n, o, null);
1622: }
1623:
1624: public void bind(String s, Object o) throws NamingException {
1625: bind(convertFromStringToName(s), o);
1626: }
1627:
1628: public void close() throws NamingException {
1629: if (!isClosed) {
1630: isClosed = true;
1631: client = null;
1632: }
1633: }
1634:
1635: /**
1636: * Only instance of LdapName or CompositeName are acceptable. If both
1637: * <code>name</code> and <code>prefix</code> are LdapName, a new
1638: * LdapName instance composed of the two name will be return , else a
1639: * CompositeName will be return.
1640: */
1641: public Name composeName(Name name, Name prefix)
1642: throws NamingException {
1643: if (name == null || prefix == null) {
1644: // jndi.2E=The name is null
1645: throw new NullPointerException(Messages
1646: .getString("jndi.2E")); //$NON-NLS-1$
1647: }
1648:
1649: Name result = null;
1650: if (name instanceof LdapName && prefix instanceof LdapName) {
1651: result = (LdapName) prefix.clone();
1652: result.addAll(name);
1653: } else if (name instanceof LdapName
1654: && prefix instanceof CompositeName) {
1655: result = new CompositeName();
1656: result.addAll(prefix);
1657: result.add(name.toString());
1658: } else if (prefix instanceof LdapName
1659: && name instanceof CompositeName) {
1660: result = new CompositeName();
1661: result.add(prefix.toString());
1662: result.addAll(name);
1663: } else if (prefix instanceof CompositeName
1664: && name instanceof CompositeName) {
1665: result = new CompositeName();
1666: result.addAll(prefix);
1667: result.addAll(name);
1668: } else {
1669: throw new NamingException(Messages.getString("ldap.26")); //$NON-NLS-1$
1670: }
1671: return result;
1672: }
1673:
1674: public String composeName(String s, String pfx)
1675: throws NamingException {
1676: return composeName(convertFromStringToName(s),
1677: convertFromStringToName(pfx)).toString();
1678: }
1679:
1680: public Context createSubcontext(Name name) throws NamingException {
1681: return createSubcontext(name, null);
1682: }
1683:
1684: private Attributes getAttributesFromDN(Name name)
1685: throws InvalidNameException {
1686: if (name instanceof LdapName) {
1687: Rdn rdn = ((LdapName) name).getRdn(name.size() - 1);
1688: return rdn.toAttributes();
1689: }
1690:
1691: if (name instanceof CompositeName) {
1692: LdapName lname = new LdapName(name.get(0));
1693: Rdn rdn = lname.getRdn(lname.size() - 1);
1694: return rdn.toAttributes();
1695: }
1696:
1697: throw new InvalidNameException(Messages.getString("ldap.26")); //$NON-NLS-1$
1698: }
1699:
1700: public Context createSubcontext(String s) throws NamingException {
1701: return createSubcontext(convertFromStringToName(s));
1702: }
1703:
1704: public void destroySubcontext(Name name) throws NamingException {
1705: checkName(name);
1706:
1707: if (name instanceof CompositeName && name.size() > 1) {
1708: if (!(name.getPrefix(0) instanceof LdapName)) {
1709: throw new InvalidNameException(Messages
1710: .getString("ldap.26")); //$NON-NLS-1$
1711: }
1712: /*
1713: * multi ns, find next ns context, delegate operation to the next
1714: * context
1715: */
1716: Context nns = findNnsContext(name);
1717: Name remainingName = name.getSuffix(1);
1718: nns.destroySubcontext(remainingName);
1719: return;
1720: }
1721:
1722: /*
1723: * there is only one ldap ns
1724: */
1725: // absolute dn name to list
1726: String targetDN = getTargetDN(name, contextDn);
1727: DeleteOp op = new DeleteOp(targetDN);
1728: try {
1729: doBasicOperation(op);
1730: } catch (ReferralException e) {
1731: if (isFollowReferral(e)) {
1732: DirContext referralContext = getReferralContext(e);
1733: referralContext.destroySubcontext(name);
1734: return;
1735: }
1736: throw e;
1737: } catch (NameNotFoundException e) {
1738: // target dn doesn't exist, do nothing
1739: }
1740: }
1741:
1742: public void destroySubcontext(String s) throws NamingException {
1743: destroySubcontext(convertFromStringToName(s));
1744: }
1745:
1746: public Hashtable<?, ?> getEnvironment() throws NamingException {
1747: return (Hashtable<?, ?>) env.clone();
1748: }
1749:
1750: public String getNameInNamespace() throws NamingException {
1751: return contextDn.toString();
1752: }
1753:
1754: public NameParser getNameParser(Name name) throws NamingException {
1755: if (name instanceof CompositeName && name.size() > 1) {
1756: if (!(name.getPrefix(0) instanceof LdapName)) {
1757: throw new InvalidNameException(Messages
1758: .getString("ldap.26")); //$NON-NLS-1$
1759: }
1760: /*
1761: * multi ns, find next ns context, delegate operation to the next
1762: * context
1763: */
1764: Context nns = findNnsContext(name);
1765: Name remainingName = name.getSuffix(1);
1766: return nns.getNameParser(remainingName);
1767: }
1768:
1769: return parser;
1770: }
1771:
1772: public NameParser getNameParser(String s) throws NamingException {
1773: return getNameParser(convertFromStringToName(s));
1774: }
1775:
1776: public NamingEnumeration<NameClassPair> list(Name name)
1777: throws NamingException {
1778: checkName(name);
1779:
1780: if (name instanceof CompositeName && name.size() > 1) {
1781: if (!(name.getPrefix(0) instanceof LdapName)) {
1782: throw new InvalidNameException(Messages
1783: .getString("ldap.26")); //$NON-NLS-1$
1784: }
1785: /*
1786: * multi ns, find next ns context, delegate operation to the next
1787: * context
1788: */
1789: Context nns = findNnsContext(name);
1790: Name remainingName = name.getSuffix(1);
1791: return nns.list(remainingName);
1792: }
1793:
1794: /*
1795: * there is only one ldap ns
1796: */
1797: // absolute dn name to list
1798: String targetDN = getTargetDN(name, contextDn);
1799:
1800: // construct one-level search using filter "(objectclass=*)"
1801: SearchControls controls = new SearchControls();
1802: controls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
1803: Filter filter = new Filter(Filter.PRESENT_FILTER);
1804: filter.setValue("objectClass");
1805:
1806: LdapSearchResult result = doSearch(targetDN, filter, controls);
1807:
1808: List<NameClassPair> list = new ArrayList<NameClassPair>();
1809: Map<String, Attributes> entries = result.getEntries();
1810: Name tempName = new LdapName(contextDn.toString());
1811: tempName.addAll(name);
1812: String baseDN = tempName.toString();
1813: for (String dn : entries.keySet()) {
1814: String relativeName = convertToRelativeName(dn, baseDN);
1815: Attributes attrs = entries.get(dn);
1816: Attribute attrClass = attrs.get("javaClassName");
1817: String className = null;
1818: if (attrClass != null) {
1819: className = (String) attrClass.get(0);
1820: } else {
1821: className = DirContext.class.getName();
1822: }
1823: NameClassPair pair = new NameClassPair(relativeName,
1824: className, true);
1825: pair.setNameInNamespace(dn);
1826: list.add(pair);
1827: }
1828:
1829: // no entries return
1830: if (list.size() == 0 && result.getException() != null) {
1831: throw result.getException();
1832: }
1833:
1834: return new LdapNamingEnumeration<NameClassPair>(list, result
1835: .getException());
1836: }
1837:
1838: /**
1839: * convert absolute dn to the dn relatived to the dn of
1840: * <code>targetContextDN</code>.
1841: *
1842: * @param dn
1843: * absolute dn
1844: * @param base
1845: * base dn of the relative name
1846: * @return dn relatived to the <code>dn</code> of <code>base</code>
1847: */
1848: protected String convertToRelativeName(String dn, String base) {
1849:
1850: if (base.equals("")) {
1851: return dn;
1852: }
1853:
1854: int index = dn.lastIndexOf(base);
1855: if (index == 0) {
1856: return "";
1857: }
1858:
1859: return dn.substring(0, index - 1);
1860: }
1861:
1862: protected String getTargetDN(Name name, Name prefix)
1863: throws NamingException, InvalidNameException {
1864: Name target = null;
1865: if (name.size() == 0) {
1866: target = prefix;
1867: } else if (name instanceof LdapName) {
1868: target = composeName(name, prefix);
1869: } else if (name instanceof CompositeName) {
1870: LdapName alt = new LdapName(name.get(0));
1871: target = composeName(alt, prefix);
1872: } else {
1873: throw new InvalidNameException(Messages
1874: .getString("ldap.26")); //$NON-NLS-1$
1875: }
1876: return target.toString();
1877: }
1878:
1879: protected Context findNnsContext(Name name) throws NamingException {
1880: CannotProceedException cpe = null;
1881: if (env.containsKey(NamingManager.CPE)) {
1882: cpe = (CannotProceedException) env.get(NamingManager.CPE);
1883: } else {
1884: cpe = new CannotProceedException();
1885: }
1886:
1887: Name remainingName = name.getSuffix(1);
1888: Name altName = name.getPrefix(0);
1889: Name targetName = composeName(altName, contextDn);
1890:
1891: Name resolvedName = cpe.getResolvedName();
1892: if (resolvedName == null) {
1893: resolvedName = new CompositeName();
1894:
1895: } else if (resolvedName.size() >= 2
1896: && resolvedName.get(resolvedName.size() - 1).equals("")) {
1897: // remove the last component if it is ""
1898: // (the sign of the next naming system), so there must be at least
1899: // one name before "".
1900: resolvedName.remove(resolvedName.size() - 1);
1901: }
1902:
1903: resolvedName.add(targetName.toString());
1904: // add empty component name to indicate nns pointer
1905: resolvedName.add("");
1906:
1907: cpe.setAltName(altName);
1908: cpe.setAltNameCtx(this );
1909: cpe.setEnvironment((Hashtable<Object, Object>) env.clone());
1910: cpe.setRemainingName(remainingName);
1911: cpe.setResolvedName(resolvedName);
1912:
1913: final LdapContextImpl context = new LdapContextImpl(this , env,
1914: composeName(altName, contextDn).toString());
1915:
1916: RefAddr addr = new RefAddr("nns") { //$NON-NLS-1$
1917:
1918: private static final long serialVersionUID = -5428706819217461955L;
1919:
1920: @Override
1921: public Object getContent() {
1922: return context;
1923: }
1924:
1925: };
1926:
1927: Reference ref = new Reference(context.getClass().getName(),
1928: addr);
1929: cpe.setResolvedObj(ref);
1930:
1931: return DirectoryManager.getContinuationDirContext(cpe);
1932: }
1933:
1934: public NamingEnumeration<NameClassPair> list(String s)
1935: throws NamingException {
1936: return list(convertFromStringToName(s));
1937: }
1938:
1939: public NamingEnumeration<Binding> listBindings(Name name)
1940: throws NamingException {
1941: checkName(name);
1942:
1943: if (name instanceof CompositeName && name.size() > 1) {
1944: /*
1945: * multi ns, find next ns context, delegate operation to the next
1946: * contex
1947: */
1948: DirContext nns = (DirContext) findNnsContext(name);
1949: Name remainingName = name.getSuffix(1);
1950: return nns.listBindings(remainingName);
1951: }
1952:
1953: /*
1954: * there is only one ldap ns
1955: */
1956:
1957: NamingEnumeration<NameClassPair> enu = list(name);
1958:
1959: List<Binding> bindings = new ArrayList<Binding>();
1960:
1961: while (enu.hasMore()) {
1962: NameClassPair pair = enu.next();
1963: Object bound = null;
1964: if (!pair.getClassName().equals(DirContext.class.getName())) {
1965: bound = lookup(pair.getName());
1966: } else {
1967: bound = new LdapContextImpl(this , env, contextDn
1968: .toString());
1969: }
1970:
1971: Binding binding = new Binding(pair.getName(), bound
1972: .getClass().getName(), bound);
1973: binding.setNameInNamespace(pair.getNameInNamespace());
1974: bindings.add(binding);
1975:
1976: }
1977:
1978: // FIXME: deal with exception
1979: return new LdapNamingEnumeration<Binding>(bindings, null);
1980: }
1981:
1982: public NamingEnumeration<Binding> listBindings(String s)
1983: throws NamingException {
1984: return listBindings(convertFromStringToName(s));
1985: }
1986:
1987: public Object lookup(Name name) throws NamingException {
1988: checkName(name);
1989:
1990: if (name instanceof CompositeName && name.size() > 1) {
1991: /*
1992: * multi ns, find next ns context, delegate operation to the next
1993: * context
1994: */
1995: DirContext nns = (DirContext) findNnsContext(name);
1996: Name remainingName = name.getSuffix(1);
1997: return nns.lookup(remainingName);
1998: }
1999:
2000: /*
2001: * there is only one ldap ns
2002: */
2003: Attributes attributes = getAttributes(name);
2004: if (!hasAttribute(attributes, "objectClass", "javaContainer")
2005: && !hasAttribute(attributes, "objectClass",
2006: "javaObject")) {
2007: // this is no java object, return the context
2008:
2009: // get absolute dn name
2010: String targetDN = getTargetDN(name, contextDn);
2011: return new LdapContextImpl(this , env, targetDN);
2012: }
2013:
2014: Object boundObject = null;
2015: // serializable object
2016: if (hasAttribute(attributes, "objectClass",
2017: "javaSerializedObject")) {
2018: byte[] data = (byte[]) attributes.get("javaSerializedData")
2019: .get();
2020: ObjectInputStream in = null;
2021: try {
2022: in = new ObjectInputStream(new ByteArrayInputStream(
2023: data));
2024: boundObject = in.readObject();
2025: } catch (IOException e) {
2026: NamingException ex = new NamingException();
2027: ex.setRootCause(e);
2028: throw ex;
2029: } catch (ClassNotFoundException e) {
2030: // TODO use javaCodebase attribute to load class defination
2031: } finally {
2032: if (in != null) {
2033: try {
2034: in.close();
2035: } catch (IOException e) {
2036: // ignore
2037: }
2038: }
2039: }
2040: } else if (hasAttribute(attributes, "objectClass",
2041: "javaNamingReference")) {
2042: String className = (String) attributes.get("javaClassName")
2043: .get();
2044:
2045: Attribute temp = attributes.get("javaFactory");
2046: String factory = null;
2047: if (temp != null) {
2048: factory = (String) temp.get();
2049: }
2050:
2051: temp = attributes.get("javaCodebase");
2052: String location = null;
2053: if (temp != null) {
2054: location = (String) temp.get();
2055: }
2056:
2057: Reference ref = new Reference(className, factory, location);
2058: Attribute refAddress = attributes
2059: .get("javaReferenceAddress");
2060: if (refAddress != null) {
2061: NamingEnumeration<?> enu = refAddress.getAll();
2062: String separator = (String) env
2063: .get("java.naming.ldap.ref.separator");
2064: if (separator == null) {
2065: separator = "#";
2066: }
2067: TreeMap<Integer, StringRefAddr> addrsMap = new TreeMap<Integer, StringRefAddr>();
2068:
2069: // sort addresses to TreeMap
2070: while (enu.hasMore()) {
2071: String address = (String) enu.next();
2072: StringTokenizer st = new StringTokenizer(address,
2073: separator);
2074: int index = Integer.parseInt(st.nextToken());
2075: String type = st.nextToken();
2076: String content = st.nextToken();
2077: StringRefAddr refAddr = new StringRefAddr(type,
2078: content);
2079: addrsMap.put(Integer.valueOf(index), refAddr);
2080: // ref.add(index, refAddr);
2081: }
2082:
2083: for (StringRefAddr addr : addrsMap.values()) {
2084: ref.add(addr);
2085: }
2086: }
2087: boundObject = ref;
2088: }
2089:
2090: try {
2091: boundObject = DirectoryManager.getObjectInstance(
2092: boundObject, name, this , env);
2093: if (boundObject == null) {
2094: boundObject = new LdapContextImpl(this , env,
2095: getTargetDN(name, contextDn));
2096: }
2097: return boundObject;
2098:
2099: } catch (NamingException e) {
2100: throw e;
2101: } catch (Exception e) {
2102: // jndi.83=NamingManager.getObjectInstance() failed
2103: throw (NamingException) new NamingException(Messages
2104: .getString("jndi.83")).initCause(e); //$NON-NLS-1$
2105: }
2106: }
2107:
2108: private boolean hasAttribute(Attributes attributes, String type,
2109: Object value) throws NamingException {
2110: Attribute attr = attributes.get(type);
2111: if (attr == null) {
2112: return false;
2113: }
2114:
2115: NamingEnumeration<?> enu = attr.getAll();
2116: while (enu.hasMore()) {
2117: Object o = enu.next();
2118: if (value.equals(o)) {
2119: return true;
2120: }
2121: }
2122:
2123: return false;
2124: }
2125:
2126: public Object lookup(String s) throws NamingException {
2127: return lookup(convertFromStringToName(s));
2128: }
2129:
2130: /**
2131: * convert <code>String</code> name to <code>Name</code> instance, we
2132: * assume the <code>String</code> name parameter is using composite name
2133: * syntax (see LDAP service providers guidlines, part 4).
2134: *
2135: * @param s
2136: * <code>String</code> name to be converted
2137: * @return <code>Name</code> instance equivalent to <code>s</code>
2138: * @throws InvalidNameException
2139: * occurs error while converting
2140: */
2141: protected Name convertFromStringToName(String s)
2142: throws InvalidNameException {
2143: if (s == null) {
2144: // jndi.2E=The name is null
2145: throw new NullPointerException(Messages
2146: .getString("jndi.2E")); //$NON-NLS-1$
2147: }
2148:
2149: CompositeName name = new CompositeName(s);
2150: if (name.size() == 0) {
2151: // return empty name
2152: return new LdapName(""); //$NON-NLS-1$
2153: }
2154:
2155: return name;
2156: }
2157:
2158: public Object lookupLink(Name name) throws NamingException {
2159: return lookup(name);
2160: }
2161:
2162: public Object lookupLink(String s) throws NamingException {
2163: return lookupLink(convertFromStringToName(s));
2164: }
2165:
2166: public void rebind(Name n, Object o) throws NamingException {
2167: rebind(n, o, null);
2168: }
2169:
2170: public void rebind(String s, Object o) throws NamingException {
2171: rebind(convertFromStringToName(s), o);
2172: }
2173:
2174: public Object removeFromEnvironment(String s)
2175: throws NamingException {
2176: Object preValue = env.remove(s);
2177:
2178: // if s doesn't exist in env
2179: if (preValue == null) {
2180: return preValue;
2181: }
2182:
2183: updateEnvironment(s);
2184:
2185: return preValue;
2186: }
2187:
2188: private void updateEnvironment(String propName)
2189: throws NamingException,
2190: AuthenticationNotSupportedException,
2191: CommunicationException, ConfigurationException {
2192: if (connectionProperties.contains(propName)) {
2193: if (propName.equals("java.naming.ldap.factory.socket")) {
2194: // use new socket factory to connect server
2195: String address = client.getAddress();
2196: int port = client.getPort();
2197:
2198: client = LdapClient.newInstance(address, port, env);
2199: try {
2200: doBindOperation(connCtls);
2201: } catch (IOException e) {
2202: CommunicationException ex = new CommunicationException();
2203: ex.setRootCause(e);
2204: throw ex;
2205: }
2206: } else {
2207:
2208: reconnect(connCtls);
2209: }
2210: }
2211: }
2212:
2213: public void rename(Name nOld, Name nNew) throws NamingException {
2214: checkName(nOld);
2215: checkName(nNew);
2216:
2217: if (!isInSameNamespace(nOld, nNew)) {
2218: throw new InvalidNameException(Messages
2219: .getString("ldap.2A")); //$NON-NLS-1$
2220: }
2221:
2222: if (nOld instanceof CompositeName && nOld.size() > 1
2223: && nNew instanceof CompositeName && nNew.size() > 1) {
2224: Context context = findNnsContext(nOld);
2225: context.rename(nOld.getSuffix(1), nNew.getSuffix(1));
2226: return;
2227: }
2228:
2229: // get absolute dn name
2230: String oldTargetDN = getTargetDN(nOld, contextDn);
2231: String newTargetDN = getTargetDN(nNew, contextDn);
2232: LdapName name = new LdapName(newTargetDN);
2233: Rdn rdn = name.getRdn(name.size() - 1);
2234: String value = (String) env.get(LDAP_DELETE_RDN);
2235: // true is default value
2236: boolean isDeleteRdn = true;
2237: if (value != null) {
2238: isDeleteRdn = Boolean.getBoolean(value);
2239: }
2240:
2241: ModifyDNOp op = new ModifyDNOp(oldTargetDN, rdn.toString(),
2242: isDeleteRdn, name.getPrefix(name.size() - 1).toString());
2243:
2244: try {
2245: doBasicOperation(op);
2246: } catch (ReferralException e) {
2247: if (isFollowReferral(e)) {
2248: DirContext referralContext = getReferralContext(e);
2249: referralContext.rename(nOld, nNew);
2250: return;
2251: }
2252: throw e;
2253: }
2254: }
2255:
2256: private boolean isInSameNamespace(Name first, Name second) {
2257: if (first instanceof CompositeName
2258: && second instanceof CompositeName) {
2259: // TODO need more test in detail
2260: return first.size() == second.size();
2261: }
2262:
2263: if (first instanceof LdapName && second instanceof LdapName) {
2264: return true;
2265: }
2266:
2267: return false;
2268: }
2269:
2270: public void rename(String sOld, String sNew) throws NamingException {
2271: rename(convertFromStringToName(sOld),
2272: convertFromStringToName(sNew));
2273: }
2274:
2275: public void unbind(Name n) throws NamingException {
2276: // unbind and destroySubcontext do the same thing
2277: destroySubcontext(n);
2278: }
2279:
2280: public void unbind(String s) throws NamingException {
2281: unbind(convertFromStringToName(s));
2282: }
2283:
2284: /**
2285: * Do operations with one request and one response which contains
2286: * LdapResult. This is the convenience way to do the most of ldap operation
2287: * except search opeartion which has multi-response, bind operation, abandon
2288: * and unbind operations which have no response.
2289: *
2290: * @param op
2291: * @throws NamingException
2292: */
2293: protected void doBasicOperation(LdapOperation op)
2294: throws NamingException {
2295: LdapMessage message = null;
2296: try {
2297: message = client.doOperation(op, requestControls);
2298: } catch (IOException e) {
2299: CommunicationException ex = new CommunicationException();
2300: ex.setRootCause(e);
2301: // operation failed, clear responseControls
2302: responseControls = null;
2303: throw ex;
2304: }
2305:
2306: Control[] rawControls = message.getControls();
2307: responseControls = narrowingControls(rawControls);
2308:
2309: LdapResult result = op.getResult();
2310: if (result.getResultCode() == LdapResult.REFERRAL) {
2311: throw new ReferralExceptionImpl(contextDn.toString(),
2312: result.getReferrals(), env);
2313: }
2314:
2315: if (LdapUtils.getExceptionFromResult(result) != null) {
2316: throw LdapUtils.getExceptionFromResult(result);
2317: }
2318: }
2319:
2320: private boolean isFollowReferral(ReferralException e)
2321: throws ReferralException, PartialResultException {
2322: // ignore referral
2323: String action = (String) env.get(Context.REFERRAL);
2324: if (action == null) {
2325: action = "ignore";
2326: }
2327:
2328: if ("follow".equals(action)) {
2329: return true;
2330: } else if ("throw".equals(action)) {
2331: return false;
2332:
2333: } else if ("ignore".equals(action)) {
2334: // ldap.1A=[LDAP: error code 10 - Referral]
2335: throw new PartialResultException(Messages
2336: .getString("ldap.1A"));
2337:
2338: } else {
2339: throw new IllegalArgumentException(Messages.getString(
2340: "ldap.30", new Object[] { //$NON-NLS-1$
2341: env.get(Context.REFERRAL), Context.REFERRAL }));
2342: }
2343: }
2344:
2345: /**
2346: * convert raw controls to particular type of controls using
2347: * <code>getControlInstance(Control, Context,
2348: Hashtable<?, ?>)</code>
2349: *
2350: * @param rawControls
2351: * raw controls
2352: * @return particular type of controls
2353: *
2354: * @throws NamingException
2355: */
2356: private Control[] narrowingControls(Control[] rawControls)
2357: throws NamingException {
2358: if (rawControls == null) {
2359: return null;
2360: }
2361:
2362: Control[] controls = new Control[rawControls.length];
2363: for (int i = 0; i < rawControls.length; ++i) {
2364: controls[i] = ControlFactory.getControlInstance(
2365: rawControls[i], this , env);
2366: }
2367:
2368: return controls;
2369: }
2370:
2371: private void checkName(Name name) {
2372: if (name == null) {
2373: // jndi.2E=The name is null
2374: throw new NullPointerException(Messages
2375: .getString("jndi.2E")); //$NON-NLS-1$
2376: }
2377: }
2378:
2379: @Override
2380: protected void finalize() {
2381: try {
2382: close();
2383: } catch (NamingException e) {
2384: // ignore
2385: }
2386: }
2387:
2388: public void addNamingListener(Name name, String filter,
2389: Object[] filterArgs, SearchControls searchControls,
2390: NamingListener namingListener) throws NamingException {
2391: checkName(name);
2392:
2393: if (namingListener == null) {
2394: return;
2395: }
2396:
2397: if (!(name instanceof LdapName)) {
2398: if (name instanceof CompositeName && name.size() == 1) {
2399: name = name.getPrefix(1);
2400: } else {
2401: // FIXME: read message from file
2402: throw new InvalidNameException(
2403: "Target cannot span multiple namespaces: "
2404: + name.toString());
2405: }
2406: }
2407:
2408: if (namingListener instanceof UnsolicitedNotificationListener) {
2409: if (unls == null) {
2410: unls = new ArrayList<UnsolicitedNotificationListener>();
2411: addUnsolicitedListener();
2412: }
2413:
2414: unls.add((UnsolicitedNotificationListener) namingListener);
2415:
2416: if (!(namingListener instanceof NamespaceChangeListener)
2417: && !(namingListener instanceof ObjectChangeListener)) {
2418: return;
2419: }
2420: }
2421:
2422: if (searchControls == null) {
2423: searchControls = new SearchControls();
2424: }
2425:
2426: Filter f = LdapUtils.parseFilter(filter, filterArgs);
2427:
2428: String targetDN = getTargetDN(name, contextDn);
2429:
2430: Name tempName = new LdapName(contextDn.toString());
2431: tempName.addAll(name);
2432: String baseDN = tempName.toString();
2433:
2434: int messageId = doPersistentSearch(targetDN, baseDN, f,
2435: searchControls, namingListener);
2436:
2437: if (listeners == null) {
2438: listeners = new HashMap<NamingListener, List<Integer>>();
2439: }
2440:
2441: List<Integer> idList = listeners.get(namingListener);
2442: if (idList == null) {
2443: idList = new ArrayList<Integer>();
2444: }
2445:
2446: idList.add(Integer.valueOf(messageId));
2447: }
2448:
2449: public void addNamingListener(Name name, String filter,
2450: SearchControls searchControls, NamingListener namingListener)
2451: throws NamingException {
2452: addNamingListener(name, filter, new Object[0], searchControls,
2453: namingListener);
2454: }
2455:
2456: public void addNamingListener(String name, String filter,
2457: Object[] filterArgs, SearchControls searchControls,
2458: NamingListener namingListener) throws NamingException {
2459: addNamingListener(convertFromStringToName(name), filter,
2460: filterArgs, searchControls, namingListener);
2461: }
2462:
2463: public void addNamingListener(String name, String filter,
2464: SearchControls searchControls, NamingListener namingListener)
2465: throws NamingException {
2466: addNamingListener(convertFromStringToName(name), filter,
2467: searchControls, namingListener);
2468: }
2469:
2470: public static interface UnsolicitedListener {
2471: public void receiveNotification(UnsolicitedNotificationImpl un,
2472: Control[] cs);
2473: }
2474:
2475: public void addNamingListener(Name name, int scope,
2476: NamingListener namingListener) throws NamingException {
2477: checkName(name);
2478:
2479: if (namingListener == null) {
2480: return;
2481: }
2482:
2483: // only ldap name is supportted
2484: if (!(name instanceof LdapName)) {
2485: if (name instanceof CompositeName && name.size() == 1) {
2486: name = name.getPrefix(0);
2487: } else {
2488: // ldap.32=Target cannot span multiple namespaces: {0}
2489: throw new InvalidNameException(Messages.getString(
2490: "ldap.32", //$NON-NLS-1$
2491: new Object[] { name.toString() }));
2492: }
2493: }
2494:
2495: if (namingListener instanceof UnsolicitedNotificationListener) {
2496: if (unls == null) {
2497: unls = new ArrayList<UnsolicitedNotificationListener>();
2498: addUnsolicitedListener();
2499: }
2500:
2501: unls.add((UnsolicitedNotificationListener) namingListener);
2502:
2503: if (!(namingListener instanceof NamespaceChangeListener)
2504: && !(namingListener instanceof ObjectChangeListener)) {
2505: return;
2506: }
2507: }
2508:
2509: // ri is silent in this case
2510: if (scope != EventContext.OBJECT_SCOPE
2511: && scope != EventContext.ONELEVEL_SCOPE
2512: && scope != EventContext.SUBTREE_SCOPE) {
2513: // ldap.33=Scope should be one of 'OBJECT_SCOPE', 'ONELEVEL_SCOPE'
2514: // or 'SUBTREE_SCOPE'
2515: throw new IllegalArgumentException(Messages
2516: .getString("ldap.33")); //$NON-NLS-1$
2517: }
2518:
2519: String targetDN = getTargetDN(name, contextDn);
2520:
2521: Filter filter = new Filter(Filter.PRESENT_FILTER);
2522: filter.setValue("objectClass");
2523:
2524: SearchControls controls = new SearchControls();
2525: controls.setSearchScope(scope);
2526:
2527: Name tempName = new LdapName(contextDn.toString());
2528: tempName.addAll(name);
2529: String baseDN = tempName.toString();
2530:
2531: int messageId = doPersistentSearch(targetDN, baseDN, filter,
2532: controls, namingListener);
2533:
2534: if (listeners == null) {
2535: listeners = new HashMap<NamingListener, List<Integer>>();
2536: }
2537:
2538: List<Integer> idList = listeners.get(namingListener);
2539: if (idList == null) {
2540: idList = new ArrayList<Integer>();
2541: listeners.put(namingListener, idList);
2542: }
2543:
2544: idList.add(Integer.valueOf(messageId));
2545:
2546: }
2547:
2548: private void addUnsolicitedListener() {
2549: client.addUnsolicitedListener(new UnsolicitedListener() {
2550:
2551: public void receiveNotification(
2552: UnsolicitedNotificationImpl un, Control[] cs) {
2553: EventObject event = null;
2554: try {
2555: un.setControls(narrowingControls(cs));
2556: event = new UnsolicitedNotificationEvent(this , un);
2557: } catch (NamingException e) {
2558: event = new NamingExceptionEvent(
2559: LdapContextImpl.this , e);
2560: }
2561:
2562: for (UnsolicitedNotificationListener listener : unls) {
2563: notifyNamingListener(listener, event);
2564: }
2565:
2566: }
2567:
2568: });
2569: }
2570:
2571: private int doPersistentSearch(String targetDN,
2572: final String baseDN, Filter filter,
2573: SearchControls controls, NamingListener namingListener)
2574: throws CommunicationException {
2575:
2576: SearchOp op = new SearchOp(targetDN, controls, filter);
2577:
2578: final NamingListener listener = namingListener;
2579: op.setSearchResult(new PersistentSearchResult() {
2580:
2581: @Override
2582: public void receiveNotificationHook(Object obj) {
2583: EventObject event = null;
2584: // construct event
2585: if (obj instanceof ECNotificationControl) {
2586: ECNotificationControl control = (ECNotificationControl) obj;
2587: event = constructNamingEvent(this , control, baseDN);
2588: }
2589:
2590: if (obj instanceof LdapResult) {
2591: LdapResult ldapResult = (LdapResult) obj;
2592: NamingException ex = LdapUtils
2593: .getExceptionFromResult(ldapResult);
2594: // may not happen
2595: if (ex == null) {
2596: return;
2597: }
2598:
2599: event = new NamingExceptionEvent(
2600: LdapContextImpl.this , ex);
2601: }
2602:
2603: // notify listener
2604: notifyNamingListener(listener, event);
2605: }
2606:
2607: });
2608:
2609: try {
2610: return client.addPersistentSearch(op);
2611: } catch (IOException e) {
2612: CommunicationException ex = new CommunicationException();
2613: ex.setRootCause(e);
2614: throw ex;
2615: }
2616: }
2617:
2618: private void notifyNamingListener(final NamingListener listener,
2619: final EventObject event) {
2620: /*
2621: * start new thread to notify listener, so user code may not affect
2622: * dispatcher thread
2623: */
2624: Thread thread = new Thread(new Runnable() {
2625:
2626: public void run() {
2627: if (event instanceof NamingEvent) {
2628: NamingEvent namingEvent = (NamingEvent) event;
2629: namingEvent.dispatch(listener);
2630: } else if (event instanceof NamingExceptionEvent) {
2631: NamingExceptionEvent exceptionEvent = (NamingExceptionEvent) event;
2632: listener.namingExceptionThrown(exceptionEvent);
2633: } else if (event instanceof UnsolicitedNotificationEvent) {
2634: UnsolicitedNotificationEvent namingEvent = (UnsolicitedNotificationEvent) event;
2635: namingEvent
2636: .dispatch((UnsolicitedNotificationListener) listener);
2637: }
2638:
2639: }
2640:
2641: });
2642:
2643: thread.start();
2644: }
2645:
2646: public void addNamingListener(String s, int i,
2647: NamingListener namingListener) throws NamingException {
2648: addNamingListener(convertFromStringToName(s), i, namingListener);
2649: }
2650:
2651: public void removeNamingListener(NamingListener namingListener)
2652: throws NamingException {
2653: if (listeners == null || !listeners.containsKey(namingListener)) {
2654: return;
2655: }
2656:
2657: if (namingListener instanceof UnsolicitedNotificationListener) {
2658: unls.remove(namingListener);
2659: }
2660:
2661: List<Integer> idList = listeners.remove(namingListener);
2662: if (idList == null) {
2663: return;
2664: }
2665:
2666: try {
2667: for (Integer id : idList) {
2668: client.removePersistentSearch(id.intValue(),
2669: requestControls);
2670: }
2671: } catch (IOException e) {
2672: CommunicationException ex = new CommunicationException();
2673: ex.setRootCause(e);
2674: }
2675: }
2676:
2677: public boolean targetMustExist() throws NamingException {
2678: // FIXME
2679: return false;
2680: }
2681:
2682: private NamingEvent constructNamingEvent(
2683: PersistentSearchResult result,
2684: ECNotificationControl control, String baseDN) {
2685: Binding newBinding = null;
2686: Binding oldBinding = null;
2687:
2688: switch (control.getChangeType()) {
2689: case ECNotificationControl.ADD:
2690: String newName = convertToRelativeName(result.getDn(),
2691: baseDN);
2692: newBinding = new Binding(newName, null);
2693: newBinding.setNameInNamespace(result.getDn());
2694: break;
2695: case ECNotificationControl.DELETE:
2696: String deleteName = convertToRelativeName(result.getDn(),
2697: baseDN);
2698: oldBinding = new Binding(deleteName, null);
2699: oldBinding.setNameInNamespace(result.getDn());
2700: break;
2701: case ECNotificationControl.MODIFY_DN:
2702: if (result.getDn() != null) {
2703: newBinding = new Binding(convertToRelativeName(result
2704: .getDn(), baseDN), null);
2705: newBinding.setNameInNamespace(result.getDn());
2706: }
2707:
2708: if (control.getPreviousDN() != null) {
2709: oldBinding = new Binding(convertToRelativeName(control
2710: .getPreviousDN(), baseDN), null);
2711: oldBinding.setNameInNamespace(control.getPreviousDN());
2712: }
2713: break;
2714: case ECNotificationControl.MODIFY:
2715: String relativeName = convertToRelativeName(result.getDn(),
2716: baseDN);
2717: newBinding = new Binding(relativeName, null);
2718: newBinding.setNameInNamespace(result.getDn());
2719: // FIXME: how to get old binding?
2720: oldBinding = new Binding(relativeName, null);
2721: oldBinding.setNameInNamespace(result.getDn());
2722: }
2723:
2724: NamingEvent event = new NamingEvent(this, control
2725: .getJNDIChangeType(), newBinding, oldBinding, Integer
2726: .valueOf(control.getChangeNumber()));
2727:
2728: return event;
2729: }
2730: }
|