com.unboundid.ldap.sdk.persist.PersistUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unboundid-ldapsdk Show documentation
Show all versions of unboundid-ldapsdk Show documentation
The UnboundID LDAP SDK for Java is a fast, comprehensive, and easy-to-use
Java API for communicating with LDAP directory servers and performing
related tasks like reading and writing LDIF, encoding and decoding data
using base64 and ASN.1 BER, and performing secure communication. This
package contains the Standard Edition of the LDAP SDK, which is a
complete, general-purpose library for communicating with LDAPv3 directory
servers.
/*
* Copyright 2009-2023 Ping Identity Corporation
* All Rights Reserved.
*/
/*
* Copyright 2009-2023 Ping Identity Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2009-2023 Ping Identity Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (GPLv2 only)
* or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
package com.unboundid.ldap.sdk.persist;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.DNEntrySource;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPInterface;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.util.CryptoHelper;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.Validator;
import static com.unboundid.ldap.sdk.persist.PersistMessages.*;
/**
* This class provides a set of utilities that may be used in the course of
* persistence processing.
*/
@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
public final class PersistUtils
{
/**
* Prevent this utility class from being instantiated.
*/
private PersistUtils()
{
// No implementation required.
}
/**
* Indicates whether the provided string could be used as a valid attribute or
* object class name. Numeric OIDs will also be considered acceptable.
*
* @param s The string for which to make the determination.
* @param r A buffer to which the unacceptable reason may be appended. It
* must not be {@code null}.
*
* @return {@code true} if the provided string is acceptable for use as an
* LDAP attribute or object class name, or {@code false} if not.
*/
public static boolean isValidLDAPName(@NotNull final String s,
@NotNull final StringBuilder r)
{
return isValidLDAPName(s, false, r);
}
/**
* Indicates whether the provided string could be used as a valid attribute or
* object class name. Numeric OIDs will also be considered acceptable.
*
* @param s The string for which to make the determination.
* @param o Indicates whether the name should be allowed to contain
* attribute options (e.g., a semicolon with one or more valid
* characters after it).
* @param r A buffer to which the unacceptable reason may be appended. It
* must not be {@code null}.
*
* @return {@code true} if the provided string is acceptable for use as an
* LDAP attribute or object class name, or {@code false} if not.
*/
public static boolean isValidLDAPName(@NotNull final String s,
final boolean o,
@NotNull final StringBuilder r)
{
int length;
if ((s == null) || ((length = s.length()) == 0))
{
r.append(ERR_LDAP_NAME_VALIDATOR_EMPTY.get());
return false;
}
final String baseName;
final int semicolonPos = s.indexOf(';');
if (semicolonPos > 0)
{
if (! o)
{
r.append(ERR_LDAP_NAME_VALIDATOR_INVALID_CHAR.get(s, ';',
semicolonPos));
return false;
}
baseName = s.substring(0, semicolonPos);
length = baseName.length();
final String optionsStr = s.substring(semicolonPos+1);
if (! isValidOptionSet(baseName, optionsStr, r))
{
return false;
}
}
else
{
baseName = s;
}
if (StaticUtils.isNumericOID(baseName))
{
return true;
}
for (int i=0; i < length; i++)
{
final char c = baseName.charAt(i);
if (((c >= 'a') && (c <= 'z')) ||
((c >= 'A') && (c <= 'Z')))
{
// This will always be acceptable.
}
else if (((c >= '0') && (c <= '9')) || (c == '-'))
{
// This will be acceptable for all but the first character.
if (i == 0)
{
r.append(ERR_LDAP_NAME_VALIDATOR_INVALID_FIRST_CHAR.get(s));
return false;
}
}
else
{
r.append(ERR_LDAP_NAME_VALIDATOR_INVALID_CHAR.get(s, c, i));
return false;
}
}
return true;
}
/**
* Indicates whether the provided string represents a valid set of attribute
* options. It should not contain the initial semicolon.
*
* @param b The base name for the attribute, without the option string or
* the semicolon used to delimit the option string from the base
* name.
* @param o The option string to examine. It must not be {@code null}, and
* must not contain the initial semicolon.
* @param r A buffer to which the unacceptable reason may be appended. It
* must not be {@code null}.
*
* @return {@code true} if the provided string represents a valid set of
* options, or {@code false} if not.
*/
private static boolean isValidOptionSet(@NotNull final String b,
@NotNull final String o,
@NotNull final StringBuilder r)
{
boolean lastWasSemicolon = true;
for (int i=0; i < o.length(); i++)
{
final char c = o.charAt(i);
if (c == ';')
{
if (lastWasSemicolon)
{
r.append(
ERR_LDAP_NAME_VALIDATOR_OPTION_WITH_CONSECUTIVE_SEMICOLONS.get(
b + ';' + o));
return false;
}
else
{
lastWasSemicolon = true;
}
}
else
{
lastWasSemicolon = false;
if (((c >= 'a') && (c <= 'z')) ||
((c >= 'A') && (c <= 'Z')) ||
((c >= '0') && (c <= '9')) ||
(c == '-'))
{
// This will always be acceptable.
}
else
{
r.append(ERR_LDAP_NAME_VALIDATOR_INVALID_OPTION_CHAR.get(
(b + ';' + o), c, (b.length() + 1 + i)));
return false;
}
}
}
if (lastWasSemicolon)
{
r.append(ERR_LDAP_NAME_VALIDATOR_ENDS_WITH_SEMICOLON.get(b + ';' + o));
return false;
}
return true;
}
/**
* Indicates whether the provided string could be used as a valid Java
* identifier. The identifier must begin with an ASCII letter or underscore,
* and must contain only ASCII letters, ASCII digits, and the underscore
* character. Even though a dollar sign is technically allowed, it will not
* be considered valid for the purpose of this method. Similarly, even though
* Java keywords are not allowed, they will not be rejected by this method.
*
* @param s The string for which to make the determination. It must not be
* {@code null}.
* @param r A buffer to which the unacceptable reason may be appended. It
* must not be {@code null}.
*
* @return {@code true} if the provided string is acceptable for use as a
* Java identifier, or {@code false} if not.
*/
public static boolean isValidJavaIdentifier(@NotNull final String s,
@NotNull final StringBuilder r)
{
final int length = s.length();
for (int i=0; i < length; i++)
{
final char c = s.charAt(i);
if (((c >= 'a') && (c <= 'z')) ||
((c >= 'A') && (c <= 'Z')) ||
(c == '_'))
{
// This will always be acceptable.
}
else if ((c >= '0') && (c <= '9'))
{
if (i == 0)
{
r.append(ERR_JAVA_NAME_VALIDATOR_INVALID_FIRST_CHAR_DIGIT.get(s));
return false;
}
}
else
{
r.append(ERR_JAVA_NAME_VALIDATOR_INVALID_CHAR.get(s, c, i));
return false;
}
}
return true;
}
/**
* Transforms the provided string if necessary so that it may be used as a
* valid Java identifier. If the provided string is already a valid Java
* identifier, then it will be returned as-is. Otherwise, it will be
* transformed to make it more suitable.
*
* @param s The attribute or object class name to be converted to a Java
* identifier.
*
* @return A string that may be used as a valid Java identifier.
*/
@NotNull()
public static String toJavaIdentifier(@NotNull final String s)
{
final int length;
if ((s == null) || ((length = s.length()) == 0))
{
// This will be ugly, but safe.
return toJavaIdentifier(CryptoHelper.getRandomUUID().toString());
}
boolean nextUpper = false;
final StringBuilder b = new StringBuilder(length);
for (int i=0; i < length; i++)
{
final char c = s.charAt(i);
if (((c >= 'a') && (c <= 'z')) ||
((c >= 'A') && (c <= 'Z')))
{
if (nextUpper)
{
b.append(Character.toUpperCase(c));
}
else
{
b.append(c);
}
nextUpper = false;
}
else if ((c >= '0') && (c <= '9'))
{
if (i == 0)
{
// Java identifiers can't begin with a digit, but they can begin with
// an underscore followed by a digit, so we'll use that instead.
b.append('_');
}
b.append(c);
nextUpper = false;
}
else
{
// If the provided string was a valid LDAP attribute or object class
// name, then this should be a dash, but we'll be safe and take the same
// action for any remaining character.
nextUpper = true;
}
}
if (b.length() == 0)
{
// This should only happen if the provided string wasn't a valid LDAP
// attribute or object class name to start with.
return toJavaIdentifier(CryptoHelper.getRandomUUID().toString());
}
return b.toString();
}
/**
* Retrieves the entry with the specified DN and decodes it as an object of
* the specified type.
*
* @param The type of object as which to decode the entry.
*
* @param dn The DN of the entry to retrieve. It must not be
* {@code null}.
* @param type The type of object as which the entry should be decoded. It
* must not be {@code null}, and the class must be marked with
* the {@link LDAPObject} annotation type.
* @param conn The connection that should be used to retrieve the entry. It
* must not be {@code null}.
*
* @return The object decoded from the specified entry, or {@code null} if
* the entry cannot be retrieved (e.g., because it does not exist or
* is not readable by the authenticated user).
*
* @throws LDAPException If a problem occurs while trying to retrieve the
* entry or decode it as the specified type of object.
*/
@Nullable()
public static T getEntryAsObject(@NotNull final DN dn,
@NotNull final Class type,
@NotNull final LDAPInterface conn)
throws LDAPException
{
Validator.ensureNotNull(dn, type, conn);
final LDAPPersister p = LDAPPersister.getInstance(type);
final Entry e = conn.getEntry(dn.toString(),
p.getObjectHandler().getAttributesToRequest());
if (e == null)
{
return null;
}
return p.decode(e);
}
/**
* Retrieves and decodes the indicated entries as objects of the specified
* type.
*
* @param The type of object as which to decode the entries.
*
* @param dns The DNs of the entries to retrieve. It must not be
* {@code null}.
* @param type The type of object as which the entries should be decoded.
* It must not be {@code null}, and the class must be marked
* with the {@link LDAPObject} annotation type.
* @param conn The connection that should be used to retrieve the entries.
* It must not be {@code null}.
*
* @return A {@code PersistedObjects} result that may be used to access the
* objects decoded from the provided set of DNs.
*
* @throws LDAPPersistException If the requested type cannot be used with
* the LDAP SDK persistence framework.
*/
@NotNull()
public static PersistedObjects getEntriesAsObjects(
@NotNull final DN[] dns,
@NotNull final Class type,
@NotNull final LDAPInterface conn)
throws LDAPPersistException
{
Validator.ensureNotNull(dns, type, conn);
final LDAPPersister p = LDAPPersister.getInstance(type);
final DNEntrySource entrySource = new DNEntrySource(conn, dns,
p.getObjectHandler().getAttributesToRequest());
return new PersistedObjects<>(p, entrySource);
}
}