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-2015 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/**
065 * An LDAP management connection context.
066 */
067public final class LDAPManagementContext extends DriverBasedManagementContext {
068
069    private static final class ManagementContextWrapper implements ManagementContext {
070        private final ManagementContext delegate;
071        private final List<IOException> exceptions;
072
073        private ManagementContextWrapper(ManagementContext result, List<IOException> exceptions) {
074            this.delegate = result;
075            this.exceptions = exceptions;
076        }
077
078        @Override
079        public boolean managedObjectExists(ManagedObjectPath<?, ?> path) throws ManagedObjectNotFoundException,
080                LdapException {
081            return delegate.managedObjectExists(path);
082        }
083
084        @Override
085        public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
086                ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd) throws ManagedObjectNotFoundException,
087                LdapException {
088            return delegate.listManagedObjects(parent, rd);
089        }
090
091        @Override
092        public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
093                ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd,
094                AbstractManagedObjectDefinition<? extends C, ? extends S> d) throws ManagedObjectNotFoundException,
095                LdapException {
096            return delegate.listManagedObjects(parent, rd, d);
097        }
098
099        @Override
100        public <C extends ConfigurationClient, S extends Configuration> String[] listManagedObjects(
101                ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd)
102                throws ManagedObjectNotFoundException, LdapException {
103            return delegate.listManagedObjects(parent, rd);
104        }
105
106        @Override
107        public ManagedObject<RootCfgClient> getRootConfigurationManagedObject() {
108            return delegate.getRootConfigurationManagedObject();
109        }
110
111        @Override
112        public RootCfgClient getRootConfiguration() {
113            return delegate.getRootConfiguration();
114        }
115
116        @Override
117        public <P> SortedSet<P> getPropertyValues(ManagedObjectPath<?, ?> path, PropertyDefinition<P> pd)
118                throws DefinitionDecodingException, LdapException, ManagedObjectNotFoundException {
119            return delegate.getPropertyValues(path, pd);
120        }
121
122        @Override
123        public <P> P getPropertyValue(ManagedObjectPath<?, ?> path, PropertyDefinition<P> pd)
124                throws DefinitionDecodingException, LdapException, ManagedObjectNotFoundException {
125            return delegate.getPropertyValue(path, pd);
126        }
127
128        @Override
129        public <C extends ConfigurationClient, S extends Configuration> ManagedObject<? extends C> getManagedObject(
130                ManagedObjectPath<C, S> path) throws DefinitionDecodingException, ManagedObjectDecodingException,
131                ManagedObjectNotFoundException, LdapException {
132            return delegate.getManagedObject(path);
133        }
134
135        @Override
136        public <C extends ConfigurationClient, S extends Configuration> boolean deleteManagedObject(
137                ManagedObjectPath<?, ?> parent, SetRelationDefinition<C, S> rd, String name)
138                throws ManagedObjectNotFoundException, OperationRejectedException, LdapException {
139            return delegate.deleteManagedObject(parent, rd, name);
140        }
141
142        @Override
143        public <C extends ConfigurationClient, S extends Configuration> boolean deleteManagedObject(
144                ManagedObjectPath<?, ?> parent, OptionalRelationDefinition<C, S> rd)
145                throws ManagedObjectNotFoundException, OperationRejectedException, LdapException {
146            return delegate.deleteManagedObject(parent, rd);
147        }
148
149        @Override
150        public <C extends ConfigurationClient, S extends Configuration> boolean deleteManagedObject(
151                ManagedObjectPath<?, ?> parent, InstantiableRelationDefinition<C, S> rd, String name)
152                throws ManagedObjectNotFoundException, OperationRejectedException, LdapException {
153            return delegate.deleteManagedObject(parent, rd, name);
154        }
155
156        @Override
157        public void close() throws IOException {
158            delegate.close();
159            if (!exceptions.isEmpty()) {
160                throw exceptions.get(0);
161            }
162        }
163    }
164
165    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
166
167    /**
168     * Create a new LDAP management context using the provided LDAP connection.
169     *
170     * @param connection
171     *            The LDAP connection.
172     * @param profile
173     *            The LDAP profile.
174     * @return Returns the new management context.
175     */
176    public static ManagementContext newManagementContext(Connection connection, LDAPProfile profile) {
177        Reject.ifNull(connection, profile);
178        LDAPDriver driver = new LDAPDriver(connection, profile);
179        LDAPManagementContext context = new LDAPManagementContext(driver);
180        driver.setManagementContext(context);
181        return context;
182    }
183
184    private static ManagementContext newLDIFManagementContext(final File ldifFile, final LDAPProfile profile,
185            final List<IOException> exceptions) throws IOException {
186        final BufferedReader configReader = new BufferedReader(new FileReader(ldifFile));
187        try {
188            final MemoryBackend memoryBackend = new MemoryBackend(new LDIFEntryReader(configReader));
189            final Connection co = new AbstractConnectionWrapper<Connection>(newInternalConnection(memoryBackend)) {
190                @Override
191                public void close() {
192                    try {
193                        final BufferedWriter configWriter = new BufferedWriter(new FileWriter(ldifFile));
194                        try {
195                            final Iterator<Entry> entries = memoryBackend.getAll().iterator();
196                            entries.next(); // skip RootDSE
197                            LDIF.copyTo(LDIF.newEntryIteratorReader(entries), new LDIFEntryWriter(configWriter));
198                        } finally {
199                            configWriter.close();
200                        }
201                    } catch (IOException e) {
202                        if (exceptions != null) {
203                            exceptions.add(e);
204                        } else {
205                            logger.error(LocalizableMessage.raw(
206                                    "IOException occured during LDIF context management close:", e));
207                        }
208                    }
209                }
210
211                @Override
212                public void close(UnbindRequest request, String reason) {
213                    close();
214                }
215            };
216
217            // We need to add the root dse entry to make the configuration framework work.
218            co.add(LDIFEntryReader.valueOfLDIFEntry("dn:", "objectClass:top", "objectClass:ds-root-dse"));
219            return LDAPManagementContext.newManagementContext(co, LDAPProfile.getInstance());
220        } finally {
221            configReader.close();
222        }
223    }
224
225    /**
226     * Returns a LDIF management context on the provided LDIF file.
227     *
228     * @param ldifFile
229     *            The LDIF file to manage
230     * @param profile
231     *            The LDAP profile
232     * @return A LDIF file management context
233     * @throws IOException
234     *             If problems occurs while reading the file.
235     */
236    public static ManagementContext newLDIFManagementContext(final File ldifFile, final LDAPProfile profile)
237            throws IOException {
238        final List<IOException> exceptions = new ArrayList<>();
239        return new ManagementContextWrapper(newLDIFManagementContext(ldifFile, profile, exceptions), exceptions);
240    }
241
242    /** The LDAP management context driver. */
243    private final LDAPDriver driver;
244
245    /** Private constructor. */
246    private LDAPManagementContext(LDAPDriver driver) {
247        this.driver = driver;
248    }
249
250    /** {@inheritDoc} */
251    @Override
252    protected Driver getDriver() {
253        return driver;
254    }
255}