001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2011-2016 ForgeRock AS. 016 */ 017package org.opends.guitools.controlpanel.datamodel; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.SortedSet; 025import java.util.TreeSet; 026 027import javax.naming.CompositeName; 028import javax.naming.Name; 029import javax.naming.NamingEnumeration; 030import javax.naming.NamingException; 031import javax.naming.directory.Attribute; 032import javax.naming.directory.Attributes; 033import javax.naming.directory.SearchResult; 034 035import org.forgerock.opendj.ldap.AttributeDescription; 036import org.forgerock.opendj.ldap.ByteString; 037import org.forgerock.opendj.ldap.DN; 038import org.forgerock.opendj.ldap.schema.AttributeType; 039import org.opends.guitools.controlpanel.util.Utilities; 040import org.opends.server.core.DirectoryServer; 041import org.opends.server.types.AttributeBuilder; 042import org.opends.server.types.Entry; 043import org.forgerock.opendj.ldap.schema.ObjectClass; 044import org.opends.server.types.OpenDsException; 045import org.opends.server.util.LDIFReader; 046 047/** 048 * This is a commodity class used to wrap the SearchResult class of JNDI. 049 * Basically it retrieves all the attributes and values on the SearchResult and 050 * calculates its DN. Using it we avoid having to handle the NamingException 051 * exceptions that most of the methods in SearchResult throw. 052 */ 053public class CustomSearchResult implements Comparable<CustomSearchResult> 054{ 055 private final String dn; 056 private Map<String, List<Object>> attributes; 057 private SortedSet<String> attrNames; 058 private String toString; 059 private int hashCode; 060 061 /** 062 * Constructor of an empty search result. This constructor is used by the 063 * LDAP entry editor which 'build' their own CustomSearchResult. The entry 064 * editors use some methods that require CustomSearchResult. 065 * @param dn the dn of the entry. 066 */ 067 public CustomSearchResult(String dn) 068 { 069 this.dn = dn; 070 attributes = new HashMap<>(); 071 attrNames = new TreeSet<>(); 072 toString = calculateToString(); 073 hashCode = calculateHashCode(); 074 } 075 076 /** 077 * Constructor of a search result using a SearchResult as basis. 078 * @param sr the SearchResult. 079 * @param baseDN the base DN of the search that returned the SearchResult. 080 * @throws NamingException if there is an error retrieving the attribute 081 * values. 082 */ 083 public CustomSearchResult(SearchResult sr, String baseDN) 084 throws NamingException 085 { 086 String sName = sr.getName(); 087 Name name; 088 if (baseDN != null && baseDN.length() > 0) 089 { 090 if (sName != null && sName.length() > 0) 091 { 092 name = new CompositeName(sName); 093 name.add(baseDN); 094 } 095 else { 096 name = Utilities.getJNDIName(baseDN); 097 } 098 } 099 else { 100 name = new CompositeName(sName); 101 } 102 StringBuilder buf = new StringBuilder(); 103 for (int i=0; i<name.size(); i++) 104 { 105 String n = name.get(i); 106 if (n != null && n.length() > 0) 107 { 108 if (buf.length() != 0) 109 { 110 buf.append(","); 111 } 112 buf.append(n); 113 } 114 } 115 dn = buf.toString(); 116 117 attributes = new HashMap<>(); 118 attrNames = new TreeSet<>(); 119 Attributes attrs = sr.getAttributes(); 120 if (attrs != null) 121 { 122 NamingEnumeration<?> en = attrs.getAll(); 123 try 124 { 125 while (en.hasMore()) { 126 Attribute attr = (Attribute)en.next(); 127 String attrName = attr.getID(); 128 attrNames.add(attrName); 129 List<Object> values = new ArrayList<>(); 130 for (int i=0; i<attr.size(); i++) 131 { 132 Object v = attr.get(i); 133 if (!"".equals(v.toString())) 134 { 135 values.add(v); 136 } 137 } 138 attributes.put(attrName.toLowerCase(), values); 139 } 140 } 141 finally 142 { 143 en.close(); 144 } 145 } 146 toString = calculateToString(); 147 hashCode = calculateHashCode(); 148 } 149 150 /** 151 * Returns the DN of the entry. 152 * @return the DN of the entry. 153 */ 154 public String getDN() { 155 return dn; 156 } 157 158 /** 159 * Returns the values for a given attribute. It returns an empty Set if 160 * the attribute is not defined. 161 * @param name the name of the attribute. 162 * @return the values for a given attribute. It returns an empty Set if 163 * the attribute is not defined. 164 */ 165 public List<Object> getAttributeValues(String name) { 166 List<Object> values = attributes.get(name.toLowerCase()); 167 if (values == null) 168 { 169 values = Collections.emptyList(); 170 } 171 return values; 172 } 173 174 /** 175 * Returns all the attribute names of the entry. 176 * @return the attribute names of the entry. 177 */ 178 public SortedSet<String> getAttributeNames() { 179 return attrNames; 180 } 181 182 @Override 183 public int compareTo(CustomSearchResult o) { 184 if (this.equals(o)) 185 { 186 return 0; 187 } 188 return toString().compareTo(o.toString()); 189 } 190 191 @Override 192 public boolean equals(Object o) 193 { 194 if (o == this) 195 { 196 return true; 197 } 198 if (o instanceof CustomSearchResult) 199 { 200 CustomSearchResult sr = (CustomSearchResult)o; 201 return getDN().equals(sr.getDN()) 202 && getAttributeNames().equals(sr.getAttributeNames()) 203 && attrValuesEqual(sr); 204 } 205 return false; 206 } 207 208 private boolean attrValuesEqual(CustomSearchResult sr) 209 { 210 for (String attrName : getAttributeNames()) 211 { 212 if (!getAttributeValues(attrName).equals(sr.getAttributeValues(attrName))) 213 { 214 return false; 215 } 216 } 217 return true; 218 } 219 220 @Override 221 public String toString() { 222 return toString; 223 } 224 225 @Override 226 public int hashCode() { 227 return hashCode; 228 } 229 230 /** 231 * Sets the values for a given attribute name. 232 * @param attrName the name of the attribute. 233 * @param values the values for the attribute. 234 */ 235 public void set(String attrName, List<Object> values) 236 { 237 attrNames.add(attrName); 238 attrName = attrName.toLowerCase(); 239 attributes.put(attrName, values); 240 toString = calculateToString(); 241 hashCode = calculateHashCode(); 242 } 243 244 private String calculateToString() 245 { 246 return "dn: "+dn+"\nattributes: "+attributes; 247 } 248 249 private int calculateHashCode() 250 { 251 return 23 + toString.hashCode(); 252 } 253 254 /** 255 * Gets the Entry object equivalent to this CustomSearchResult. 256 * The method assumes that the schema in DirectoryServer has been initialized. 257 * @return the Entry object equivalent to this CustomSearchResult. 258 * @throws OpenDsException if there is an error parsing the DN or retrieving 259 * the attributes definition and objectclasses in the schema of the server. 260 */ 261 public Entry getEntry() throws OpenDsException 262 { 263 DN dn = DN.valueOf(getDN()); 264 Map<ObjectClass,String> objectClasses = new HashMap<>(); 265 Map<AttributeType,List<org.opends.server.types.Attribute>> userAttributes = new HashMap<>(); 266 Map<AttributeType,List<org.opends.server.types.Attribute>> operationalAttributes = new HashMap<>(); 267 268 for (String wholeName : getAttributeNames()) 269 { 270 final AttributeDescription attrDesc = LDIFReader.parseAttrDescription(wholeName); 271 final AttributeType attrType = attrDesc.getAttributeType(); 272 273 // See if this is an objectclass or an attribute. Then get the 274 // corresponding definition and add the value to the appropriate hash. 275 if (attrType.isObjectClass()) 276 { 277 for (Object value : getAttributeValues(attrType.getNameOrOID())) 278 { 279 String ocName = value.toString().trim(); 280 objectClasses.put(DirectoryServer.getSchema().getObjectClass(ocName), ocName); 281 } 282 } 283 else 284 { 285 AttributeBuilder builder = new AttributeBuilder(attrDesc); 286 for (Object value : getAttributeValues(attrType.getNameOrOID())) 287 { 288 ByteString bs; 289 if (value instanceof byte[]) 290 { 291 bs = ByteString.wrap((byte[])value); 292 } 293 else 294 { 295 bs = ByteString.valueOfUtf8(value.toString()); 296 } 297 builder.add(bs); 298 } 299 300 List<org.opends.server.types.Attribute> attrList = builder.toAttributeList(); 301 if (attrType.isOperational()) 302 { 303 operationalAttributes.put(attrType, attrList); 304 } 305 else 306 { 307 userAttributes.put(attrType, attrList); 308 } 309 } 310 } 311 312 return new Entry(dn, objectClasses, userAttributes, operationalAttributes); 313 } 314}