/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2007-2009 Sun Microsystems, Inc.
*/
package org.opends.server.admin.client.ldap;
import javax.naming.NameAlreadyBoundException;
import javax.naming.NamingException;
import javax.naming.NoPermissionException;
import javax.naming.OperationNotSupportedException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;
import org.opends.messages.Message;
import org.opends.server.admin.AggregationPropertyDefinition;
import org.opends.server.admin.Configuration;
import org.opends.server.admin.ConfigurationClient;
import org.opends.server.admin.InstantiableRelationDefinition;
import org.opends.server.admin.ManagedObjectAlreadyExistsException;
import org.opends.server.admin.ManagedObjectDefinition;
import org.opends.server.admin.ManagedObjectNotFoundException;
import org.opends.server.admin.ManagedObjectPath;
import org.opends.server.admin.PropertyDefinition;
import org.opends.server.admin.PropertyOption;
import org.opends.server.admin.PropertyValueVisitor;
import org.opends.server.admin.Reference;
import org.opends.server.admin.RelationDefinition;
import org.opends.server.admin.SetRelationDefinition;
import org.opends.server.admin.UnknownPropertyDefinitionException;
import org.opends.server.admin.client.AuthorizationException;
import org.opends.server.admin.client.CommunicationException;
import org.opends.server.admin.client.ConcurrentModificationException;
import org.opends.server.admin.client.ManagedObject;
import org.opends.server.admin.client.OperationRejectedException;
import org.opends.server.admin.client.OperationRejectedException.OperationType;
import org.opends.server.admin.client.spi.AbstractManagedObject;
import org.opends.server.admin.client.spi.Driver;
import org.opends.server.admin.client.spi.Property;
import org.opends.server.admin.client.spi.PropertySet;
/**
* A managed object bound to an LDAP connection.
*
* @param <T>
* The type of client configuration represented by the client
* managed object.
*/
final class LDAPManagedObject<T extends ConfigurationClient> extends
AbstractManagedObject<T> {
/**
* A visitor which is used to encode property LDAP values.
*/
private static final class ValueEncoder extends
PropertyValueVisitor<Object, Void> {
// Prevent instantiation.
private ValueEncoder() {
// No implementation required.
}
/**
* {@inheritDoc}
*/
@Override
public <C extends ConfigurationClient, S extends Configuration>
Object visitAggregation(
AggregationPropertyDefinition<C, S> pd, String v, Void p) {
// Aggregations values are stored as full DNs in LDAP, but
// just their common name is exposed in the admin framework.
Reference<C, S> reference = Reference.parseName(pd.getParentPath(), pd
.getRelationDefinition(), v);
return reference.toDN().toString();
}
/**
* {@inheritDoc}
*/
@Override
public <PD> Object visitUnknown(PropertyDefinition<PD> pd, PD v, Void p)
throws UnknownPropertyDefinitionException {
return pd.encodeValue(v);
}
}
// The LDAP management driver associated with this managed object.
private final LDAPDriver driver;
/**
* Creates a new LDAP managed object instance.
*
* @param driver
* The underlying LDAP management driver.
* @param d
* The managed object's definition.
* @param path
* The managed object's path.
* @param properties
* The managed object's properties.
* @param existsOnServer
* Indicates whether or not the managed object already
* exists.
* @param namingPropertyDefinition
* The managed object's naming property definition if there
* is one.
*/
LDAPManagedObject(LDAPDriver driver,
ManagedObjectDefinition<T, ? extends Configuration> d,
ManagedObjectPath<T, ? extends Configuration> path,
PropertySet properties, boolean existsOnServer,
PropertyDefinition<?> namingPropertyDefinition) {
super(d, path, properties, existsOnServer, namingPropertyDefinition);
this.driver = driver;
}
/**
* {@inheritDoc}
*/
@Override
protected void addNewManagedObject() throws AuthorizationException,
CommunicationException, OperationRejectedException,
ConcurrentModificationException, ManagedObjectAlreadyExistsException {
// First make sure that the parent managed object still exists.
ManagedObjectDefinition<?, ?> d = getManagedObjectDefinition();
ManagedObjectPath<?, ?> path = getManagedObjectPath();
ManagedObjectPath<?, ?> parent = path.parent();
try {
if (!driver.managedObjectExists(parent)) {
throw new ConcurrentModificationException();
}
} catch (ManagedObjectNotFoundException e) {
throw new ConcurrentModificationException();
}
// We may need to create the parent "relation" entry if this is a
// child of an instantiable or set relation.
RelationDefinition<?, ?> r = path.getRelationDefinition();
if (r instanceof InstantiableRelationDefinition
|| r instanceof SetRelationDefinition) {
// TODO: this implementation does not handle relations which
// comprise of more than one RDN arc (this will probably never
// be required anyway).
LdapName dn;
if (r instanceof InstantiableRelationDefinition) {
dn = LDAPNameBuilder.create(parent,
(InstantiableRelationDefinition) r, driver.getLDAPProfile());
} else {
dn = LDAPNameBuilder.create(parent,
(SetRelationDefinition) r, driver.getLDAPProfile());
}
if (!driver.entryExists(dn)) {
// We need to create the entry.
Attributes attributes = new BasicAttributes();
// Create the branch's object class attribute.
Attribute oc = new BasicAttribute("objectClass");
for (String objectClass : driver.getLDAPProfile()
.getRelationObjectClasses(r)) {
oc.add(objectClass);
}
attributes.put(oc);
// Create the branch's naming attribute.
Rdn rdn = dn.getRdn(dn.size() - 1);
attributes.put(rdn.getType(), rdn.getValue().toString());
// Create the entry.
try {
driver.getLDAPConnection().createEntry(dn, attributes);
} catch (OperationNotSupportedException e) {
// Unwilling to perform.
if (e.getMessage() == null) {
throw new OperationRejectedException(OperationType.CREATE, d
.getUserFriendlyName());
} else {
Message m = Message.raw("%s", e.getMessage());
throw new OperationRejectedException(OperationType.CREATE, d
.getUserFriendlyName(), m);
}
} catch (NamingException e) {
driver.adaptNamingException(e);
}
}
}
// Now add the entry representing this new managed object.
LdapName dn = LDAPNameBuilder.create(path, driver.getLDAPProfile());
Attributes attributes = new BasicAttributes(true);
// Create the object class attribute.
Attribute oc = new BasicAttribute("objectclass");
ManagedObjectDefinition<?, ?> definition = getManagedObjectDefinition();
for (String objectClass : driver.getLDAPProfile().getObjectClasses(
definition)) {
oc.add(objectClass);
}
attributes.put(oc);
// Create the naming attribute if there is not naming property.
PropertyDefinition<?> npd = getNamingPropertyDefinition();
if (npd == null) {
Rdn rdn = dn.getRdn(dn.size() - 1);
attributes.put(rdn.getType(), rdn.getValue().toString());
}
// Create the remaining attributes.
for (PropertyDefinition<?> pd : definition.getAllPropertyDefinitions()) {
String attrID = driver.getLDAPProfile().getAttributeName(definition, pd);
Attribute attribute = new BasicAttribute(attrID);
encodeProperty(attribute, pd);
if (attribute.size() != 0) {
attributes.put(attribute);
}
}
try {
// Create the entry.
driver.getLDAPConnection().createEntry(dn, attributes);
} catch (NameAlreadyBoundException e) {
throw new ManagedObjectAlreadyExistsException();
} catch (OperationNotSupportedException e) {
// Unwilling to perform.
if (e.getMessage() == null) {
throw new OperationRejectedException(OperationType.CREATE, d
.getUserFriendlyName());
} else {
Message m = Message.raw("%s", e.getMessage());
throw new OperationRejectedException(OperationType.CREATE, d
.getUserFriendlyName(), m);
}
} catch (NamingException e) {
driver.adaptNamingException(e);
}
}
/**
* {@inheritDoc}
*/
@Override
protected Driver getDriver() {
return driver;
}
/**
* {@inheritDoc}
*/
@Override
protected void modifyExistingManagedObject()
throws ConcurrentModificationException, OperationRejectedException,
AuthorizationException, CommunicationException {
// Build the list of modified attributes.
Attributes mods = new BasicAttributes();
ManagedObjectDefinition<?, ?> d = getManagedObjectDefinition();
for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
Property<?> p = getProperty(pd);
if (p.isModified()) {
String attrID = driver.getLDAPProfile().getAttributeName(d, pd);
Attribute attribute = new BasicAttribute(attrID);
encodeProperty(attribute, pd);
mods.put(attribute);
}
}
// Perform the LDAP modification if something has changed.
if (mods.size() > 0) {
try {
ManagedObjectPath<?, ?> path = getManagedObjectPath();
LdapName dn = LDAPNameBuilder.create(path, driver.getLDAPProfile());
driver.getLDAPConnection().modifyEntry(dn, mods);
} catch (NoPermissionException e) {
throw new AuthorizationException(e);
} catch (OperationNotSupportedException e) {
// Unwilling to perform.
if (e.getMessage() == null) {
throw new OperationRejectedException(OperationType.MODIFY, d
.getUserFriendlyName());
} else {
Message m = Message.raw("%s", e.getMessage());
throw new OperationRejectedException(OperationType.MODIFY, d
.getUserFriendlyName(), m);
}
} catch (NamingException e) {
// Just treat it as a communication problem.
throw new CommunicationException(e);
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected <M extends ConfigurationClient> ManagedObject<M> newInstance(
ManagedObjectDefinition<M, ?> d, ManagedObjectPath<M, ?> path,
PropertySet properties, boolean existsOnServer,
PropertyDefinition<?> namingPropertyDefinition) {
return new LDAPManagedObject<M>(driver, d, path, properties,
existsOnServer, namingPropertyDefinition);
}
// Encode a property into LDAP string values.
private <PD> void encodeProperty(Attribute attribute,
PropertyDefinition<PD> pd) {
PropertyValueVisitor<Object, Void> visitor = new ValueEncoder();
Property<PD> p = getProperty(pd);
if (pd.hasOption(PropertyOption.MANDATORY)) {
// For mandatory properties we fall-back to the default values
// if defined which can sometimes be the case e.g when a
// mandatory property is overridden.
for (PD value : p.getEffectiveValues()) {
attribute.add(pd.accept(visitor, value, null));
}
} else {
for (PD value : p.getPendingValues()) {
attribute.add(pd.accept(visitor, value, null));
}
}
}
/**
* {@inheritDoc}
*/
public boolean isModified() {
ManagedObjectDefinition<?, ?> d = getManagedObjectDefinition();
for (PropertyDefinition<?> pd : d.getAllPropertyDefinitions()) {
Property<?> p = getProperty(pd);
if (p.isModified()) {
return true;
}
}
return false;
}
}