/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions copyright [year] [name of copyright owner]". * * Portions copyright 2011-2015 ForgeRock AS. */ package org.forgerock.openidm.sync.impl; // Java Standard Edition import org.forgerock.json.JsonValue; import org.forgerock.json.resource.http.HttpUtils; import org.forgerock.openidm.sync.ReconAction; import org.forgerock.openidm.sync.impl.Scripts.Script; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.script.ScriptException; import java.util.HashMap; import java.util.Map; /** * Policies to determine an action for a given {@link org.forgerock.openidm.sync.impl.Situation}. */ class Policy { /** * Set up logging for {@link org.forgerock.openidm.sync.impl.Policy}. */ private final static Logger LOGGER = LoggerFactory.getLogger(Policy.class); /** * Synchronization Situation {@link org.forgerock.openidm.sync.impl.Situation}. */ private final Situation situation; /** * Reconciliation Action {@link org.forgerock.openidm.sync.ReconAction}. */ private final ReconAction action; /** * Script for action. */ private final Script script; /** * Script for postAction. */ private Script postAction; /** * Condition used to determine if policy should be enforced. */ private Condition condition; /** * A Constructor for the Policy class. * * @param config a {@link JsonValue} object representing the policy configuration. */ public Policy(JsonValue config) { situation = config.get("situation").required().asEnum(Situation.class); JsonValue action = config.get("action").required(); condition = new Condition(config.get("condition")); if (action.isString()) { this.action = action.asEnum(ReconAction.class); this.script = null; } else { this.action = null; // Scripts.newInstance will copy the scope variables from action this.script = Scripts.newInstance(action); } JsonValue pAction = config.get("postAction"); if (!pAction.isNull()) { this.postAction = Scripts.newInstance(pAction); } } /** * Returns the condition for this policy. * * @return a {@link org.forgerock.openidm.sync.impl.Condition} object representing * * a condition for this policy */ public Condition getCondition() { return condition; } /** * Returns the situation for this policy. * * @return a {@link Situation} object representing the situation for this policy */ public Situation getSituation() { return situation; } /** * Returns the action for this policy. The action may be dynamically determined by a script. * * @param source a {@link LazyObjectAccessor} representing the source object * @param target a {@link LazyObjectAccessor} representing the target object * @param syncOperation the parent {@link ObjectMapping.SyncOperation} instance * @param linkQualifier the linkQualifier for the policy * @return a {@link ReconAction} object representing the action for this policy * @throws SynchronizationException TODO. */ public ReconAction getAction(LazyObjectAccessor source, LazyObjectAccessor target, final ObjectMapping.SyncOperation syncOperation, String linkQualifier) throws SynchronizationException { if (action != null) { // static action specified return action; } if (script != null) { // action is dynamically determined Map<String, Object> scope = new HashMap<String, Object>(); Map<String, Object> recon = new HashMap<String, Object>(); scope.put("recon", recon); JsonValue actionParam = syncOperation.toJsonValue(); actionParam.put(HttpUtils.PARAM_ACTION, "performAction"); recon.put("actionParam", actionParam.getObject()); scope.put("sourceAction", (syncOperation instanceof ObjectMapping.SourceSyncOperation)); scope.put("linkQualifier", linkQualifier); if (source != null) { scope.put("source", source.asMap()); } if (target != null) { scope.put("target", target.asMap()); } try { return ReconAction.valueOf(script.exec(scope).toString()); } catch (NullPointerException npe) { throw new SynchronizationException("action script returned null value"); } catch (IllegalArgumentException iae) { throw new SynchronizationException("action script returned invalid action"); } catch (ScriptException se) { LOGGER.debug("action script encountered exception", se); throw new SynchronizationException(se); } } return ReconAction.IGNORE; } /** * Evaluates a post action script, if present. * * @param source a {@link LazyObjectAccessor} representing the source object * @param target a {@link LazyObjectAccessor} representing the target object * @param action the {@link ReconAction} that was performed * @param linkQualifier the linkQualifier * @param sourceAction true if this is a source sync operation, false if it is a target sync operation * @throws SynchronizationException */ public void evaluatePostAction(LazyObjectAccessor source, LazyObjectAccessor target, ReconAction action, boolean sourceAction, String linkQualifier, String reconId) throws SynchronizationException { if (postAction != null) { Map<String, Object> scope = new HashMap<String, Object>(); scope.put("linkQualifier", linkQualifier); scope.put("sourceAction", sourceAction); scope.put("action", action.name()); scope.put("situation", situation.name()); scope.put("reconId", reconId); if (source != null) { scope.put("source", source.asMap()); } if (target != null) { scope.put("target", target.asMap()); } try { postAction.exec(scope); } catch (ScriptException se) { LOGGER.debug("action script encountered exception", se); throw new SynchronizationException(se); } } } }