001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2008 Sun Microsystems, Inc.
015 * Portions copyright 2014-2016 ForgeRock AS.
016 */
017
018package org.forgerock.opendj.config.client.ldap;
019
020import static org.forgerock.opendj.ldap.Connections.*;
021
022import java.io.BufferedReader;
023import java.io.BufferedWriter;
024import java.io.File;
025import java.io.FileReader;
026import java.io.FileWriter;
027import java.io.IOException;
028import java.util.ArrayList;
029import java.util.Iterator;
030import java.util.List;
031import java.util.SortedSet;
032
033import org.forgerock.i18n.LocalizableMessage;
034import org.forgerock.i18n.slf4j.LocalizedLogger;
035import org.forgerock.opendj.config.AbstractManagedObjectDefinition;
036import org.forgerock.opendj.config.Configuration;
037import org.forgerock.opendj.config.ConfigurationClient;
038import org.forgerock.opendj.config.DefinitionDecodingException;
039import org.forgerock.opendj.config.InstantiableRelationDefinition;
040import org.forgerock.opendj.config.LDAPProfile;
041import org.forgerock.opendj.config.ManagedObjectNotFoundException;
042import org.forgerock.opendj.config.ManagedObjectPath;
043import org.forgerock.opendj.config.OptionalRelationDefinition;
044import org.forgerock.opendj.config.PropertyDefinition;
045import org.forgerock.opendj.config.SetRelationDefinition;
046import org.forgerock.opendj.config.client.DriverBasedManagementContext;
047import org.forgerock.opendj.config.client.ManagedObject;
048import org.forgerock.opendj.config.client.ManagedObjectDecodingException;
049import org.forgerock.opendj.config.client.ManagementContext;
050import org.forgerock.opendj.config.client.OperationRejectedException;
051import org.forgerock.opendj.config.client.spi.Driver;
052import org.forgerock.opendj.ldap.AbstractConnectionWrapper;
053import org.forgerock.opendj.ldap.Connection;
054import org.forgerock.opendj.ldap.Entry;
055import org.forgerock.opendj.ldap.LdapException;
056import org.forgerock.opendj.ldap.MemoryBackend;
057import org.forgerock.opendj.ldap.requests.UnbindRequest;
058import org.forgerock.opendj.ldif.LDIF;
059import org.forgerock.opendj.ldif.LDIFEntryReader;
060import org.forgerock.opendj.ldif.LDIFEntryWriter;
061import org.forgerock.opendj.server.config.client.RootCfgClient;
062import org.forgerock.util.Reject;
063
064/** An LDAP management connection context. */
065public final class LDAPManagementContext extends DriverBasedManagementContext {
066
067    private static final class ManagementContextWrapper implements ManagementContext {
068        private final ManagementContext delegate;
069        private final List<IOException> exceptions;
070
071        private ManagementContextWrapper(ManagementContext result, List<IOException> exceptions) {
072            this.delegate = result;
073            this.exceptions = exceptions;
074        }
075
076        @Override
077        public boolean managedObjectExists(ManagedObjectPath<?, ?> path) throws ManagedObjectNotFoundException,
078                LdapException {
079            return delegate.managedObjectExists(path);
080        }
081
082        @Override
083        public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
084                ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd) throws ManagedObjectNotFoundException,
085                LdapException {
086            return delegate.listManagedObjects(parent, rd);
087        }
088
089        @Override
090        public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
091                ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
092                AbstractManagedObjectDefinition<? extends C, ? extends S> d) throws ManagedObjectNotFoundException,
093                LdapException {
094            return delegate.listManagedObjects(parent, rd, d);
095        }
096
097        @Override
098        public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
099                ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
100                throws ManagedObjectNotFoundException, LdapException {
101            return delegate.listManagedObjects(parent, rd);
102        }
103
104        @Override
105        public ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
106            return delegate.getRootConfigurationManagedObject();
107        }
108
109        @Override
110        public RootCfgClient getRootConfiguration() {
111            return delegate.getRootConfiguration();
112        }
113
114        @Override
115        public <P> SortedSet<P> getPropertyValues(ManagedObjectPath<?, ?> path, PropertyDefinition<P> pd)
116                throws DefinitionDecodingException, LdapException, ManagedObjectNotFoundException {
117            return delegate.getPropertyValues(path, pd);
118        }
119
120        @Override
121        public <P> P getPropertyValue(ManagedObjectPath<?, ?> path, PropertyDefinition<P> pd)
122                throws DefinitionDecodingException, LdapException, ManagedObjectNotFoundException {
123            return delegate.getPropertyValue(path, pd);
124        }
125
126        @Override
127        public <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getManagedObject(
128                ManagedObjectPath<C, S> path) throws DefinitionDecodingException, ManagedObjectDecodingException,
129                ManagedObjectNotFoundException, LdapException {
130            return delegate.getManagedObject(path);
131        }
132
133        @Override
134        public <C extends ConfigurationClient, S extends Configuration> boolean deleteManagedObject(
135                ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd, String name)
136                throws ManagedObjectNotFoundException, OperationRejectedException, LdapException {
137            return delegate.deleteManagedObject(parent, rd, name);
138        }
139
140        @Override
141        public <C extends ConfigurationClient, S extends Configuration> boolean deleteManagedObject(
142                ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
143                throws ManagedObjectNotFoundException, OperationRejectedException, LdapException {
144            return delegate.deleteManagedObject(parent, rd);
145        }
146
147        @Override
148        public <C extends ConfigurationClient, S extends Configuration> boolean deleteManagedObject(
149                ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd, String name)
150                throws ManagedObjectNotFoundException, OperationRejectedException, LdapException {
151            return delegate.deleteManagedObject(parent, rd, name);
152        }
153
154        @Override
155        public void close() throws IOException {
156            delegate.close();
157            if (!exceptions.isEmpty()) {
158                throw exceptions.get(0);
159            }
160        }
161    }
162
163    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
164
165    /**
166     * Create a new LDAP management context using the provided LDAP connection.
167     *
168     * @param connection
169     *            The LDAP connection.
170     * @param profile
171     *            The LDAP profile.
172     * @return Returns the new management context.
173     */
174    public static ManagementContext newManagementContext(Connection connection, LDAPProfile profile) {
175        Reject.ifNull(connection, profile);
176        LDAPDriver driver = new LDAPDriver(connection, profile);
177        LDAPManagementContext context = new LDAPManagementContext(driver);
178        driver.setManagementContext(context);
179        return context;
180    }
181
182    private static ManagementContext newLDIFManagementContext(final File ldifFile, final List<IOException> exceptions)
183            throws IOException {
184        try (final FileReader fileReader = new FileReader(ldifFile);
185            final BufferedReader configReader = new BufferedReader(fileReader)) {
186            final MemoryBackend memoryBackend = new MemoryBackend(new LDIFEntryReader(configReader));
187            final Connection co = new AbstractConnectionWrapper<Connection>(newInternalConnection(memoryBackend)) {
188                @Override
189                public void close() {
190                    try (final FileWriter fileWriter = new FileWriter(ldifFile);
191                        final BufferedWriter configWriter = new BufferedWriter(fileWriter)) {
192                        final Iterator<Entry> entries = memoryBackend.getAll().iterator();
193                        entries.next(); // skip RootDSE
194                        LDIF.copyTo(LDIF.newEntryIteratorReader(entries), new LDIFEntryWriter(configWriter));
195                    } catch (IOException e) {
196                        if (exceptions != null) {
197                            exceptions.add(e);
198                        } else {
199                            logger.error(LocalizableMessage.raw(
200                                    "IOException occured during LDIF context management close:", e));
201                        }
202                    }
203                }
204
205                @Override
206                public void close(UnbindRequest request, String reason) {
207                    close();
208                }
209            };
210
211            // We need to add the root dse entry to make the configuration framework work.
212            co.add(LDIFEntryReader.valueOfLDIFEntry("dn:", "objectClass:top", "objectClass:ds-root-dse"));
213            return LDAPManagementContext.newManagementContext(co, LDAPProfile.getInstance());
214        }
215    }
216
217    /**
218     * Returns a LDIF management context on the provided LDIF file.
219     *
220     * @param ldifFile
221     *            The LDIF file to manage
222     * @return A LDIF file management context
223     * @throws IOException
224     *             If problems occurs while reading the file.
225     */
226    public static ManagementContext newLDIFManagementContext(final File ldifFile) throws IOException {
227        final List<IOException> exceptions = new ArrayList<>();
228        return new ManagementContextWrapper(newLDIFManagementContext(ldifFile, exceptions), exceptions);
229    }
230
231    /** The LDAP management context driver. */
232    private final LDAPDriver driver;
233
234    /** Private constructor. */
235    private LDAPManagementContext(LDAPDriver driver) {
236        this.driver = driver;
237    }
238
239    @Override
240    protected Driver getDriver() {
241        return driver;
242    }
243}