/* * 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 2009 Sun Microsystems, Inc. */ package org.opends.sdk.schema; import static com.sun.opends.sdk.messages.Messages.*; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import org.opends.sdk.*; import org.opends.sdk.requests.Requests; import org.opends.sdk.requests.SearchRequest; import org.opends.sdk.responses.Responses; import org.opends.sdk.responses.Result; import org.opends.sdk.responses.SearchResultEntry; import com.sun.opends.sdk.util.FutureResultTransformer; import com.sun.opends.sdk.util.RecursiveFutureResult; import com.sun.opends.sdk.util.StaticUtils; /** * This class defines a data structure that holds information about the * components of the LDAP schema. It includes the following kinds of elements: *
* If the requested schema is not returned by the Directory Server then the * request will fail with an {@link EntryNotFoundException}. More * specifically, the returned future will never return {@code null}. *
* This method uses a Search operation to read the schema and does not perform
* caching. More specifically, it does not use the
* {@link AsynchronousConnection#readSchema} method.
*
* @param connection
* A connection to the Directory Server whose schema is to be read.
* @param name
* The distinguished name of the subschema sub-entry.
* @param handler
* A result handler which can be used to asynchronously process the
* operation result when it is received, may be {@code null}.
* @return A future representing the result of the operation.
* @throws UnsupportedOperationException
* If this connection does not support search operations.
* @throws IllegalStateException
* If this connection has already been closed, i.e. if {@code
* isClosed() == true}.
* @throws NullPointerException
* If the {@code connection} or {@code name} was {@code null}.
*/
public static FutureResult
* If the requested schema is not returned by the Directory Server then the
* request will fail with an {@link EntryNotFoundException}. More
* specifically, this method will never return {@code null}.
*
* This method uses a Search operation to read the schema and does not perform
* caching. More specifically, it does not use the
* {@link Connection#readSchema} method.
*
* @param connection
* A connection to the Directory Server whose schema is to be read.
* @param name
* The distinguished name of the subschema sub-entry.
* @return The schema from the Directory Server.
* @throws ErrorResultException
* If the result code indicates that the request failed for some
* reason.
* @throws InterruptedException
* If the current thread was interrupted while waiting.
* @throws UnsupportedOperationException
* If the connection does not support search operations.
* @throws IllegalStateException
* If the connection has already been closed, i.e. if {@code
* isClosed() == true}.
* @throws NullPointerException
* If the {@code connection} or {@code name} was {@code null}.
*/
public static Schema readSchema(final Connection connection, final DN name)
throws ErrorResultException, InterruptedException,
UnsupportedOperationException, IllegalStateException,
NullPointerException
{
final SearchRequest request = getReadSchemaSearchRequest(name);
final Entry entry = connection.searchSingleEntry(request);
return valueOf(entry);
}
/**
* Reads the schema from the Directory Server which applies to the named
* entry.
*
* If the requested entry or its associated schema are not returned by the
* Directory Server then the request will fail with an
* {@link EntryNotFoundException}. More specifically, the returned future will
* never return {@code null}.
*
* This implementation first reads the {@code subschemaSubentry} attribute of
* the entry in order to identify the schema and then invokes
* {@link #readSchema} to read the schema. More specifically, it does not use
* the {@link AsynchronousConnection#readSchemaForEntry} method.
*
* @param connection
* A connection to the Directory Server whose schema is to be read.
* @param name
* The distinguished name of the entry whose schema is to be located.
* @param handler
* A result handler which can be used to asynchronously process the
* operation result when it is received, may be {@code null}.
* @return A future representing the result of the operation.
* @throws UnsupportedOperationException
* If this connection does not support search operations.
* @throws IllegalStateException
* If this connection has already been closed, i.e. if {@code
* isClosed() == true}.
* @throws NullPointerException
* If the {@code connection} or {@code name} was {@code null}.
*/
public static FutureResult
* If the requested entry or its associated schema are not returned by the
* Directory Server then the request will fail with an
* {@link EntryNotFoundException}. More specifically, this method will never
* return {@code null}.
*
* This implementation first reads the {@code subschemaSubentry} attribute of
* the entry in order to identify the schema and then invokes
* {@link #readSchema} to read the schema. More specifically, it does not use
* the {@link Connection#readSchemaForEntry} method.
*
* @param connection
* A connection to the Directory Server whose schema is to be read.
* @param name
* The distinguished name of the entry whose schema is to be located.
* @return The schema from the Directory Server which applies to the named
* entry.
* @throws ErrorResultException
* If the result code indicates that the request failed for some
* reason.
* @throws InterruptedException
* If the current thread was interrupted while waiting.
* @throws UnsupportedOperationException
* If the connection does not support search operations.
* @throws IllegalStateException
* If the connection has already been closed, i.e. if {@code
* isClosed() == true}.
* @throws NullPointerException
* If the {@code connection} or {@code name} was {@code null}.
*/
public static Schema readSchemaForEntry(final Connection connection,
final DN name) throws ErrorResultException, InterruptedException,
UnsupportedOperationException, IllegalStateException,
NullPointerException
{
final SearchRequest request = getReadSchemaForEntrySearchRequest(name);
final Entry entry = connection.searchSingleEntry(request);
final DN subschemaDN = getSubschemaSubentryDN(name, entry);
return readSchema(connection, subschemaDN);
}
/**
* Sets the default schema which should be used by this application. The
* default schema is initially set to the core schema.
*
* @param schema
* The default schema which should be used by this application.
*/
public static void setDefaultSchema(final Schema schema)
{
defaultSchema = schema;
}
/**
* Parses the provided entry as a subschema subentry. Any problems encountered
* while parsing the entry can be retrieved using the returned schema's
* {@link #getWarnings()} method.
*
* @param entry
* The subschema subentry to be parsed.
* @return The parsed schema.
*/
public static Schema valueOf(final Entry entry)
{
final SchemaBuilder builder = new SchemaBuilder(entry.getName().toString());
Attribute attr = entry.getAttribute(ATTR_LDAP_SYNTAXES);
if (attr != null)
{
for (final ByteString def : attr)
{
try
{
builder.addSyntax(def.toString(), true);
}
catch (final LocalizedIllegalArgumentException e)
{
builder.addWarning(e.getMessageObject());
}
}
}
attr = entry.getAttribute(ATTR_ATTRIBUTE_TYPES);
if (attr != null)
{
for (final ByteString def : attr)
{
try
{
builder.addAttributeType(def.toString(), true);
}
catch (final LocalizedIllegalArgumentException e)
{
builder.addWarning(e.getMessageObject());
}
}
}
attr = entry.getAttribute(ATTR_OBJECT_CLASSES);
if (attr != null)
{
for (final ByteString def : attr)
{
try
{
builder.addObjectClass(def.toString(), true);
}
catch (final LocalizedIllegalArgumentException e)
{
builder.addWarning(e.getMessageObject());
}
}
}
attr = entry.getAttribute(ATTR_MATCHING_RULE_USE);
if (attr != null)
{
for (final ByteString def : attr)
{
try
{
builder.addMatchingRuleUse(def.toString(), true);
}
catch (final LocalizedIllegalArgumentException e)
{
builder.addWarning(e.getMessageObject());
}
}
}
attr = entry.getAttribute(ATTR_MATCHING_RULES);
if (attr != null)
{
for (final ByteString def : attr)
{
try
{
builder.addMatchingRule(def.toString(), true);
}
catch (final LocalizedIllegalArgumentException e)
{
builder.addWarning(e.getMessageObject());
}
}
}
attr = entry.getAttribute(ATTR_DIT_CONTENT_RULES);
if (attr != null)
{
for (final ByteString def : attr)
{
try
{
builder.addDITContentRule(def.toString(), true);
}
catch (final LocalizedIllegalArgumentException e)
{
builder.addWarning(e.getMessageObject());
}
}
}
attr = entry.getAttribute(ATTR_DIT_STRUCTURE_RULES);
if (attr != null)
{
for (final ByteString def : attr)
{
try
{
builder.addDITStructureRule(def.toString(), true);
}
catch (final LocalizedIllegalArgumentException e)
{
builder.addWarning(e.getMessageObject());
}
}
}
attr = entry.getAttribute(ATTR_NAME_FORMS);
if (attr != null)
{
for (final ByteString def : attr)
{
try
{
builder.addNameForm(def.toString(), true);
}
catch (final LocalizedIllegalArgumentException e)
{
builder.addWarning(e.getMessageObject());
}
}
}
return builder.toSchema();
}
static MatchingRule getDefaultMatchingRule()
{
return CoreSchema.getOctetStringMatchingRule();
}
static Syntax getDefaultSyntax()
{
return CoreSchema.getOctetStringSyntax();
}
// Constructs a search request for retrieving the subschemaSubentry
// attribute from the named entry.
private static SearchRequest getReadSchemaForEntrySearchRequest(final DN dn)
{
return Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT, Filter
.getObjectClassPresentFilter(), SUBSCHEMA_SUBENTRY_ATTRS);
}
// Constructs a search request for retrieving the named subschema
// sub-entry.
private static SearchRequest getReadSchemaSearchRequest(final DN dn)
{
return Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT,
SUBSCHEMA_FILTER, SUBSCHEMA_ATTRS);
}
private static DN getSubschemaSubentryDN(final DN name, final Entry entry)
throws ErrorResultException
{
final Attribute subentryAttr = entry.getAttribute(ATTR_SUBSCHEMA_SUBENTRY);
if (subentryAttr == null || subentryAttr.isEmpty())
{
// Did not get the subschema sub-entry attribute.
final Result result = Responses.newResult(
ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED).setDiagnosticMessage(
ERR_NO_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString()).toString());
throw ErrorResultException.wrap(result);
}
final String dnString = subentryAttr.iterator().next().toString();
DN subschemaDN;
try
{
subschemaDN = DN.valueOf(dnString);
}
catch (final LocalizedIllegalArgumentException e)
{
final Result result = Responses.newResult(
ResultCode.CLIENT_SIDE_NO_RESULTS_RETURNED).setDiagnosticMessage(
ERR_INVALID_SUBSCHEMA_SUBENTRY_ATTR.get(name.toString(), dnString,
e.getMessageObject()).toString());
throw ErrorResultException.wrap(result);
}
return subschemaDN;
}
private final Impl impl;
Schema(final String schemaName,
final Map