diff --git a/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/AttributeTests.java b/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/AttributeTests.java index a4d40c8c..1a091f1f 100644 --- a/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/AttributeTests.java +++ b/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/AttributeTests.java @@ -31,12 +31,12 @@ import java.util.HashSet; import java.util.List; import java.util.Set; - import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.api.operations.APIOperation; import org.identityconnectors.framework.api.operations.CreateApiOp; import org.identityconnectors.framework.api.operations.DeleteApiOp; import org.identityconnectors.framework.api.operations.GetApiOp; +import org.identityconnectors.framework.api.operations.LiveSyncApiOp; import org.identityconnectors.framework.api.operations.SchemaApiOp; import org.identityconnectors.framework.api.operations.SearchApiOp; import org.identityconnectors.framework.api.operations.SyncApiOp; @@ -46,6 +46,7 @@ import org.identityconnectors.framework.common.objects.AttributeInfo; import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.ObjectClassInfo; import org.identityconnectors.framework.common.objects.ObjectClassInfoBuilder; @@ -67,9 +68,7 @@ * Tests check: * @@ -199,13 +198,10 @@ public void testReturnedByDefault(final ObjectClass objectClass) { @MethodSource("objectClasses") public void testNonUpdateable(final ObjectClass objectClass) { boolean exceptionCaught = false; - /** is there any non updateable item? (if not skip this test) */ - boolean isChanged = false; /** logging info bean */ LogInfo logInfo = null; if (ConnectorHelper.operationsSupported(getConnectorFacade(), objectClass, getAPIOperations())) { - ConnectorObject obj = null; Uid uid = null; try { @@ -216,7 +212,7 @@ public void testNonUpdateable(final ObjectClass objectClass) { assertNotNull(uid, "Create returned null Uid."); // get by uid - obj = getConnectorFacade().getObject(objectClass, + ConnectorObject obj = getConnectorFacade().getObject(objectClass, uid, getOperationOptionsByOp(objectClass, GetApiOp.class)); assertNotNull(obj, "Cannot retrieve created object."); @@ -226,7 +222,7 @@ public void testNonUpdateable(final ObjectClass objectClass) { Set nonUpdateableAttrs = getNonUpdateableAttributes(schema, objectClass); // null indicates an empty set ==> no non-updateable attributes - isChanged = (nonUpdateableAttrs != null); + boolean isChanged = (nonUpdateableAttrs != null); if (isChanged) { //keep logging info @@ -286,7 +282,6 @@ public void testNonUpdateable(final ObjectClass objectClass) { } else { printSkipTestMsg("testNonUpdateable", objectClass); } - } /** @@ -459,6 +454,10 @@ private void testReturnedByDefault(final ObjectClass objectClass, final ApiOpera obj = coObjects.get(0); break; + case LIVE_SYNC: + uid = testLivesync(objectClass, uid, attrs, oci, testMarkMsg); + break; + case SYNC: uid = testSync(objectClass, uid, token, attrs, oci, testMarkMsg); break; @@ -468,7 +467,7 @@ private void testReturnedByDefault(final ObjectClass objectClass, final ApiOpera * Check if attribute set contains non-returned by default * Attributes. This is specific for AttributeTests */ - if (!apiOp.equals(ApiOperations.SYNC)) { + if (apiOp != ApiOperations.SYNC && apiOp != ApiOperations.LIVE_SYNC) { assertNotNull(obj, "Unable to retrieve newly created object"); // obj is null for sync tests checkAttributes(obj, oci, apiOp); @@ -517,22 +516,136 @@ private void checkAttributes(ConnectorObject obj, ObjectClassInfo oci, ApiOperat /** * test sync * - * @param token - * initialized token - * @param attrs - * newly created attributes - * @param uid - * the newly created object - * @param oci - * object class info + * @param attrs newly created attributes + * @param uid the newly created object + * @param oci object class info * @param testMarkMsg test marker * @return the updated Uid */ - private Uid testSync(ObjectClass objectClass, Uid uid, SyncToken token, - Set attrs, ObjectClassInfo oci, String testMarkMsg) { + private Uid testLivesync( + ObjectClass objectClass, + Uid uid, + Set attrs, + ObjectClassInfo oci, + String testMarkMsg) { + + /* + * CREATE: (was handled in the calling method, result of create is in + * param uid, cleanup is also in caller method.) + */ + if (SyncApiOpTests.canSyncAfterOp(CreateApiOp.class)) { + // sync after create + List deltas = ConnectorHelper.livesync(getConnectorFacade(), objectClass, null); + + // check that returned one delta + String msg = "%s Sync should have returned one sync delta after creation of one object, but returned: %d"; + assertTrue(deltas.size() == 1, String.format(msg, testMarkMsg, deltas.size())); + + // check delta + ConnectorHelper.checkLiveSyncDelta(getObjectClassInfo(objectClass), deltas.get(0), uid, attrs, false); + + /* + * check the attributes inside delta This is specific for + * AttributeTests + */ + ConnectorObject obj = deltas.get(0).getObject(); + checkAttributes(obj, oci, ApiOperations.LIVE_SYNC); + } + + /* UPDATE: */ + if (ConnectorHelper.operationSupported(getConnectorFacade(), UpdateApiOp.class) + && SyncApiOpTests.canSyncAfterOp(UpdateApiOp.class)) { + + Set replaceAttributes = ConnectorHelper.getUpdateableAttributes( + getDataProvider(), + getObjectClassInfo(objectClass), + getTestName(), + SyncApiOpTests.MODIFIED, + 0, + false, + false); + + // update only in case there is something to update + if (!replaceAttributes.isEmpty()) { + replaceAttributes.add(uid); + + assertTrue(!replaceAttributes.isEmpty(), testMarkMsg + " no update attributes were found"); + Uid newUid = getConnectorFacade().update( + objectClass, + uid, + AttributeUtil.filterUid(replaceAttributes), + null); + + // Update change of Uid must be propagated to + // replaceAttributes + if (!newUid.equals(uid)) { + replaceAttributes.remove(uid); + replaceAttributes.add(newUid); + uid = newUid; + } + + // sync after update + List deltas = ConnectorHelper.livesync(getConnectorFacade(), objectClass, null); + + // check that returned one delta + String msg = "%s Sync should have returned one sync delta after update of one object, but returned: %d"; + assertTrue(deltas.size() == 1, String.format(msg, testMarkMsg, deltas.size())); + + // check delta + ConnectorHelper.checkLiveSyncDelta( + getObjectClassInfo(objectClass), deltas.get(0), uid, replaceAttributes, false); + + /* + * check the attributes inside delta This is specific for + * AttributeTests + */ + ConnectorObject obj = deltas.get(0).getObject(); + checkAttributes(obj, oci, ApiOperations.LIVE_SYNC); + } + } + + /* DELETE: */ + if (SyncApiOpTests.canSyncAfterOp(DeleteApiOp.class)) { + // delete object + getConnectorFacade().delete(objectClass, uid, null); + + // sync after delete + List deltas = ConnectorHelper.livesync(getConnectorFacade(), objectClass, null); + + // check that returned one delta + String msg = "%s Sync should have returned one sync delta after delete of one object, but returned: %d"; + assertTrue(deltas.size() == 1, String.format(msg, testMarkMsg, deltas.size())); + + // check delta + ConnectorHelper.checkLiveSyncDelta(getObjectClassInfo(objectClass), deltas.get(0), uid, null, false); + + /* + * check the attributes inside delta This is specific for + * AttributeTests + */ + ConnectorObject obj = deltas.get(0).getObject(); + checkAttributes(obj, oci, ApiOperations.LIVE_SYNC); + } + return uid; + } - List deltas = null; - String msg = null; + /** + * test sync + * + * @param token initialized token + * @param attrs newly created attributes + * @param uid the newly created object + * @param oci object class info + * @param testMarkMsg test marker + * @return the updated Uid + */ + private Uid testSync( + ObjectClass objectClass, + Uid uid, + SyncToken token, + Set attrs, + ObjectClassInfo oci, + String testMarkMsg) { /* * CREATE: (was handled in the calling method, result of create is in @@ -540,17 +653,15 @@ private Uid testSync(ObjectClass objectClass, Uid uid, SyncToken token, */ if (SyncApiOpTests.canSyncAfterOp(CreateApiOp.class)) { // sync after create - deltas = ConnectorHelper.sync(getConnectorFacade(), - objectClass, token, - null); + List deltas = ConnectorHelper.sync(getConnectorFacade(), objectClass, token, null); // check that returned one delta - msg = "%s Sync should have returned one sync delta after creation of one object, but returned: %d"; + String msg = "%s Sync should have returned one sync delta after creation of one object, but returned: %d"; assertTrue(deltas.size() == 1, String.format(msg, testMarkMsg, deltas.size())); // check delta - ConnectorHelper.checkSyncDelta(getObjectClassInfo(objectClass), deltas.get(0), - uid, attrs, SyncDeltaType.CREATE_OR_UPDATE, false); + ConnectorHelper.checkSyncDelta( + getObjectClassInfo(objectClass), deltas.get(0), uid, attrs, SyncDeltaType.CREATE_OR_UPDATE, false); /* * check the attributes inside delta This is specific for @@ -572,10 +683,10 @@ private Uid testSync(ObjectClass objectClass, Uid uid, SyncToken token, SyncApiOpTests.MODIFIED, 0, false, false); // update only in case there is something to update - if (replaceAttributes.size() > 0) { + if (!replaceAttributes.isEmpty()) { replaceAttributes.add(uid); - assertTrue(replaceAttributes.size() > 0, testMarkMsg + " no update attributes were found"); + assertTrue(!replaceAttributes.isEmpty(), testMarkMsg + " no update attributes were found"); Uid newUid = getConnectorFacade().update( objectClass, uid, @@ -591,12 +702,10 @@ private Uid testSync(ObjectClass objectClass, Uid uid, SyncToken token, } // sync after update - deltas = ConnectorHelper.sync(getConnectorFacade(), - objectClass, token, - null); + List deltas = ConnectorHelper.sync(getConnectorFacade(), objectClass, token, null); // check that returned one delta - msg = "%s Sync should have returned one sync delta after update of one object, but returned: %d"; + String msg = "%s Sync should have returned one sync delta after update of one object, but returned: %d"; assertTrue(deltas.size() == 1, String.format(msg, testMarkMsg, deltas.size())); // check delta @@ -618,21 +727,18 @@ private Uid testSync(ObjectClass objectClass, Uid uid, SyncToken token, /* DELETE: */ if (SyncApiOpTests.canSyncAfterOp(DeleteApiOp.class)) { // delete object - getConnectorFacade().delete(objectClass, uid, - null); + getConnectorFacade().delete(objectClass, uid, null); // sync after delete - deltas = ConnectorHelper.sync(getConnectorFacade(), - objectClass, token, - null); + List deltas = ConnectorHelper.sync(getConnectorFacade(), objectClass, token, null); // check that returned one delta - msg = "%s Sync should have returned one sync delta after delete of one object, but returned: %d"; + String msg = "%s Sync should have returned one sync delta after delete of one object, but returned: %d"; assertTrue(deltas.size() == 1, String.format(msg, testMarkMsg, deltas.size())); // check delta - ConnectorHelper.checkSyncDelta(getObjectClassInfo(objectClass), deltas.get(0), - uid, null, SyncDeltaType.DELETE, false); + ConnectorHelper.checkSyncDelta( + getObjectClassInfo(objectClass), deltas.get(0), uid, null, SyncDeltaType.DELETE, false); /* * check the attributes inside delta This is specific for @@ -650,6 +756,7 @@ private Uid testSync(ObjectClass objectClass, Uid uid, SyncToken token, enum ApiOperations { SEARCH(SearchApiOp.class), GET(GetApiOp.class), + LIVE_SYNC(LiveSyncApiOp.class), SYNC(SyncApiOp.class); private final String s; diff --git a/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ConnectorHelper.java b/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ConnectorHelper.java index 545c1514..263adc67 100644 --- a/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ConnectorHelper.java +++ b/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/ConnectorHelper.java @@ -69,6 +69,7 @@ import org.identityconnectors.framework.common.objects.AttributeDeltaUtil; import org.identityconnectors.framework.common.objects.AttributeInfo; import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; import org.identityconnectors.framework.common.objects.Name; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.ObjectClassInfo; @@ -317,6 +318,23 @@ public static Map search2Map( return foundObjects; } + /** + * Performs livesync on connector facade. + * + * @returns list of deltas + */ + public static List livesync(ConnectorFacade connectorFacade, ObjectClass objClass, + OperationOptions opOptions) { + final List returnedDeltas = new ArrayList<>(); + + connectorFacade.livesync(objClass, delta -> { + returnedDeltas.add(delta); + return true; + }, opOptions); + + return returnedDeltas; + } + /** * Performs sync on connector facade. * @@ -386,9 +404,9 @@ public static boolean checkObject(ObjectClassInfo objectClassInfo, ConnectorObje boolean success = true; requestedAttributes.stream(). - filter((attribute) -> (isReadable(objectClassInfo, attribute))). - filter((attribute) -> (checkNotReturnedByDefault || isReturnedByDefault(objectClassInfo, attribute))). - forEachOrdered((attribute) -> { + filter(attribute -> isReadable(objectClassInfo, attribute)). + filter(attribute -> checkNotReturnedByDefault || isReturnedByDefault(objectClassInfo, attribute)). + forEachOrdered(attribute -> { Attribute createdAttribute = connectorObj.getAttributeByName(attribute.getName()); if (createdAttribute == null) { fail(String.format("Attribute '%s' is missing.", attribute.getName())); @@ -474,10 +492,33 @@ static boolean checkValue(List fetchedValues, List expectedValues) { } /** - * Check that passed SyncDelta has exptected values. + * Check that passed SyncDelta has expected values. */ - public static void checkSyncDelta(ObjectClassInfo ocInfo, SyncDelta delta, Uid uid, Set attributes, - SyncDeltaType deltaType, boolean checkNotReturnedByDefault) { + public static void checkLiveSyncDelta( + final ObjectClassInfo ocInfo, + final LiveSyncDelta delta, + final Uid uid, Set attributes, + final boolean checkNotReturnedByDefault) { + + // check that Uid is correct + String msg = "Sync returned wrong Uid, expected: %s, returned: %s."; + assertEquals(delta.getUid(), uid, String.format(msg, uid, delta.getUid())); + + // check that attributes are correct + ConnectorHelper.checkObject(ocInfo, delta.getObject(), attributes, checkNotReturnedByDefault); + } + + /** + * Check that passed SyncDelta has expected values. + */ + public static void checkSyncDelta( + final ObjectClassInfo ocInfo, + final SyncDelta delta, + final Uid uid, + final Set attributes, + final SyncDeltaType deltaType, + boolean checkNotReturnedByDefault) { + // check that Uid is correct String msg = "Sync returned wrong Uid, expected: %s, returned: %s."; assertEquals(delta.getUid(), uid, String.format(msg, uid, delta.getUid())); diff --git a/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/SyncApiOpTests.java b/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/SyncApiOpTests.java index c0fea0af..5dcfe742 100644 --- a/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/SyncApiOpTests.java +++ b/java/connector-framework-contract/src/main/java/org/identityconnectors/contract/test/SyncApiOpTests.java @@ -89,18 +89,14 @@ public Set> getAPIOperations() { @Override protected void testRun(ObjectClass objectClass) { Uid uid = null; - Set attrs = null; - List deltas = null; - SyncToken token = null; - String msg = null; try { // start synchronizing from now - token = getConnectorFacade().getLatestSyncToken(objectClass); + SyncToken token = getConnectorFacade().getLatestSyncToken(objectClass); /* CREATE: */ // create record - attrs = ConnectorHelper.getCreateableAttributes(getDataProvider(), + Set attrs = ConnectorHelper.getCreateableAttributes(getDataProvider(), getObjectClassInfo(objectClass), getTestName(), 0, true, false); uid = getConnectorFacade().create(objectClass, attrs, getOperationOptionsByOp(objectClass, CreateApiOp.class)); @@ -108,16 +104,16 @@ protected void testRun(ObjectClass objectClass) { if (canSyncAfterOp(CreateApiOp.class)) { // sync after create - deltas = ConnectorHelper.sync(getConnectorFacade(), objectClass, token, + List deltas = ConnectorHelper.sync(getConnectorFacade(), objectClass, token, getOperationOptionsByOp(objectClass, SyncApiOp.class)); // check that returned one delta - msg = "Sync should have returned one sync delta after creation of one object, but returned: %d"; + String msg = "Sync should have returned one sync delta after creation of one object, but returned: %d"; assertTrue(deltas.size() == 1, String.format(msg, deltas.size())); // check delta - ConnectorHelper.checkSyncDelta(getObjectClassInfo(objectClass), deltas.get(0), uid, attrs, - SyncDeltaType.CREATE_OR_UPDATE, true); + ConnectorHelper.checkSyncDelta( + getObjectClassInfo(objectClass), deltas.get(0), uid, attrs, SyncDeltaType.CREATE_OR_UPDATE, true); token = deltas.get(0).getToken(); } @@ -131,10 +127,10 @@ && canSyncAfterOp(UpdateApiOp.class)) { false); // update only in case there is something to update - if (replaceAttributes.size() > 0) { + if (!replaceAttributes.isEmpty()) { replaceAttributes.add(uid); - assertTrue((replaceAttributes.size() > 0), "no update attributes were found"); + assertTrue((!replaceAttributes.isEmpty()), "no update attributes were found"); Uid newUid = getConnectorFacade().update( objectClass, uid, AttributeUtil.filterUid(replaceAttributes), getOperationOptionsByOp(objectClass, UpdateApiOp.class)); @@ -148,11 +144,12 @@ && canSyncAfterOp(UpdateApiOp.class)) { } // sync after update - deltas = ConnectorHelper.sync(getConnectorFacade(), objectClass, token, + List deltas = ConnectorHelper.sync(getConnectorFacade(), objectClass, token, getOperationOptionsByOp(objectClass, SyncApiOp.class)); // check that returned one delta - msg = "Sync should have returned one sync delta after update of one object, but returned: %d"; + String msg = + "Sync should have returned one sync delta after update of one object, but returned: %d"; assertTrue(deltas.size() == 1, String.format(msg, deltas.size())); // check delta @@ -170,16 +167,16 @@ && canSyncAfterOp(UpdateApiOp.class)) { getOperationOptionsByOp(objectClass, DeleteApiOp.class)); // sync after delete - deltas = ConnectorHelper.sync(getConnectorFacade(), objectClass, token, + List deltas = ConnectorHelper.sync(getConnectorFacade(), objectClass, token, getOperationOptionsByOp(objectClass, SyncApiOp.class)); // check that returned one delta - msg = "Sync should have returned one sync delta after delete of one object, but returned: %d"; + String msg = "Sync should have returned one sync delta after delete of one object, but returned: %d"; assertTrue(deltas.size() == 1, String.format(msg, deltas.size())); // check delta - ConnectorHelper.checkSyncDelta(getObjectClassInfo(objectClass), deltas.get(0), uid, null, - SyncDeltaType.DELETE, true); + ConnectorHelper.checkSyncDelta( + getObjectClassInfo(objectClass), deltas.get(0), uid, null, SyncDeltaType.DELETE, true); } } finally { // cleanup test data @@ -209,8 +206,7 @@ && canSyncAfterOp(CreateApiOp.class)) { uid = getConnectorFacade().create(objectClass, attrs, null); assertNotNull(uid, "Create returned null uid."); - List deltas = ConnectorHelper.sync(getConnectorFacade(), - objectClass, token, null); + List deltas = ConnectorHelper.sync(getConnectorFacade(), objectClass, token, null); // check that returned one delta final String MSG = diff --git a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/AttributesToGetResultsHandler.java b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/AttributesToGetResultsHandler.java index 82ab10a9..0830176d 100644 --- a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/AttributesToGetResultsHandler.java +++ b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/AttributesToGetResultsHandler.java @@ -19,13 +19,14 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2024 ConnId */ package org.identityconnectors.framework.impl.api.local.operations; import java.util.HashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; - import org.identityconnectors.common.Assertions; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeUtil; @@ -33,18 +34,18 @@ import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; import org.identityconnectors.framework.common.objects.OperationOptions; - /** * Simple class for common results handler components that involve * {@link OperationOptions#OP_ATTRIBUTES_TO_GET}. */ public abstract class AttributesToGetResultsHandler { + private final String[] attrsToGet; /** * Keep the attribute to get.. */ - public AttributesToGetResultsHandler(String[] attributesToGet) { + public AttributesToGetResultsHandler(final String[] attributesToGet) { Assertions.nullCheck(attributesToGet, "attrsToGet"); this.attrsToGet = attributesToGet; } @@ -54,26 +55,22 @@ public AttributesToGetResultsHandler(String[] attributesToGet) { * not in the {@link OperationOptions#OP_ATTRIBUTES_TO_GET} set. * * @param attributesToGet - * case insensitive set of attribute names. + * case insensitive set of attribute names. */ public Set reduceToAttrsToGet(Set attributesToGet) { - Set ret = new HashSet(attrsToGet.length); + Set ret = new HashSet<>(attrsToGet.length); Map map = AttributeUtil.toMap(attributesToGet); for (String attrName : attrsToGet) { Attribute attr = map.get(attrName); - // TODO: Should we throw if the attribute is not yet it was - // requested?? Or do we ignore because the API maybe asking - // for what the resource doesn't have?? - if (attr != null) { - ret.add(attr); - } + // TODO: Should we throw if the attribute is not yet it was requested?? Or do we ignore because the API + // maybe asking for what the resource doesn't have?? + Optional.ofNullable(attr).ifPresent(ret::add); } return ret; } public ConnectorObject reduceToAttrsToGet(ConnectorObject obj) { - // clone the object and reduce the attributes only the set of - // attributes. + // clone the object and reduce the attributes only the set of attributes. ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); bld.setUid(obj.getUid()); bld.setName(obj.getName()); diff --git a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/LiveSyncImpl.java b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/LiveSyncImpl.java index 1bdb3b8f..4e35236a 100644 --- a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/LiveSyncImpl.java +++ b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/LiveSyncImpl.java @@ -22,6 +22,7 @@ */ package org.identityconnectors.framework.impl.api.local.operations; +import java.util.Optional; import org.identityconnectors.common.Assertions; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.api.ResultsHandlerConfiguration; @@ -93,7 +94,7 @@ public static class AttributesToGetLiveSyncResultsHandler private final LiveSyncResultsHandler handler; - public AttributesToGetLiveSyncResultsHandler(final LiveSyncResultsHandler handler, String[] attrsToGet) { + public AttributesToGetLiveSyncResultsHandler(final LiveSyncResultsHandler handler, final String[] attrsToGet) { super(attrsToGet); this.handler = handler; } @@ -101,9 +102,7 @@ public AttributesToGetLiveSyncResultsHandler(final LiveSyncResultsHandler handle @Override public boolean handle(final LiveSyncDelta delta) { LiveSyncDeltaBuilder bld = new LiveSyncDeltaBuilder(delta); - if (delta.getObject() != null) { - bld.setObject(reduceToAttrsToGet(delta.getObject())); - } + Optional.ofNullable(delta.getObject()).ifPresent(obj -> bld.setObject(reduceToAttrsToGet(obj))); return handler.handle(bld.build()); } } diff --git a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/SyncImpl.java b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/SyncImpl.java index 287f8be7..695c8611 100644 --- a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/SyncImpl.java +++ b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/api/local/operations/SyncImpl.java @@ -25,6 +25,7 @@ */ package org.identityconnectors.framework.impl.api.local.operations; +import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import org.identityconnectors.common.Assertions; import org.identityconnectors.common.logging.Log; @@ -155,7 +156,7 @@ public static class AttributesToGetSyncResultsHandler private final SyncResultsHandler handler; - public AttributesToGetSyncResultsHandler(final SyncResultsHandler handler, String[] attrsToGet) { + public AttributesToGetSyncResultsHandler(final SyncResultsHandler handler, final String[] attrsToGet) { super(attrsToGet); this.handler = handler; } @@ -163,9 +164,7 @@ public AttributesToGetSyncResultsHandler(final SyncResultsHandler handler, Strin @Override public boolean handle(final SyncDelta delta) { SyncDeltaBuilder bld = new SyncDeltaBuilder(delta); - if (delta.getObject() != null) { - bld.setObject(reduceToAttrsToGet(delta.getObject())); - } + Optional.ofNullable(delta.getObject()).ifPresent(obj -> bld.setObject(reduceToAttrsToGet(obj))); return handler.handle(bld.build()); } } diff --git a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/CommonObjectHandlers.java b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/CommonObjectHandlers.java index b116d501..f4c4c1d3 100644 --- a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/CommonObjectHandlers.java +++ b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/CommonObjectHandlers.java @@ -21,6 +21,7 @@ * ==================== * Portions Copyrighted 2010-2013 ForgeRock AS. * Portions Copyrighted 2015-2016 Evolveum + * Portions Copyrighted 2024 ConnId */ package org.identityconnectors.framework.impl.serializer; @@ -33,7 +34,6 @@ import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; - import org.identityconnectors.common.script.Script; import org.identityconnectors.common.script.ScriptBuilder; import org.identityconnectors.framework.api.operations.APIOperation; @@ -716,6 +716,27 @@ public void serialize(final Object object, final ObjectEncoder encoder) { } }); + HANDLERS.add(new AbstractObjectSerializationHandler(LiveSyncDelta.class, "LiveSyncDelta") { + + @Override + public Object deserialize(final ObjectDecoder decoder) { + return new LiveSyncDeltaBuilder(). + setObjectClass((ObjectClass) decoder.readObjectField("ObjectClass", ObjectClass.class, null)). + setUid((Uid) decoder.readObjectField("Uid", Uid.class, null)). + setObject((ConnectorObject) decoder. + readObjectField("ConnectorObject", ConnectorObject.class, null)). + build(); + } + + @Override + public void serialize(final Object object, final ObjectEncoder encoder) { + final LiveSyncDelta val = (LiveSyncDelta) object; + encoder.writeObjectField("ObjectClass", val.getObjectClass(), true); + encoder.writeObjectField("Uid", val.getUid(), true); + encoder.writeObjectField("ConnectorObject", val.getObject(), true); + } + }); + HANDLERS.add(new AbstractObjectSerializationHandler(SyncDelta.class, "SyncDelta") { @Override diff --git a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/ObjectTypeMapper.java b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/ObjectTypeMapper.java index 1c453ae8..429626be 100644 --- a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/ObjectTypeMapper.java +++ b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/ObjectTypeMapper.java @@ -19,30 +19,33 @@ * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== + * Portions Copyrighted 2024 ConnId */ package org.identityconnectors.framework.impl.serializer; /** - * Interface to be implemented to handle the serialization/ - * deserialization of an object. + * Interface to be implemented to handle the serialization/deserialization of an object. */ public interface ObjectTypeMapper { /** - * Returns the type of object being serialized. This is - * an abstract type name that is intended to be language - * neutral. + * Returns the type of object being serialized. + * + * @return an abstract type name that is intended to be language neutral */ String getHandledSerialType(); /** * Returns the java class handled by this handler. + * + * @return the java class handled by this handler */ Class getHandledObjectType(); /** - * Should we match subclasses of the given class or only - * the exact class? + * Should we match subclasses of the given class or only the exact class? + * + * @return whether subclasses of the given class or only the exact class should be matched */ boolean isMatchSubclasses(); } diff --git a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/Primitives.java b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/Primitives.java index b3d4ca24..69f5b543 100644 --- a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/Primitives.java +++ b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/Primitives.java @@ -40,7 +40,6 @@ import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; - import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.security.EncryptorFactory; import org.identityconnectors.common.security.GuardedByteArray; @@ -50,10 +49,9 @@ class Primitives { - public static final List HANDLERS = new ArrayList(); + public static final List HANDLERS = new ArrayList<>(); static { - HANDLERS.add(new AbstractObjectSerializationHandler(Boolean.class, "Boolean") { @Override @@ -376,10 +374,9 @@ public Object deserialize(final ObjectDecoder decoder) { @Override public void serialize(final Object object, final ObjectEncoder encoder) { final ZonedDateTime val = (ZonedDateTime) object; - // Make sure we have timezone as (numeric) offset instead of - // using zone ID. We do this by invoking toOffsetDateTime(). - // This makes the timestamp ISO-8601 compatible and therefore - // more portable. + // Make sure we have timezone as (numeric) offset instead of using zone ID. We do this by invoking + // toOffsetDateTime(). + // This makes the timestamp ISO-8601 compatible and therefore more portable. encoder.writeStringContents(val.toOffsetDateTime().toString()); } }); @@ -441,14 +438,11 @@ public void serialize(final Object object, final ObjectEncoder encoder) { // special case - for case insensitive maps if (CollectionUtil.isCaseInsensitiveMap(map)) { encoder.writeBooleanField("caseInsensitive", true); - } // for all other sorted maps, we don't know how - // to serialize them + } // for all other sorted maps, we don't know how to serialize them else if (map instanceof SortedMap) { throw new IllegalArgumentException("Serialization of SortedMap not supported"); } - map.forEach((key, value) -> { - encoder.writeObjectContents(new MapEntry(key, value)); - }); + map.forEach((key, value) -> encoder.writeObjectContents(new MapEntry(key, value))); } @Override @@ -513,9 +507,7 @@ public void serialize(final Object object, final ObjectEncoder encoder) { else if (set instanceof SortedSet) { throw new IllegalArgumentException("Serialization of SortedSet not supported"); } - set.forEach((obj) -> { - encoder.writeObjectContents(obj); - }); + set.forEach((obj) -> encoder.writeObjectContents(obj)); } @Override diff --git a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/binary/BinaryObjectEncoder.java b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/binary/BinaryObjectEncoder.java index 23cbef1f..5c4d7ce6 100644 --- a/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/binary/BinaryObjectEncoder.java +++ b/java/connector-framework-internal/src/main/java/org/identityconnectors/framework/impl/serializer/binary/BinaryObjectEncoder.java @@ -379,7 +379,7 @@ public void writeDoubleField(String fieldName, double v) { public void writeFloatContents(float v) { internalEncoder.startAnonymousField(); // write as double since C# only knows how to deal with that - internalEncoder.writeDouble((double) v); + internalEncoder.writeDouble(v); internalEncoder.endField(); } @@ -387,7 +387,7 @@ public void writeFloatContents(float v) { public void writeFloatField(String fieldName, float v) { internalEncoder.startField(fieldName); // write as double since C# only knows how to deal with that - internalEncoder.writeDouble((double) v); + internalEncoder.writeDouble(v); internalEncoder.endField(); } diff --git a/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/ConnectorFacadeTests.java b/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/ConnectorFacadeTests.java index c8a3c534..87e18e3f 100644 --- a/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/ConnectorFacadeTests.java +++ b/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/ConnectorFacadeTests.java @@ -32,7 +32,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; - import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.api.APIConfiguration; @@ -47,6 +46,7 @@ import org.identityconnectors.framework.common.objects.AttributeBuilder; import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.ConnectorObject; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; import org.identityconnectors.framework.common.objects.Name; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.ResultsHandler; @@ -510,6 +510,24 @@ public void checkCalls(List calls) { }); } + @Test + public void livesyncCallPattern() { + testCallPattern(new TestOperationPattern() { + + @Override + public void makeCall(ConnectorFacade facade) { + // create an empty results handler.. + // call the search method.. + facade.livesync(ObjectClass.ACCOUNT, (LiveSyncDelta delta) -> true, null); + } + + @Override + public void checkCalls(List calls) { + assertEquals(calls.remove(0).getMethodName(), "livesync"); + } + }); + } + @Test public void syncCallPattern() { testCallPattern(new TestOperationPattern() { @@ -585,8 +603,8 @@ public void updateMergeTests() { addAttrSet.add(AttributeBuilder.build(ATTR_NAME, ADDED)); Name name = obj.getName(); addAttrSet.remove(name); - Uid uid = facade. - addAttributeValues(ObjectClass.ACCOUNT, obj.getUid(), AttributeUtil.filterUid(addAttrSet), null); + Uid uid = facade.addAttributeValues( + ObjectClass.ACCOUNT, obj.getUid(), AttributeUtil.filterUid(addAttrSet), null); // get back the object and see if there are the same.. addAttrSet.add(name); ConnectorObject addO = new ConnectorObject(ObjectClass.ACCOUNT, addAttrSet); @@ -603,8 +621,8 @@ public void updateMergeTests() { // attempt to delete a value from an attribute.. Set deleteAttrs = CollectionUtil.newSet(addO.getAttributes()); deleteAttrs.remove(name); - uid = facade.removeAttributeValues(ObjectClass.ACCOUNT, addO.getUid(), AttributeUtil.filterUid(deleteAttrs), - null); + uid = facade.removeAttributeValues( + ObjectClass.ACCOUNT, addO.getUid(), AttributeUtil.filterUid(deleteAttrs), null); obj = facade.getObject(ObjectClass.ACCOUNT, uid, null); expected = AttributeBuilder.build(ATTR_NAME, ADDED); actual = obj.getAttributeByName(ATTR_NAME); @@ -613,8 +631,7 @@ public void updateMergeTests() { Set nonExist = new HashSet<>(); nonExist.add(newUid(1)); nonExist.add(AttributeBuilder.build("does not exist", "asdfe")); - uid = facade.removeAttributeValues(ObjectClass.ACCOUNT, addO.getUid(), AttributeUtil.filterUid(nonExist), - null); + facade.removeAttributeValues(ObjectClass.ACCOUNT, addO.getUid(), AttributeUtil.filterUid(nonExist), null); obj = facade.getObject(ObjectClass.ACCOUNT, newUid(1), null); assertTrue(obj.getAttributeByName("does not exist") == null); } diff --git a/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacadeTests.java b/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacadeTests.java index c2f59654..4d12572f 100644 --- a/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacadeTests.java +++ b/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/api/local/operations/ObjectNormalizerFacadeTests.java @@ -31,6 +31,8 @@ import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.framework.common.objects.ConnectorObject; import org.identityconnectors.framework.common.objects.ConnectorObjectBuilder; +import org.identityconnectors.framework.common.objects.LiveSyncDelta; +import org.identityconnectors.framework.common.objects.LiveSyncDeltaBuilder; import org.identityconnectors.framework.common.objects.ObjectClass; import org.identityconnectors.framework.common.objects.SyncDelta; import org.identityconnectors.framework.common.objects.SyncDeltaBuilder; @@ -189,6 +191,29 @@ public void testConnectorObject() { assertFalse(expected.equals(v1)); } + @Test + public void testLiveSyncDelta() { + ConnectorObjectBuilder objbuilder = new ConnectorObjectBuilder(); + objbuilder.setName("myname"); + objbuilder.setUid("myuid"); + objbuilder.addAttribute(createTestAttribute()); + ConnectorObject obj = objbuilder.build(); + + LiveSyncDeltaBuilder builder = new LiveSyncDeltaBuilder(); + builder.setObject(obj); + LiveSyncDelta v1 = builder.build(); + LiveSyncDelta v2 = createTestNormalizer().normalizeLiveSyncDelta(v1); + builder = new LiveSyncDeltaBuilder(); + objbuilder = new ConnectorObjectBuilder(); + objbuilder.setName("myname"); + objbuilder.setUid("myuid"); + objbuilder.addAttribute(createNormalizedTestAttribute()); + builder.setObject(objbuilder.build()); + LiveSyncDelta expected = builder.build(); + assertEquals(v2, expected); + assertFalse(expected.equals(v1)); + } + @Test public void testSyncDelta() { ConnectorObjectBuilder objbuilder = new ConnectorObjectBuilder(); diff --git a/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/serializer/ObjectSerializationTests.java b/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/serializer/ObjectSerializationTests.java index 268784ae..a81e0ddf 100644 --- a/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/serializer/ObjectSerializationTests.java +++ b/java/connector-framework-internal/src/test/java/org/identityconnectors/framework/impl/serializer/ObjectSerializationTests.java @@ -46,7 +46,6 @@ import java.util.Map; import java.util.Set; import java.util.Collections; - import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.pooling.ObjectPoolConfiguration; import org.identityconnectors.common.script.Script; @@ -1076,6 +1075,28 @@ public void testSyncDelta() { assertEquals(v1, v2); } + @Test + public void testLiveSyncDelta() { + ConnectorObjectBuilder bld = new ConnectorObjectBuilder(); + bld.setUid("foo"); + bld.setName("name"); + LiveSyncDeltaBuilder builder = new LiveSyncDeltaBuilder(); + builder.setObject(bld.build()); + LiveSyncDelta v1 = builder.build(); + LiveSyncDelta v2 = (LiveSyncDelta) cloneObject(v1); + assertEquals(new Uid("foo"), v2.getObject().getUid()); + assertEquals(v1, v2); + + builder = new LiveSyncDeltaBuilder(); + builder.setObjectClass(ObjectClass.ACCOUNT); + builder.setUid(new Uid("foo")); + v1 = builder.build(); + v2 = (LiveSyncDelta) cloneObject(v1); + assertEquals(ObjectClass.ACCOUNT, v2.getObjectClass()); + assertEquals(new Uid("foo"), v2.getUid()); + assertEquals(v1, v2); + } + @Test public void testNull() { Object v1 = null; diff --git a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/FrameworkUtil.java b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/FrameworkUtil.java index 499af23c..aed73b97 100644 --- a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/FrameworkUtil.java +++ b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/FrameworkUtil.java @@ -40,7 +40,6 @@ import java.util.Map; import java.util.Properties; import java.util.Set; - import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.IOUtil; import org.identityconnectors.common.ReflectionUtil; @@ -128,8 +127,7 @@ public static Set> allAPIOperations() { } /** - * Determines the default set of operations that a {@link Connector} - * supports. + * Determines the default set of operations that a {@link Connector} supports. */ public static Set> getDefaultSupportedOperations( Class connector) { @@ -198,8 +196,7 @@ public static Set> getAllSupportedConfigTypes() { /** * Determines if the class is a supported configuration type. * - * @param clazz - * the type to check against the list of supported types. + * @param clazz the type to check against the list of supported types. * @return true if the type is in the list otherwise false. */ public static boolean isSupportedConfigurationType(Class clazz) { @@ -249,8 +246,7 @@ public static Set> getAllSupportedAttributeTypes() { /** * Determines if the class is a supported attribute type. * - * @param clazz - * the type to check against a supported list of types. + * @param clazz the type to check against a supported list of types. * @return true if the type is on the supported list otherwise false. */ public static boolean isSupportedAttributeType(final Class clazz) { @@ -284,10 +280,8 @@ public static boolean isSupportedAttributeType(final Class clazz) { *
  • ZonedDateTime.class
  • * * - * @param clazz - * type to check against the support list of types. - * @throws IllegalArgumentException - * if the type is not on the supported list. + * @param clazz type to check against the support list of types. + * @throws IllegalArgumentException if the type is not on the supported list. */ public static void checkAttributeType(final Class clazz) { if (!FrameworkUtil.isSupportedAttributeType(clazz)) { @@ -297,13 +291,11 @@ public static void checkAttributeType(final Class clazz) { } /** - * Determines if the class of the object is a supported attribute type. If - * not it throws an {@link IllegalArgumentException}. + * Determines if the class of the object is a supported attribute type. If not it throws an + * {@link IllegalArgumentException}. * - * @param value - * The value to check or null. - * @throws IllegalArgumentException - * If the class of the object is a supported attribute type. + * @param value The value to check or null. + * @throws IllegalArgumentException If the class of the object is a supported attribute type. */ public static void checkAttributeValue(Object value) { if (value != null) { @@ -312,15 +304,12 @@ public static void checkAttributeValue(Object value) { } /** - * Determines if the class of the object is a supported attribute type. If - * not it throws an {@link IllegalArgumentException}. + * Determines if the class of the object is a supported attribute type. If not it throws an + * {@link IllegalArgumentException}. * - * @param name - * The name of the attribute to check - * @param value - * The value to check or null. - * @throws IllegalArgumentException - * If the class of the object is a supported attribute type. + * @param name The name of the attribute to check + * @param value The value to check or null. + * @throws IllegalArgumentException If the class of the object is a supported attribute type. */ public static void checkAttributeValue(String name, Object value) { if (value instanceof Map) { @@ -352,10 +341,9 @@ private static void checkAttributeValue(StringBuilder name, Object value) { checkAttributeValue(nameBuilder, entryValue); } } else { - throw new IllegalArgumentException( - MessageFormat - .format("Map Attribute ''{0}'' must have String key, type ''{1}'' is not supported.", - name, null != key ? key.getClass() : "null")); + throw new IllegalArgumentException(MessageFormat.format( + "Map Attribute ''{0}'' must have String key, type ''{1}'' is not supported.", + name, null != key ? key.getClass() : "null")); } } } else if (value != null) { @@ -367,17 +355,14 @@ private static void checkAttributeValue(StringBuilder name, Object value) { } /** - * Determines if the class is a supported type for an OperationOption. If - * not it throws an {@link IllegalArgumentException}. + * Determines if the class is a supported type for an OperationOption. If not it throws an + * {@link IllegalArgumentException}. * - * @param clazz - * type to check against the support list of types. - * @throws IllegalArgumentException - * if the type is not on the supported list. + * @param clazz type to check against the support list of types. + * @throws IllegalArgumentException if the type is not on the supported list. */ public static void checkOperationOptionType(final Class clazz) { - // the set of supported operation option types - // is the same as that for configuration beans plus Name, + // the set of supported operation option types is the same as that for configuration beans plus Name, // ObjectClass, Uid, and QualifiedUid if (clazz.isArray()) { @@ -405,19 +390,15 @@ public static void checkOperationOptionType(final Class clazz) { return; // ok } - throw new IllegalArgumentException("OperationOption type '" + clazz.getName() - + "' is not supported."); + throw new IllegalArgumentException("OperationOption type '" + clazz.getName() + "' is not supported."); } /** - * Determines if the class of the object is a supported attribute type. If - * not it throws an {@link IllegalArgumentException}. - * - * @param value - * The value to check or null. - * @throws IllegalArgumentException - * if the class of the object is a supported attribute type + * Determines if the class of the object is a supported attribute type. If not it throws an + * {@link IllegalArgumentException}. * + * @param value The value to check or null. + * @throws IllegalArgumentException if the class of the object is a supported attribute type */ public static void checkOperationOptionValue(Object value) { if (value != null) { @@ -451,8 +432,7 @@ static Version getFrameworkVersion(ClassLoader loader) throws IOException { String version = props.getProperty(PROP_FRAMEWORK_VERSION); if (version == null) { throw new IllegalStateException( - "connectors-framework.properties does not contain a " - + PROP_FRAMEWORK_VERSION + " property"); + "connectors-framework.properties does not contain a " + PROP_FRAMEWORK_VERSION + " property"); } if (StringUtil.isBlank(version)) { throw new IllegalStateException( diff --git a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/AttributeBuilder.java b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/AttributeBuilder.java index e780f387..8f2fc463 100644 --- a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/AttributeBuilder.java +++ b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/AttributeBuilder.java @@ -20,6 +20,7 @@ * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2016 Evolveum + * Portions Copyrighted 2024 ConnId */ package org.identityconnectors.framework.common.objects; @@ -28,7 +29,6 @@ import java.util.Collection; import java.util.Date; import java.util.List; - import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.security.GuardedString; @@ -37,14 +37,11 @@ import org.identityconnectors.framework.common.FrameworkUtil; /** - * Simplifies constructing instances of {@link Attribute}. A {@code Connector} - * developer does not need to implement the {@code Attribute} interface. The - * builder returns an instance of an implementation of {@link Attribute} that - * overrides the methods {@code equals()}, {@code hashcode()} and - * {@code toString()} to provide a uniform and robust class. This implementation - * is backed by an {@link ArrayList} that contains the values and preserves the - * order of those values (in case the order of values is significant to the - * target system or application). + * Simplifies constructing instances of {@link Attribute}. A {@code Connector} developer does not need to implement the + * {@code Attribute} interface. The builder returns an instance of an implementation of {@link Attribute} that + * overrides the methods {@code equals()}, {@code hashcode()} and {@code toString()} to provide a uniform and robust + * class. This implementation is backed by an {@link ArrayList} that contains the values and preserves the order of + * those values (in case the order of values is significant to the target system or application). * * @author Will Droste * @since 1.0 @@ -53,17 +50,16 @@ public final class AttributeBuilder { private final static String NAME_ERROR = "Name must not be blank!"; - String name; + private String name; + + private List value; - List value; - - AttributeValueCompleteness attributeValueCompleteness = AttributeValueCompleteness.COMPLETE; + private AttributeValueCompleteness attributeValueCompleteness = AttributeValueCompleteness.COMPLETE; /** * Creates a attribute with the specified name and a {@code null} value. * - * @param name - * unique name of the attribute. + * @param name unique name of the attribute. * @return instance of {@code Attribute} with a {@code null} value. */ public static Attribute build(final String name) { @@ -75,13 +71,9 @@ public static Attribute build(final String name) { /** * Creates an {@code Attribute} with the name and the values provided. * - * @param name - * unique name of the attribute. - * @param args - * variable number of arguments that are used as values for the - * attribute. - * @return instance of {@code Attribute} with the specified name and a value - * that includes the arguments provided. + * @param name unique name of the attribute. + * @param args variable number of arguments that are used as values for the attribute. + * @return instance of {@code Attribute} with the specified name and a value that includes the arguments provided. */ public static Attribute build(final String name, final Object... args) { AttributeBuilder bld = new AttributeBuilder(); @@ -93,13 +85,9 @@ public static Attribute build(final String name, final Object... args) { /** * Creates an {@code Attribute} with the name and the values provided. * - * @param name - * unique name of the attribute. - * @param obj - * a collection of objects that are used as values for the - * attribute. - * @return instance of {@code Attribute} with the specified name and a value - * that includes the arguments provided. + * @param name unique name of the attribute. + * @param obj a collection of objects that are used as values for the attribute. + * @return instance of {@code Attribute} with the specified name and a value that includes the arguments provided. */ public static Attribute build(final String name, final Collection obj) { // this method needs to be able to create the sub-classes @@ -122,8 +110,7 @@ public String getName() { /** * Set the name of the attribute that is being built. * - * @throws IllegalArgumentException - * if the name parameter is blank. + * @throws IllegalArgumentException if the name parameter is blank. */ public AttributeBuilder setName(final String name) { if (StringUtil.isBlank(name)) { @@ -146,10 +133,8 @@ public List getValue() { * Add each of the specified objects as a values for the attribute that is * being built. * - * @param objs - * the values to add - * @throws NullPointerException - * if any of the values is null. + * @param objs the values to add + * @throws NullPointerException if any of the values is null. */ public AttributeBuilder addValue(final Object... objs) { if (objs != null) { @@ -159,13 +144,10 @@ public AttributeBuilder addValue(final Object... objs) { } /** - * Adds each object in the collection as a value for the attribute that is - * being built. + * Adds each object in the collection as a value for the attribute that is being built. * - * @param obj - * the values to add - * @throws NullPointerException - * if any of the values is null. + * @param obj the values to add + * @throws NullPointerException if any of the values is null. */ public AttributeBuilder addValue(final Collection obj) { addValuesInternal(obj); @@ -173,10 +155,8 @@ public AttributeBuilder addValue(final Collection obj) { } /** - * @return a new attribute with the name and any values that have been - * provided to the builder. - * @throws IllegalArgumentException - * if no name has been provided. + * @return a new attribute with the name and any values that have been provided to the builder. + * @throws IllegalArgumentException if no name has been provided. */ public Attribute build() { if (StringUtil.isBlank(name)) { @@ -192,11 +172,9 @@ public Attribute build() { } /** - * Confirm that the attribute that is being built has at most a single - * value. + * Confirm that the attribute that is being built has at most a single value. * - * @throws IllegalArgumentException - * if the attribute contains more than a single value. + * @throws IllegalArgumentException if the attribute contains more than a single value. */ private void checkSingleValue() { if (value == null || value.size() != 1) { @@ -208,9 +186,8 @@ private void checkSingleValue() { /** * @return the single string value of the attribute that is being built. - * @throws IllegalArgumentException - * if the attribute contains more than a single value, or if the - * value is not of type {@code String}. + * @throws IllegalArgumentException if the attribute contains more than a single value, or if the + * value is not of type {@code String}. */ private String getSingleStringValue() { checkSingleValue(); @@ -224,7 +201,7 @@ private void addValuesInternal(final Iterable values) { if (values != null) { // make sure the list is ready to receive values. if (value == null) { - value = new ArrayList(); + value = new ArrayList<>(); } // add each value checking to make sure its correct for (Object v : values) { @@ -235,113 +212,93 @@ private void addValuesInternal(final Iterable values) { } public AttributeValueCompleteness getAttributeValueCompleteness() { - return attributeValueCompleteness; - } + return attributeValueCompleteness; + } - public void setAttributeValueCompleteness(AttributeValueCompleteness attributeValueCompleteness) { - this.attributeValueCompleteness = attributeValueCompleteness; - } + public void setAttributeValueCompleteness(AttributeValueCompleteness attributeValueCompleteness) { + this.attributeValueCompleteness = attributeValueCompleteness; + } - // ======================================================================= + // ======================================================================= // Operational Attributes // ======================================================================= /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents the date and time that a password will expire on a target - * system or application. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents the date and time that a + * password will expire on a target system or application. * - * @param dateTime - * UTC time in milliseconds. + * @param dateTime UTC time in milliseconds. * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#PASSWORD_EXPIRATION_DATE_NAME - * pre-defined name for password expiration date}. + * {@linkplain OperationalAttributes#PASSWORD_EXPIRATION_DATE_NAME pre-defined name for password expiration date}. */ public static Attribute buildPasswordExpirationDate(final Date dateTime) { return buildPasswordExpirationDate(dateTime.getTime()); } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents the date/time that a password will expire on a target system - * or application. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents the date/time that a + * password will expire on a target system or application. * - * @param dateTime - * UTC time in milliseconds. + * @param dateTime UTC time in milliseconds. * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#PASSWORD_EXPIRATION_DATE_NAME - * pre-defined name for password expiration date}. + * {@linkplain OperationalAttributes#PASSWORD_EXPIRATION_DATE_NAME pre-defined name for password expiration date}. */ public static Attribute buildPasswordExpirationDate(final long dateTime) { return build(OperationalAttributes.PASSWORD_EXPIRATION_DATE_NAME, dateTime); } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents the password of an object on a target system or application. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents the password of an object on + * a target system or application. * - * @param password - * the string that represents a password. - * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#PASSWORD_NAME predefined name - * for password}. + * @param password the string that represents a password. + * @return an {@code Attribute} with the {@linkplain OperationalAttributes#PASSWORD_NAME predefined name + * for password}. */ public static Attribute buildPassword(final GuardedString password) { return build(OperationalAttributes.PASSWORD_NAME, password); } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents the password of an object on a target system or application. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents the password of an object on + * a target system or application. *

    * The caller is responsible for clearing out the array of characters. * - * @param password - * the characters that represent a password. - * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#PASSWORD_NAME predefined name - * for password}. + * @param password the characters that represent a password. + * @return an {@code Attribute} with the {@linkplain OperationalAttributes#PASSWORD_NAME predefined name + * for password}. */ public static Attribute buildPassword(final char[] password) { return buildPassword(new GuardedString(password)); } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents the current password of an object on a target system or - * application. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents the current password of an + * object on a target system or application. *

    - * Passing the current password indicates the account owner (and not an - * administrator) is changing the password. The use case is that an - * administrator password change may not keep history or validate against - * policy. + * Passing the current password indicates the account owner (and not an administrator) is changing the password. + * The use case is that an administrator password change may not keep history or validate against policy. * - * @param password - * the string that represents a password. - * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#CURRENT_PASSWORD_NAME - * predefined name for current password}. + * @param password the string that represents a password. + * @return an {@code Attribute} with the {@linkplain OperationalAttributes#PASSWORD_NAME predefined name + * for current password}. */ public static Attribute buildCurrentPassword(final GuardedString password) { return build(OperationalAttributes.CURRENT_PASSWORD_NAME, password); } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents the current password of an object on a target system or - * application. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents the current password of an + * object on a target system or application. *

    - * Passing the current password indicates the account owner (and not an - * administrator) is changing the password. The use case is that an - * administrator password change may not keep history or validate against - * policy. + * Passing the current password indicates the account owner (and not an administrator) is changing the password. + * The use case is that an administrator password change may not keep history or validate against policy. *

    * The caller is responsible for clearing out the array of characters. * - * @param password - * the characters that represent a password. - * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#CURRENT_PASSWORD_NAME - * predefined name for current password}. + * @param password the string that represents a password. + * @return an {@code Attribute} with the {@linkplain OperationalAttributes#PASSWORD_NAME predefined name + * for current password}. */ public static Attribute buildCurrentPassword(final char[] password) { return buildCurrentPassword(new GuardedString(password)); @@ -362,9 +319,9 @@ public static Attribute buildCurrentPassword(final char[] password) { * * * @param value - * true indicates the object is enabled; otherwise false. + * true indicates the object is enabled; otherwise false. * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#ENABLE_NAME predefined name for + * {@linkplain OperationalAttributes#ENABLE_NAME predefined name for * enabled}. */ public static Attribute buildEnabled(final boolean value) { @@ -372,157 +329,125 @@ public static Attribute buildEnabled(final boolean value) { } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents the date and time to enable an object on a target system or - * application. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents the date and time to enable + * an object on a target system or application. *

      *
    • Use this attribute with {@link CreateApiOp} or - * {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to - * set a date and time to enable an object.
    • - *
    • Read this attribute from - * {@link org.identityconnectors.framework.api.operations.GetApiOp} to - * determine when an object will be enabled.
    • - *
    • Use this attribute with {@link SearchApiOp} to select objects that - * are scheduled to be enabled at a certain date and time.
    • + * {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to set a date and time to enable an object. + * + *
    • Read this attribute from {@link org.identityconnectors.framework.api.operations.GetApiOp} to determine when + * an object will be enabled.
    • + *
    • Use this attribute with {@link SearchApiOp} to select objects that are scheduled to be enabled at a certain + * date and time.
    • *
    * - * @param date - * The date and time to enable a particular object. - * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#ENABLE_DATE_NAME predefined - * name for enable date}. + * @param date The date and time to enable a particular object. + * @return an {@code Attribute} with the {@linkplain OperationalAttributes#ENABLE_DATE_NAME predefined name for + * enable date}. */ public static Attribute buildEnableDate(final Date date) { return buildEnableDate(date.getTime()); } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents the date and time to enable an object on a target system or - * application. The date-and-time parameter is UTC in milliseconds. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents the date and time to enable + * an object on a target system or application. The date-and-time parameter is UTC in milliseconds. *
      *
    • Use this attribute with {@link CreateApiOp} or - * {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to - * set a date and time to enable an object.
    • - *
    • Read this attribute from - * {@link org.identityconnectors.framework.api.operations.GetApiOp} to - * determine when an object will be enabled.
    • - *
    • Use this attribute with {@link SearchApiOp} to select objects that - * are scheduled to be enabled at a certain date and time.
    • + * {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to set a date and time to enable an object. + * + *
    • Read this attribute from {@link org.identityconnectors.framework.api.operations.GetApiOp} to determine when + * an object will be enabled.
    • + *
    • Use this attribute with {@link SearchApiOp} to select objects that are scheduled to be enabled at a certain + * date and time.
    • *
    * - * @param date - * The date and time (UTC in milliseconds) to enable a particular - * object. - * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#ENABLE_DATE_NAME predefined - * name for enable date}. + * @param date The date and time (UTC in milliseconds) to enable a particular object. + * @return an {@code Attribute} with the {@linkplain OperationalAttributes#ENABLE_DATE_NAME predefined name for + * enable date}. */ public static Attribute buildEnableDate(final long date) { return build(OperationalAttributes.ENABLE_DATE_NAME, date); } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents the date and time to disable an object on a target system or - * application. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents the date and time to disable + * an object on a target system or application. *
      *
    • Use this attribute with {@link CreateApiOp} or - * {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to - * set a date and time to disable an object.
    • - *
    • Read this attribute from - * {@link org.identityconnectors.framework.api.operations.GetApiOp} to + * {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to set a date and time to disable an object. + *
    • + *
    • Read this attribute from {@link org.identityconnectors.framework.api.operations.GetApiOp} to * determine when an object will be disabled.
    • - *
    • Use this attribute with {@link SearchApiOp} to select objects that - * are scheduled to be disabled at a certain date and time.
    • + *
    • Use this attribute with {@link SearchApiOp} to select objects that are scheduled to be disabled at a certain + * date and time.
    • *
    * - * @param date - * The date and time to disable a particular object. - * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#DISABLE_DATE_NAME predefined - * name for disable date}. + * @param date The date and time to disable a particular object. + * @return an {@code Attribute} with the {@linkplain OperationalAttributes#DISABLE_DATE_NAME predefined name for + * disable date}. */ public static Attribute buildDisableDate(final Date date) { return buildDisableDate(date.getTime()); } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents the date and time to disable an object on a target system or - * application. The date-and-time parameter is UTC in milliseconds. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents the date and time to disable + * an object on a target system or application. *
      *
    • Use this attribute with {@link CreateApiOp} or - * {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to - * set a date and time to disable an object.
    • - *
    • Read this attribute from - * {@link org.identityconnectors.framework.api.operations.GetApiOp} to + * {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to set a date and time to disable an object. + *
    • + *
    • Read this attribute from {@link org.identityconnectors.framework.api.operations.GetApiOp} to * determine when an object will be disabled.
    • - *
    • Use this attribute with {@link SearchApiOp} to select objects that - * are scheduled to be disabled at a certain date and time.
    • + *
    • Use this attribute with {@link SearchApiOp} to select objects that are scheduled to be disabled at a certain + * date and time.
    • *
    * - * @param date - * The date and time (UTC in milliseconds) to disable a - * particular object. - * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#DISABLE_DATE_NAME predefined - * name for disable date}. + * @param date The date and time (UTC in milliseconds) to disable a particular object. + * @return an {@code Attribute} with the {@linkplain OperationalAttributes#DISABLE_DATE_NAME predefined name for + * disable date}. */ public static Attribute buildDisableDate(final long date) { return build(OperationalAttributes.DISABLE_DATE_NAME, date); } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents whether an object is locked out on a target system or - * application. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents whether an object is locked + * out on a target system or application. *
      *
    • Read this attribute from - * {@link org.identityconnectors.framework.api.operations.GetApiOp} to - * determine whether an object is currently locked out.
    • - *
    • Use this attribute with - * {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to - * clear the lock-out status of an object (or to set the lock-out status of - * an object).
    • - *
    • Use this attribute with {@link SearchApiOp} to select objects that - * are currently locked out (or to select objects that are not currently - * locked out).
    • + * {@link org.identityconnectors.framework.api.operations.GetApiOp} to determine whether an object is currently + * locked out. + *
    • Use this attribute with {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to + * clear the lock-out status of an object (or to set the lock-out status of an object).
    • + *
    • Use this attribute with {@link SearchApiOp} to select objects that are currently locked out (or to select + * objects that are not currently locked out).
    • *
    * - * @param lock - * true if the object is locked out; otherwise false. - * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#LOCK_OUT_NAME predefined name - * for lockout state}. + * @param lock true if the object is locked out; otherwise false. + * @return an {@code Attribute} with the {@linkplain OperationalAttributes#LOCK_OUT_NAME predefined name for + * lockout state}. */ public static Attribute buildLockOut(final boolean lock) { return build(OperationalAttributes.LOCK_OUT_NAME, lock); } /** - * Builds an {@linkplain OperationalAttributes operational attribute} that - * represents whether the password of an object is expired on a target - * system or application. + * Builds an {@linkplain OperationalAttributes operational attribute} that represents whether the password of an + * object is expired on a target system or application. *
      - *
    • Read this attribute from - * {@link org.identityconnectors.framework.api.operations.GetApiOp} to + *
    • Read this attribute from {@link org.identityconnectors.framework.api.operations.GetApiOp} to * determine whether the password of an object is currently expired.
    • - *
    • Use this attribute with - * {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to - * expire the password of an object (or to clear the expired status of the - * password of an object).
    • - *
    • Use this attribute with {@link SearchApiOp} to select objects that - * have passwords that are currently expired (or to select objects that have - * passwords that are not currently expired).
    • + *
    • Use this attribute with {@link org.identityconnectors.framework.api.operations.UpdateApiOp} to + * expire the password of an object (or to clear the expired status of the password of an object).
    • + *
    • Use this attribute with {@link SearchApiOp} to select objects that have passwords that are currently expired + * (or to select objects that have passwords that are not currently expired).
    • *
    * - * @param value - * from the API true expires and from the SPI its shows its - * either expired or not. - * @return an {@code Attribute} with the - * {@linkplain OperationalAttributes#PASSWORD_EXPIRED_NAME - * predefined name for password expiration state}. + * @param value from the API true expires and from the SPI its shows its either expired or not. + * @return an {@code Attribute} with the {@linkplain OperationalAttributes#PASSWORD_EXPIRED_NAME predefined name + * for password expiration state}. */ public static Attribute buildPasswordExpired(final boolean value) { return build(OperationalAttributes.PASSWORD_EXPIRED_NAME, value); @@ -531,88 +456,70 @@ public static Attribute buildPasswordExpired(final boolean value) { // ======================================================================= // Pre-defined Attributes // ======================================================================= - /** - * Builds an {@linkplain PredefinedAttributes pre-defined attribute} that - * represents the date and time of the most recent login for an object (such - * as an account) on a target system or application. + * Builds an {@linkplain PredefinedAttributes pre-defined attribute} that represents the date and time of the most + * recent login for an object (such as an account) on a target system or application. * - * @param date - * The date and time of the last login. - * @return an {@code Attribute} with the - * {@linkplain PredefinedAttributes#LAST_LOGIN_DATE_NAME predefined - * name for password expiration state}. + * @param date The date and time of the last login. + * @return an {@code Attribute} with the {@linkplain PredefinedAttributes#LAST_LOGIN_DATE_NAME predefined name for + * password expiration state}. */ public static Attribute buildLastLoginDate(final Date date) { return buildLastLoginDate(date.getTime()); } /** - * Builds an {@linkplain PredefinedAttributes pre-defined attribute} that - * represents the date and time of the most recent login for an object (such - * as an account) on a target system or application. + * Builds an {@linkplain PredefinedAttributes pre-defined attribute} that represents the date and time of the most + * recent login for an object (such as an account) on a target system or application. *

    * The time parameter is UTC in milliseconds. * - * @param date - * The date and time (UTC in milliseconds) of the last login. - * @return an {@code Attribute} with the - * {@linkplain PredefinedAttributes#LAST_LOGIN_DATE_NAME predefined - * name for password expiration state}. + * @param date The date and time (UTC in milliseconds) of the last login. + * @return an {@code Attribute} with the {@linkplain PredefinedAttributes#LAST_LOGIN_DATE_NAME predefined name for + * password expiration state}. */ public static Attribute buildLastLoginDate(final long date) { return build(PredefinedAttributes.LAST_LOGIN_DATE_NAME, date); } /** - * Builds an {@linkplain PredefinedAttributes pre-defined attribute} that - * represents the date and time that the password was most recently changed - * for an object (such as an account) on a target system or application. + * Builds an {@linkplain PredefinedAttributes pre-defined attribute} that represents the date and time that the + * password was most recently changed for an object (such as an account) on a target system or application. * - * @param date - * The date and time that the password was most recently changed. - * @return an {@code Attribute} with the - * {@linkplain PredefinedAttributes#LAST_PASSWORD_CHANGE_DATE_NAME - * predefined name for password expiration state}. + * @param date The date and time that the password was most recently changed. + * @return an {@code Attribute} with the {@linkplain PredefinedAttributes#LAST_PASSWORD_CHANGE_DATE_NAME predefined + * name for password expiration state}. */ public static Attribute buildLastPasswordChangeDate(final Date date) { return buildLastPasswordChangeDate(date.getTime()); } /** - * Builds an {@linkplain PredefinedAttributes pre-defined attribute} that - * represents the date and time that the password was most recently changed - * for an object (such as an account) on a target system or application. + * Builds an {@linkplain PredefinedAttributes pre-defined attribute} that represents the date and time that the + * password was most recently changed for an object (such as an account) on a target system or application. *

    * The time parameter is UTC in milliseconds. * - * @param date - * The date and time that the password was most recently changed. - * @return an {@code Attribute} with the - * {@linkplain PredefinedAttributes#LAST_PASSWORD_CHANGE_DATE_NAME - * predefined name for password expiration state}. + * @param date The date and time that the password was most recently changed. + * @return an {@code Attribute} with the {@linkplain PredefinedAttributes#LAST_PASSWORD_CHANGE_DATE_NAME predefined + * name for password expiration state}. */ public static Attribute buildLastPasswordChangeDate(final long date) { return build(PredefinedAttributes.LAST_PASSWORD_CHANGE_DATE_NAME, date); } /** - * Builds an {@linkplain PredefinedAttributes pre-defined attribute} that - * represents how often the password must be changed for an object (such as - * an account) on a target system or application. + * Builds an {@linkplain PredefinedAttributes pre-defined attribute} that represents how often the password must be + * changed for an object (such as an account) on a target system or application. *

    * The value for this attribute is expressed in milliseconds. * - * @param value - * The number of milliseconds between the time that the password - * was most recently changed and the time when the password must - * be changed again. - * @return an {@code Attribute} with the - * {@linkplain PredefinedAttributes#PASSWORD_CHANGE_INTERVAL_NAME - * predefined name for password expiration state}. + * @param value The number of milliseconds between the time that the password was most recently changed and the + * time when the password must be changed again. + * @return an {@code Attribute} with the {@linkplain PredefinedAttributes#PASSWORD_CHANGE_INTERVAL_NAME predefined + * name for password expiration state}. */ public static Attribute buildPasswordChangeInterval(final long value) { return build(PredefinedAttributes.PASSWORD_CHANGE_INTERVAL_NAME, value); } - } diff --git a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/ConnectorObjectBuilder.java b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/ConnectorObjectBuilder.java index 552a5d15..77024459 100644 --- a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/ConnectorObjectBuilder.java +++ b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/ConnectorObjectBuilder.java @@ -21,6 +21,7 @@ * ==================== * Portions Copyrighted 2014 ForgeRock AS. * Portions Copyrighted 2015 Evolveum + * Portions Copyrighted 2024 ConnId */ package org.identityconnectors.framework.common.objects; @@ -28,31 +29,20 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; - import org.identityconnectors.common.Assertions; import org.identityconnectors.common.CollectionUtil; /** * Builder class to create a {@link ConnectorObject}. * - * The developer of a Connector will construct a ConnectorObjectBuilder, and - * then call the ConnectorObjectBuilder to set a {@link Uid}, add attributes, - * and then finally to {@link #build()} the actual {@link ConnectorObject}. + * The developer of a Connector will construct a ConnectorObjectBuilder, and then call the ConnectorObjectBuilder to set + * a {@link Uid}, add attributes, and then finally to {@link #build()} the actual {@link ConnectorObject}. */ public final class ConnectorObjectBuilder { - private ObjectClass objectClass; - - private Map attributeMap; + private ObjectClass objectClass = ObjectClass.ACCOUNT; - // ======================================================================= - // Constructors - // ======================================================================= - public ConnectorObjectBuilder() { - attributeMap = new HashMap<>(); - // default always add the account object class.. - setObjectClass(ObjectClass.ACCOUNT); - } + private final Map attributeMap = new HashMap<>(); // ======================================================================= // Uid Setters @@ -83,7 +73,7 @@ public ConnectorObjectBuilder setName(final Name name) { // ======================================================================= // ObjectClass Setter // ======================================================================= - public ConnectorObjectBuilder setObjectClass(ObjectClass oclass) { + public ConnectorObjectBuilder setObjectClass(final ObjectClass oclass) { if (ObjectClass.ALL.equals(oclass)) { throw new IllegalArgumentException("Connector object class can not be type of __ALL__"); } @@ -98,7 +88,7 @@ public ConnectorObjectBuilder setObjectClass(ObjectClass oclass) { * Takes all the attribute from a {@link ConnectorObject} and add/overwrite * the current attributes. */ - public ConnectorObjectBuilder add(ConnectorObject obj) { + public ConnectorObjectBuilder add(final ConnectorObject obj) { // simply add all the attributes for (Attribute attr : obj.getAttributes()) { addAttribute(attr); @@ -113,7 +103,7 @@ public ConnectorObjectBuilder add(ConnectorObject obj) { /** * Adds one or many attributes to the {@link ConnectorObject}. */ - public ConnectorObjectBuilder addAttribute(Attribute... attrs) { + public ConnectorObjectBuilder addAttribute(final Attribute... attrs) { Assertions.nullCheck(attrs, "attrs"); for (Attribute a : attrs) { attributeMap.put(a.getName(), a); @@ -124,7 +114,7 @@ public ConnectorObjectBuilder addAttribute(Attribute... attrs) { /** * Add all the {@link Attribute}s of a {@link Collection}. */ - public ConnectorObjectBuilder addAttributes(Collection attrs) { + public ConnectorObjectBuilder addAttributes(final Collection attrs) { Assertions.nullCheck(attrs, "attrs"); for (Attribute a : attrs) { attributeMap.put(a.getName(), a); @@ -135,7 +125,7 @@ public ConnectorObjectBuilder addAttributes(Collection attrs) { /** * Adds values to the attribute. */ - public ConnectorObjectBuilder addAttribute(String name, Object... objs) { + public ConnectorObjectBuilder addAttribute(final String name, final Object... objs) { addAttribute(AttributeBuilder.build(name, objs)); return this; } @@ -143,7 +133,7 @@ public ConnectorObjectBuilder addAttribute(String name, Object... objs) { /** * Adds each object in the collection. */ - public ConnectorObjectBuilder addAttribute(String name, Collection obj) { + public ConnectorObjectBuilder addAttribute(final String name, final Collection obj) { addAttribute(AttributeBuilder.build(name, obj)); return this; } diff --git a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/LiveSyncResultsHandler.java b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/LiveSyncResultsHandler.java index c8a8a89a..d7e6dcb6 100644 --- a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/LiveSyncResultsHandler.java +++ b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/LiveSyncResultsHandler.java @@ -25,16 +25,17 @@ import org.identityconnectors.framework.api.operations.LiveSyncApiOp; /** - * Callback interface that an application implements in order to handle results - * from {@link LiveSyncApiOp} in a stream-processing fashion. + * Callback interface that an application implements in order to handle results from {@link LiveSyncApiOp} in a + * stream-processing fashion. */ +@FunctionalInterface public interface LiveSyncResultsHandler { /** * Called to handle a delta in the stream. The Connector framework will call this method multiple times, once for * each result. Although this method is callback, the framework will invoke it synchronously. Thus, the framework * guarantees that once an application's call to {@link LiveSyncApiOp#sync LiveSyncApiOp#sync()} returns, the - * framework will no longer call this method to handle results from that {@code sync} operation. + * framework will no longer call this method to handle results from that {@code livesync} operation. * * @param delta The change * @return True if the application wants to continue processing more results. diff --git a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/ResultsHandler.java b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/ResultsHandler.java index 0f81aa5e..2e2595cf 100644 --- a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/ResultsHandler.java +++ b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/ResultsHandler.java @@ -25,31 +25,23 @@ /** * Callback interface for operations that are returning one or more results. - * Currently used only by - * {@link org.identityconnectors.framework.api.operations.SearchApiOp Search}, - * but may be used by other operations in the future. + * Currently used only by {@link org.identityconnectors.framework.api.operations.SearchApiOp Search}, but may be used + * by other operations in the future. */ +@FunctionalInterface public interface ResultsHandler { /** - * Invoked each time a matching {@link ConnectorObject} is returned from a - * query request. + * Invoked each time a matching {@link ConnectorObject} is returned from a query request. * - * @param connectorObject - * The matching ConnectorObject. - * @return {@code true} if this handler should continue to be notified of - * any remaining matching ConnectorObjects, or {@code false} if the - * remaining ConnectorObjects should be skipped for some reason - * (e.g. a client side size limit has been reached or the failed to - * handle the last item). If returns {@code false} the last items - * should be considers unhandled and in next page request it should - * be the first item. + * @param connectorObject The matching ConnectorObject. + * @return {@code true} if this handler should continue to be notified of any remaining matching ConnectorObjects, + * or {@code false} if the remaining ConnectorObjects should be skipped for some reason (e.g. a client side size + * limit has been reached or the failed to handle the last item). If returns {@code false} the last items should be + * considers unhandled and in next page request it should be the first item. * - * @throws RuntimeException - * the implementor should throw a {@link RuntimeException} that - * wraps any native exception (or that describes any other - * problem during execution) that is serious enough to stop the - * iteration. + * @throws RuntimeException the implementor should throw a {@link RuntimeException} that wraps any native exception + * (or that describes any other problem during execution) that is serious enough to stop the iteration. */ boolean handle(final ConnectorObject connectorObject); } diff --git a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/SyncResultsHandler.java b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/SyncResultsHandler.java index a58c6b2b..de02319f 100644 --- a/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/SyncResultsHandler.java +++ b/java/connector-framework/src/main/java/org/identityconnectors/framework/common/objects/SyncResultsHandler.java @@ -25,27 +25,22 @@ import org.identityconnectors.framework.api.operations.SyncApiOp; /** - * Callback interface that an application implements in order to handle results - * from {@link SyncApiOp} in a stream-processing fashion. + * Callback interface that an application implements in order to handle results from {@link SyncApiOp} in a + * stream-processing fashion. */ +@FunctionalInterface public interface SyncResultsHandler { /** - * Called to handle a delta in the stream. The Connector framework will call - * this method multiple times, once for each result. Although this method is - * callback, the framework will invoke it synchronously. Thus, the framework - * guarantees that once an application's call to {@link SyncApiOp#sync - * SyncApiOp#sync()} returns, the framework will no longer call this method - * to handle results from that sync() operation. + * Called to handle a delta in the stream. The Connector framework will call this method multiple times, once for + * each result. Although this method is callback, the framework will invoke it synchronously. Thus, the framework + * guarantees that once an application's call to {@link SyncApiOp#sync SyncApiOp#sync()} returns, the framework will + * no longer call this method to handle results from that {@code sync()} operation. * - * @param delta - * The change - * @return True if the application wants to continue processing more - * results. - * @throws RuntimeException - * If the application encounters an exception. This will stop - * iteration and the exception will propagate to the - * application. + * @param delta The change + * @return True if the application wants to continue processing more results. + * @throws RuntimeException If the application encounters an exception. This will stop iteration and the exception + * will propagate to the application. */ - public boolean handle(SyncDelta delta); + boolean handle(SyncDelta delta); }