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