1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott/*
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * The contents of this file are subject to the terms of the Common Development and
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * Distribution License (the License). You may not use this file except in compliance with the
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * License.
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott *
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * specific language governing permission and limitations under the License.
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott *
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * When distributing Covered Software, include this CDDL Header Notice in each file and include
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * Header, with the fields enclosed by brackets [] replaced by your own identifying
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * information: "Portions copyright [year] [name of copyright owner]".
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott *
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshott * Copyright 2015-2016 ForgeRock AS.
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott */
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshottpackage com.iplanet.services.naming;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshottimport java.security.AccessController;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshottimport java.security.PrivilegedAction;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshottimport java.util.ArrayList;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshottimport java.util.Collection;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshottimport java.util.HashMap;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshottimport java.util.Map;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshottimport javax.inject.Inject;
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshott
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshottimport com.iplanet.sso.SSOException;
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshottimport com.iplanet.sso.SSOToken;
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshottimport com.sun.identity.sm.SMSException;
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshottimport com.sun.identity.sm.ServiceConfigManager;
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshottimport com.sun.identity.sm.ServiceListener;
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshottimport com.sun.identity.sm.ServiceSchemaManager;
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott/**
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * ServiceListeners provides a simplified API for creating appropriate {@link ServiceListener} instances
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * to respond to changes in either Configuration or Schema.
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott *
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * This class is intended to reduce the boiler plate code required to enable a service to respond
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * to configuration changes.
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott *
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * For an example of usage, the following indicates an example of listening for changes to the
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * global configuration changes in iPlanetAMNamingService.
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott *
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * <code>
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * ServiceListeners builder = ...
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * Action action = ... // Implementation to respond when config/service changes.
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * builder.config("iPlanetAMNamingService").global(action).listen();
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * </code>
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott *
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * Note: The listener triggering order should be considered non-deterministic. <b>Do not depend
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * on the order</b> of listener triggering.
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott */
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshottpublic class ServiceListeners {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott private final PrivilegedAction<SSOToken> action;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott @Inject
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public ServiceListeners(PrivilegedAction<SSOToken> action) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott this.action = action;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public ListenerBuilder config(String serviceName) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott try {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott return new ListenerBuilder(new ServiceConfigManager(serviceName, AccessController.doPrivileged(action)));
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott } catch (SMSException | SSOException e) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott throw new IllegalStateException(e);
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public ListenerBuilder schema(String serviceName) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott try {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott return new ListenerBuilder(new ServiceSchemaManager(serviceName, AccessController.doPrivileged(action)));
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott } catch (SMSException | SSOException e) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott throw new IllegalStateException(e);
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott private enum ConfigType {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott GLOBAL,
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott ORGANISATION,
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott SCHEMA
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshott /**
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshott * Builder responsible for providing fluent-like functions for building up
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshott * Action instances which will respond to changes in Service configuration.
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshott */
d78764efc954da87cd81023cc846a6a5af360d95Robert Wapshott public static class ListenerBuilder {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott private final Map<ConfigType, Collection<Action>> actions;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott private ServiceSchemaManager schemaManager = null;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott private ServiceConfigManager configManager = null;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public ListenerBuilder() {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott actions = new HashMap<>();
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott for (ConfigType type : ConfigType.values()) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott actions.put(type, new ArrayList<Action>());
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public ListenerBuilder(ServiceConfigManager configManager) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott this();
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott this.configManager = configManager;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public ListenerBuilder(ServiceSchemaManager schemaManager) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott this();
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott this.schemaManager = schemaManager;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public ListenerBuilder global(Action action) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott add(ConfigType.GLOBAL, action);
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott return this;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public ListenerBuilder organisation(Action action) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott add(ConfigType.ORGANISATION, action);
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott return this;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public ListenerBuilder schema(Action action) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott add(ConfigType.SCHEMA, action);
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott return this;
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott private void add(ConfigType t, Action a) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott actions.get(t).add(a);
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public void listen() {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott ServiceListener listener = new ServiceListener() {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott @Override
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public void schemaChanged(String serviceName, String version) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott for (Action a : actions.get(ConfigType.SCHEMA)) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott a.performUpdate();
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott @Override
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public void globalConfigChanged(String serviceName, String version, String groupName, String serviceComponent, int type) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott for (Action a : actions.get(ConfigType.GLOBAL)) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott a.performUpdate();
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott @Override
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public void organizationConfigChanged(String serviceName, String version, String orgName, String groupName, String serviceComponent, int type) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott for (Action a : actions.get(ConfigType.ORGANISATION)) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott a.performUpdate();
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott };
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott if (schemaManager != null) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott schemaManager.addListener(listener);
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott if (configManager != null) {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott configManager.addListener(listener);
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott /**
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott * A generic listener which will respond to a configuration or schema change event.
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott */
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott public interface Action {
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott void performUpdate();
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott }
1d407e39b7d8f68d9a2b1e178f35fab037d9835aRobert Wapshott}