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