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