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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.tasks; 028 import org.opends.messages.Message; 029 030 031 032 import java.io.File; 033 import java.util.LinkedHashSet; 034 import java.util.LinkedList; 035 import java.util.List; 036 import java.util.TreeSet; 037 import java.util.concurrent.locks.Lock; 038 039 import org.opends.server.admin.std.server.SynchronizationProviderCfg; 040 import org.opends.server.api.ClientConnection; 041 import org.opends.server.api.SynchronizationProvider; 042 import org.opends.server.backends.task.Task; 043 import org.opends.server.backends.task.TaskState; 044 import org.opends.server.config.ConfigException; 045 import org.opends.server.core.DirectoryServer; 046 import org.opends.server.core.SchemaConfigManager; 047 import org.opends.server.types.Attribute; 048 import org.opends.server.types.AttributeType; 049 import org.opends.server.types.AttributeValue; 050 import org.opends.server.types.DebugLogLevel; 051 import org.opends.server.types.DirectoryException; 052 import org.opends.server.types.DN; 053 import org.opends.server.types.Entry; 054 import org.opends.server.types.InitializationException; 055 import org.opends.server.types.LockManager; 056 import org.opends.server.types.Modification; 057 import org.opends.server.types.Operation; 058 import org.opends.server.types.Privilege; 059 import org.opends.server.types.ResultCode; 060 import org.opends.server.types.Schema; 061 062 import static org.opends.server.config.ConfigConstants.*; 063 import static org.opends.server.loggers.debug.DebugLogger.*; 064 import org.opends.server.loggers.debug.DebugTracer; 065 import static org.opends.messages.TaskMessages.*; 066 import static org.opends.server.util.ServerConstants.*; 067 import static org.opends.server.util.StaticUtils.*; 068 069 070 071 /** 072 * This class provides an implementation of a Directory Server task that can be 073 * used to add the contents of a new schema file into the server schema. 074 */ 075 public class AddSchemaFileTask 076 extends Task 077 { 078 /** 079 * The tracer object for the debug logger. 080 */ 081 private static final DebugTracer TRACER = getTracer(); 082 083 // The list of files to be added to the server schema. 084 TreeSet<String> filesToAdd; 085 086 /** 087 * {@inheritDoc} 088 */ 089 public Message getDisplayName() { 090 return INFO_TASK_ADD_SCHEMA_FILE_NAME.get(); 091 } 092 093 /** 094 * {@inheritDoc} 095 */ 096 @Override 097 public void initializeTask() 098 throws DirectoryException 099 { 100 // If the client connection is available, then make sure the associated 101 // client has the UPDATE_SCHEMA privilege. 102 Operation operation = getOperation(); 103 if (operation != null) 104 { 105 ClientConnection clientConnection = operation.getClientConnection(); 106 if (! clientConnection.hasPrivilege(Privilege.UPDATE_SCHEMA, operation)) 107 { 108 Message message = ERR_TASK_ADDSCHEMAFILE_INSUFFICIENT_PRIVILEGES.get(); 109 throw new DirectoryException(ResultCode.INSUFFICIENT_ACCESS_RIGHTS, 110 message); 111 } 112 } 113 114 115 // Get the attribute that specifies which schema file(s) to add. 116 Entry taskEntry = getTaskEntry(); 117 AttributeType attrType = DirectoryServer.getAttributeType( 118 ATTR_TASK_ADDSCHEMAFILE_FILENAME, true); 119 List<Attribute> attrList = taskEntry.getAttribute(attrType); 120 if ((attrList == null) || attrList.isEmpty()) 121 { 122 Message message = ERR_TASK_ADDSCHEMAFILE_NO_FILENAME.get( 123 ATTR_TASK_ADDSCHEMAFILE_FILENAME, String.valueOf(taskEntry.getDN())); 124 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message); 125 } 126 127 128 // Get the name(s) of the schema files to add and make sure they exist in 129 // the schema directory. 130 String schemaDirectory = SchemaConfigManager.getSchemaDirectoryPath(); 131 filesToAdd = new TreeSet<String>(); 132 for (Attribute a : attrList) 133 { 134 for (AttributeValue v : a.getValues()) 135 { 136 String filename = v.getStringValue(); 137 filesToAdd.add(filename); 138 139 try 140 { 141 File schemaFile = new File(schemaDirectory, filename); 142 if ((! schemaFile.exists()) || 143 (! schemaFile.getParent().equals(schemaDirectory))) 144 { 145 Message message = ERR_TASK_ADDSCHEMAFILE_NO_SUCH_FILE.get( 146 filename, schemaDirectory); 147 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 148 message); 149 } 150 } 151 catch (Exception e) 152 { 153 if (debugEnabled()) 154 { 155 TRACER.debugCaught(DebugLogLevel.ERROR, e); 156 } 157 158 Message message = ERR_TASK_ADDSCHEMAFILE_ERROR_CHECKING_FOR_FILE.get( 159 filename, schemaDirectory, getExceptionMessage(e)); 160 throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, 161 message, e); 162 } 163 } 164 } 165 166 167 // Create a new dummy schema and make sure that we can add the contents of 168 // all the schema files into it. Even though this duplicates work we'll 169 // have to do later, it will be good to do it now as well so we can reject 170 // the entry immediately which will fail the attempt by the client to add it 171 // to the server, rather than having to check its status after the fact. 172 Schema schema = DirectoryServer.getSchema().duplicate(); 173 for (String schemaFile : filesToAdd) 174 { 175 try 176 { 177 SchemaConfigManager.loadSchemaFile(schema, schemaFile); 178 } 179 catch (ConfigException ce) 180 { 181 if (debugEnabled()) 182 { 183 TRACER.debugCaught(DebugLogLevel.ERROR, ce); 184 } 185 186 Message message = ERR_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE.get( 187 String.valueOf(schemaFile), ce.getMessage()); 188 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 189 message, ce); 190 } 191 catch (InitializationException ie) 192 { 193 if (debugEnabled()) 194 { 195 TRACER.debugCaught(DebugLogLevel.ERROR, ie); 196 } 197 198 Message message = ERR_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE.get( 199 String.valueOf(schemaFile), ie.getMessage()); 200 throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), 201 message, ie); 202 } 203 } 204 } 205 206 207 208 /** 209 * {@inheritDoc} 210 */ 211 protected TaskState runTask() 212 { 213 // Obtain a write lock on the server schema so that we can be sure nothing 214 // else tries to write to it at the same time. 215 DN schemaDN = DirectoryServer.getSchemaDN(); 216 Lock schemaLock = LockManager.lockWrite(schemaDN); 217 for (int i=0; ((schemaLock == null) && (i < 3)); i++) 218 { 219 schemaLock = LockManager.lockWrite(schemaDN); 220 } 221 222 if (schemaLock == null) 223 { 224 Message message = ERR_TASK_ADDSCHEMAFILE_CANNOT_LOCK_SCHEMA.get( 225 String.valueOf(schemaDN)); 226 logError(message); 227 return TaskState.STOPPED_BY_ERROR; 228 } 229 230 try 231 { 232 LinkedList<Modification> mods = new LinkedList<Modification>(); 233 Schema schema = DirectoryServer.getSchema().duplicate(); 234 for (String schemaFile : filesToAdd) 235 { 236 try 237 { 238 List<Modification> modList = 239 SchemaConfigManager.loadSchemaFile(schema, schemaFile); 240 for (Modification m : modList) 241 { 242 Attribute a = m.getAttribute(); 243 LinkedHashSet<AttributeValue> valuesWithFileElement = 244 new LinkedHashSet<AttributeValue>(); 245 for (AttributeValue v : a.getValues()) 246 { 247 String s = v.getStringValue(); 248 if (s.indexOf(SCHEMA_PROPERTY_FILENAME) < 0) 249 { 250 if (s.endsWith(" )")) 251 { 252 s = s.substring(0, s.length()-1) + SCHEMA_PROPERTY_FILENAME + 253 " '" + schemaFile + "' )"; 254 } 255 else if (s.endsWith(")")) 256 { 257 s = s.substring(0, s.length()-1) + " " + 258 SCHEMA_PROPERTY_FILENAME + " '" + schemaFile + "' )"; 259 } 260 } 261 262 valuesWithFileElement.add(new AttributeValue(a.getAttributeType(), 263 s)); 264 } 265 266 Attribute attrWithFile = new Attribute(a.getAttributeType(), 267 a.getName(), 268 valuesWithFileElement); 269 mods.add(new Modification(m.getModificationType(), attrWithFile)); 270 } 271 } 272 catch (ConfigException ce) 273 { 274 if (debugEnabled()) 275 { 276 TRACER.debugCaught(DebugLogLevel.ERROR, ce); 277 } 278 279 Message message = ERR_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE. 280 get(String.valueOf(schemaFile), ce.getMessage()); 281 logError(message); 282 return TaskState.STOPPED_BY_ERROR; 283 } 284 catch (InitializationException ie) 285 { 286 if (debugEnabled()) 287 { 288 TRACER.debugCaught(DebugLogLevel.ERROR, ie); 289 } 290 291 Message message = ERR_TASK_ADDSCHEMAFILE_ERROR_LOADING_SCHEMA_FILE. 292 get(String.valueOf(schemaFile), ie.getMessage()); 293 logError(message); 294 return TaskState.STOPPED_BY_ERROR; 295 } 296 } 297 298 if (! mods.isEmpty()) 299 { 300 for (SynchronizationProvider<SynchronizationProviderCfg> provider : 301 DirectoryServer.getSynchronizationProviders()) 302 { 303 try 304 { 305 provider.processSchemaChange(mods); 306 } 307 catch (Exception e) 308 { 309 if (debugEnabled()) 310 { 311 TRACER.debugCaught(DebugLogLevel.ERROR, e); 312 } 313 314 Message message = 315 ERR_TASK_ADDSCHEMAFILE_CANNOT_NOTIFY_SYNC_PROVIDER. 316 get(provider.getClass().getName(), getExceptionMessage(e)); 317 logError(message); 318 } 319 } 320 321 Schema.writeConcatenatedSchema(); 322 } 323 324 schema.setYoungestModificationTime(System.currentTimeMillis()); 325 DirectoryServer.setSchema(schema); 326 return TaskState.COMPLETED_SUCCESSFULLY; 327 } 328 finally 329 { 330 LockManager.unlock(schemaDN, schemaLock); 331 } 332 } 333 } 334