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 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.core;
028    
029    
030    
031    import java.lang.reflect.Method;
032    import java.util.ArrayList;
033    import java.util.Iterator;
034    import java.util.List;
035    import java.util.concurrent.ConcurrentHashMap;
036    
037    import org.opends.server.admin.ClassPropertyDefinition;
038    import org.opends.server.admin.server.ConfigurationAddListener;
039    import org.opends.server.admin.server.ConfigurationChangeListener;
040    import org.opends.server.admin.server.ConfigurationDeleteListener;
041    import org.opends.server.admin.std.meta.AttributeSyntaxCfgDefn;
042    import org.opends.server.admin.std.server.AttributeSyntaxCfg;
043    import org.opends.server.admin.std.server.RootCfg;
044    import org.opends.server.admin.server.ServerManagementContext;
045    import org.opends.server.api.AttributeSyntax;
046    import org.opends.server.config.ConfigException;
047    import org.opends.messages.Message;
048    import org.opends.server.types.AttributeType;
049    import org.opends.server.types.ConfigChangeResult;
050    import org.opends.server.types.DirectoryException;
051    import org.opends.server.types.DN;
052    
053    
054    import org.opends.server.types.InitializationException;
055    import org.opends.server.types.ResultCode;
056    
057    import static org.opends.server.loggers.ErrorLogger.*;
058    import static org.opends.messages.ConfigMessages.*;
059    import static org.opends.server.util.StaticUtils.*;
060    
061    
062    
063    /**
064     * This class defines a utility that will be used to manage the set of attribute
065     * syntaxes defined in the Directory Server.  It wil initialize the syntaxes
066     * when the server starts, and then will manage any additions, removals, or
067     * modifications to any syntaxes while the server is running.
068     */
069    public class AttributeSyntaxConfigManager
070           implements ConfigurationChangeListener<AttributeSyntaxCfg>,
071                      ConfigurationAddListener<AttributeSyntaxCfg>,
072                      ConfigurationDeleteListener<AttributeSyntaxCfg>
073    
074    {
075      // A mapping between the DNs of the config entries and the associated
076      // attribute syntaxes.
077      private ConcurrentHashMap<DN,AttributeSyntax> syntaxes;
078    
079    
080    
081      /**
082       * Creates a new instance of this attribute syntax config manager.
083       */
084      public AttributeSyntaxConfigManager()
085      {
086        syntaxes = new ConcurrentHashMap<DN,AttributeSyntax>();
087      }
088    
089    
090    
091      /**
092       * Initializes all attribute syntaxes currently defined in the Directory
093       * Server configuration.  This should only be called at Directory Server
094       * startup.
095       *
096       * @throws  ConfigException  If a configuration problem causes the attribute
097       *                           syntax initialization process to fail.
098       *
099       * @throws  InitializationException  If a problem occurs while initializing
100       *                                   the attribute syntaxes that is not
101       *                                   related to the server configuration.
102       */
103      public void initializeAttributeSyntaxes()
104             throws ConfigException, InitializationException
105      {
106        // Get the root configuration object.
107        ServerManagementContext managementContext =
108             ServerManagementContext.getInstance();
109        RootCfg rootConfiguration =
110             managementContext.getRootConfiguration();
111    
112    
113        // Register as an add and delete listener with the root configuration so we
114        // can be notified if any attribute syntax entries are added or removed.
115        rootConfiguration.addAttributeSyntaxAddListener(this);
116        rootConfiguration.addAttributeSyntaxDeleteListener(this);
117    
118    
119        //Initialize the existing attribute syntaxes.
120        for (String name : rootConfiguration.listAttributeSyntaxes())
121        {
122          AttributeSyntaxCfg syntaxConfiguration =
123               rootConfiguration.getAttributeSyntax(name);
124          syntaxConfiguration.addChangeListener(this);
125    
126          if (syntaxConfiguration.isEnabled())
127          {
128            String className = syntaxConfiguration.getJavaClass();
129            try
130            {
131              AttributeSyntax syntax = loadSyntax(className, syntaxConfiguration,
132                                                  true);
133    
134              try
135              {
136                DirectoryServer.registerAttributeSyntax(syntax, false);
137                syntaxes.put(syntaxConfiguration.dn(), syntax);
138              }
139              catch (DirectoryException de)
140              {
141                Message message = WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get(
142                   String.valueOf(syntaxConfiguration.dn()), de.getMessageObject());
143                logError(message);
144                continue;
145              }
146            }
147            catch (InitializationException ie)
148            {
149              logError(ie.getMessageObject());
150              continue;
151            }
152          }
153        }
154      }
155    
156    
157    
158      /**
159       * {@inheritDoc}
160       */
161      public boolean isConfigurationAddAcceptable(
162                          AttributeSyntaxCfg configuration,
163                          List<Message> unacceptableReasons)
164      {
165        if (configuration.isEnabled())
166        {
167          // Get the name of the class and make sure we can instantiate it as an
168          // attribute syntax.
169          String className = configuration.getJavaClass();
170          try
171          {
172            loadSyntax(className, configuration, false);
173          }
174          catch (InitializationException ie)
175          {
176            unacceptableReasons.add(ie.getMessageObject());
177            return false;
178          }
179        }
180    
181        // If we've gotten here, then it's fine.
182        return true;
183      }
184    
185    
186    
187      /**
188       * {@inheritDoc}
189       */
190      public ConfigChangeResult applyConfigurationAdd(
191                                     AttributeSyntaxCfg configuration)
192      {
193        ResultCode         resultCode          = ResultCode.SUCCESS;
194        boolean            adminActionRequired = false;
195        ArrayList<Message> messages            = new ArrayList<Message>();
196    
197        configuration.addChangeListener(this);
198    
199        if (! configuration.isEnabled())
200        {
201          return new ConfigChangeResult(resultCode, adminActionRequired, messages);
202        }
203    
204        AttributeSyntax syntax = null;
205    
206        // Get the name of the class and make sure we can instantiate it as an
207        // attribute syntax.
208        String className = configuration.getJavaClass();
209        try
210        {
211          syntax = loadSyntax(className, configuration, true);
212    
213          try
214          {
215            DirectoryServer.registerAttributeSyntax(syntax, false);
216            syntaxes.put(configuration.dn(), syntax);
217          }
218          catch (DirectoryException de)
219          {
220            Message message = WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get(
221                    String.valueOf(configuration.dn()), de.getMessageObject());
222            messages.add(message);
223    
224            if (resultCode == ResultCode.SUCCESS)
225            {
226              resultCode = DirectoryServer.getServerErrorResultCode();
227            }
228          }
229        }
230        catch (InitializationException ie)
231        {
232          if (resultCode == ResultCode.SUCCESS)
233          {
234            resultCode = DirectoryServer.getServerErrorResultCode();
235          }
236    
237          messages.add(ie.getMessageObject());
238        }
239    
240        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
241      }
242    
243    
244    
245      /**
246       * {@inheritDoc}
247       */
248      public boolean isConfigurationDeleteAcceptable(
249                          AttributeSyntaxCfg configuration,
250                          List<Message> unacceptableReasons)
251      {
252        // If the syntax is enabled, then check to see if there are any defined
253        // attribute types that use the syntax.  If so, then don't allow it to be
254        // deleted.
255        boolean configAcceptable = true;
256        AttributeSyntax syntax = syntaxes.get(configuration.dn());
257        if (syntax != null)
258        {
259          String oid = syntax.getOID();
260          for (AttributeType at : DirectoryServer.getAttributeTypes().values())
261          {
262            if (oid.equals(at.getSyntaxOID()))
263            {
264              Message message = WARN_CONFIG_SCHEMA_CANNOT_DELETE_SYNTAX_IN_USE.get(
265                      syntax.getSyntaxName(), at.getNameOrOID());
266              unacceptableReasons.add(message);
267    
268              configAcceptable = false;
269            }
270          }
271        }
272    
273        return configAcceptable;
274      }
275    
276    
277    
278      /**
279       * {@inheritDoc}
280       */
281      public ConfigChangeResult applyConfigurationDelete(
282                                     AttributeSyntaxCfg configuration)
283      {
284        ResultCode        resultCode          = ResultCode.SUCCESS;
285        boolean           adminActionRequired = false;
286        ArrayList<Message> messages            = new ArrayList<Message>();
287    
288        AttributeSyntax syntax = syntaxes.remove(configuration.dn());
289        if (syntax != null)
290        {
291          DirectoryServer.deregisterAttributeSyntax(syntax);
292          syntax.finalizeSyntax();
293        }
294    
295        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
296      }
297    
298    
299    
300      /**
301       * {@inheritDoc}
302       */
303      public boolean isConfigurationChangeAcceptable(
304                          AttributeSyntaxCfg configuration,
305                          List<Message> unacceptableReasons)
306      {
307        if (configuration.isEnabled())
308        {
309          // Get the name of the class and make sure we can instantiate it as an
310          // attribute syntax.
311          String className = configuration.getJavaClass();
312          try
313          {
314            loadSyntax(className, configuration, false);
315          }
316          catch (InitializationException ie)
317          {
318            unacceptableReasons.add(ie.getMessageObject());
319            return false;
320          }
321        }
322        else
323        {
324          // If the syntax is currently enabled and the change would make it
325          // disabled, then only allow it if the syntax isn't already in use.
326          AttributeSyntax syntax = syntaxes.get(configuration.dn());
327          if (syntax != null)
328          {
329            String oid = syntax.getOID();
330            for (AttributeType at : DirectoryServer.getAttributeTypes().values())
331            {
332              if (oid.equals(at.getSyntaxOID()))
333              {
334                Message message =
335                        WARN_CONFIG_SCHEMA_CANNOT_DISABLE_SYNTAX_IN_USE.get(
336                                syntax.getSyntaxName(),
337                                at.getNameOrOID());
338                unacceptableReasons.add(message);
339                return false;
340              }
341            }
342          }
343        }
344    
345        // If we've gotten here, then it's fine.
346        return true;
347      }
348    
349    
350    
351      /**
352       * {@inheritDoc}
353       */
354      public ConfigChangeResult applyConfigurationChange(
355                                     AttributeSyntaxCfg configuration)
356      {
357        ResultCode        resultCode          = ResultCode.SUCCESS;
358        boolean           adminActionRequired = false;
359        ArrayList<Message> messages            = new ArrayList<Message>();
360    
361    
362        // Get the existing syntax if it's already enabled.
363        AttributeSyntax existingSyntax = syntaxes.get(configuration.dn());
364    
365    
366        // If the new configuration has the syntax disabled, then disable it if it
367        // is enabled, or do nothing if it's already disabled.
368        if (! configuration.isEnabled())
369        {
370          if (existingSyntax != null)
371          {
372            DirectoryServer.deregisterAttributeSyntax(existingSyntax);
373    
374            AttributeSyntax syntax = syntaxes.remove(configuration.dn());
375            if (syntax != null)
376            {
377              syntax.finalizeSyntax();
378            }
379          }
380    
381          return new ConfigChangeResult(resultCode, adminActionRequired, messages);
382        }
383    
384    
385        // Get the class for the attribute syntax.  If the syntax is already
386        // enabled, then we shouldn't do anything with it although if the class has
387        // changed then we'll at least need to indicate that administrative action
388        // is required.  If the syntax is disabled, then instantiate the class and
389        // initialize and register it as an attribute syntax.
390        String className = configuration.getJavaClass();
391        if (existingSyntax != null)
392        {
393          if (! className.equals(existingSyntax.getClass().getName()))
394          {
395            adminActionRequired = true;
396          }
397    
398          return new ConfigChangeResult(resultCode, adminActionRequired, messages);
399        }
400    
401        AttributeSyntax syntax = null;
402        try
403        {
404          syntax = loadSyntax(className, configuration, true);
405    
406          try
407          {
408            DirectoryServer.registerAttributeSyntax(syntax, false);
409            syntaxes.put(configuration.dn(), syntax);
410          }
411          catch (DirectoryException de)
412          {
413            Message message = WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get(
414                    String.valueOf(configuration.dn()),
415                    de.getMessageObject());
416            messages.add(message);
417    
418            if (resultCode == ResultCode.SUCCESS)
419            {
420              resultCode = DirectoryServer.getServerErrorResultCode();
421            }
422          }
423        }
424        catch (InitializationException ie)
425        {
426          if (resultCode == ResultCode.SUCCESS)
427          {
428            resultCode = DirectoryServer.getServerErrorResultCode();
429          }
430    
431          messages.add(ie.getMessageObject());
432        }
433    
434        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
435      }
436    
437    
438    
439      /**
440       * Loads the specified class, instantiates it as an attribute syntax, and
441       * optionally initializes that instance.
442       *
443       * @param  className      The fully-qualified name of the attribute syntax
444       *                        class to load, instantiate, and initialize.
445       * @param  configuration  The configuration to use to initialize the attribute
446       *                        syntax.  It should not be {@code null}.
447       * @param  initialize     Indicates whether the attribute syntax instance
448       *                        should be initialized.
449       *
450       * @return  The possibly initialized attribute syntax.
451       *
452       * @throws  InitializationException  If a problem occurred while attempting to
453       *                                   initialize the attribute syntax.
454       */
455      private AttributeSyntax loadSyntax(String className,
456                                         AttributeSyntaxCfg configuration,
457                                         boolean initialize)
458              throws InitializationException
459      {
460        try
461        {
462          AttributeSyntaxCfgDefn definition =
463               AttributeSyntaxCfgDefn.getInstance();
464          ClassPropertyDefinition propertyDefinition =
465               definition.getJavaClassPropertyDefinition();
466          Class<? extends AttributeSyntax> syntaxClass =
467               propertyDefinition.loadClass(className, AttributeSyntax.class);
468          AttributeSyntax syntax = syntaxClass.newInstance();
469    
470          if (initialize)
471          {
472            Method method = syntax.getClass().getMethod("initializeSyntax",
473                configuration.configurationClass());
474            method.invoke(syntax, configuration);
475          }
476          else
477          {
478            Method method = syntax.getClass().getMethod("isConfigurationAcceptable",
479                                                        AttributeSyntaxCfg.class,
480                                                        List.class);
481    
482            List<Message> unacceptableReasons = new ArrayList<Message>();
483            Boolean acceptable = (Boolean) method.invoke(syntax, configuration,
484                                                         unacceptableReasons);
485            if (! acceptable)
486            {
487              StringBuilder buffer = new StringBuilder();
488              if (! unacceptableReasons.isEmpty())
489              {
490                Iterator<Message> iterator = unacceptableReasons.iterator();
491                buffer.append(iterator.next());
492                while (iterator.hasNext())
493                {
494                  buffer.append(".  ");
495                  buffer.append(iterator.next());
496                }
497              }
498    
499              Message message = ERR_CONFIG_SCHEMA_SYNTAX_CONFIG_NOT_ACCEPTABLE.get(
500                  String.valueOf(configuration.dn()), buffer.toString());
501              throw new InitializationException(message);
502            }
503          }
504    
505          return syntax;
506        }
507        catch (Exception e)
508        {
509          Message message = ERR_CONFIG_SCHEMA_SYNTAX_CANNOT_INITIALIZE.
510              get(className, String.valueOf(configuration.dn()),
511                  stackTraceToSingleLineString(e));
512          throw new InitializationException(message, e);
513        }
514      }
515    }
516