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