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