001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2009-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS 026 */ 027package org.opends.guitools.controlpanel.util; 028 029import java.util.HashSet; 030import java.util.Set; 031 032import javax.naming.NamingEnumeration; 033import javax.naming.NamingException; 034import javax.naming.directory.SearchControls; 035import javax.naming.directory.SearchResult; 036import javax.naming.ldap.InitialLdapContext; 037 038import org.forgerock.opendj.config.server.ConfigException; 039import org.forgerock.opendj.ldap.ByteStringBuilder; 040import org.forgerock.opendj.ldap.ResultCode; 041import org.forgerock.opendj.ldap.schema.CoreSchema; 042import org.forgerock.opendj.ldap.schema.MatchingRule; 043import org.forgerock.opendj.ldap.schema.MatchingRuleImpl; 044import org.forgerock.opendj.ldap.schema.SchemaBuilder; 045import org.opends.guitools.controlpanel.browser.BrowserController; 046import org.opends.guitools.controlpanel.datamodel.CustomSearchResult; 047import org.opends.server.api.AttributeSyntax; 048import org.opends.server.config.ConfigConstants; 049import org.opends.server.core.DirectoryServer; 050import org.opends.server.core.ServerContext; 051import org.opends.server.replication.plugin.HistoricalCsnOrderingMatchingRuleImpl; 052import org.opends.server.schema.AciSyntax; 053import org.opends.server.schema.AttributeTypeSyntax; 054import org.opends.server.schema.LDAPSyntaxDescriptionSyntax; 055import org.opends.server.schema.ObjectClassSyntax; 056import org.opends.server.schema.SchemaConstants; 057import org.opends.server.schema.SubtreeSpecificationSyntax; 058import org.opends.server.types.DirectoryException; 059import org.opends.server.types.InitializationException; 060import org.opends.server.types.LDAPSyntaxDescription; 061import org.opends.server.types.Schema; 062 063/** Class used to retrieve the schema from the schema files. */ 064public class RemoteSchemaLoader extends SchemaLoader 065{ 066 private Schema schema; 067 068 /** 069 * In remote mode we cannot load the matching rules and syntaxes from local 070 * configuration, so we should instead bootstrap them from the SDK's core schema. 071 */ 072 public RemoteSchemaLoader() 073 { 074 matchingRulesToKeep.clear(); 075 syntaxesToKeep.clear(); 076 matchingRulesToKeep.addAll(org.forgerock.opendj.ldap.schema.Schema.getCoreSchema().getMatchingRules()); 077 syntaxesToKeep.addAll(org.forgerock.opendj.ldap.schema.Schema.getCoreSchema().getSyntaxes()); 078 } 079 /** 080 * Reads the schema. 081 * 082 * @param ctx 083 * the connection to be used to load the schema. 084 * @throws NamingException 085 * if an error occurs reading the schema. 086 * @throws DirectoryException 087 * if an error occurs parsing the schema. 088 * @throws InitializationException 089 * if an error occurs finding the base schema. 090 * @throws ConfigException 091 * if an error occurs loading the configuration required to use the 092 * schema classes. 093 */ 094 public void readSchema(InitialLdapContext ctx) throws NamingException, DirectoryException, InitializationException, 095 ConfigException 096 { 097 final String[] schemaAttrs = { ConfigConstants.ATTR_LDAP_SYNTAXES_LC, ConfigConstants.ATTR_ATTRIBUTE_TYPES_LC, 098 ConfigConstants.ATTR_OBJECTCLASSES_LC }; 099 final SearchControls searchControls = new SearchControls(); 100 searchControls.setSearchScope(SearchControls.OBJECT_SCOPE); 101 searchControls.setReturningAttributes(schemaAttrs); 102 final NamingEnumeration<SearchResult> srs = ctx.search( 103 ConfigConstants.DN_DEFAULT_SCHEMA_ROOT, BrowserController.ALL_OBJECTS_FILTER, searchControls); 104 SearchResult sr = null; 105 try 106 { 107 while (srs.hasMore()) 108 { 109 sr = srs.next(); 110 } 111 } 112 finally 113 { 114 srs.close(); 115 } 116 117 final CustomSearchResult csr = new CustomSearchResult(sr, ConfigConstants.DN_DEFAULT_SCHEMA_ROOT); 118 schema = getBaseSchema(); 119 // Add missing matching rules and attribute syntaxes to base schema to allow read of remote server schema 120 // (see OPENDJ-1122 for more details) 121 addMissingSyntaxesToBaseSchema(new AciSyntax(), new SubtreeSpecificationSyntax()); 122 addMissingMatchingRuleToBaseSchema("1.3.6.1.4.1.26027.1.4.4", "historicalCsnOrderingMatch", 123 "1.3.6.1.4.1.1466.115.121.1.40", new HistoricalCsnOrderingMatchingRuleImpl()); 124 for (final String str : schemaAttrs) 125 { 126 registerSchemaAttr(csr, str); 127 } 128 } 129 130 private void addMissingSyntaxesToBaseSchema(final AttributeSyntax<?>... syntaxes) 131 throws DirectoryException, InitializationException, ConfigException 132 { 133 for (AttributeSyntax<?> syntax : syntaxes) 134 { 135 final ServerContext serverContext = DirectoryServer.getInstance().getServerContext(); 136 if (!serverContext.getSchemaNG().hasSyntax(syntax.getOID())) 137 { 138 syntax.initializeSyntax(null, serverContext); 139 } 140 schema.registerSyntax(syntax.getSDKSyntax(serverContext.getSchemaNG()), true); 141 } 142 } 143 144 private void addMissingMatchingRuleToBaseSchema(final String oid, final String name, final String syntaxOID, 145 final MatchingRuleImpl impl) 146 throws InitializationException, ConfigException, DirectoryException 147 { 148 final MatchingRule matchingRule; 149 if (CoreSchema.getInstance().hasMatchingRule(name)) 150 { 151 matchingRule = CoreSchema.getInstance().getMatchingRule(name); 152 } 153 else 154 { 155 matchingRule = new SchemaBuilder(CoreSchema.getInstance()).buildMatchingRule(oid) 156 .names(name) 157 .syntaxOID(syntaxOID) 158 .implementation(impl) 159 .addToSchema().toSchema().getMatchingRule(oid); 160 } 161 schema.registerMatchingRule(matchingRule, true); 162 } 163 164 private void registerSchemaAttr(final CustomSearchResult csr, final String schemaAttr) throws DirectoryException 165 { 166 final Set<Object> remainingAttrs = new HashSet<>(csr.getAttributeValues(schemaAttr)); 167 if (schemaAttr.equals(ConfigConstants.ATTR_LDAP_SYNTAXES_LC)) 168 { 169 registerSchemaLdapSyntaxDefinitions(remainingAttrs); 170 return; 171 } 172 173 while (!remainingAttrs.isEmpty()) 174 { 175 DirectoryException lastException = null; 176 final Set<Object> registered = new HashSet<>(); 177 for (final Object definition : remainingAttrs) 178 { 179 final ByteStringBuilder sb = new ByteStringBuilder(); 180 sb.appendObject(definition); 181 try 182 { 183 switch (schemaAttr) 184 { 185 case ConfigConstants.ATTR_ATTRIBUTE_TYPES_LC: 186 schema.registerAttributeType(AttributeTypeSyntax.decodeAttributeType(sb, schema, false), true); 187 break; 188 case ConfigConstants.ATTR_OBJECTCLASSES_LC: 189 schema.registerObjectClass(ObjectClassSyntax.decodeObjectClass(sb, schema, false), true); 190 break; 191 } 192 registered.add(definition); 193 } 194 catch (DirectoryException de) 195 { 196 lastException = de; 197 } 198 } 199 if (registered.isEmpty()) 200 { 201 throw lastException; 202 } 203 remainingAttrs.removeAll(registered); 204 } 205 } 206 207 private void registerSchemaLdapSyntaxDefinitions(Set<Object> remainingAttrs) throws DirectoryException 208 { 209 for (final Object definition : remainingAttrs) 210 { 211 final ByteStringBuilder sb = new ByteStringBuilder(); 212 sb.appendObject(definition); 213 if (definition.toString().contains(SchemaConstants.OID_OPENDS_SERVER_BASE)) 214 { 215 try 216 { 217 final LDAPSyntaxDescription syntaxDesc = LDAPSyntaxDescriptionSyntax.decodeLDAPSyntax( 218 sb, DirectoryServer.getInstance().getServerContext(), schema, false, false); 219 schema.registerLdapSyntaxDescription(syntaxDesc, true); 220 } 221 catch (DirectoryException e) 222 { 223 // Filter error code to ignore exceptions raised on description syntaxes. 224 if (e.getResultCode() != ResultCode.UNWILLING_TO_PERFORM) 225 { 226 throw e; 227 } 228 } 229 } 230 } 231 } 232 233 /** 234 * Returns the schema that was read. 235 * 236 * @return the schema that was read. 237 */ 238 @Override 239 public Schema getSchema() 240 { 241 return schema; 242 } 243}