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