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 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2017 ForgeRock AS. 016 */ 017package org.opends.server.tasks; 018 019import java.io.File; 020import java.util.LinkedList; 021import java.util.List; 022import java.util.TreeSet; 023 024import org.forgerock.i18n.LocalizableMessage; 025import org.forgerock.i18n.slf4j.LocalizedLogger; 026import org.forgerock.opendj.config.server.ConfigException; 027import org.forgerock.opendj.ldap.ByteString; 028import org.forgerock.opendj.ldap.ResultCode; 029import org.forgerock.opendj.ldap.schema.AttributeType; 030import org.forgerock.opendj.server.config.server.SynchronizationProviderCfg; 031import org.opends.server.api.ClientConnection; 032import org.opends.server.api.SynchronizationProvider; 033import org.opends.server.backends.task.Task; 034import org.opends.server.backends.task.TaskState; 035import org.opends.server.core.DirectoryServer; 036import org.opends.server.core.SchemaConfigManager; 037import org.opends.server.types.Attribute; 038import org.opends.server.types.AttributeBuilder; 039import org.opends.server.types.DirectoryException; 040import org.opends.server.types.Entry; 041import org.opends.server.types.InitializationException; 042import org.opends.server.types.LockManager.DNLock; 043import org.opends.server.types.Modification; 044import org.opends.server.types.Operation; 045import org.opends.server.types.Privilege; 046import org.opends.server.types.Schema; 047 048import static org.opends.messages.TaskMessages.*; 049import static org.opends.server.config.ConfigConstants.*; 050import static org.opends.server.core.DirectoryServer.*; 051import static org.opends.server.util.StaticUtils.*; 052 053/** 054 * This class provides an implementation of a Directory Server task that can be 055 * used to add the contents of a new schema file into the server schema. 056 */ 057public class AddSchemaFileTask 058 extends Task 059{ 060 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 061 062 /** The list of files to be added to the server schema. */ 063 private TreeSet<String> filesToAdd; 064 065 @Override 066 public LocalizableMessage getDisplayName() { 067 return INFO_TASK_ADD_SCHEMA_FILE_NAME.get(); 068 } 069 070 @Override 071 public void initializeTask() 072 throws DirectoryException 073 { 074 // If the client connection is available, then make sure the associated 075 // client has the UPDATE_SCHEMA privilege. 076 Operation operation = getOperation(); 077 if (operation != null) 078 { 079 ClientConnection clientConnection = operation.getClientConnection(); 080 if (! clientConnection.hasPrivilege(Privilege.UPDATE_SCHEMA, operation)) 081 { 082 LocalizableMessage message = ERR_TASK_ADDSCHEMAFILE_INSUFFICIENT_PRIVILEGES.get(); 083 throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 084 message); 085 } 086 } 087 088 // Get the attribute that specifies which schema file(s) to add. 089 Entry taskEntry = getTaskEntry(); 090 AttributeType attrType = DirectoryServer.getSchema().getAttributeType(ATTR_TASK_ADDSCHEMAFILE_FILENAME); 091 List<Attribute> attrList = taskEntry.getAttribute(attrType); 092 if (attrList.isEmpty()) 093 { 094 LocalizableMessage message = ERR_TASK_ADDSCHEMAFILE_NO_FILENAME.get( 095 ATTR_TASK_ADDSCHEMAFILE_FILENAME, taskEntry.getName()); 096 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 097 } 098 099 // Get the name(s) of the schema files to add and make sure they exist in 100 // the schema directory. 101 String schemaInstanceDirectory = 102 SchemaConfigManager.getSchemaDirectoryPath(); 103 filesToAdd = new TreeSet<>(); 104 for (Attribute a : attrList) 105 { 106 for (ByteString v : a) 107 { 108 String filename = v.toString(); 109 filesToAdd.add(filename); 110 111 try 112 { 113 File schemaFile = new File(schemaInstanceDirectory, filename); 114 if (! schemaFile.exists()) 115 { 116 LocalizableMessage message = ERR_TASK_ADDSCHEMAFILE_NO_SUCH_FILE.get( 117 filename, schemaInstanceDirectory); 118 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 119 message); 120 } 121 } 122 catch (Exception e) 123 { 124 logger.traceException(e); 125 126 LocalizableMessage message = ERR_TASK_ADDSCHEMAFILE_ERROR_CHECKING_FOR_FILE.get( 127 filename, schemaInstanceDirectory, 128 getExceptionMessage(e)); 129 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 130 message, e); 131 } 132 } 133 } 134 135 // Create a new dummy schema and make sure that we can add the contents of 136 // all the schema files into it. Even though this duplicates work we'll 137 // have to do later, it will be good to do it now as well so we can reject 138 // the entry immediately which will fail the attempt by the client to add it 139 // to the server, rather than having to check its status after the fact. 140 Schema schema = DirectoryServer.getSchema().duplicate(); 141 for (String schemaFile : filesToAdd) 142 { 143 try 144 { 145 SchemaConfigManager.loadSchemaFile(schema, null, schemaFile); 146 } 147 catch (ConfigException | InitializationException e) 148 { 149 logger.traceException(e); 150 151 LocalizableMessage message = ERR_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE.get(schemaFile, e.getMessage()); 152 throw new DirectoryException(getServerErrorResultCode(), message, e); 153 } 154 } 155 } 156 157 @Override 158 protected TaskState runTask() 159 { 160 // Obtain a write lock on the server schema so that we can be sure nothing 161 // else tries to write to it at the same time. 162 final DNLock schemaLock = DirectoryServer.getLockManager().tryWriteLockEntry(getSchemaDN()); 163 if (schemaLock == null) 164 { 165 logger.error(ERR_TASK_ADDSCHEMAFILE_CANNOT_LOCK_SCHEMA, getSchemaDN()); 166 return TaskState.STOPPED_BY_ERROR; 167 } 168 169 try 170 { 171 LinkedList<Modification> mods = new LinkedList<>(); 172 Schema schema = DirectoryServer.getSchema().duplicate(); 173 for (String schemaFile : filesToAdd) 174 { 175 try 176 { 177 List<Modification> modList = SchemaConfigManager.loadSchemaFileReturnModifications(schema, schemaFile, null); 178 for (Modification m : modList) 179 { 180 Attribute a = m.getAttribute(); 181 AttributeBuilder builder = new AttributeBuilder(a.getAttributeDescription()); 182 for (ByteString v : a) 183 { 184 builder.add(Schema.addSchemaFileToElementDefinitionIfAbsent(v.toString(), schemaFile)); 185 } 186 187 mods.add(new Modification(m.getModificationType(), builder.toAttribute())); 188 } 189 } 190 catch (ConfigException | InitializationException e) 191 { 192 logger.traceException(e); 193 logger.error(ERR_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE, schemaFile, e.getMessage()); 194 return TaskState.STOPPED_BY_ERROR; 195 } 196 } 197 198 if (! mods.isEmpty()) 199 { 200 for (SynchronizationProvider<SynchronizationProviderCfg> provider : 201 DirectoryServer.getSynchronizationProviders()) 202 { 203 try 204 { 205 provider.processSchemaChange(mods); 206 } 207 catch (Exception e) 208 { 209 logger.traceException(e); 210 211 logger.error(ERR_TASK_ADDSCHEMAFILE_CANNOT_NOTIFY_SYNC_PROVIDER, 212 provider.getClass().getName(), getExceptionMessage(e)); 213 } 214 } 215 216 Schema.writeConcatenatedSchema(); 217 } 218 219 schema.setYoungestModificationTime(System.currentTimeMillis()); 220 DirectoryServer.setSchema(schema); 221 return TaskState.COMPLETED_SUCCESSFULLY; 222 } 223 finally 224 { 225 schemaLock.unlock(); 226 } 227 } 228}