org.apache.directory.server.configuration.ApacheDS.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.directory.server.configuration.ApacheDS.java

Source

/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 *
 */
package org.apache.directory.server.configuration;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.directory.api.ldap.model.constants.SchemaConstants;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.ldap.model.schema.SchemaManager;
import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
import org.apache.directory.api.ldap.schema.extractor.SchemaLdifExtractor;
import org.apache.directory.api.ldap.schema.extractor.impl.DefaultSchemaLdifExtractor;
import org.apache.directory.api.ldap.schema.loader.LdifSchemaLoader;
import org.apache.directory.api.ldap.schema.manager.impl.DefaultSchemaManager;
import org.apache.directory.api.util.Strings;
import org.apache.directory.api.util.exception.Exceptions;
import org.apache.directory.server.constants.ApacheSchemaConstants;
import org.apache.directory.server.constants.ServerDNConstants;
import org.apache.directory.server.core.DefaultDirectoryService;
import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.api.entry.ClonedServerEntry;
import org.apache.directory.server.core.api.partition.Partition;
import org.apache.directory.server.core.api.schema.SchemaPartition;
import org.apache.directory.server.core.partition.impl.btree.AbstractBTreePartition;
import org.apache.directory.server.core.partition.ldif.LdifPartition;
import org.apache.directory.server.i18n.I18n;
import org.apache.directory.server.ldap.LdapServer;
import org.apache.directory.server.protocol.shared.store.LdifFileLoader;
import org.apache.directory.server.protocol.shared.store.LdifLoadFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Apache Directory Server top level.
 *
 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
 */
public class ApacheDS {
    private static final Logger LOG = LoggerFactory.getLogger(ApacheDS.class);

    /** Default delay between two flushes to the backend */
    private static final long DEFAULT_SYNC_PERIOD_MILLIS = 20000;

    /** Wainting period between two flushes to the backend */
    private long synchPeriodMillis = DEFAULT_SYNC_PERIOD_MILLIS;

    /** Directory where are stored the LDIF files to be loaded at startup */
    private File ldifDirectory;

    private final List<LdifLoadFilter> ldifFilters = new ArrayList<>();

    /** The LDAP server protocol handler */
    private final LdapServer ldapServer;

    /** The directory service */
    private DirectoryService directoryService;

    /**
     * Creates a new instance of the ApacheDS server
     * 
     * @param ldapServer The ldap server protocol handler
     * @throws LdapException If we can't create teh ApacheDS instance
     */
    public ApacheDS(LdapServer ldapServer) throws LdapException {
        LOG.info("Starting the Apache Directory Server");

        this.ldapServer = ldapServer;

        directoryService = ldapServer.getDirectoryService();

        if (directoryService == null) {
            directoryService = new DefaultDirectoryService();
        }
    }

    /**
     * Start the server :
     * <ul>
     *  <li>initialize the DirectoryService</li>
     *  <li>start the LDAP server</li>
     *  <li>start the LDAPS server</li>
     * </ul>
     * 
     * @throws Exception If the server cannot be started
     */
    public void startup() throws Exception {
        LOG.debug("Starting the server");

        initSchema();

        SchemaManager schemaManager = directoryService.getSchemaManager();

        if (!directoryService.isStarted()) {
            // inject the schema manager and set the partition directory
            // once the CiDIT gets done we need not do this kind of dirty hack
            Set<? extends Partition> partitions = directoryService.getPartitions();

            for (Partition p : partitions) {
                if (p instanceof AbstractBTreePartition) {
                    File partitionPath = new File(directoryService.getInstanceLayout().getPartitionsDirectory(),
                            p.getId());
                    ((AbstractBTreePartition) p).setPartitionPath(partitionPath.toURI());
                }

                if (p.getSchemaManager() == null) {
                    LOG.info("setting the schema manager for partition {}", p.getSuffixDn());
                    p.setSchemaManager(schemaManager);
                }
            }

            Partition sysPartition = directoryService.getSystemPartition();

            if (sysPartition instanceof AbstractBTreePartition) {
                File partitionPath = new File(directoryService.getInstanceLayout().getPartitionsDirectory(),
                        sysPartition.getId());
                ((AbstractBTreePartition) sysPartition).setPartitionPath(partitionPath.toURI());
            }

            if (sysPartition.getSchemaManager() == null) {
                LOG.info("setting the schema manager for partition {}", sysPartition.getSuffixDn());
                sysPartition.setSchemaManager(schemaManager);
            }

            // Start the directory service if not started yet
            LOG.debug("1. Starting the DirectoryService");
            directoryService.startup();
        }

        // Load the LDIF files - if any - into the server
        loadLdifs();

        // Start the LDAP server
        if (ldapServer != null && !ldapServer.isStarted()) {
            LOG.debug("3. Starting the LDAP server");
            ldapServer.start();
        }

        LOG.debug("Server successfully started");
    }

    public boolean isStarted() {
        if (ldapServer != null) {
            return (ldapServer.isStarted());
        }

        return directoryService.isStarted();
    }

    public void shutdown() throws Exception {
        if (ldapServer != null && ldapServer.isStarted()) {
            ldapServer.stop();
        }

        directoryService.shutdown();
    }

    public LdapServer getLdapServer() {
        return ldapServer;
    }

    public DirectoryService getDirectoryService() {
        return directoryService;
    }

    public long getSynchPeriodMillis() {
        return synchPeriodMillis;
    }

    public void setSynchPeriodMillis(long synchPeriodMillis) {
        LOG.info("Set the synchPeriodMillis to {}", synchPeriodMillis);
        this.synchPeriodMillis = synchPeriodMillis;
    }

    /**
     * Get the directory where the LDIF files are stored
     * 
     * @return The directory where the LDIF files are stored
     */
    public File getLdifDirectory() {
        return ldifDirectory;
    }

    public void setLdifDirectory(File ldifDirectory) {
        LOG.info("The LDIF directory file is {}", ldifDirectory.getAbsolutePath());
        this.ldifDirectory = ldifDirectory;
    }

    // ----------------------------------------------------------------------
    // From CoreContextFactory: presently in intermediate step but these
    // methods will be moved to the appropriate protocol service eventually.
    // This is here simply to start to remove the JNDI dependency then further
    // refactoring will be needed to place these where they belong.
    // ----------------------------------------------------------------------

    /**
     * Check that the entry where are stored the loaded Ldif files is created.
     * 
     * If not, create it.
     * 
     * The files are stored in ou=loadedLdifFiles,ou=configuration,ou=system
     */
    private void ensureLdifFileBase() throws LdapException {
        Dn dn = new Dn(ServerDNConstants.LDIF_FILES_DN);
        Entry entry = null;

        try {
            entry = directoryService.getAdminSession().lookup(dn);
        } catch (Exception e) {
            LOG.info("Failure while looking up {}. The entry will be created now.", ServerDNConstants.LDIF_FILES_DN,
                    e);
        }

        if (entry == null) {
            entry = directoryService.newEntry(new Dn(ServerDNConstants.LDIF_FILES_DN));
            entry.add(SchemaConstants.OU_AT, "loadedLdifFiles");
            entry.add(SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC,
                    SchemaConstants.ORGANIZATIONAL_UNIT_OC);

            directoryService.getAdminSession().add(entry);
        }
    }

    /**
     * Create a string containing a hex dump of the loaded ldif file name.
     * 
     * It is associated with the attributeType wrt to the underlying system.
     */
    private Dn buildProtectedFileEntryDn(File ldif) throws Exception {
        String fileSep = File.separatorChar == '\\' ? ApacheSchemaConstants.WINDOWS_FILE_AT
                : ApacheSchemaConstants.UNIX_FILE_AT;

        return new Dn(fileSep + "=" + Strings.dumpHexPairs(Strings.getBytesUtf8(getCanonical(ldif))) + ","
                + ServerDNConstants.LDIF_FILES_DN);
    }

    private void addFileEntry(File ldif) throws Exception {
        String rdnAttr = File.separatorChar == '\\' ? ApacheSchemaConstants.WINDOWS_FILE_AT
                : ApacheSchemaConstants.UNIX_FILE_AT;
        String oc = File.separatorChar == '\\' ? ApacheSchemaConstants.WINDOWS_FILE_OC
                : ApacheSchemaConstants.UNIX_FILE_OC;

        Entry entry = directoryService.newEntry(buildProtectedFileEntryDn(ldif));
        entry.add(rdnAttr, getCanonical(ldif));
        entry.add(SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.TOP_OC, oc);
        directoryService.getAdminSession().add(entry);
    }

    private String getCanonical(File file) {
        String canonical;

        try {
            canonical = file.getCanonicalPath();
        } catch (IOException e) {
            LOG.error(I18n.err(I18n.ERR_179), e);
            return null;
        }

        return StringUtils.replace(canonical, "\\", "\\\\");
    }

    /**
     * Load a ldif into the directory.
     * 
     * @param root The context in which we will inject the entries
     * @param ldifFile The ldif file to read
     * @throws NamingException If something went wrong while loading the entries
     */
    // This will suppress PMD.EmptyCatchBlock warnings in this method
    @SuppressWarnings("PMD.EmptyCatchBlock")
    private void loadLdif(File ldifFile) throws Exception {
        Entry fileEntry = null;

        try {
            fileEntry = directoryService.getAdminSession().lookup(buildProtectedFileEntryDn(ldifFile));
        } catch (Exception e) {
            // if does not exist
        }

        if (fileEntry != null) {
            String time = ((ClonedServerEntry) fileEntry).getOriginalEntry()
                    .get(SchemaConstants.CREATE_TIMESTAMP_AT).getString();
            LOG.info("Load of LDIF file '{}' skipped.  It has already been loaded on {}.", getCanonical(ldifFile),
                    time);
        } else {
            LdifFileLoader loader = new LdifFileLoader(directoryService.getAdminSession(), ldifFile, ldifFilters);
            int count = loader.execute();
            LOG.info("Loaded {} entries from LDIF file '{}", count, getCanonical(ldifFile));
            addFileEntry(ldifFile);
        }
    }

    /**
     * Load the existing LDIF files in alphabetic order
     * 
     * @throws LdapException If we can't load the ldifs
     */
    public void loadLdifs() throws LdapException {
        // LOG and bail if property not set
        if (ldifDirectory == null) {
            LOG.info("LDIF load directory not specified.  No LDIF files will be loaded.");
            return;
        }

        // LOG and bail if LDIF directory does not exists
        if (!ldifDirectory.exists()) {
            LOG.warn("LDIF load directory '{}' does not exist.  No LDIF files will be loaded.",
                    getCanonical(ldifDirectory));
            return;
        }

        ensureLdifFileBase();

        // if ldif directory is a file try to load it
        if (ldifDirectory.isFile()) {
            if (LOG.isInfoEnabled()) {
                LOG.info("LDIF load directory '{}' is a file. Will attempt to load as LDIF.",
                        getCanonical(ldifDirectory));
            }

            try {
                loadLdif(ldifDirectory);
            } catch (Exception ne) {
                // If the file can't be read, log the error, and stop
                // loading LDIFs.
                LOG.error(I18n.err(I18n.ERR_180, ldifDirectory.getAbsolutePath(), ne.getLocalizedMessage()));
                throw new LdapException(ne.getMessage(), ne);
            }
        } else {
            // get all the ldif files within the directory
            File[] ldifFiles = ldifDirectory.listFiles(new FileFilter() {
                public boolean accept(File pathname) {
                    boolean isLdif = Strings.toLowerCaseAscii(pathname.getName()).endsWith(".ldif");
                    return pathname.isFile() && pathname.canRead() && isLdif;
                }
            });

            // LOG and bail if we could not find any LDIF files
            if ((ldifFiles == null) || (ldifFiles.length == 0)) {
                LOG.warn("LDIF load directory '{}' does not contain any LDIF files. No LDIF files will be loaded.",
                        getCanonical(ldifDirectory));
                return;
            }

            // Sort ldifFiles in alphabetic order
            Arrays.sort(ldifFiles, new Comparator<File>() {
                public int compare(File f1, File f2) {
                    return f1.getName().compareTo(f2.getName());
                }
            });

            // load all the ldif files and load each one that is loaded
            for (File ldifFile : ldifFiles) {
                try {
                    LOG.info("Loading LDIF file '{}'", ldifFile.getName());
                    loadLdif(ldifFile);
                } catch (Exception ne) {
                    // If the file can't be read, log the error, and stop
                    // loading LDIFs.
                    LOG.error(I18n.err(I18n.ERR_180, ldifFile.getAbsolutePath(), ne.getLocalizedMessage()));
                    throw new LdapException(ne.getMessage(), ne);
                }
            }
        }
    }

    /**
     * initialize the schema partition by loading the schema LDIF files
     * 
     * @throws Exception in case of any problems while extracting and writing the schema files
     */
    private void initSchema() throws Exception {
        SchemaPartition schemaPartition = directoryService.getSchemaPartition();
        String workingDirectory = directoryService.getInstanceLayout().getPartitionsDirectory().getPath();

        // Extract the schema on disk (a brand new one) and load the registries
        File schemaRepository = new File(workingDirectory, "schema");

        if (schemaRepository.exists()) {
            LOG.info("schema partition already exists, skipping schema extraction");
        } else {
            SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor(new File(workingDirectory));
            extractor.extractOrCopy();
        }

        SchemaLoader loader = new LdifSchemaLoader(schemaRepository);
        SchemaManager schemaManager = new DefaultSchemaManager(loader);
        directoryService.setSchemaManager(schemaManager);

        // Init the LdifPartition
        LdifPartition ldifPartition = new LdifPartition(schemaManager, directoryService.getDnFactory());
        ldifPartition.setPartitionPath(new File(workingDirectory, "schema").toURI());

        schemaPartition.setWrappedPartition(ldifPartition);

        // We have to load the schema now, otherwise we won't be able
        // to initialize the Partitions, as we won't be able to parse
        // and normalize their suffix Dn
        schemaManager.loadAllEnabled();

        schemaPartition.setSchemaManager(schemaManager);

        List<Throwable> errors = schemaManager.getErrors();

        if (!errors.isEmpty()) {
            throw new Exception(I18n.err(I18n.ERR_317, Exceptions.printErrors(errors)));
        }
    }
}