package com.hwlcn.ldap.ldap.sdk.controls; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.UUID; import com.hwlcn.ldap.asn1.ASN1Boolean; import com.hwlcn.ldap.asn1.ASN1Constants; import com.hwlcn.ldap.asn1.ASN1Element; import com.hwlcn.ldap.asn1.ASN1OctetString; import com.hwlcn.ldap.asn1.ASN1Sequence; import com.hwlcn.ldap.asn1.ASN1Set; import com.hwlcn.ldap.ldap.sdk.Control; import com.hwlcn.ldap.ldap.sdk.IntermediateResponse; import com.hwlcn.ldap.ldap.sdk.LDAPException; import com.hwlcn.ldap.ldap.sdk.ResultCode; import com.hwlcn.ldap.util.Debug; import com.hwlcn.core.annotation.NotMutable; import com.hwlcn.ldap.util.StaticUtils; import com.hwlcn.core.annotation.ThreadSafety; import com.hwlcn.ldap.util.ThreadSafetyLevel; import com.hwlcn.ldap.util.Validator; import static com.hwlcn.ldap.ldap.sdk.controls.ControlMessages.*; @NotMutable() @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) public final class ContentSyncInfoIntermediateResponse extends IntermediateResponse { public static final String SYNC_INFO_OID = "1.3.6.1.4.1.4203.1.9.1.4"; private static final long serialVersionUID = 4464376009337157433L; private final ASN1OctetString cookie; private final boolean refreshDeletes; private final boolean refreshDone; private final ContentSyncInfoType type; private final List<UUID> entryUUIDs; private ContentSyncInfoIntermediateResponse(final ContentSyncInfoType type, final ASN1OctetString value, final ASN1OctetString cookie, final boolean refreshDone, final boolean refreshDeletes, final List<UUID> entryUUIDs, final Control... controls) { super(SYNC_INFO_OID, value, controls); this.type = type; this.cookie = cookie; this.refreshDone = refreshDone; this.refreshDeletes = refreshDeletes; this.entryUUIDs = entryUUIDs; } public static ContentSyncInfoIntermediateResponse createNewCookieResponse( final ASN1OctetString cookie, final Control... controls) { Validator.ensureNotNull(cookie); final ContentSyncInfoType type = ContentSyncInfoType.NEW_COOKIE; return new ContentSyncInfoIntermediateResponse(type, encodeValue(type, cookie, false, null, false), cookie, false, false, null, controls); } public static ContentSyncInfoIntermediateResponse createRefreshDeleteResponse( final ASN1OctetString cookie, final boolean refreshDone, final Control... controls) { final ContentSyncInfoType type = ContentSyncInfoType.REFRESH_DELETE; return new ContentSyncInfoIntermediateResponse(type, encodeValue(type, cookie, refreshDone, null, false), cookie, refreshDone, false, null, controls); } public static ContentSyncInfoIntermediateResponse createRefreshPresentResponse(final ASN1OctetString cookie, final boolean refreshDone, final Control... controls) { final ContentSyncInfoType type = ContentSyncInfoType.REFRESH_PRESENT; return new ContentSyncInfoIntermediateResponse(type, encodeValue(type, cookie, refreshDone, null, false), cookie, refreshDone, false, null, controls); } public static ContentSyncInfoIntermediateResponse createSyncIDSetResponse( final ASN1OctetString cookie, final List<UUID> entryUUIDs, final boolean refreshDeletes, final Control... controls) { Validator.ensureNotNull(entryUUIDs); final ContentSyncInfoType type = ContentSyncInfoType.SYNC_ID_SET; return new ContentSyncInfoIntermediateResponse(type, encodeValue(type, cookie, false, entryUUIDs, refreshDeletes), cookie, false, refreshDeletes, Collections.unmodifiableList(entryUUIDs), controls); } public static ContentSyncInfoIntermediateResponse decode( final IntermediateResponse r) throws LDAPException { final ASN1OctetString value = r.getValue(); if (value == null) { throw new LDAPException(ResultCode.DECODING_ERROR, ERR_SYNC_INFO_IR_NO_VALUE.get()); } final ASN1Element valueElement; try { valueElement = ASN1Element.decode(value.getValue()); } catch (final Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.DECODING_ERROR, ERR_SYNC_INFO_IR_VALUE_NOT_ELEMENT.get( StaticUtils.getExceptionMessage(e)), e); } final ContentSyncInfoType type = ContentSyncInfoType.valueOf(valueElement.getType()); if (type == null) { throw new LDAPException(ResultCode.DECODING_ERROR, ERR_SYNC_INFO_IR_VALUE_UNRECOGNIZED_TYPE.get( StaticUtils.toHex(valueElement.getType()))); } ASN1OctetString cookie = null; boolean refreshDone = false; boolean refreshDeletes = false; List<UUID> entryUUIDs = null; try { switch (type) { case NEW_COOKIE: cookie = new ASN1OctetString(valueElement.getValue()); break; case REFRESH_DELETE: case REFRESH_PRESENT: refreshDone = true; ASN1Sequence s = valueElement.decodeAsSequence(); for (final ASN1Element e : s.elements()) { switch (e.getType()) { case ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE: cookie = ASN1OctetString.decodeAsOctetString(e); break; case ASN1Constants.UNIVERSAL_BOOLEAN_TYPE: refreshDone = ASN1Boolean.decodeAsBoolean(e).booleanValue(); break; default: throw new LDAPException(ResultCode.DECODING_ERROR, ERR_SYNC_INFO_IR_VALUE_INVALID_SEQUENCE_TYPE.get( type.name(), StaticUtils.toHex(e.getType()))); } } break; case SYNC_ID_SET: s = valueElement.decodeAsSequence(); for (final ASN1Element e : s.elements()) { switch (e.getType()) { case ASN1Constants.UNIVERSAL_OCTET_STRING_TYPE: cookie = ASN1OctetString.decodeAsOctetString(e); break; case ASN1Constants.UNIVERSAL_BOOLEAN_TYPE: refreshDeletes = ASN1Boolean.decodeAsBoolean(e).booleanValue(); break; case ASN1Constants.UNIVERSAL_SET_TYPE: final ASN1Set uuidSet = ASN1Set.decodeAsSet(e); final ASN1Element[] uuidElements = uuidSet.elements(); entryUUIDs = new ArrayList<UUID>(uuidElements.length); for (final ASN1Element uuidElement : uuidElements) { try { entryUUIDs.add(StaticUtils.decodeUUID( uuidElement.getValue())); } catch (final ParseException pe) { Debug.debugException(pe); throw new LDAPException(ResultCode.DECODING_ERROR, ERR_SYNC_INFO_IR_INVALID_UUID.get(type.name(), pe.getMessage()), pe); } } break; default: throw new LDAPException(ResultCode.DECODING_ERROR, ERR_SYNC_INFO_IR_VALUE_INVALID_SEQUENCE_TYPE.get( type.name(), StaticUtils.toHex(e.getType()))); } } if (entryUUIDs == null) { throw new LDAPException(ResultCode.DECODING_ERROR, ERR_SYNC_INFO_IR_NO_UUID_SET.get(type.name())); } break; } } catch (final LDAPException le) { throw le; } catch (final Exception e) { Debug.debugException(e); throw new LDAPException(ResultCode.DECODING_ERROR, ERR_SYNC_INFO_IR_VALUE_DECODING_ERROR.get( StaticUtils.getExceptionMessage(e)), e); } return new ContentSyncInfoIntermediateResponse(type, value, cookie, refreshDone, refreshDeletes, entryUUIDs, r.getControls()); } private static ASN1OctetString encodeValue(final ContentSyncInfoType type, final ASN1OctetString cookie, final boolean refreshDone, final List<UUID> entryUUIDs, final boolean refreshDeletes) { final ASN1Element e; switch (type) { case NEW_COOKIE: e = new ASN1OctetString(type.getType(), cookie.getValue()); break; case REFRESH_DELETE: case REFRESH_PRESENT: ArrayList<ASN1Element> l = new ArrayList<ASN1Element>(2); if (cookie != null) { l.add(cookie); } if (! refreshDone) { l.add(new ASN1Boolean(refreshDone)); } e = new ASN1Sequence(type.getType(), l); break; case SYNC_ID_SET: l = new ArrayList<ASN1Element>(3); if (cookie != null) { l.add(cookie); } if (refreshDeletes) { l.add(new ASN1Boolean(refreshDeletes)); } final ArrayList<ASN1Element> uuidElements = new ArrayList<ASN1Element>(entryUUIDs.size()); for (final UUID uuid : entryUUIDs) { uuidElements.add(new ASN1OctetString(StaticUtils.encodeUUID(uuid))); } l.add(new ASN1Set(uuidElements)); e = new ASN1Sequence(type.getType(), l); break; default: throw new AssertionError("Unexpected sync info type: " + type.name()); } return new ASN1OctetString(e.encode()); } public ContentSyncInfoType getType() { return type; } public ASN1OctetString getCookie() { return cookie; } public boolean refreshDone() { return refreshDone; } public List<UUID> getEntryUUIDs() { return entryUUIDs; } public boolean refreshDeletes() { return refreshDeletes; } @Override() public String getIntermediateResponseName() { return INFO_INTERMEDIATE_RESPONSE_NAME_SYNC_INFO.get(); } @Override() public String valueToString() { final StringBuilder buffer = new StringBuilder(); buffer.append("syncInfoType='"); buffer.append(type.name()); buffer.append('\''); if (cookie != null) { buffer.append(" cookie='"); StaticUtils.toHex(cookie.getValue(), buffer); buffer.append('\''); } switch (type) { case REFRESH_DELETE: case REFRESH_PRESENT: buffer.append(" refreshDone='"); buffer.append(refreshDone); buffer.append('\''); break; case SYNC_ID_SET: buffer.append(" entryUUIDs={"); final Iterator<UUID> iterator = entryUUIDs.iterator(); while (iterator.hasNext()) { buffer.append('\''); buffer.append(iterator.next().toString()); buffer.append('\''); if (iterator.hasNext()) { buffer.append(','); } } buffer.append('}'); break; case NEW_COOKIE: default: break; } return buffer.toString(); } @Override() public void toString(final StringBuilder buffer) { buffer.append("ContentSyncInfoIntermediateResponse("); final int messageID = getMessageID(); if (messageID >= 0) { buffer.append("messageID="); buffer.append(messageID); buffer.append(", "); } buffer.append("type='"); buffer.append(type.name()); buffer.append('\''); if (cookie != null) { buffer.append(", cookie='"); StaticUtils.toHex(cookie.getValue(), buffer); buffer.append("', "); } switch (type) { case NEW_COOKIE: break; case REFRESH_DELETE: case REFRESH_PRESENT: buffer.append(", refreshDone="); buffer.append(refreshDone); break; case SYNC_ID_SET: buffer.append(", entryUUIDs={"); final Iterator<UUID> iterator = entryUUIDs.iterator(); while (iterator.hasNext()) { buffer.append('\''); buffer.append(iterator.next()); buffer.append('\''); if (iterator.hasNext()) { buffer.append(','); } } buffer.append("}, refreshDeletes="); buffer.append(refreshDeletes); break; } buffer.append(')'); } }