/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. * * You can obtain a copy of the License at * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== * Portions Copyrighted 2010-2013 ForgeRock AS. */ package org.identityconnectors.framework.impl.api.remote; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.TimerTask; import java.util.Vector; import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.event.ConnectorEvent; import org.identityconnectors.common.event.ConnectorEventHandler; import org.identityconnectors.common.event.ConnectorEventPublisher; import org.identityconnectors.common.l10n.CurrentLocale; import org.identityconnectors.common.logging.Log; import org.identityconnectors.framework.api.ConnectorInfo; import org.identityconnectors.framework.api.ConnectorInfoManager; import org.identityconnectors.framework.api.ConnectorKey; import org.identityconnectors.framework.api.RemoteFrameworkConnectionInfo; import org.identityconnectors.framework.common.exceptions.ConnectorException; import org.identityconnectors.framework.common.exceptions.ConnectorIOException; import org.identityconnectors.framework.common.serializer.SerializerUtil; import org.identityconnectors.framework.impl.api.remote.messages.HelloRequest; import org.identityconnectors.framework.impl.api.remote.messages.HelloResponse; public class RemoteConnectorInfoManagerImpl implements ConnectorInfoManager, ConnectorEventPublisher, Runnable { /** * Logger. */ private static final Log LOG = Log.getLog(RemoteConnectorInfoManagerImpl.class); private final RemoteFrameworkConnectionInfo frameworkConnectionInfo; private List<ConnectorInfo> connectorInfoList; private Long serverStartTime = null; private final Vector<ConnectorEventHandler> eventHandlers = new Vector<ConnectorEventHandler>(); private RemoteConnectorInfoManagerImpl() { frameworkConnectionInfo = null; } public RemoteConnectorInfoManagerImpl(RemoteFrameworkConnectionInfo info) throws RuntimeException { this(info, true); } public RemoteConnectorInfoManagerImpl(RemoteFrameworkConnectionInfo info, boolean loadConnectorInfo) throws RuntimeException { frameworkConnectionInfo = info; if (loadConnectorInfo) { init(); } else { connectorInfoList = null; } } private void init() { RemoteFrameworkConnection connection = new RemoteFrameworkConnection(frameworkConnectionInfo); HelloResponse response = null; try { connection.writeObject(CurrentLocale.get()); connection.writeObject(frameworkConnectionInfo.getKey()); connection.writeObject(new HelloRequest(HelloRequest.CONNECTOR_INFO)); response = (HelloResponse) connection.readObject(); } finally { connection.close(); } if (null == response) { LOG.error("HelloResponse is null from {0}", frameworkConnectionInfo); throw new ConnectorIOException("HelloResponse is null from " + frameworkConnectionInfo.toString()); } if (response.getException() != null) { throw ConnectorException.wrap(response.getException()); } List<RemoteConnectorInfoImpl> remoteInfos = response.getConnectorInfos(); // populate transient fields not serialized for (RemoteConnectorInfoImpl remoteInfo : remoteInfos) { remoteInfo.setRemoteConnectionInfo(frameworkConnectionInfo); } List<ConnectorInfo> connectorInfoBefore = connectorInfoList; connectorInfoList = CollectionUtil.<ConnectorInfo>newReadOnlyList(remoteInfos); Object o = response.getServerInfo().get(HelloResponse.SERVER_START_TIME); if (o instanceof Long) { serverStartTime = (Long) o; } else { serverStartTime = System.currentTimeMillis(); } // Notify all the listeners List<ConnectorInfo> unchanged = new ArrayList<ConnectorInfo>(connectorInfoList.size()); if (null != connectorInfoBefore) { for (ConnectorInfo oldCi : connectorInfoBefore) { boolean unregistered = true; for (ConnectorInfo newCi : connectorInfoList) { if (oldCi.getConnectorKey().equals(newCi.getConnectorKey())) { unregistered = false; unchanged.add(newCi); break; } } if (unregistered) { notifyListeners(new ConnectorEvent(ConnectorEvent.CONNECTOR_UNREGISTERING, oldCi.getConnectorKey())); } } } for (ConnectorInfo newCi : connectorInfoList) { if (!unchanged.contains(newCi)) { notifyListeners(new ConnectorEvent(ConnectorEvent.CONNECTOR_REGISTERED, newCi .getConnectorKey())); } } } /** * Derives another RemoteConnectorInfoManagerImpl with a different RemoteFrameworkConnectionInfo but with the same * metadata. * * @param info * @return */ public RemoteConnectorInfoManagerImpl derive(RemoteFrameworkConnectionInfo info) { RemoteConnectorInfoManagerImpl rv = new RemoteConnectorInfoManagerImpl(); if (null == connectorInfoList || connectorInfoList.isEmpty()) { rv.connectorInfoList = Collections.emptyList(); } else { @SuppressWarnings("unchecked") List<RemoteConnectorInfoImpl> remoteInfos = (List<RemoteConnectorInfoImpl>) SerializerUtil.cloneObject(connectorInfoList); for (RemoteConnectorInfoImpl remoteInfo : remoteInfos) { remoteInfo.setRemoteConnectionInfo(info); } rv.connectorInfoList = CollectionUtil.<ConnectorInfo>newReadOnlyList(remoteInfos); } return rv; } public ConnectorInfo findConnectorInfo(ConnectorKey key) { for (ConnectorInfo info : getConnectorInfos()) { if (info.getConnectorKey().equals(key)) { return info; } } return null; } public List<ConnectorInfo> getConnectorInfos() { List<ConnectorInfo> result = connectorInfoList; if (null == result) { result = Collections.emptyList(); } return result; } /** * {@inheritDoc} */ public void run() { try { Map<String, Object> serverInfo = getServerInfo(); Object o = serverInfo.get(HelloResponse.SERVER_START_TIME); if (o instanceof Long) { if (null == serverStartTime || ((Long) o) > serverStartTime) { if (LOG.isOk()) { if (null != serverStartTime) { LOG.ok("Connector server has been restarted since {0}, new start time: {1}", new Date(serverStartTime), new Date((Long) o)); } else { LOG.ok("First connection to connector server has been established, new start time: {0}", new Date((Long) o)); } } init(); } } } catch (ConnectorIOException e) { // Server is unreachable and we notify all listeners if (null != connectorInfoList) { for (ConnectorInfo connectorInfo : connectorInfoList) { notifyListeners(new ConnectorEvent(ConnectorEvent.CONNECTOR_UNREGISTERING, connectorInfo.getConnectorKey())); } } connectorInfoList = null; LOG.error("Failed to connect to remote connector server {0}", frameworkConnectionInfo); } catch (Exception e) { LOG.error(e, "Failed to update the ConnectorInfo from remote connector server"); } } /** * {@inheritDoc} */ public void addConnectorEventHandler(ConnectorEventHandler hook) { if (hook == null) { throw new NullPointerException(); } if (!eventHandlers.contains(hook)) { if (null != connectorInfoList) { for (ConnectorInfo connectorInfo : connectorInfoList) { hook.handleEvent(new ConnectorEvent(ConnectorEvent.CONNECTOR_REGISTERED, connectorInfo.getConnectorKey())); } } eventHandlers.addElement(hook); } } /** * {@inheritDoc} */ public void deleteConnectorEventHandler(ConnectorEventHandler hook) { eventHandlers.removeElement(hook); } public Map<String, Object> getServerInfo() throws RuntimeException { RemoteFrameworkConnection connection = new RemoteFrameworkConnection(frameworkConnectionInfo); try { connection.writeObject(CurrentLocale.get()); connection.writeObject(frameworkConnectionInfo.getKey()); connection.writeObject(new HelloRequest(HelloRequest.SERVER_INFO)); HelloResponse response = (HelloResponse) connection.readObject(); if (response.getException() instanceof ConnectorException) { throw (ConnectorException) response.getException(); } else if (response.getException() != null) { throw ConnectorException.wrap(response.getException()); } return response.getServerInfo(); } finally { connection.close(); } } public List<ConnectorKey> getConnectorKeys() throws RuntimeException { RemoteFrameworkConnection connection = new RemoteFrameworkConnection(frameworkConnectionInfo); try { connection.writeObject(CurrentLocale.get()); connection.writeObject(frameworkConnectionInfo.getKey()); connection.writeObject(new HelloRequest(HelloRequest.CONNECTOR_KEY_LIST)); HelloResponse response = (HelloResponse) connection.readObject(); if (response.getException() instanceof ConnectorException) { throw (ConnectorException) response.getException(); } else if (response.getException() != null) { throw ConnectorException.wrap(response.getException()); } return response.getConnectorKeys(); } finally { connection.close(); } } private void notifyListeners(ConnectorEvent event) { /* * a temporary array buffer, used as a snapshot of the state of current * Handlers. */ Object[] arrLocal = eventHandlers.toArray(); for (int i = arrLocal.length - 1; i >= 0; i--) { try { ((ConnectorEventHandler) arrLocal[i]).handleEvent(new ConnectorEvent(event)); } catch (Throwable t) { /* ignore */ } } } }