/* * Copyright (C) 2012 Issa Gorissen <issa-gorissen@usa.net>. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jivesoftware.openfire.crowd; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.group.AbstractGroupProvider; import org.jivesoftware.openfire.group.Group; import org.jivesoftware.openfire.group.GroupNotFoundException; import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.cache.Cache; import org.jivesoftware.util.cache.CacheFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xmpp.packet.JID; /** * Atlassian Crowd implementation of the GroupProvider. We do not permit * modifications of groups from this provider - only read-only access. */ public class CrowdGroupProvider extends AbstractGroupProvider { private static final Logger LOG = LoggerFactory.getLogger(CrowdGroupProvider.class); private static final int CACHE_TTL = 3600; // ttl in seconds - one hour private static final String JIVE_CROWD_GROUPS_CACHE_TTL_SECS = "crowd.groups.cache.ttl.seconds"; private static final String GROUP_CACHE_NAME = "crowdGroup"; private static final String GROUP_MEMBERSHIP_CACHE_NAME = "crowdGroupMembership"; private static final String USER_MEMBERSHIP_CACHE_NAME = "crowdUserMembership"; private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private static final ScheduledExecutorService crowdGroupSync = Executors.newSingleThreadScheduledExecutor(); private static final CrowdManager manager = CrowdManager.getInstance(); private static List<String> groups = new ArrayList<>(); private final XMPPServer server = XMPPServer.getInstance(); static { String propertyValue = JiveGlobals.getProperty(JIVE_CROWD_GROUPS_CACHE_TTL_SECS); int ttl = (propertyValue == null || propertyValue.trim().length() == 0) ? CACHE_TTL : Integer.parseInt(propertyValue); crowdGroupSync.scheduleAtFixedRate(new GroupSynch(), 0, ttl, TimeUnit.SECONDS); JiveGlobals.setProperty(JIVE_CROWD_GROUPS_CACHE_TTL_SECS, String.valueOf(ttl)); } public CrowdGroupProvider() { String propertyValue = JiveGlobals.getProperty(JIVE_CROWD_GROUPS_CACHE_TTL_SECS); int ttl = (propertyValue == null || propertyValue.trim().length() == 0) ? CACHE_TTL : Integer.parseInt(propertyValue); Cache<String, Collection<JID>> groupMembershipCache = CacheFactory.createLocalCache(GROUP_MEMBERSHIP_CACHE_NAME); groupMembershipCache.setMaxCacheSize(-1); groupMembershipCache.setMaxLifetime(ttl * 1000); // msecs instead of sec - see Cache API Cache<JID, Collection<String>> userMembershipCache = CacheFactory.createLocalCache(USER_MEMBERSHIP_CACHE_NAME); userMembershipCache.setMaxCacheSize(-1); userMembershipCache.setMaxLifetime(ttl * 1000); // msecs instead of sec - see Cache API Cache<String, org.jivesoftware.openfire.crowd.jaxb.Group> groupCache = CacheFactory.createLocalCache(GROUP_CACHE_NAME); groupCache.setMaxCacheSize(-1); groupCache.setMaxLifetime(ttl * 1000); // msecs instead of sec - see Cache API } @Override public Group getGroup(String name) throws GroupNotFoundException { try { Cache<String, org.jivesoftware.openfire.crowd.jaxb.Group> groupCache = CacheFactory.createLocalCache(GROUP_CACHE_NAME); org.jivesoftware.openfire.crowd.jaxb.Group group = groupCache.get(name); if (group == null) { group = manager.getGroup(name); groupCache.put(name, group); } Collection<JID> members = getGroupMembers(name); Collection<JID> admins = Collections.emptyList(); return new Group(name, group.description, members, admins); } catch (RemoteException re) { LOG.error("Failure to load group:" + String.valueOf(name), re); throw new GroupNotFoundException(re); } } private Collection<JID> getGroupMembers(String groupName) { Cache<String, Collection<JID>> groupMembershipCache = CacheFactory.createLocalCache(GROUP_MEMBERSHIP_CACHE_NAME); Collection<JID> members = groupMembershipCache.get(groupName); if (members != null) { return members; } try { List<String> users = manager.getGroupMembers(groupName); Collection<JID> results = new ArrayList<>(); for (String username : users) { results.add(server.createJID(username, null)); } groupMembershipCache.put(groupName, results); return results; } catch (RemoteException re) { LOG.error("Failure to get the members of crowd group:" + String.valueOf(groupName), re); } groupMembershipCache.put(groupName, new ArrayList<JID>()); return Collections.emptyList(); } @Override public Collection<String> getGroupNames(JID user) { Cache<JID, Collection<String>> userMembershipCache = CacheFactory.createCache(USER_MEMBERSHIP_CACHE_NAME); Collection<String> groups = userMembershipCache.get(user); if (groups != null) { return groups; } try { groups = manager.getUserGroups(user.getNode()); userMembershipCache.put(user, groups); return groups; } catch (RemoteException re) { LOG.error("Failure to load the groups of user:" + String.valueOf(user), re); } userMembershipCache.put(user, new ArrayList<String>()); return Collections.emptyList(); } @Override public int getGroupCount() { lock.readLock().lock(); try { return groups.size(); } finally { lock.readLock().unlock(); } } @Override public Collection<String> getGroupNames() { lock.readLock().lock(); try { return groups; } finally { lock.readLock().unlock(); } } @Override public Collection<String> getGroupNames(int startIndex, int numResults) { lock.readLock().lock(); try { Collection<String> results = new ArrayList<>(numResults); for (int i = 0, j = startIndex; i < numResults && j < groups.size(); ++i, ++j) { results.add(groups.get(j)); } return results; } finally { lock.readLock().unlock(); } } @Override public Collection<String> search(String query) { lock.readLock().lock(); try { ArrayList<String> results = new ArrayList<>(); if (query != null && query.trim().length() > 0) { if (query.endsWith("*")) { query = query.substring(0, query.length() - 1); } if (query.startsWith("*")) { query = query.substring(1); } query = query.toLowerCase(); for (String groupName : groups) { if (groupName.toLowerCase().contains(query)) { results.add(groupName); } } } return results; } finally { lock.readLock().unlock(); } } @Override public Collection<String> search(String query, int startIndex, int numResults) { lock.readLock().lock(); try { ArrayList<String> foundGroups = (ArrayList<String>) search(query); Collection<String> results = new ArrayList<>(); for (int i = 0, j = startIndex; i < numResults && j < foundGroups.size(); ++i, ++j) { results.add(foundGroups.get(j)); } return results; } finally { lock.readLock().unlock(); } } /** * Modifying group not implemented - read-only for now */ @Override public boolean isReadOnly() { return true; } @Override public boolean isSearchSupported() { return true; } /** * @see org.jivesoftware.openfire.group.AbstractGroupProvider#search(java.lang.String, java.lang.String) */ // TODO search on group attributes in Crowd @Override public Collection<String> search(String key, String value) { LOG.info("Search groups on attibutes not implemented yet"); return Collections.emptyList(); } static class GroupSynch implements Runnable { @Override public void run() { LOG.info("running synch with crowd..."); CrowdManager manager = null; try { manager = CrowdManager.getInstance(); } catch (Exception e) { LOG.error("Failure to load the Crowd manager", e); return; } List<String> allGroups = null; try { allGroups = manager.getAllGroupNames(); } catch (RemoteException re) { LOG.error("Failure to fetch all crowd groups name", re); return; } if (allGroups != null && allGroups.size() > 0) { CrowdGroupProvider.lock.writeLock().lock(); try { CrowdGroupProvider.groups = allGroups; } finally { CrowdGroupProvider.lock.writeLock().unlock(); } } LOG.info("crowd synch done, returned " + allGroups.size() + " groups"); } } }