From 30d203e7581fd5c48a39989004d7dfab55b7f358 Mon Sep 17 00:00:00 2001 From: Cristi Ciuc Starasciuc Date: Wed, 21 May 2014 17:48:38 +0300 Subject: [PATCH] XX-11117 move openfire configuration to mongo --- sipXopenfire/.classpath | 4 +- sipXopenfire/Makefile.am | 3 +- sipXopenfire/common.am | 5 +- sipXopenfire/configure.ac | 3 +- sipXopenfire/mongo-lib/Makefile.am | 39 + sipXopenfire/mongo-lib/src/Makefile.am | 25 + .../connection/MongoConnMgrWrapper.java | 122 ++ .../openfire/connection/MongoMetaData.java | 1069 +++++++++++++++++ .../sipfoundry/openfire/group/MongoGroup.java | 38 + .../openfire/group/MongoGroupProvider.java | 183 +++ .../openfire/provider/BaseMongoProvider.java | 50 + .../openfire/provider/CacheHolder.java | 131 ++ .../openfire/provider/MongoAuthProvider.java | 106 ++ .../provider/MongoConnectivityProvider.java | 17 + .../MongoExternalComponentProvider.java | 111 ++ .../MongoGroupPropertiesProvider.java | 182 +++ .../provider/MongoGroupProviderAlt.java | 314 +++++ .../provider/MongoLockOutProvider.java | 123 ++ .../provider/MongoOfflineMessageProvider.java | 217 ++++ .../provider/MongoPresenceProvider.java | 77 ++ .../provider/MongoPrivacyListProvider.java | 191 +++ .../provider/MongoPrivateStorageProvider.java | 115 ++ .../provider/MongoPubSubProvider.java | 947 +++++++++++++++ .../provider/MongoRemoteServerProvider.java | 98 ++ .../provider/MongoRosterProvider.java | 158 +++ .../provider/MongoSecurityAuditProvider.java | 134 +++ .../openfire/provider/MongoUIDProvider.java | 54 + .../provider/MongoUserPropertiesProvider.java | 92 ++ .../provider/MongoUserProviderAlt.java | 261 ++++ .../openfire/provider/MongoVCardProvider.java | 434 +++++++ .../openfire/provider/package-info.java | 7 + .../sipfoundry/openfire/user/MongoUser.java | 37 + .../openfire/user/MongoUserProvider.java | 187 +++ .../vcard/ContactInfoHandlerImpl.java | 33 + sipXopenfire/mongo-lib/test/Makefile.am | 6 + sipXopenfire/mongo-lib/test/java/Makefile.am | 34 + .../openfire/provider/BaseMongoTest.java | 64 + .../openfire/provider/GroupProviderTest.java | 112 ++ .../provider/OfflineMessageProviderTest.java | 51 + .../openfire/provider/PubsubProviderTest.java | 52 + .../openfire/provider/UIDProviderTest.java | 54 + .../mongo-lib/test/java/resources/README.TXT | 1 + .../test/java/resources/conf/openfire.xml | 47 + .../test/java/resources/mongo-client.ini | 3 + .../test/java/resources/openfire.properties | 52 + .../resources/resources/security/keystore | Bin 0 -> 1749 bytes .../resources/resources/security/truststore | Bin 0 -> 1703 bytes .../mongo-lib/test/resources/README.TXT | 1 + .../test/resources/conf/openfire.xml | 47 + .../mongo-lib/test/resources/mongo-client.ini | 3 + .../test/resources/openfire.properties | 52 + .../resources/resources/security/keystore | Bin 0 -> 1749 bytes .../resources/resources/security/truststore | Bin 0 -> 1703 bytes sipXopenfire/mongo-sync-plugin/Makefile.am | 31 + sipXopenfire/mongo-sync-plugin/plugin.xml | 19 + .../openfire/plugin/MongoOperation.java | 18 + .../openfire/plugin/MongoSyncPlugin.java | 34 + .../openfire/plugin/QueueManager.java | 78 ++ .../sipfoundry/openfire/plugin/job/Job.java | 7 + .../openfire/plugin/job/JobFactory.java | 224 ++++ .../plugin/job/group/GroupDeleteJob.java | 58 + .../plugin/job/group/GroupShared.java | 22 + .../plugin/job/group/GroupUpdateJob.java | 130 ++ .../plugin/job/user/UserDeleteJob.java | 59 + .../openfire/plugin/job/user/UserShared.java | 53 + .../plugin/job/user/UserUpdateJob.java | 263 ++++ .../plugin/job/vcard/VcardDeleteJob.java | 84 ++ .../plugin/job/vcard/VcardUpdateJob.java | 188 +++ .../plugin/listener/ImdbOplogListener.java | 36 + .../plugin/listener/MongoOplogListener.java | 102 ++ .../listener/ProfilesOplogListener.java | 31 + sipXopenfire/vcard-provider/Makefile.am | 39 - .../vcard/provider/ContactInfoHandlerImp.java | 33 - .../provider/CustomSSLSocketFactory.java | 87 -- .../vcard/provider/RestInterface.java | 560 --------- .../vcard/provider/SipXVCardProvider.java | 408 ------- sipXopenfire/vcard-synchserver/Makefile.am | 2 +- 77 files changed, 7580 insertions(+), 1132 deletions(-) create mode 100644 sipXopenfire/mongo-lib/Makefile.am create mode 100644 sipXopenfire/mongo-lib/src/Makefile.am create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/connection/MongoConnMgrWrapper.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/connection/MongoMetaData.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/group/MongoGroup.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/group/MongoGroupProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/BaseMongoProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/CacheHolder.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoAuthProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoConnectivityProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoExternalComponentProvider.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoGroupPropertiesProvider.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoGroupProviderAlt.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoLockOutProvider.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoOfflineMessageProvider.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPresenceProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPrivacyListProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPrivateStorageProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPubSubProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoRemoteServerProvider.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoRosterProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoSecurityAuditProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUIDProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUserPropertiesProvider.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUserProviderAlt.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoVCardProvider.java create mode 100644 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/package-info.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/user/MongoUser.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/user/MongoUserProvider.java create mode 100755 sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/vcard/ContactInfoHandlerImpl.java create mode 100644 sipXopenfire/mongo-lib/test/Makefile.am create mode 100644 sipXopenfire/mongo-lib/test/java/Makefile.am create mode 100644 sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/BaseMongoTest.java create mode 100644 sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/GroupProviderTest.java create mode 100644 sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/OfflineMessageProviderTest.java create mode 100644 sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/PubsubProviderTest.java create mode 100644 sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/UIDProviderTest.java create mode 100644 sipXopenfire/mongo-lib/test/java/resources/README.TXT create mode 100644 sipXopenfire/mongo-lib/test/java/resources/conf/openfire.xml create mode 100644 sipXopenfire/mongo-lib/test/java/resources/mongo-client.ini create mode 100644 sipXopenfire/mongo-lib/test/java/resources/openfire.properties create mode 100644 sipXopenfire/mongo-lib/test/java/resources/resources/security/keystore create mode 100644 sipXopenfire/mongo-lib/test/java/resources/resources/security/truststore create mode 100644 sipXopenfire/mongo-lib/test/resources/README.TXT create mode 100644 sipXopenfire/mongo-lib/test/resources/conf/openfire.xml create mode 100644 sipXopenfire/mongo-lib/test/resources/mongo-client.ini create mode 100644 sipXopenfire/mongo-lib/test/resources/openfire.properties create mode 100644 sipXopenfire/mongo-lib/test/resources/resources/security/keystore create mode 100644 sipXopenfire/mongo-lib/test/resources/resources/security/truststore create mode 100644 sipXopenfire/mongo-sync-plugin/Makefile.am create mode 100644 sipXopenfire/mongo-sync-plugin/plugin.xml create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/MongoOperation.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/MongoSyncPlugin.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/QueueManager.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/Job.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/JobFactory.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupDeleteJob.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupShared.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupUpdateJob.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserDeleteJob.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserShared.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserUpdateJob.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/vcard/VcardDeleteJob.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/vcard/VcardUpdateJob.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/ImdbOplogListener.java create mode 100755 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/MongoOplogListener.java create mode 100644 sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/ProfilesOplogListener.java delete mode 100644 sipXopenfire/vcard-provider/Makefile.am delete mode 100644 sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/ContactInfoHandlerImp.java delete mode 100644 sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/CustomSSLSocketFactory.java delete mode 100644 sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/RestInterface.java delete mode 100644 sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/SipXVCardProvider.java diff --git a/sipXopenfire/.classpath b/sipXopenfire/.classpath index fce1119b4d..0a918675aa 100644 --- a/sipXopenfire/.classpath +++ b/sipXopenfire/.classpath @@ -2,8 +2,10 @@ + + + - diff --git a/sipXopenfire/Makefile.am b/sipXopenfire/Makefile.am index a79e1df47a..8ab03a1cd4 100644 --- a/sipXopenfire/Makefile.am +++ b/sipXopenfire/Makefile.am @@ -9,9 +9,10 @@ SUBDIRS = \ presence-plugin \ sqa-plugin \ vcard-synchserver \ - vcard-provider \ config-plugin \ ws-plugin \ + mongo-lib \ + mongo-sync-plugin \ etc \ bin diff --git a/sipXopenfire/common.am b/sipXopenfire/common.am index 7500b8977f..eb2dd83936 100644 --- a/sipXopenfire/common.am +++ b/sipXopenfire/common.am @@ -103,4 +103,7 @@ provider_PKGS_RT = \ gnu-crypto sipxvcard_JAVAROOT = $(abspath $(top_builddir)/vcard-provider/classes) -vcardsynchserver_JAVAROOT = $(abspath $(top_builddir)/vcard-synchserver/classes) +vcardsynchserver_JAVAROOT = $(abspath $(top_builddir)/vcard-synchserver) + +mongolib_JAVAROOT = $(abspath $(top_builddir)/mongo-lib/classes) +mongosync_JAVAROOT = $(abspath $(top_builddir)/mongo-sync-plugin) \ No newline at end of file diff --git a/sipXopenfire/configure.ac b/sipXopenfire/configure.ac index 6a22cd4761..475874d003 100644 --- a/sipXopenfire/configure.ac +++ b/sipXopenfire/configure.ac @@ -19,8 +19,9 @@ AC_CONFIG_FILES([ presence-plugin/Makefile sqa-plugin/Makefile vcard-synchserver/Makefile - vcard-provider/Makefile config-plugin/Makefile ws-plugin/Makefile + mongo-lib/Makefile + mongo-sync-plugin/Makefile ]) AC_OUTPUT diff --git a/sipXopenfire/mongo-lib/Makefile.am b/sipXopenfire/mongo-lib/Makefile.am new file mode 100644 index 0000000000..f0f7e530fc --- /dev/null +++ b/sipXopenfire/mongo-lib/Makefile.am @@ -0,0 +1,39 @@ +include $(top_srcdir)/config/utility.am +include $(top_srcdir)/config/java.am +include $(top_srcdir)/common.am + +EXTRA_DIST = \ + $(cfg_SRC) \ + $(test_SRC) + +noinst_DATA = \ + javac-cfg + +cfg_JAR = sipx-ofmongo-lib.jar +jar_DATA = $(cfg_JAR) +jardir = @SIPX_JAVADIR@/sipXopenfire/lib + +cfg_DEPS = \ + $(call JavaDep,@SIPX_JAVADIR@/sipXcommons,$(plugin_PKGS)) \ + @OPENFIRE_HOME@/lib/openfire.jar \ + ${vcardsynchserver_JAVAROOT}/sipx-openfire-vcard-synchserver.jar + +cfg_SRC = \ + $(shell cd $(srcdir); find src -name '*.java') + +$(cfg_JAR) : javac-cfg + jar -cf $@ \ + $(call JarInclude,$(mongolib_JAVAROOT),.) + +test_JAVAROOT = classes.test +test_PKGS = \ + $(openfire_PKGS) \ + junit \ + easymock \ + commons-io \ + sipxcommons + +test_DEPS = \ + $(call JavaDep,@SIPX_JAVADIR@/sipXcommons @SIPX_JAVADIR@/sipXconfig,$(test_PKGS)) + +test_SRC = $(shell cd $(srcdir); find test -name '*.java') diff --git a/sipXopenfire/mongo-lib/src/Makefile.am b/sipXopenfire/mongo-lib/src/Makefile.am new file mode 100644 index 0000000000..4ece607de9 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/Makefile.am @@ -0,0 +1,25 @@ +include $(top_srcdir)/config/utility.am +include $(top_srcdir)/config/java.am +include $(top_srcdir)/common.am + +EXTRA_DIST = \ + $(cfg_SRC) + +noinst_DATA = \ + javac-cfg + +cfg_JAR = sipx-ofmongo-lib.jar +jar_DATA = $(cfg_JAR) +jardir = @SIPX_JAVADIR@/sipXopenfire/lib + +cfg_DEPS = \ + $(call JavaDep,@SIPX_JAVADIR@/sipXcommons,$(plugin_PKGS)) \ + @OPENFIRE_HOME@/lib/openfire.jar \ + @SIPX_JAVADIR@/sipXopenfire/lib/sipx-openfire-vcard-synchserver.jar + +cfg_SRC = \ + $(shell cd $(srcdir); find . -name '*.java') + +$(cfg_JAR) : javac-cfg + jar -cf $@ \ + $(call JarInclude,$(mongolib_JAVAROOT),.) \ No newline at end of file diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/connection/MongoConnMgrWrapper.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/connection/MongoConnMgrWrapper.java new file mode 100755 index 0000000000..dd1882f248 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/connection/MongoConnMgrWrapper.java @@ -0,0 +1,122 @@ +package org.sipfoundry.openfire.connection; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import org.jivesoftware.database.ConnectionProvider; +import org.jivesoftware.openfire.container.Plugin; +import org.jivesoftware.openfire.provider.ConnectionManagerWrapper; + +/** + * Wraps access to mongodb-enabled storage + * + * @see ConnectionManagerWrapper + * @author Alex Mateescu + * + */ +public class MongoConnMgrWrapper implements ConnectionManagerWrapper { + + private static boolean profilingEnabled; + private static ConnectionProvider provider; + private static final Object PROVIDER_LOCK = new Object(); + + /** + * {@inheritDoc}
+ * Newer versions of openuc provide an up-to-date schema. Nothing to do here. + */ + @Override + public boolean checkPluginSchema(Plugin plugin) { + return true; + } + + /** + * {@inheritDoc}
+ */ + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return new MongoMetaData(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getTestQuery(String driver) { + return ""; + } + + /** + * {@inheritDoc} + */ + @Override + public int getTransactionIsolation() { + return Connection.TRANSACTION_NONE; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isEmbeddedDB() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isProfilingEnabled() { + return profilingEnabled; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSetupMode() { + return provider == null; + } + + /** + * {@inheritDoc} + */ + @Override + public void setConnectionProvider(ConnectionProvider provider) { + synchronized (new byte[0]) { + MongoConnMgrWrapper.provider = provider; + } + } + + /** + * {@inheritDoc} + */ + @Override + public ConnectionProvider getConnectionProvider() { + return provider; + } + + /** + * {@inheritDoc} + */ + @Override + public void setProfilingEnabled(boolean enabled) { + synchronized (new byte[0]) { + profilingEnabled = enabled; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void shutdown() { + synchronized (PROVIDER_LOCK) { + if (provider != null) { + provider.destroy(); + provider = null; + } + } + } + +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/connection/MongoMetaData.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/connection/MongoMetaData.java new file mode 100755 index 0000000000..9a662fa4af --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/connection/MongoMetaData.java @@ -0,0 +1,1069 @@ +package org.sipfoundry.openfire.connection; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.RowIdLifetime; +import java.sql.SQLException; + +import org.sipfoundry.commons.mongo.MongoFactory; + +import com.mongodb.Mongo; + +public class MongoMetaData implements DatabaseMetaData { + + @Override + public T unwrap(Class iface) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isWrapperFor(Class< ? > iface) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean allProceduresAreCallable() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean allTablesAreSelectable() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getURL() throws SQLException { + // TODO Auto-generated method stub + return MongoFactory.getConnectionURL(); + } + + @Override + public String getUserName() throws SQLException { + return "N/A"; + } + + @Override + public boolean isReadOnly() throws SQLException { + return false; + } + + @Override + public boolean nullsAreSortedHigh() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean nullsAreSortedLow() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean nullsAreSortedAtStart() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean nullsAreSortedAtEnd() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getDatabaseProductName() throws SQLException { + return "MongoDB"; + } + + @Override + public String getDatabaseProductVersion() throws SQLException { + return getDatabaseMajorVersion() + "." + getDatabaseMinorVersion(); + } + + @Override + public String getDriverName() throws SQLException { + return "MongoDB"; + } + + @Override + public String getDriverVersion() throws SQLException { + return getDriverMajorVersion() + "." + getDriverMinorVersion(); + } + + @Override + public int getDriverMajorVersion() { + return Mongo.getMajorVersion(); + } + + @Override + public int getDriverMinorVersion() { + return Mongo.getMinorVersion(); + } + + @Override + public boolean usesLocalFiles() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean usesLocalFilePerTable() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsMixedCaseIdentifiers() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean storesUpperCaseIdentifiers() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean storesLowerCaseIdentifiers() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean storesMixedCaseIdentifiers() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getIdentifierQuoteString() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getSQLKeywords() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getNumericFunctions() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getStringFunctions() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getSystemFunctions() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getTimeDateFunctions() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getSearchStringEscape() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getExtraNameCharacters() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean supportsAlterTableWithAddColumn() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsAlterTableWithDropColumn() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsColumnAliasing() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean nullPlusNonNullIsNull() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsConvert() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsConvert(int fromType, int toType) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsTableCorrelationNames() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsDifferentTableCorrelationNames() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsExpressionsInOrderBy() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsOrderByUnrelated() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsGroupBy() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsGroupByUnrelated() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsGroupByBeyondSelect() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsLikeEscapeClause() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsMultipleResultSets() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsMultipleTransactions() throws SQLException { + return false; + } + + @Override + public boolean supportsNonNullableColumns() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsMinimumSQLGrammar() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsCoreSQLGrammar() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsExtendedSQLGrammar() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsANSI92EntryLevelSQL() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsANSI92IntermediateSQL() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsANSI92FullSQL() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsIntegrityEnhancementFacility() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsOuterJoins() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsFullOuterJoins() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsLimitedOuterJoins() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getSchemaTerm() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getProcedureTerm() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getCatalogTerm() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean isCatalogAtStart() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public String getCatalogSeparator() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean supportsSchemasInDataManipulation() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsSchemasInProcedureCalls() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsSchemasInTableDefinitions() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsSchemasInIndexDefinitions() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsCatalogsInDataManipulation() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsCatalogsInProcedureCalls() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsCatalogsInTableDefinitions() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsCatalogsInIndexDefinitions() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsPositionedDelete() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsPositionedUpdate() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsSelectForUpdate() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsStoredProcedures() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsSubqueriesInComparisons() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsSubqueriesInExists() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsSubqueriesInIns() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsSubqueriesInQuantifieds() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsCorrelatedSubqueries() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsUnion() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsUnionAll() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsOpenCursorsAcrossCommit() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsOpenCursorsAcrossRollback() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsOpenStatementsAcrossCommit() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsOpenStatementsAcrossRollback() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getMaxBinaryLiteralLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxCharLiteralLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxColumnNameLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxColumnsInGroupBy() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxColumnsInIndex() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxColumnsInOrderBy() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxColumnsInSelect() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxColumnsInTable() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxConnections() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxCursorNameLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxIndexLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxSchemaNameLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxProcedureNameLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxCatalogNameLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxRowSize() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getMaxStatementLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxStatements() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxTableNameLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxTablesInSelect() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getMaxUserNameLength() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getDefaultTransactionIsolation() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean supportsTransactions() throws SQLException { + return false; + } + + @Override + public boolean supportsTransactionIsolationLevel(int level) throws SQLException { + return false; + } + + @Override + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsDataManipulationTransactionsOnly() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean dataDefinitionCausesTransactionCommit() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean dataDefinitionIgnoredInTransactions() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) + throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, + String columnNamePattern) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) + throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getSchemas() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getCatalogs() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getTableTypes() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, + String columnNamePattern) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) + throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) + throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) + throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable, + String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getTypeInfo() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) + throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean supportsResultSetType(int type) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean ownUpdatesAreVisible(int type) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean ownDeletesAreVisible(int type) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean ownInsertsAreVisible(int type) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean othersUpdatesAreVisible(int type) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean othersDeletesAreVisible(int type) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean othersInsertsAreVisible(int type) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean updatesAreDetected(int type) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean deletesAreDetected(int type) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean insertsAreDetected(int type) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsBatchUpdates() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) + throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public Connection getConnection() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean supportsSavepoints() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsNamedParameters() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsMultipleOpenResults() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsGetGeneratedKeys() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) + throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, + String attributeNamePattern) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean supportsResultSetHoldability(int holdability) throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getResultSetHoldability() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getDatabaseMajorVersion() throws SQLException { + return Mongo.getMajorVersion(); + } + + @Override + public int getDatabaseMinorVersion() throws SQLException { + return Mongo.getMinorVersion(); + } + + @Override + public int getJDBCMajorVersion() throws SQLException { + return Mongo.getMajorVersion(); + } + + @Override + public int getJDBCMinorVersion() throws SQLException { + return Mongo.getMinorVersion(); + } + + @Override + public int getSQLStateType() throws SQLException { + // TODO Auto-generated method stub + return 0; + } + + @Override + public boolean locatorsUpdateCopy() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean supportsStatementPooling() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public RowIdLifetime getRowIdLifetime() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean autoCommitFailureClosesAllResultSets() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + @Override + public ResultSet getClientInfoProperties() throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) + throws SQLException { + // TODO Auto-generated method stub + return null; + } + + @Override + public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, + String columnNamePattern) throws SQLException { + // TODO Auto-generated method stub + return null; + } + + // JDK 1.7 method + public boolean generatedKeyAlwaysReturned() throws SQLException { + // TODO Auto-generated method stub + return false; + } + + // JDK 1.7 method + public ResultSet getPseudoColumns(String arg0, String arg1, String arg2, String arg3) throws SQLException { + // TODO Auto-generated method stub + return null; + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/group/MongoGroup.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/group/MongoGroup.java new file mode 100755 index 0000000000..e1f97e1b5b --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/group/MongoGroup.java @@ -0,0 +1,38 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.group; + +import java.util.Collection; + +import org.jivesoftware.openfire.group.DefaultGroupPropertyMap; +import org.jivesoftware.openfire.group.Group; +import org.jivesoftware.util.PersistableMap; +import org.xmpp.packet.JID; + +/** + * Was only used by {@link MongoGroupProvider} + */ +@Deprecated +public class MongoGroup extends Group { + private PersistableMap m_properties; + + public MongoGroup() { + + } + + public MongoGroup(String name, String description, Collection members, Collection administrators) { + super(name, description, members, administrators); + } + + @Override + public PersistableMap getProperties() { + if (m_properties == null) { + m_properties = new DefaultGroupPropertyMap(this); + } + return m_properties; + } + +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/group/MongoGroupProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/group/MongoGroupProvider.java new file mode 100755 index 0000000000..d211c28c7d --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/group/MongoGroupProvider.java @@ -0,0 +1,183 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.group; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jivesoftware.openfire.XMPPServer; +import org.jivesoftware.openfire.group.Group; +import org.jivesoftware.openfire.group.GroupAlreadyExistsException; +import org.jivesoftware.openfire.group.GroupNotFoundException; +import org.jivesoftware.openfire.provider.GroupProvider; +import org.jivesoftware.util.PersistableMap; +import org.sipfoundry.commons.userdb.UserGroup; +import org.sipfoundry.commons.util.UnfortunateLackOfSpringSupportFactory; +import org.xmpp.packet.JID; + +/** + * Deprecated because of performance issues. Use {@link MongoGroupProviderAlt} instead + */ +@Deprecated +public class MongoGroupProvider implements GroupProvider { + private final Map m_groupMapping = new HashMap(); + + @Override + public Group createGroup(String userName) throws UnsupportedOperationException, GroupAlreadyExistsException { + try { + return getGroup(userName); + } catch (GroupNotFoundException ex) { + throw new UnsupportedOperationException(); + } + } + + @Override + public void deleteGroup(String groupName) throws UnsupportedOperationException { + m_groupMapping.values().remove(groupName); + } + + @Override + public Group getGroup(String groupName) throws GroupNotFoundException { + UserGroup userGroup = UnfortunateLackOfSpringSupportFactory.getValidUsers().getImGroup(groupName); + if (userGroup == null) { + throw new GroupNotFoundException("Group with name " + groupName + " not found."); + } + List usernames = UnfortunateLackOfSpringSupportFactory.getValidUsers().getImUsernamesInGroup( + groupName); + String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain(); + List jids = new ArrayList(); + for (String username : usernames) { + jids.add(new JID(username + "@" + domain)); + } + if (userGroup.isImbotEnabled()) { + String myBuddyName = UnfortunateLackOfSpringSupportFactory.getValidUsers().getImBotName(); + if (myBuddyName != null) { + jids.add(new JID(myBuddyName + "@" + domain)); + } + } + Group openfireGroup = new MongoGroup(userGroup.getGroupName(), userGroup.getDescription(), + new ArrayList(), jids); + openfireGroup.getProperties().put("sharedRoster.showInRoster", "onlyGroup"); + openfireGroup.getProperties().put("sharedRoster.displayName", userGroup.getGroupName()); + m_groupMapping.put(userGroup.getSysId(), userGroup.getGroupName()); + return openfireGroup; + } + + @Override + public int getGroupCount() { + Long count = UnfortunateLackOfSpringSupportFactory.getValidUsers().getImGroupCount(); + return Integer.parseInt(Long.toString(count)); + } + + @Override + public Collection getGroupNames() { + return getGroupNames(0, getGroupCount()); + } + + @Override + public Collection getGroupNames(JID user) { + String jid = user.toBareJID(); + jid = jid.substring(0, jid.lastIndexOf("@")); + List names = UnfortunateLackOfSpringSupportFactory.getValidUsers().getImGroupnamesForUser(jid); + return names; + } + + @Override + public Collection getGroupNames(int startIndex, int numResults) { + return UnfortunateLackOfSpringSupportFactory.getValidUsers().getImGroupNames(startIndex, numResults); + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public boolean isSearchSupported() { + return true; + } + + @Override + public Collection search(String query) { + return search(query, 0, getGroupCount()); + } + + @Override + public Collection search(String arg0, int arg1, int arg2) { + return null; + } + + public String getGroupName(String sysId) { + return m_groupMapping.get(sysId); + } + + @Override + public void addMember(String arg0, JID arg1, boolean arg2) throws UnsupportedOperationException { + // groups are read-only + } + + @Override + public void deleteMember(String arg0, JID arg1) throws UnsupportedOperationException { + // groups are read-only + } + + @Override + public void setDescription(String arg0, String arg1) throws GroupNotFoundException { + // groups are read-only + } + + @Override + public void setName(String arg0, String arg1) throws UnsupportedOperationException, GroupAlreadyExistsException { + // groups are read-only + } + + @Override + public void updateMember(String arg0, JID arg1, boolean arg2) throws UnsupportedOperationException { + // groups are read-only + } + + // dummy methods - this class is deprecated, but as long as we keep it + // around, we need it to compile + @Override + public boolean isSharingSupported() { + return false; + } + + @Override + public Collection getSharedGroupNames() { + return null; + } + + @Override + public Collection getSharedGroupNames(JID user) { + return null; + } + + @Override + public Collection getPublicSharedGroupNames() { + return null; + } + + @Override + public Collection getVisibleGroupNames(String userGroup) { + return null; + } + + @Override + public Collection search(String key, String value) { + return null; + } + + @Override + public PersistableMap loadProperties(Group group) { + return null; + } + + // end dummy methods +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/BaseMongoProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/BaseMongoProvider.java new file mode 100644 index 0000000000..4cfb2a83e8 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/BaseMongoProvider.java @@ -0,0 +1,50 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import org.sipfoundry.commons.util.UnfortunateLackOfSpringSupportFactory; + +import com.mongodb.DB; +import com.mongodb.DBCollection; + +/** + * Root class for mongo providers. Provides common methods for collections retrieval. + */ +public abstract class BaseMongoProvider { + /** + * When a provider uses only one collection or has a more frequently used collection, it + * should set its name as default for simplified retrieval. + */ + private String m_defaultCollectionName; + + /** + * Returns the default collection for this class + * + * @return {@link DBCollection} + */ + protected DBCollection getDefaultCollection() { + return getCollection(m_defaultCollectionName); + } + + /** + * Returns the collection having the specified name + * + * @param collectionName Name of the collection to retrieve. + * @return {@link DBCollection} + */ + protected static DBCollection getCollection(String collectionName) { + DB db = UnfortunateLackOfSpringSupportFactory.getOpenfiredb(); + + return db.getCollection(collectionName); + } + + /** + * @param collectionName Name of the default collection. + */ + protected void setDefaultCollectionName(String collectionName) { + m_defaultCollectionName = collectionName; + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/CacheHolder.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/CacheHolder.java new file mode 100644 index 0000000000..0798b85132 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/CacheHolder.java @@ -0,0 +1,131 @@ +package org.sipfoundry.openfire.provider; + +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.log4j.Logger; +import org.jivesoftware.util.cache.Cache; +import org.jivesoftware.util.cache.CacheFactory; + +/** + * Config doesn't use names as primary keys, whereas Openfire does. Thus, an update in config + * equals a remove and a create in Openfire. Since for updates we don't receive the previous name + * of the entity, we need to cache them.
+ * This is more complicated for MUC rooms created for each configured conference, where we can't + * retain the id either. We use two caches in this case.
+ * CacheFactory will use a distributed cache when used in a clustered environment. + */ +public class CacheHolder { + private static Logger logger = Logger.getLogger(CacheHolder.class); + private static final Cache USER_CACHE = CacheFactory.createCache("mongoUser"); + private static final Cache GROUP_CACHE = CacheFactory.createCache("mongoGroup"); + private static final Cache MUC_ROOM_CACHE = CacheFactory.createCache("mongoMucRoom"); + private static final Cache AVATAR_CACHE = CacheFactory.createCache("mongoAvatar"); + private static final Cache UID_CACHE = CacheFactory.createCache("uidToImId"); + + public static void putUser(String id, String name) { + USER_CACHE.put(id, name); + } + + public static String getUserName(String id) { + return USER_CACHE.get(id); + } + + public static void removeUser(String id) { + USER_CACHE.remove(id); + } + + public static void removeUserByName(String name) { + if (name != null) { + String id = null; + for (Map.Entry entry : USER_CACHE.entrySet()) { + if (name.equals(entry.getValue())) { + id = entry.getKey(); + break; + } + } + USER_CACHE.remove(id); + } + } + + public static void putGroup(String id, String name) { + GROUP_CACHE.put(id, name); + } + + public static String getGroupName(String id) { + return GROUP_CACHE.get(id); + } + + public static void removeGroup(String id) { + GROUP_CACHE.remove(id); + } + + public static void removeGroupByName(String name) { + if (name != null) { + Set ids = new HashSet(); + for (Map.Entry entry : GROUP_CACHE.entrySet()) { + if (name.equals(entry.getValue())) { + ids.add(entry.getKey()); + } + } + for (String id : ids) { + GROUP_CACHE.remove(id); + } + } + } + + public static void putRoom(Long id, String name) { + MUC_ROOM_CACHE.put(id, name); + } + + public static String getRoomName(Long id) { + return MUC_ROOM_CACHE.get(id); + } + + public static void removeRoom(Long id) { + MUC_ROOM_CACHE.remove(id); + } + + public static void putAvatar(String id, String userName) { + logger.debug(String.format("Updating cache for %s: get: %s, put %s", id, AVATAR_CACHE.get(id), getImId(userName))); + AVATAR_CACHE.put(id, getImId(userName)); + } + + public static String getImIdByAvatar(String id) { + return AVATAR_CACHE.get(id); + } + + private static String getAvatarByImId(String imId) { + String avatar = null; + for (Entry entry : AVATAR_CACHE.entrySet()) { + if (entry.getValue().equals(imId)) { + avatar = entry.getKey(); + break; + } + } + return avatar; + } + + public static void removeAvatarByImId(String imId) { + String avatar = getAvatarByImId(imId); + logger.debug(String.format("Removing cache for %s: get: %s, put %s", imId, avatar, AVATAR_CACHE.get(avatar))); + if ( avatar != null) { + AVATAR_CACHE.remove(avatar); + } + } + + public static void removeAvatar(String id) { + logger.debug(String.format("Removing cache for %s", id)); + AVATAR_CACHE.remove(id); + } + + public static void putImId(String uid, String imId) { + UID_CACHE.put(uid, imId); + } + + public static String getImId(String uid) { + return UID_CACHE.get(uid); + } +} \ No newline at end of file diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoAuthProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoAuthProvider.java new file mode 100755 index 0000000000..351135a2c7 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoAuthProvider.java @@ -0,0 +1,106 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import org.jivesoftware.openfire.XMPPServer; +import org.jivesoftware.openfire.auth.AuthFactory; +import org.jivesoftware.openfire.auth.ConnectionException; +import org.jivesoftware.openfire.auth.InternalUnauthenticatedException; +import org.jivesoftware.openfire.auth.UnauthorizedException; +import org.jivesoftware.openfire.provider.AuthProvider; +import org.jivesoftware.openfire.user.UserNotFoundException; +import org.sipfoundry.commons.userdb.User; +import org.sipfoundry.commons.util.UnfortunateLackOfSpringSupportFactory; + +public class MongoAuthProvider implements AuthProvider { + public static final String SUPERADMIN = "superadmin"; + private String m_xmppDomain; + + @Override + public void authenticate(String userName, String password) throws UnauthorizedException, ConnectionException, + InternalUnauthenticatedException { + User user = UnfortunateLackOfSpringSupportFactory.getValidUsers().getUserByInsensitiveJid( + getUserName(userName)); + if (user == null) { + throw new UnauthorizedException("no user"); + } + if (!user.getUserName().equals(SUPERADMIN) && !user.isImEnabled()) { + throw new UnauthorizedException("im not enabled for user"); + } + if (!user.getPintoken().equals(password)) { + throw new UnauthorizedException("wrong password"); + } + } + + @Override + public void authenticate(String username, String token, String digest) throws UnauthorizedException, + ConnectionException, InternalUnauthenticatedException { + if (username == null || token == null || digest == null) { + throw new UnauthorizedException(); + } + User user = UnfortunateLackOfSpringSupportFactory.getValidUsers().getUserByInsensitiveJid( + getUserName(username)); + if (user == null) { + throw new UnauthorizedException("no user"); + } + if (!user.isImEnabled()) { + throw new UnauthorizedException("im not enabled for user"); + } + String anticipatedDigest = AuthFactory.createDigest(token, user.getPintoken()); + if (!digest.equalsIgnoreCase(anticipatedDigest)) { + throw new UnauthorizedException("bad digest"); + } + } + + @Override + public String getPassword(String userName) throws UserNotFoundException, UnsupportedOperationException { + throw new UnsupportedOperationException("get password not supported"); + } + + @Override + public boolean isDigestSupported() { + return true; + } + + @Override + public boolean isPlainSupported() { + return true; + } + + @Override + public void setPassword(String arg0, String arg1) throws UserNotFoundException, UnsupportedOperationException { + throw new UnsupportedOperationException("set password not supported"); + } + + @Override + public boolean supportsPasswordRetrieval() { + return false; + } + + private String getDomain() { + if (m_xmppDomain == null) { + m_xmppDomain = XMPPServer.getInstance().getServerInfo().getXMPPDomain(); + } + return m_xmppDomain; + } + + private String getUserName(String username) throws UnauthorizedException { + String newUsername = username.trim().toLowerCase(); + if (newUsername.contains("@")) { + // Check that the specified domain matches the server's domain + int index = newUsername.indexOf("@"); + String domain = newUsername.substring(index + 1); + if (domain.equals(getDomain())) { + newUsername = newUsername.substring(0, index); + } else { + // Unknown domain. Return authentication failed. + throw new UnauthorizedException("unknow domain"); + } + } + return newUsername; + } + +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoConnectivityProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoConnectivityProvider.java new file mode 100644 index 0000000000..d335b3e1a2 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoConnectivityProvider.java @@ -0,0 +1,17 @@ +package org.sipfoundry.openfire.provider; + +import org.jivesoftware.openfire.provider.ConnectivityProvider; +import org.sipfoundry.commons.util.UnfortunateLackOfSpringSupportFactory; + +public class MongoConnectivityProvider implements ConnectivityProvider { + + @Override + public void verifyDataSource() throws IllegalArgumentException { + try { + UnfortunateLackOfSpringSupportFactory.getOpenfiredb(); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoExternalComponentProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoExternalComponentProvider.java new file mode 100644 index 0000000000..ed4c8c38f0 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoExternalComponentProvider.java @@ -0,0 +1,111 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.util.ArrayList; +import java.util.Collection; + +import org.jivesoftware.openfire.component.ExternalComponentConfiguration; +import org.jivesoftware.openfire.component.ExternalComponentConfiguration.Permission; +import org.jivesoftware.openfire.provider.ExternalComponentProvider; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +public class MongoExternalComponentProvider extends BaseMongoProvider implements ExternalComponentProvider { + private static final String COLLECTION_NAME = "ofExtComponentConf"; + + public MongoExternalComponentProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection extCompCollection = getDefaultCollection(); + + DBObject index = new BasicDBObject(); + index.put("subdomain", 1); + + extCompCollection.ensureIndex(index); + } + + @Override + public ExternalComponentConfiguration getConfiguration(String subdomain, boolean useWildcard) { + ExternalComponentConfiguration conf = null; + DBCollection extCompCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + + query.put("subdomain", subdomain); + query.put("wildcard", false); + + DBObject confObj = extCompCollection.findOne(query); + + if (confObj != null) { + String secret = (String) confObj.get("secret"); + String permission = (String) confObj.get("permission"); + conf = new ExternalComponentConfiguration(subdomain, false, Permission.valueOf(permission), secret); + } else if (useWildcard) { + query = new BasicDBObject(); + + query.put("subdomain", "/" + subdomain + "/"); // mongodb regex + query.put("wildcard", false); + + confObj = extCompCollection.findOne(query); + if (confObj != null) { + String secret = (String) confObj.get("secret"); + String permission = (String) confObj.get("permission"); + + conf = new ExternalComponentConfiguration(subdomain, false, Permission.valueOf(permission), secret); + } + } + + return conf; + } + + @Override + public void addConfiguration(ExternalComponentConfiguration configuration) { + DBCollection extCompCollection = getDefaultCollection(); + + DBObject toInsert = new BasicDBObject(); + + toInsert.put("subdomain", configuration.getSubdomain()); + toInsert.put("wildcard", configuration.isWildcard()); + toInsert.put("permission", configuration.getPermission().toString()); + toInsert.put("secret", configuration.getSecret()); + + extCompCollection.insert(toInsert); + } + + @Override + public Collection getConfigurations(Permission permission) { + Collection confs = new ArrayList(); + DBCollection extCompCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + + query.put("permission", permission != null ? permission.toString() : null); + + for (DBObject confObj : extCompCollection.find(query)) { + String subdomain = (String) confObj.get("subdomain"); + String secret = (String) confObj.get("secret"); + Boolean wildcard = (Boolean) confObj.get("wildcard"); + + confs.add(new ExternalComponentConfiguration(subdomain, wildcard, permission, secret)); + } + + return confs; + } + + @Override + public void deleteConfigurationFromDB(ExternalComponentConfiguration configuration) { + DBCollection extCompCollection = getDefaultCollection(); + + DBObject toDelete = new BasicDBObject(); + + toDelete.put("subdomain", configuration.getSubdomain()); + toDelete.put("wildcard", configuration.isWildcard()); + + extCompCollection.remove(toDelete); + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoGroupPropertiesProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoGroupPropertiesProvider.java new file mode 100755 index 0000000000..eef465d9ec --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoGroupPropertiesProvider.java @@ -0,0 +1,182 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +import org.jivesoftware.openfire.group.DefaultGroupPropertyMap; +import org.jivesoftware.openfire.group.Group; +import org.jivesoftware.openfire.group.GroupAlreadyExistsException; +import org.jivesoftware.openfire.provider.GroupPropertiesProvider; +import org.jivesoftware.util.PersistableMap; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +public class MongoGroupPropertiesProvider extends BaseMongoProvider implements GroupPropertiesProvider { + private static final String COLLECTION_NAME = "ofGroupProp"; + + // GPN = group property name + private static final String GPN_DISPLAY_NAME = "sharedRoster.displayName"; + private static final String GPN_SHOW_IN_ROSTER = "sharedRoster.showInRoster"; + + public MongoGroupPropertiesProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection grpPropsCollection = getDefaultCollection(); + DBObject index = new BasicDBObject(); + + index.put("groupname", 1); + index.put("name", 1); + grpPropsCollection.ensureIndex(index); + } + + /** + * {@inheritDoc} + */ + @Override + public PersistableMap loadProperties(Group group) { + PersistableMap grpProps = new DefaultGroupPropertyMap(group); + + DBCollection grpPropsCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + + query.put("groupname", group.getName()); + + for (DBObject grpPropsObj : grpPropsCollection.find(query)) { + String propName = (String) grpPropsObj.get("name"); + String propValue = (String) grpPropsObj.get("propValue"); + grpProps.put(propName, propValue, false); + } + + // if missing, add properties without persisting and handle persistence separately + // trying to add with persistence will trigger in infinite recursion + if (grpProps.get(GPN_DISPLAY_NAME) == null) { + grpProps.put(GPN_DISPLAY_NAME, group.getName(), false); + insertProperty(group.getName(), GPN_DISPLAY_NAME, group.getName()); + } + if (grpProps.get(GPN_SHOW_IN_ROSTER) == null) { + grpProps.put(GPN_SHOW_IN_ROSTER, "onlyGroup", false); + insertProperty(group.getName(), GPN_SHOW_IN_ROSTER, "onlyGroup"); + } + + return grpProps; + } + + /** + * {@inheritDoc} + */ + @Override + public void insertProperty(String groupName, String propName, String propValue) { + DBCollection grpPropsCollection = getDefaultCollection(); + + DBObject toInsert = new BasicDBObject(); + + toInsert.put("groupname", groupName); + toInsert.put("name", propName); + toInsert.put("propValue", propValue); + + grpPropsCollection.insert(toInsert); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateProperty(String groupName, String propName, String propValue) { + // nothing to do + } + + /** + * {@inheritDoc} + */ + @Override + public void deleteProperty(String groupName, String propName) { + // nothing to do + } + + /** + * {@inheritDoc} + */ + @Override + public boolean deleteGroupProperties(String groupName) { + // nothing to do + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public Set getSharedGroupsNames() { + throw new UnsupportedOperationException("Not implemented"); + } + + /** + * {@inheritDoc} + */ + @Override + public Collection getPublicSharedGroupNames() { + return search(GPN_SHOW_IN_ROSTER, "everybody"); + } + + /** + * {@inheritDoc} + */ + @Override + public Collection getVisibleGroupNames(String userGroup) { + DBCollection grpPropsCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + + query.put("name", "sharedRoster.groupList"); + query.put("propValue", Pattern.compile("\\.*" + userGroup + "\\.*")); + + Set names = new HashSet(); + + for (DBObject propObj : grpPropsCollection.find(query)) { + names.add((String) propObj.get("groupName")); + } + + return names; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean setName(String oldName, String newName) throws GroupAlreadyExistsException { + // nothing to do + + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public Collection search(String key, String value) { + DBCollection grpPropsCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + + query.put("name", key); + query.put("propValue", value); + + Set names = new HashSet(); + + for (DBObject propObj : grpPropsCollection.find(query)) { + names.add((String) propObj.get("groupName")); + } + + return names; + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoGroupProviderAlt.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoGroupProviderAlt.java new file mode 100755 index 0000000000..25b5e5c64c --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoGroupProviderAlt.java @@ -0,0 +1,314 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import static org.sipfoundry.commons.mongo.MongoConstants.DESCR; +import static org.sipfoundry.commons.mongo.MongoConstants.GROUPS; +import static org.sipfoundry.commons.mongo.MongoConstants.IM_ENABLED; +import static org.sipfoundry.commons.mongo.MongoConstants.IM_GROUP; +import static org.sipfoundry.commons.mongo.MongoConstants.IM_ID; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; +import org.jivesoftware.openfire.XMPPServer; +import org.jivesoftware.openfire.group.AbstractGroupProvider; +import org.jivesoftware.openfire.group.Group; +import org.jivesoftware.openfire.group.GroupAlreadyExistsException; +import org.jivesoftware.openfire.group.GroupNotFoundException; +import org.sipfoundry.commons.util.UnfortunateLackOfSpringSupportFactory; +import org.xmpp.packet.JID; + +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +public class MongoGroupProviderAlt extends AbstractGroupProvider { + private static final Logger log = Logger.getLogger(MongoGroupProviderAlt.class); + + private static final String COLLECTION_NAME = "entity"; + + @Override + public Group createGroup(String name) { + Group g = null; + + try { + g = getGroup(name); + } catch (GroupNotFoundException e) { + log.error("Group not find while trying to simulate a create [" + name + "]"); + } + + return g; + } + + @Override + public void deleteGroup(String name) { + CacheHolder.removeGroupByName(name); + // groups are read-only, no further action needed + } + + @Override + public Group getGroup(String name) throws GroupNotFoundException { + log.debug("Getting group: " + name); + + DBCollection groupsCollection = getCollection(); + + DBObject query = new BasicDBObject(); + + query.put("ent", "group"); + query.put("uid", name); + query.put("imgrp", "1"); + + DBObject groupObj = groupsCollection.findOne(query); + + if (groupObj == null) { + throw new GroupNotFoundException(name); + } + log.debug("Found group: " + groupObj); + + String id = (String) groupObj.get("_id"); + + CacheHolder.putGroup(id, name); + + return fromDBObject(groupObj); + } + + @Override + public void setName(String oldName, String newName) throws GroupAlreadyExistsException { + // groups are read-only + } + + @Override + public void setDescription(String name, String description) throws GroupNotFoundException { + // groups are read-only + } + + @Override + public int getGroupCount() { + DBCollection groupsCollection = getCollection(); + + DBObject query = new BasicDBObject(); + + query.put("ent", "group"); + query.put("imgrp", "1"); + + return (int) groupsCollection.count(query); + } + + @Override + public Collection getGroupNames() { + return getGroupNames(0, Integer.MAX_VALUE); + } + + @Override + public Collection getGroupNames(int startIndex, int numResults) { + List groupNames = new ArrayList(); + DBCollection groupsCollection = getCollection(); + + DBObject query = new BasicDBObject(); + + query.put("ent", "group"); + query.put("imgrp", "1"); + + for (DBObject groupObj : groupsCollection.find(query).skip(startIndex).limit(numResults)) { + groupNames.add((String) groupObj.get("uid")); + } + + return groupNames; + } + + @Override + public Collection getGroupNames(JID user) { + log.debug("Getting group names for " + user.toBareJID()); + + List names = new ArrayList(); + String userName = XMPPServer.getInstance().isLocal(user) ? user.getNode() : user.toString(); + DBObject query = new BasicDBObject(); + + DBCollection usersCollection = getUsersCollection(); + + query.put(IM_ENABLED, true); + query.put(IM_ID, userName); + query.put("ent", "user"); + + DBObject userObj = usersCollection.findOne(query); + if (userObj != null) { + BasicDBList groupList = (BasicDBList) userObj.get(GROUPS); + for (Object o : groupList) { + names.add((String) o); + } + } + + // if we didn't find any groups, maybe it's the imbot + if (names.isEmpty() && isImBot(userName)) { + DBCollection groupsCollection = getCollection(); + DBObject groupsQuery = new BasicDBObject(); + + groupsQuery.put(IM_GROUP, "1"); + groupsQuery.put("imbot", "1"); + groupsQuery.put("ent", "group"); + + for (DBObject grpObj : groupsCollection.find(groupsQuery)) { + names.add((String) grpObj.get("uid")); + } + + } + log.debug("Returning: " + names); + + return names; + } + + @Override + public void addMember(String groupName, JID user, boolean administrator) { + // groups are read-only + } + + @Override + public void updateMember(String groupName, JID user, boolean administrator) { + // groups are read-only + } + + @Override + public void deleteMember(String groupName, JID user) { + // groups are read-only + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public Collection search(String query) { + return search(query, 0, Integer.MAX_VALUE); + } + + @Override + public Collection search(String query, int startIndex, int numResults) { + Set groupNames = new HashSet(); + DBCollection groupsCollection = getCollection(); + + String asPattern = query.replaceAll("\\*", "\\.*"); + Pattern p = Pattern.compile(asPattern); + DBObject queryObj = new BasicDBObject(); + + queryObj.put("ent", "group"); + queryObj.put("uid", p); + + for (DBObject groupObj : groupsCollection.find(queryObj).skip(startIndex).limit(numResults)) { + groupNames.add((String) groupObj.get("uid")); + } + + return groupNames; + } + + @Override + public Collection search(String key, String value) { + Set groupNames = new HashSet(); + DBCollection groupsCollection = getCollection(); + + DBObject queryObj = new BasicDBObject(); + + queryObj.put("ent", "group"); + queryObj.put(key, value); + + for (DBObject groupObj : groupsCollection.find(queryObj)) { + groupNames.add((String) groupObj.get("uid")); + } + + return groupNames; + } + + @Override + public boolean isSearchSupported() { + return true; + } + + @Override + public boolean isSharingSupported() { + return true; + } + + private static Group fromDBObject(DBObject groupObj) { + Group g = null; + + if (groupObj != null) { + String name = (String) groupObj.get("uid"); + String description = (String) groupObj.get(DESCR); + boolean imBotEnabled = "1".equals(groupObj.get("imbot")); + Collection members = getMembers(name); + Collection administrators = Collections.emptyList(); + + if (imBotEnabled) { + DBObject imBotObj = getImBot(null); + if (imBotObj != null) { + String imBotName = (String) imBotObj.get(IM_ID); + String xmppDomain = XMPPServer.getInstance().getServerInfo().getXMPPDomain(); + JID imBotJid = new JID(imBotName, xmppDomain, null, false); + log.debug(String.format("ImBot JID: %s, domain: ", imBotJid.toBareJID(), imBotJid.getDomain())); + members.add(imBotJid); + } + } + + g = new Group(name, description, members, administrators); + } + + return g; + } + + private static Collection getMembers(String groupName) { + Collection members = new ArrayList(); + DBCollection usersCollection = getCollection(); + + DBObject query = new BasicDBObject(); + query.put("ent", "user"); + query.put(IM_ENABLED, true); + query.put("gr", groupName); + String domain = XMPPServer.getInstance().getServerInfo().getXMPPDomain(); + + for (DBObject userObj : usersCollection.find(query)) { + members.add(new JID((String) userObj.get(IM_ID) + "@" + domain)); + } + + return members; + } + + private static DBObject getImBot(String name) { + DBCollection usersCollection = getUsersCollection(); + DBObject query = new BasicDBObject(); + + query.put("ent", "imbotsettings"); + query.put(IM_ENABLED, true); + if (name != null) { + query.put(IM_ID, name); + } + + return usersCollection.findOne(query); + } + + private static boolean isImBot(String name) { + return getImBot(name) != null; + } + + private static DBCollection getUsersCollection() { + // both users and groups are stored together + return getCollection(); + } + + private static DBCollection getCollection() { + DB db = UnfortunateLackOfSpringSupportFactory.getImdb(); + + return db.getCollection(COLLECTION_NAME); + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoLockOutProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoLockOutProvider.java new file mode 100755 index 0000000000..60508ab676 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoLockOutProvider.java @@ -0,0 +1,123 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.util.Date; + +import org.bson.BasicBSONObject; +import org.jivesoftware.openfire.lockout.LockOutFlag; +import org.jivesoftware.openfire.provider.LockOutProvider; +import org.jivesoftware.util.StringUtils; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import com.mongodb.DBObject; + +public class MongoLockOutProvider extends BaseMongoProvider implements LockOutProvider { + private static final String COLLECTION_NAME = "ofUserFlag"; + + public MongoLockOutProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection lockoutCollection = getDefaultCollection(); + DBObject index = new BasicDBObject(); + + index.put("username", 1); + index.put("name", 1); + lockoutCollection.ensureIndex(index); + } + + /** + * {@inheritDoc} + */ + @Override + public LockOutFlag getDisabledStatus(String username) { + LockOutFlag flag = null; + DBCollection lockoutCollection = getDefaultCollection(); + DBObject query = new BasicDBObject(); + + query.put("username", username); + // name of the lockout flag + query.put("name", "lockout"); + + DBCursor cursor = lockoutCollection.find(query); + + if (cursor.hasNext()) { + DBObject line = cursor.next(); + BasicBSONObject obj = new BasicBSONObject(); + obj.putAll(line); + long start = obj.getLong("startTime"); + long end = obj.getLong("endTime"); + + flag = new LockOutFlag(username, new Date(start), new Date(end)); + } + + cursor.close(); + + return flag; + } + + /** + * {@inheritDoc} + */ + @Override + public void setDisabledStatus(LockOutFlag flag) { + DBCollection lockoutCollection = getDefaultCollection(); + DBObject toInsert = new BasicDBObject(); + + toInsert.put("username", flag.getUsername()); + toInsert.put("name", "lockout"); + toInsert.put("startTime", StringUtils.dateToMillis(flag.getStartTime())); + toInsert.put("endTime", StringUtils.dateToMillis(flag.getEndTime())); + + lockoutCollection.insert(toInsert); + } + + /** + * {@inheritDoc} + */ + @Override + public void unsetDisabledStatus(String username) { + DBCollection lockoutCollection = getDefaultCollection(); + DBObject toDelete = new BasicDBObject(); + + toDelete.put("username", username); + + lockoutCollection.remove(toDelete); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isReadOnly() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isDelayedStartSupported() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isTimeoutSupported() { + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean shouldNotBeCached() { + return false; + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoOfflineMessageProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoOfflineMessageProvider.java new file mode 100755 index 0000000000..ff988dcf70 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoOfflineMessageProvider.java @@ -0,0 +1,217 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.jivesoftware.openfire.OfflineMessage; +import org.jivesoftware.openfire.XMPPServer; +import org.jivesoftware.openfire.provider.OfflineMessageProvider; +import org.jivesoftware.util.FastDateFormat; +import org.jivesoftware.util.StringUtils; +import org.jivesoftware.util.XMPPDateTimeFormat; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; +import com.mongodb.WriteResult; + +public class MongoOfflineMessageProvider extends BaseMongoProvider implements OfflineMessageProvider { + private static final String COLLECTION_NAME = "ofOffline"; + + private static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance( + XMPPDateTimeFormat.XMPP_DATETIME_FORMAT, TimeZone.getTimeZone("UTC")); + private static final FastDateFormat OLD_DATE_FORMAT = FastDateFormat.getInstance( + XMPPDateTimeFormat.XMPP_DELAY_DATETIME_FORMAT, TimeZone.getTimeZone("UTC")); + + /** + * Pattern to use for detecting invalid XML characters. Invalid XML characters will be removed + * from the stored offline messages. + */ + private static final Pattern PATTERN = Pattern.compile("&\\#[\\d]+;"); + + public MongoOfflineMessageProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection offlineCollection = getDefaultCollection(); + + offlineCollection.ensureIndex("username"); + + DBObject index = new BasicDBObject(); + + index.put("username", 1); + index.put("messageID", 1); + offlineCollection.ensureIndex(index); + } + + /** + * {@inheritDoc} + */ + @Override + public void addMessage(String username, long messageID, String msgXML) { + DBCollection offlineCollection = getDefaultCollection(); + + DBObject toInsert = new BasicDBObject(); + + toInsert.put("username", username); + toInsert.put("messageID", messageID); + toInsert.put("creationDate", StringUtils.dateToMillis(new java.util.Date())); + toInsert.put("messageSize", msgXML.length()); + toInsert.put("stanza", msgXML); + + offlineCollection.insert(toInsert); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean deleteMessage(String username, Date creationDate) { + DBCollection offlineCollection = getDefaultCollection(); + + DBObject toDelete = new BasicDBObject(); + toDelete.put("username", username); + toDelete.put("creationDate", StringUtils.dateToMillis(creationDate)); + WriteResult result = offlineCollection.remove(toDelete); + + return !org.apache.commons.lang.StringUtils.isEmpty(result.getError()); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean deleteMessages(String username) { + DBCollection offlineCollection = getDefaultCollection(); + + DBObject toDelete = new BasicDBObject(); + toDelete.put("username", username); + WriteResult result = offlineCollection.remove(toDelete); + + return !org.apache.commons.lang.StringUtils.isEmpty(result.getError()); + } + + /** + * {@inheritDoc} + */ + @Override + public OfflineMessage getMessage(String username, Date creationDate, SAXReader xmlReader) { + DBCollection offlineCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + query.put("username", username); + query.put("creationDate", StringUtils.dateToMillis(creationDate)); + DBObject keys = new BasicDBObject(); + keys.put("stanza", 1); + + DBObject dbObj = offlineCollection.findOne(query); + String msgXml = (String) dbObj.get("stanza"); + + return fromString(msgXml, creationDate, xmlReader); + } + + /** + * {@inheritDoc} + */ + @Override + public Collection getMessages(String username, boolean delete, SAXReader xmlReader) { + List messages = new ArrayList(); + DBCollection offlineCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + query.put("username", username); + DBObject keys = new BasicDBObject(); + keys.put("stanza", 1); + keys.put("creationDate", 1); + + for (DBObject dbObj : offlineCollection.find(query, keys)) { + String msgXml = (String) dbObj.get("stanza"); + Date creationDate = new Date(Long.parseLong((String) dbObj.get("creationDate"))); + messages.add(fromString(msgXml, creationDate, xmlReader)); + } + + return messages; + } + + /** + * {@inheritDoc} + */ + @Override + public int getSize() { + return getSize(new BasicDBObject()); + } + + /** + * {@inheritDoc} + */ + @Override + public int getSize(String username) { + DBObject query = new BasicDBObject(); + + query.put("username", username); + + return getSize(query); + } + + private int getSize(DBObject toFind) { + DBCollection offlineCollection = getDefaultCollection(); + + int totalSize = 0; + BasicDBObject keys = new BasicDBObject(); + keys.put("messageSize", 1); + + for (DBObject dbObj : offlineCollection.find(toFind, keys)) { + int messageSize = (Integer) dbObj.get("messageSize"); + totalSize += messageSize; + } + + return totalSize; + } + + private static OfflineMessage fromString(String msgXml, Date creationDate, SAXReader xmlReader) { + OfflineMessage message = null; + try { + try { + message = new OfflineMessage(creationDate, xmlReader.read(new StringReader(msgXml)).getRootElement()); + } catch (DocumentException e) { + // Try again after removing invalid XML chars (e.g. ) + Matcher matcher = PATTERN.matcher(msgXml); + if (matcher.find()) { + String invalidRemoved = matcher.replaceAll(""); + Document doc = xmlReader.read(new StringReader(invalidRemoved)); + message = new OfflineMessage(creationDate, doc.getRootElement()); + } + } + + if (message != null) { + // Add a delayed delivery (XEP-0203) element to the message. + Element delay = message.addChildElement("delay", "urn:xmpp:delay"); + delay.addAttribute("from", XMPPServer.getInstance().getServerInfo().getXMPPDomain()); + delay.addAttribute("stamp", DATE_FORMAT.format(creationDate)); + // Add a legacy delayed delivery (XEP-0091) element to the + // message. XEP is obsolete and support should be dropped in + // future. + delay = message.addChildElement("x", "jabber:x:delay"); + delay.addAttribute("from", XMPPServer.getInstance().getServerInfo().getXMPPDomain()); + delay.addAttribute("stamp", OLD_DATE_FORMAT.format(creationDate)); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return message; + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPresenceProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPresenceProvider.java new file mode 100755 index 0000000000..9109375f96 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPresenceProvider.java @@ -0,0 +1,77 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.util.Date; + +import org.jivesoftware.openfire.provider.PresenceProvider; +import org.jivesoftware.util.StringUtils; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import com.mongodb.DBObject; +import com.mongodb.WriteConcern; + +public class MongoPresenceProvider extends BaseMongoProvider implements PresenceProvider { + private static final String COLLECTION_NAME = "ofPresence"; + + public MongoPresenceProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection presenceCollection = getDefaultCollection(); + + presenceCollection.ensureIndex("username"); + } + + @Override + public void deleteOfflinePresenceFromDB(String username) { + DBCollection presenceCollection = getDefaultCollection(); + DBObject toRemove = new BasicDBObject(); + toRemove.put("username", username); + + presenceCollection.remove(toRemove); + } + + @Override + public void insertOfflinePresenceIntoDB(String username, String offlinePresence, Date offlinePresenceDate) { + DBCollection presenceCollection = getDefaultCollection(); + DBObject query = new BasicDBObject(); + query.put("username", username); + + DBObject toInsert = new BasicDBObject(); + toInsert.put("username", username); + toInsert.put("offlinePresence", offlinePresence); + String inMillis = StringUtils.dateToMillis(offlinePresenceDate); + toInsert.put("offlinePresenceDate", inMillis); + + presenceCollection.update(query, toInsert, true, false, WriteConcern.NONE); + } + + @Override + public TimePresence loadOfflinePresence(String username) { + TimePresence tp; + DBCollection presenceCollection = getDefaultCollection(); + DBObject toFind = new BasicDBObject(); + toFind.put("username", username); + DBCursor cursor = presenceCollection.find(toFind); + + if (cursor.hasNext()) { + DBObject entry = cursor.next(); + + String lastActivity = (String) entry.get("offlinePresenceDate"); + String presence = (String) entry.get("offlinePresence"); + + tp = new TimePresence(Long.valueOf(lastActivity), presence); + } else { + tp = new TimePresence(NULL_LONG, "NULL"); + } + + cursor.close(); + + return tp; + } + +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPrivacyListProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPrivacyListProvider.java new file mode 100644 index 0000000000..92c55c50fa --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPrivacyListProvider.java @@ -0,0 +1,191 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.apache.log4j.Logger; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.jivesoftware.openfire.privacy.PrivacyList; +import org.jivesoftware.openfire.provider.PrivacyListProvider; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +public class MongoPrivacyListProvider extends BaseMongoProvider implements PrivacyListProvider { + private static final Logger log = Logger.getLogger(MongoPrivacyListProvider.class); + private static final String COLLECTION_NAME = "ofPrivacyList"; + + private static final int POOL_SIZE = 50; + private static final long POOL_TIMEOUT_SECONDS = 30; + + /** + * Pool of SAX Readers. SAXReader is not thread safe so we need to have a pool of readers. + */ + private final BlockingQueue m_xmlReaders = new LinkedBlockingQueue(POOL_SIZE); + + public MongoPrivacyListProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection prvListCollection = getDefaultCollection(); + DBObject index = new BasicDBObject(); + + index.put("username", 1); + prvListCollection.ensureIndex(index); + + for (int i = 0; i < POOL_SIZE; i++) { + SAXReader xmlReader = new SAXReader(); + xmlReader.setEncoding("UTF-8"); + m_xmlReaders.add(xmlReader); + } + + } + + @Override + public Map getPrivacyLists(String username) { + Map privacyLists = new HashMap(); + DBCollection prvListCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + query.put("username", username); + DBObject fields = new BasicDBObject(); + fields.put("name", 1); + fields.put("isDefault", 1); + + for (DBObject dbObj : prvListCollection.find(query, fields)) { + String name = (String) dbObj.get("name"); + Boolean isDefault = (Boolean) dbObj.get("isDefault"); + + privacyLists.put(name, isDefault); + } + + return privacyLists; + } + + @Override + public PrivacyList loadPrivacyList(String username, String listName) { + PrivacyList privacyList = null; + DBCollection prvListCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + query.put("username", username); + query.put("name", listName); + DBObject fields = new BasicDBObject(); + fields.put("list", 1); + fields.put("isDefault", 1); + DBObject grpPropsObj = prvListCollection.findOne(query, fields); + + if (grpPropsObj != null) { + privacyList = buildPrivacyList(username, listName, grpPropsObj); + } + + return privacyList; + } + + @Override + public PrivacyList loadDefaultPrivacyList(String username) { + + DBCollection prvListCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + query.put("username", username); + query.put("isDefault", true); + DBObject fields = new BasicDBObject(); + fields.put("list", 1); + fields.put("name", 1); + + DBObject grpPropsObj = prvListCollection.findOne(query, fields); + PrivacyList privacyList = null; + + if (grpPropsObj != null) { + privacyList = buildPrivacyList(username, null, grpPropsObj); + } + + return privacyList; + } + + @Override + public void createPrivacyList(String username, PrivacyList list) { + DBCollection prvListCollection = getDefaultCollection(); + + DBObject toInsert = new BasicDBObject(); + toInsert.put("username", username); + toInsert.put("name", list.getName()); + toInsert.put("isDefault", list.isDefault()); + toInsert.put("list", list.asElement().asXML()); + + prvListCollection.insert(toInsert); + } + + @Override + public void updatePrivacyList(String username, PrivacyList list) { + DBCollection prvListCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + query.put("username", username); + query.put("name", list.getName()); + + DBObject update = new BasicDBObject(); + update.put("isDefault", list.isDefault()); + update.put("list", list.asElement().asXML()); + + prvListCollection.findAndModify(query, new BasicDBObject("$set", update)); + } + + @Override + public void deletePrivacyList(String username, String listName) { + DBCollection prvListCollection = getDefaultCollection(); + + DBObject toDelete = new BasicDBObject(); + toDelete.put("username", username); + toDelete.put("name", listName); + + prvListCollection.insert(toDelete); + } + + @Override + public void deletePrivacyLists(String username) { + DBCollection prvListCollection = getDefaultCollection(); + + DBObject toDelete = new BasicDBObject(); + toDelete.put("username", username); + + prvListCollection.insert(toDelete); + } + + private PrivacyList buildPrivacyList(String username, String listName, DBObject dbObj) { + PrivacyList privacyList = null; + String list = (String) dbObj.get("list"); + Boolean isDefault = (Boolean) dbObj.get("isDefault"); + if (isDefault == null) { + isDefault = false; + } + + SAXReader xmlReader = null; + try { + // Get a sax reader from the pool + xmlReader = m_xmlReaders.poll(POOL_TIMEOUT_SECONDS, TimeUnit.SECONDS); + Element listElement = xmlReader.read(new StringReader(list)).getRootElement(); + String actualListName = listName != null ? listName : (String) dbObj.get("name"); + log.debug("Creating privacy list for username=" + username + "; actualListName=" + actualListName + "; isDefault=" + isDefault + "; listElement=" + listElement); + privacyList = new PrivacyList(username, actualListName, isDefault, listElement); + } catch (Exception e) { + log.error("Error reading privacy list", e); + } finally { + // Return the sax reader to the pool + if (xmlReader != null) { + m_xmlReaders.add(xmlReader); + } + } + return privacyList; + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPrivateStorageProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPrivateStorageProvider.java new file mode 100644 index 0000000000..409779d963 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPrivateStorageProvider.java @@ -0,0 +1,115 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.jivesoftware.openfire.provider.PrivateStorageProvider; +import org.jivesoftware.openfire.user.User; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +public class MongoPrivateStorageProvider extends BaseMongoProvider implements PrivateStorageProvider { + private static final Logger log = Logger.getLogger(MongoPrivateStorageProvider.class); + + private static final String COLLECTION_NAME = "ofPrivate"; + + public MongoPrivateStorageProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection prvStorageCollection = getDefaultCollection(); + DBObject index = new BasicDBObject(); + + index.put("username", 1); + index.put("namespace", 1); + index.put("name", 1); + + prvStorageCollection.ensureIndex(index); + } + + @Override + public void add(String username, Element data) { + log.debug(String.format("Writing private data for %s", username)); + try { + StringWriter writer = new StringWriter(); + data.write(writer); + log.debug(String.format("Writing private data %s", writer.toString())); + + DBCollection prvStorageCollection = getDefaultCollection(); + DBObject query = new BasicDBObject(); + + query.put("username", username); + query.put("namespace", data.getNamespaceURI()); + DBObject existing = prvStorageCollection.findOne(query); + + if (existing == null) { + log.debug("new data"); + DBObject toInsert = new BasicDBObject(); + + toInsert.put("username", username); + toInsert.put("namespace", data.getNamespaceURI()); + toInsert.put("name", data.getName()); + toInsert.put("privateData", writer.toString()); + + prvStorageCollection.insert(toInsert); + } else { + log.debug("existing data"); + existing.put("name", data.getName()); + existing.put("privateData", writer.toString()); + + prvStorageCollection.save(existing); + } + } catch (IOException e) { + log.error("Error storing data: " + e.getMessage()); + } + } + + @Override + public Element get(String username, Element data, SAXReader reader) { + DBCollection prvStorageCollection = getDefaultCollection(); + DBObject query = new BasicDBObject(); + Element result = data; + + query.put("username", username); + query.put("namespace", data.getNamespaceURI()); + log.debug(String.format("Retrieving data for user %s and namespace %s", username, data.getNamespaceURI())); + DBObject existing = prvStorageCollection.findOne(query); + + if (existing != null) { + String prvData = ((String) existing.get("privateData")).trim(); + + try { + Document doc = reader.read(new StringReader(prvData)); + result = doc.getRootElement(); + } catch (DocumentException e) { + log.error("Error retrieving data: " + e.getMessage()); + } + } + log.debug(String.format("Found data: %s", result.asXML())); + + return result; + } + + @Override + public void userDeleting(User user, Map params) { + DBCollection prvStorageCollection = getDefaultCollection(); + DBObject toDelete = new BasicDBObject(); + + toDelete.put("username", user.getUsername()); + + prvStorageCollection.remove(toDelete); + + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPubSubProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPubSubProvider.java new file mode 100644 index 0000000000..bc3eb74cec --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoPubSubProvider.java @@ -0,0 +1,947 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.bson.types.ObjectId; +import org.jivesoftware.openfire.cluster.ClusterManager; +import org.jivesoftware.openfire.pubsub.BasePubSubProvider; +import org.jivesoftware.openfire.pubsub.CollectionNode; +import org.jivesoftware.openfire.pubsub.DefaultNodeConfiguration; +import org.jivesoftware.openfire.pubsub.LeafNode; +import org.jivesoftware.openfire.pubsub.Node; +import org.jivesoftware.openfire.pubsub.NodeAffiliate; +import org.jivesoftware.openfire.pubsub.NodeSubscription; +import org.jivesoftware.openfire.pubsub.PubSubService; +import org.jivesoftware.openfire.pubsub.PublishedItem; +import org.jivesoftware.openfire.pubsub.cluster.FlushTask; +import org.jivesoftware.openfire.pubsub.models.AccessModel; +import org.jivesoftware.openfire.pubsub.models.PublisherModel; +import org.jivesoftware.util.JiveGlobals; +import org.jivesoftware.util.LinkedListNode; +import org.jivesoftware.util.cache.CacheFactory; +import org.sipfoundry.commons.util.UnfortunateLackOfSpringSupportFactory; +import org.xmpp.packet.JID; + +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; +import com.mongodb.QueryOperators; +import com.mongodb.WriteResult; + +public class MongoPubSubProvider extends BasePubSubProvider { + private static final Logger log = Logger.getLogger(MongoPubSubProvider.class); + + private static final String COLLECTION_NAME = "ofPubsubNode"; + private static final String AFFILIATION_COLLECTION_NAME = "ofPubsubAffiliation"; + private static final String DEFAULT_CFG_COLLECTION_NAME = "ofPubsubDefaultConf"; + private static final String ITEM_COLLECTION_NAME = "ofPubsubItem"; + private static final String NODE_GROUP_COLLECTION_NAME = "ofPubsubNodeGroups"; + private static final String NODE_JID_COLLECTION_NAME = "ofPubsubNodeJIDs"; + private static final String SUBSCRIPTIONS_COLLECTION_NAME = "ofPubsubSubscription"; + + @Override + public void createNode(Node node) { + DBObject toInsert = nodeToDBObject(node); + + getDefaultCollection().insert(toInsert); + + saveAssociatedElements(node); + } + + @Override + public void updateNode(Node node) { + DBObject query = new BasicDBObject(); + query.put("serviceId", node.getService().getServiceID()); + query.put("nodeId", node.getNodeID()); + + DBObject update = nodeToDBObject(node); + + getDefaultCollection().update(query, update); + + DBObject toDelete = new BasicDBObject(); + toDelete.put("serviceId", node.getService().getServiceID()); + toDelete.put("nodeId", node.getNodeID()); + + // delete old associated elements + getCollection(NODE_JID_COLLECTION_NAME).remove(toDelete); + getCollection(NODE_GROUP_COLLECTION_NAME).remove(toDelete); + + saveAssociatedElements(node); + } + + @Override + public boolean removeNode(Node node) { + DBObject toDelete = new BasicDBObject(); + toDelete.put("serviceId", node.getService().getServiceID()); + toDelete.put("nodeId", node.getNodeID()); + + getDefaultCollection().remove(toDelete); + getCollection(NODE_JID_COLLECTION_NAME).remove(toDelete); + getCollection(NODE_GROUP_COLLECTION_NAME).remove(toDelete); + getCollection(ITEM_COLLECTION_NAME).remove(toDelete); + getCollection(AFFILIATION_COLLECTION_NAME).remove(toDelete); + getCollection(SUBSCRIPTIONS_COLLECTION_NAME).remove(toDelete); + + return true; + } + + @Override + public void loadNodes(PubSubService service) { + Map nodes = new HashMap(); + + DBObject query = new BasicDBObject(); + + query.put("serviceId", service.getServiceID()); + + for (DBObject nodeObj : getDefaultCollection().find(query)) { + loadNode(service, nodes, nodeObj); + } + + loadNodeDependencies(service, nodes, query); + } + + @Override + public void loadNode(PubSubService service, String nodeId) { + Map nodes = new HashMap(); + + // Get all non-leaf nodes (to ensure parent nodes are loaded before + // their children) + DBObject query = new BasicDBObject(); + + query.put("serviceID", service.getServiceID()); + query.put("nodeID", nodeId); + + Map parentMapping = new HashMap(); + + // Rebuild loaded non-leaf nodes + for (DBObject node : getDefaultCollection().find(query)) { + loadNode(service, nodes, parentMapping, node); + } + String parentId = parentMapping.get(nodeId); + + if (parentId != null) { + CollectionNode parent = (CollectionNode) service.getNode(parentId); + + if (parent == null) { + log.error("Could not find parent node " + parentId + " for node " + nodeId); + } else { + nodes.get(nodeId).changeParent(parent); + } + } + + loadNodeDependencies(service, nodes, query); + } + + @Override + public void saveAffiliation(Node node, NodeAffiliate affiliate, boolean create) { + if (create) { + DBObject toInsert = new BasicDBObject(); + + toInsert.put("serviceId", node.getService().getServiceID()); + toInsert.put("nodeId", node.getNodeID()); + toInsert.put("jid", affiliate.getJID().toString()); + toInsert.put("affiliation", affiliate.getAffiliation().name()); + + getCollection(AFFILIATION_COLLECTION_NAME).insert(toInsert); + } else { + DBObject query = new BasicDBObject(); + + query.put("serviceId", node.getService().getServiceID()); + query.put("nodeId", node.getNodeID()); + query.put("jid", affiliate.getJID().toString()); + + DBObject update = new BasicDBObject(); + + update.put("affiliation", affiliate.getAffiliation().name()); + + getCollection(AFFILIATION_COLLECTION_NAME).update(query, new BasicDBObject("$set", update)); + } + } + + @Override + public void removeAffiliation(Node node, NodeAffiliate affiliate) { + DBObject toDelete = new BasicDBObject(); + + toDelete.put("serviceId", node.getService().getServiceID()); + toDelete.put("nodeId", node.getNodeID()); + toDelete.put("jid", affiliate.getJID().toString()); + + getCollection(AFFILIATION_COLLECTION_NAME).remove(toDelete); + } + + @Override + public void saveSubscription(Node node, NodeSubscription subscription, boolean create) { + if (create) { + DBObject toInsert = subscriptionToDBObject(node, subscription); + + getCollection(SUBSCRIPTIONS_COLLECTION_NAME).insert(toInsert); + + subscription.setSavedToDB(true); + } else { + if (NodeSubscription.State.none == subscription.getState()) { + removeSubscription(subscription); + } else { + DBObject query = new BasicDBObject(); + + query.put("serviceId", node.getService().getServiceID()); + query.put("nodeId", node.getNodeID()); + query.put("id", subscription.getID()); + + DBObject update = subscriptionToDBObject(node, subscription); + + getCollection(SUBSCRIPTIONS_COLLECTION_NAME).update(query, new BasicDBObject("$set", update)); + } + } + } + + @Override + public void removeSubscription(NodeSubscription subscription) { + Node node = subscription.getNode(); + DBObject toDelete = new BasicDBObject(); + + toDelete.put("serviceId", node.getService().getServiceID()); + toDelete.put("nodeId", node.getNodeID()); + toDelete.put("id", subscription.getID()); + + getCollection(SUBSCRIPTIONS_COLLECTION_NAME).remove(toDelete); + } + + @Override + public DefaultNodeConfiguration loadDefaultConfiguration(PubSubService service, boolean isLeafType) { + DefaultNodeConfiguration config = null; + DBObject query = new BasicDBObject(); + + query.put("serviceId", service.getServiceID()); + query.put("leaf", isLeafType); + + DBObject confObj = getCollection(DEFAULT_CFG_COLLECTION_NAME).findOne(query); + + if (confObj != null) { + config = dbObjectToConfig(confObj, isLeafType); + } + + return config; + } + + @Override + public String loadPEPServiceFromDB(String jid) { + String id = null; + DBObject query = new BasicDBObject(); + + query.put("serviceId", jid); + + DBObject fields = new BasicDBObject(); + + fields.put("serviceId", 1); + + if (getDefaultCollection().findOne(query, fields) != null) { + // if there's a node, we already know the service id + id = jid; + } + + return id; + } + + @Override + public void createDefaultConfiguration(PubSubService service, DefaultNodeConfiguration config) { + DBObject toInsert = configToDBObject(service, config); + + getCollection(DEFAULT_CFG_COLLECTION_NAME).insert(toInsert); + } + + @Override + public void updateDefaultConfiguration(PubSubService service, DefaultNodeConfiguration config) { + DBObject query = new BasicDBObject(); + query.put("serviceId", service.getServiceID()); + query.put("leaf", config.isLeaf()); + + DBObject update = configToDBObject(service, config); + + getCollection(DEFAULT_CFG_COLLECTION_NAME).update(query, new BasicDBObject("$set", update)); + } + + /** + * Flush the cache of items to be persisted and deleted. + */ + @Override + public void flushPendingItems() { + flushPendingItems(ClusterManager.isClusteringEnabled()); + } + + /** + * Flush the cache of items to be persisted and deleted. + * + * @param sendToCluster If true, delegate to cluster members, otherwise local only + */ + @Override + public void flushPendingItems(boolean sendToCluster) { + if (sendToCluster) { + CacheFactory.doSynchronousClusterTask(new FlushTask(), false); + } + + LinkedListNode addItem = getFirstToAdd(); + LinkedListNode delItem = getFirstToDelete(); + LinkedListNode addLast = getLastToAdd(); + LinkedListNode delLast = getLastToDelete(); + + if (addItem == null && delItem == null) { + return; // nothing to do for this cluster member + } + + movePendingToAdd(); + + DBCollection itemCollection = getCollection(ITEM_COLLECTION_NAME); + if (delItem != null) { + LinkedListNode delHead = delLast.next; + + // delete first (to remove possible duplicates), then add new items + while (delItem != delHead) { + DBObject toDelete = new BasicDBObject(); + PublishedItem item = delItem.object; + + toDelete.put("serviceID", item.getNode().getService().getServiceID()); + toDelete.put("nodeID", item.getNode().getNodeID()); + toDelete.put("id", item.getID()); + + itemCollection.remove(toDelete); + } + } + + if (addItem != null) { + LinkedListNode addHead = addLast.next; + Set toInsertSet = new HashSet(); + + while (addItem != addHead) { + DBObject toInsert = new BasicDBObject(); + PublishedItem item = addItem.object; + + toInsert.put("serviceID", item.getNode().getService().getServiceID()); + toInsert.put("nodeID", item.getNodeID()); + toInsert.put("id", item.getID()); + toInsert.put("jid", item.getPublisher().toString()); + toInsert.put("creationDate", item.getCreationDate().getTime()); + toInsert.put("payload", item.getPayloadXML()); + itemCollection.insert(toInsert); + + toInsertSet.add(toInsert); + + addItem = addItem.next; + } + + itemCollection.insert(toInsertSet.toArray(new BasicDBObject[toInsertSet.size()])); + } + } + + @Override + public void loadSubscription(Node node, String subId) { + Map nodes = new HashMap(); + nodes.put(node.getNodeID(), node); + + DBObject query = new BasicDBObject(); + + query.put("serviceID", node.getService().getServiceID()); + query.put("nodeID", node.getNodeID()); + query.put("id", subId); + + DBObject subscriptionObj = getDefaultCollection().findOne(query); + if (subscriptionObj != null) { + loadSubscriptions(nodes, subscriptionObj); + } + } + + @Override + public List getPublishedItems(LeafNode node, int maxRows) { + safeFlushPendingItems(); + + int maxPublished = node.getMaxPublishedItems(); + int max = Math.min(getMaxRowsFetch(), maxPublished); + + java.util.LinkedList results = new java.util.LinkedList(); + boolean descending = JiveGlobals.getBooleanProperty("xmpp.pubsub.order.descending", false); + + // Get published items of the specified node + DBObject query = new BasicDBObject(); + + query.put("serviceID", node.getService().getServiceID()); + query.put("nodeID", node.getNodeID()); + + DBObject nodeSort = new BasicDBObject("creationDate", -1); + + int counter = 0; + + // Rebuild loaded published items + for (DBObject itemObj : getCollection(ITEM_COLLECTION_NAME).find(query).sort(nodeSort)) { + String itemID = (String) itemObj.get("id"); + JID publisher = new JID((String) itemObj.get("jid")); + Date creationDate = new Date((Long) itemObj.get("creationDate")); + // Create the item + PublishedItem item = new PublishedItem(node, publisher, itemID, creationDate); + // Add the extra fields to the published item + String payload = (String) itemObj.get("payload"); + if (payload != null) { + item.setPayloadXML(payload); + } + // Add the published item to the node + if (descending) { + results.add(item); + } else { + results.addFirst(item); + } + if (++counter > max) { + break; + } + } + + return results; + } + + @Override + public PublishedItem getLastPublishedItem(LeafNode node) { + safeFlushPendingItems(); + + PublishedItem item = null; + + DBObject query = new BasicDBObject(); + + query.put("serviceID", node.getService().getServiceID()); + query.put("nodeID", node.getNodeID()); + + DBObject nodeSort = new BasicDBObject("creationDate", -1); + DBObject itemObj = getCollection(ITEM_COLLECTION_NAME).findOne(query, null, nodeSort); + + if (itemObj != null) { + String itemID = (String) itemObj.get("id"); + JID publisher = new JID((String) itemObj.get("jid")); + Date creationDate = new Date((Long) itemObj.get("creationDate")); + // Create the item + item = new PublishedItem(node, publisher, itemID, creationDate); + // Add the extra fields to the published item + String payload = (String) itemObj.get("payload"); + if (payload != null) { + item.setPayloadXML(payload); + } + } + + return item; + } + + @Override + public PublishedItem loadItem(LeafNode node, String itemID) { + PublishedItem result = null; + + flushPendingItems(); + + DBObject query = new BasicDBObject(); + + query.put("serviceID", node.getService().getServiceID()); + query.put("nodeID", node.getNodeID()); + query.put("id", itemID); + + DBObject itemObj = getCollection(ITEM_COLLECTION_NAME).findOne(query); + + if (itemObj != null) { + JID publisher = new JID((String) itemObj.get("jid")); + Date creationDate = new Date((Long) itemObj.get("creationDate")); + // Create the item + result = new PublishedItem(node, publisher, itemID, creationDate); + // Add the extra fields to the published item + String payload = (String) itemObj.get("payload"); + if (payload != null) { + result.setPayloadXML(payload); + } + log.debug("Loaded item into cache from DB"); + } + + return result; + } + + @Override + protected boolean purgeNodeFromDB(LeafNode leafNode) { + flushPendingItems(ClusterManager.isClusteringEnabled()); + + DBObject toDelete = new BasicDBObject(); + + toDelete.put("serviceID", leafNode.getService().getServiceID()); + toDelete.put("nodeID", leafNode.getNodeID()); + + WriteResult result = getCollection(ITEM_COLLECTION_NAME).remove(toDelete); + if (result.getError() != null) { + evictFromCache(leafNode); + } + + return result.getError() != null; + } + + /** + * Purges all items from the database that exceed the defined item count on all nodes. + */ + @Override + protected void purgeItems() { + DBObject nodeQuery = new BasicDBObject(); + + nodeQuery.put("leaf", true); + nodeQuery.put("persistItems", true); + nodeQuery.put("maxItems", new BasicDBObject(QueryOperators.GT, 0)); + + for (DBObject nodeObj : getDefaultCollection().find(nodeQuery)) { + String svcId = (String) nodeObj.get("serviceID"); + String nodeId = (String) nodeObj.get("nodeID"); + int maxItems = (Integer) nodeObj.get("maxItems"); + + DBObject itemQuery = new BasicDBObject(); + + itemQuery.put("serviceID", svcId); + itemQuery.put("nodeID", nodeId); + + DBObject itemSort = new BasicDBObject(); + + itemSort.put("creationDate", 1); + + // skip oldest maxItems items, delete the rest + for (DBObject itemObject : getCollection(ITEM_COLLECTION_NAME).find(itemQuery).sort(itemSort) + .skip(maxItems)) { + getCollection(ITEM_COLLECTION_NAME).remove(itemObject); + } + } + } + + private static void saveAssociatedElements(Node node) { + DBCollection nodeJidCollection = getCollection(NODE_JID_COLLECTION_NAME); + + for (JID jid : node.getContacts()) { + DBObject toInsert = nodeJidToDBObject(node, jid, "contacts"); + + nodeJidCollection.insert(toInsert); + } + for (JID jid : node.getReplyRooms()) { + DBObject toInsert = nodeJidToDBObject(node, jid, "replyRooms"); + + nodeJidCollection.insert(toInsert); + } + for (JID jid : node.getReplyTo()) { + DBObject toInsertContact = nodeJidToDBObject(node, jid, "replyTo"); + + nodeJidCollection.insert(toInsertContact); + } + if (node.isCollectionNode()) { + for (JID jid : ((CollectionNode) node).getAssociationTrusted()) { + DBObject toInsert = nodeJidToDBObject(node, jid, "associationTrusted"); + + nodeJidCollection.insert(toInsert); + } + } + + for (String groupName : node.getRosterGroupsAllowed()) { + DBObject toInsert = new BasicDBObject(); + + toInsert.put("serviceId", node.getService().getServiceID()); + toInsert.put("nodeId", node.getNodeID()); + toInsert.put("rosterGroup", groupName); + + getCollection(NODE_GROUP_COLLECTION_NAME).insert(toInsert); + } + } + + private static DBObject nodeToDBObject(Node node) { + DBObject nodeObj = new BasicDBObject(); + + nodeObj.put("serviceId", node.getService().getServiceID()); + if (!StringUtils.isEmpty(node.getNodeID())) { + nodeObj.put("nodeId", node.getNodeID()); + } else { + nodeObj.put("nodeId", new ObjectId().toString()); + } + if (node.isCollectionNode()) { + nodeObj.put("leaf", false); + nodeObj.put("maxPayloadSize", 0); + nodeObj.put("persistItems", false); + nodeObj.put("maxItems", 0); + } else { + nodeObj.put("leaf", true); + nodeObj.put("maxPayloadSize", ((LeafNode) node).getMaxPayloadSize()); + nodeObj.put("persistItems", ((LeafNode) node).isPersistPublishedItems()); + nodeObj.put("maxItems", ((LeafNode) node).getMaxPublishedItems()); + } + nodeObj.put("creationDate", node.getCreationDate()); + nodeObj.put("modificationDate", node.getModificationDate()); + nodeObj.put("parent", node.getParent() != null ? node.getParent().getNodeID() : null); + nodeObj.put("deliverPayloads", node.isPayloadDelivered()); + nodeObj.put("notifyConfigChanges", node.isNotifiedOfConfigChanges()); + nodeObj.put("notifyDelete", node.isNotifiedOfDelete()); + nodeObj.put("notifyRetract", node.isNotifiedOfRetract()); + nodeObj.put("presenceBased", node.isPresenceBasedDelivery()); + nodeObj.put("sendItemSubscribe", node.isSendItemSubscribe()); + nodeObj.put("publisherModel", node.getPublisherModel().getName()); + nodeObj.put("subscriptionEnabled", node.isSubscriptionEnabled()); + nodeObj.put("configSubscription", node.isSubscriptionConfigurationRequired()); + nodeObj.put("accessModel", node.getAccessModel().getName()); + nodeObj.put("payloadType", node.getPayloadType()); + nodeObj.put("bodyXslt", node.getBodyXSLT()); + nodeObj.put("dataformXslt", node.getDataformXSLT()); + nodeObj.put("creator", node.getCreator().toString()); + nodeObj.put("description", node.getDescription()); + nodeObj.put("language", node.getLanguage()); + nodeObj.put("name", node.getName()); + nodeObj.put("replyPolicy", node.getReplyPolicy() != null ? node.getReplyPolicy().name() : null); + if (node.isCollectionNode()) { + nodeObj.put("associationPolicy", ((CollectionNode) node).getAssociationPolicy().name()); + nodeObj.put("maxLeafNodes", ((CollectionNode) node).getMaxLeafNodes()); + } else { + nodeObj.put("associationPolicy", null); + nodeObj.put("maxLeafNodes", 0); + } + + return nodeObj; + } + + private static DBObject configToDBObject(PubSubService service, DefaultNodeConfiguration config) { + DBObject dbObj = new BasicDBObject(); + + dbObj.put("serviceId", service.getServiceID()); + dbObj.put("leaf", config.isLeaf()); + dbObj.put("deliverPayloads", config.isDeliverPayloads()); + dbObj.put("maxPayloadSize", config.getMaxPayloadSize()); + dbObj.put("persistItems", config.isPersistPublishedItems()); + dbObj.put("maxItems", config.getMaxPublishedItems()); + dbObj.put("notifyConfigChanges", config.isNotifyConfigChanges()); + dbObj.put("notifyDelete", config.isNotifyDelete()); + dbObj.put("notifyRetract", config.isNotifyRetract()); + dbObj.put("presenceBased", config.isPresenceBasedDelivery()); + dbObj.put("sendItemSubscribe", config.isSendItemSubscribe()); + dbObj.put("publisherModel", config.getPublisherModel().getName()); + dbObj.put("subscriptionEnabled", config.isSubscriptionEnabled()); + dbObj.put("accessModel", config.getAccessModel().getName()); + dbObj.put("language", config.getLanguage()); + dbObj.put("replyPolicy", config.getReplyPolicy() != null ? config.getReplyPolicy().name() : null); + dbObj.put("associationPolicy", config.getAssociationPolicy().name()); + dbObj.put("maxLeafNodes", config.getMaxLeafNodes()); + + return dbObj; + } + + private static DefaultNodeConfiguration dbObjectToConfig(DBObject confObj, boolean isLeafType) { + DefaultNodeConfiguration config = new DefaultNodeConfiguration(isLeafType); + + Boolean deliverPayloads = (Boolean) confObj.get("deliverPayloads"); + Integer maxPayloadSize = (Integer) confObj.get("maxPayloadSize"); + Boolean persistItems = (Boolean) confObj.get("persistItems"); + Integer maxItems = (Integer) confObj.get("maxItems"); + Boolean notifyConfigChanges = (Boolean) confObj.get("notifyConfigChanges"); + Boolean notifyDelete = (Boolean) confObj.get("notifyDelete"); + Boolean notifyRetract = (Boolean) confObj.get("notifyRetract"); + Boolean presenceBased = (Boolean) confObj.get("presenceBased"); + Boolean sendItemSubscribe = (Boolean) confObj.get("sendItemSubscribe"); + String publisherModel = (String) confObj.get("publisherModel"); + Boolean subscriptionEnabled = (Boolean) confObj.get("subscriptionEnabled"); + String accessModel = (String) confObj.get("accessModel"); + String language = (String) confObj.get("language"); + String replyPolicy = (String) confObj.get("replyPolicy"); + String associationPolicy = (String) confObj.get("associationPolicy"); + Integer maxLeafNodes = (Integer) confObj.get("maxLeafNodes"); + + config.setDeliverPayloads(deliverPayloads); + config.setMaxPayloadSize(maxPayloadSize); + config.setPersistPublishedItems(persistItems); + config.setMaxPublishedItems(maxItems); + config.setNotifyConfigChanges(notifyConfigChanges); + config.setNotifyDelete(notifyDelete); + config.setNotifyRetract(notifyRetract); + config.setPresenceBasedDelivery(presenceBased); + config.setSendItemSubscribe(sendItemSubscribe); + config.setPublisherModel(PublisherModel.valueOf(publisherModel)); + config.setSubscriptionEnabled(subscriptionEnabled); + config.setAccessModel(AccessModel.valueOf(accessModel)); + config.setLanguage(language); + config.setReplyPolicy(replyPolicy != null ? Node.ItemReplyPolicy.valueOf(replyPolicy) : null); + config.setAssociationPolicy(CollectionNode.LeafNodeAssociationPolicy.valueOf(associationPolicy)); + config.setMaxLeafNodes(maxLeafNodes); + + return config; + } + + private static DBObject nodeJidToDBObject(Node node, JID jid, String type) { + DBObject nodeObj = new BasicDBObject(); + + nodeObj.put("serviceId", node.getService().getServiceID()); + nodeObj.put("nodeId", node.getNodeID()); + nodeObj.put("jid", jid.toString()); + nodeObj.put("associationType", type); + + return nodeObj; + } + + private static DBObject subscriptionToDBObject(Node node, NodeSubscription subscription) { + DBObject subscrObj = new BasicDBObject(); + + subscrObj.put("serviceId", node.getService().getServiceID()); + subscrObj.put("nodeId", node.getNodeID()); + subscrObj.put("id", subscription.getID()); + subscrObj.put("jid", subscription.getJID().toString()); + subscrObj.put("owner", subscription.getOwner().toString()); + subscrObj.put("state", subscription.getState().name()); + subscrObj.put("deliver", subscription.shouldDeliverNotifications()); + subscrObj.put("digest", subscription.isUsingDigest()); + subscrObj.put("digestFrequency", subscription.getDigestFrequency()); + Date expireDate = subscription.getExpire(); + subscrObj.put("expire", expireDate != null ? expireDate.getTime() : null); + subscrObj.put("includeBody", subscription.isIncludingBody()); + subscrObj.put("showValues", encodeWithComma(subscription.getPresenceStates())); + subscrObj.put("subscriptionType", subscription.getType().name()); + subscrObj.put("subscriptionDepth", subscription.getDepth()); + subscrObj.put("keyword", subscription.getKeyword()); + return subscrObj; + } + + private static void loadNode(PubSubService service, Map nodes, DBObject nodeObj) { + loadNode(service, nodes, null, nodeObj); + } + + private static void loadNode(PubSubService service, Map nodes, Map parentMappings, + DBObject nodeObj) { + String nodeId = (String) nodeObj.get("nodeId"); + boolean leaf = (Boolean) nodeObj.get("leaf"); + String parent = (String) nodeObj.get("parent"); + + if (parent != null && parentMappings != null) { + parentMappings.put(nodeId, parent); + } + + CollectionNode parentNode = null; + if (parent != null && nodes.get(parent) == null) { + return; + } + + Date creationDate = (Date) nodeObj.get("creationDate"); + Date modificationDate = (Date) nodeObj.get("modificationDate"); + Boolean deliverPayloads = (Boolean) nodeObj.get("deliverPayloads"); + Integer maxPayloadSize = (Integer) nodeObj.get("maxPayloadSize"); + Boolean persistItems = (Boolean) nodeObj.get("persistItems"); + Integer maxItems = (Integer) nodeObj.get("maxItems"); + Boolean notifyConfigChanges = (Boolean) nodeObj.get("notifyConfigChanges"); + Boolean notifyDelete = (Boolean) nodeObj.get("notifyDelete"); + Boolean notifyRetract = (Boolean) nodeObj.get("notifyRetract"); + Boolean presenceBased = (Boolean) nodeObj.get("presenceBased"); + Boolean sendItemSubscribe = (Boolean) nodeObj.get("sendItemSubscribe"); + String publisherModel = (String) nodeObj.get("publisherModel"); + Boolean subscriptionEnabled = (Boolean) nodeObj.get("subscriptionEnabled"); + Boolean configSubscription = (Boolean) nodeObj.get("configSubscription"); + String accessModel = (String) nodeObj.get("accessModel"); + String payloadType = (String) nodeObj.get("payloadType"); + String bodyXslt = (String) nodeObj.get("bodyXslt"); + String dataformXslt = (String) nodeObj.get("dataformXslt"); + String creator = (String) nodeObj.get("creator"); + String description = (String) nodeObj.get("description"); + String language = (String) nodeObj.get("language"); + String name = (String) nodeObj.get("name"); + String replyPolicy = (String) nodeObj.get("replyPolicy"); + String associationPolicy = (String) nodeObj.get("associationPolicy"); + Integer maxLeafNodes = (Integer) nodeObj.get("maxLeafNodes"); + + JID creatorJid = new JID(creator); + Node node; + if (leaf) { + node = new LeafNode(service, parentNode, nodeId, creatorJid); + } else { + node = new CollectionNode(service, parentNode, nodeId, creatorJid); + } + + node.setCreationDate(creationDate); + node.setModificationDate(modificationDate); + node.setPayloadDelivered(deliverPayloads); + if (leaf) { + ((LeafNode) node).setMaxPayloadSize(maxPayloadSize); + ((LeafNode) node).setPersistPublishedItems(persistItems); + ((LeafNode) node).setMaxPublishedItems(maxItems); + ((LeafNode) node).setSendItemSubscribe(sendItemSubscribe); + } + node.setNotifiedOfConfigChanges(notifyConfigChanges); + node.setNotifiedOfDelete(notifyDelete); + node.setNotifiedOfRetract(notifyRetract); + node.setPresenceBasedDelivery(presenceBased); + node.setPublisherModel(PublisherModel.valueOf(publisherModel)); + node.setSubscriptionEnabled(subscriptionEnabled); + node.setSubscriptionConfigurationRequired(configSubscription); + node.setAccessModel(AccessModel.valueOf(accessModel)); + node.setPayloadType(payloadType); + node.setBodyXSLT(bodyXslt); + node.setDataformXSLT(dataformXslt); + node.setDescription(description); + node.setLanguage(language); + node.setName(name); + if (replyPolicy != null) { + node.setReplyPolicy(Node.ItemReplyPolicy.valueOf(replyPolicy)); + } + if (!leaf) { + ((CollectionNode) node).setAssociationPolicy(CollectionNode.LeafNodeAssociationPolicy + .valueOf(associationPolicy)); + ((CollectionNode) node).setMaxLeafNodes(maxLeafNodes); + } + + nodes.put(nodeId, node); + } + + private static void loadNodeDependencies(PubSubService service, Map nodes, DBObject query) { + for (DBObject nodeJidObj : getCollection(NODE_JID_COLLECTION_NAME).find(query)) { + loadAssociatedJids(nodes, nodeJidObj); + } + + for (DBObject nodeGroupObj : getCollection(NODE_GROUP_COLLECTION_NAME).find(query)) { + loadAssociatedGroups(nodes, nodeGroupObj); + } + + for (DBObject affiliationObj : getCollection(AFFILIATION_COLLECTION_NAME).find(query)) { + loadAffiliations(nodes, affiliationObj); + } + + for (DBObject subscriptionObj : getCollection(SUBSCRIPTIONS_COLLECTION_NAME).find(query)) { + loadSubscriptions(nodes, subscriptionObj); + } + + for (Node node : nodes.values()) { + node.setSavedToDB(true); + service.addNode(node); + } + } + + private static void loadAssociatedJids(Map nodes, DBObject nodeJidObj) { + String nodeId = (String) nodeJidObj.get("nodeId"); + Node node = nodes.get(nodeId); + if (node != null) { + JID jid = new JID((String) nodeJidObj.get("jid")); + String associationType = (String) nodeJidObj.get("associationType"); + if ("contacts".equals(associationType)) { + node.addContact(jid); + } else if ("replyRooms".equals(associationType)) { + node.addReplyRoom(jid); + } else if ("replyTo".equals(associationType)) { + node.addReplyTo(jid); + } else if ("associationTrusted".equals(associationType)) { + ((CollectionNode) node).addAssociationTrusted(jid); + } + } else { + log.warn("Node associated with JID not found " + nodeId + ". Will not be loaded."); + } + } + + private static void loadAssociatedGroups(Map nodes, DBObject nodeGroupObj) { + String nodeId = (String) nodeGroupObj.get("nodeId"); + Node node = nodes.get(nodeId); + if (node != null) { + node.addAllowedRosterGroup((String) nodeGroupObj.get("rosterGroup")); + } else { + log.warn("Node associated with group not found " + nodeId + ". Will not be loaded."); + } + } + + private static void loadAffiliations(Map nodes, DBObject affiliationObj) { + String nodeId = (String) affiliationObj.get("nodeId"); + Node node = nodes.get(nodeId); + if (node != null) { + String jid = (String) affiliationObj.get("jid"); + String affiliation = (String) affiliationObj.get("affiliation"); + + NodeAffiliate affiliate = new NodeAffiliate(node, new JID(jid)); + affiliate.setAffiliation(NodeAffiliate.Affiliation.valueOf(affiliation)); + node.addAffiliate(affiliate); + } else { + log.warn("Node associated with affiliation not found " + nodeId + ". Will not be loaded."); + } + + } + + private static void loadSubscriptions(Map nodes, DBObject subscriptionObj) { + String nodeId = (String) subscriptionObj.get("nodeId"); + Node node = nodes.get(nodeId); + if (node != null) { + JID owner = new JID((String) subscriptionObj.get("owner")); + if (node.getAffiliate(owner) != null) { + String subId = (String) subscriptionObj.get("id"); + JID subscriber = new JID((String) subscriptionObj.get("jid")); + String state = (String) subscriptionObj.get("state"); + Boolean deliverNotifications = (Boolean) subscriptionObj.get("deliver"); + Boolean digest = (Boolean) subscriptionObj.get("digest"); + Integer digestFrequency = (Integer) subscriptionObj.get("digestFrequency"); + Long expire = (Long) subscriptionObj.get("expire"); + Boolean includeBody = (Boolean) subscriptionObj.get("includeBody"); + String showValues = (String) subscriptionObj.get("showValues"); + String subscriptionType = (String) subscriptionObj.get("subscriptionType"); + Integer subscriptionDepth = (Integer) subscriptionObj.get("subscriptionDepth"); + String keyword = (String) subscriptionObj.get("keyword"); + + NodeSubscription subscription = new NodeSubscription(node, owner, subscriber, + NodeSubscription.State.valueOf(state), subId); + + subscription.setShouldDeliverNotifications(deliverNotifications); + subscription.setUsingDigest(digest); + subscription.setDigestFrequency(digestFrequency); + if (expire != null) { + subscription.setExpire(new Date(expire)); + } + subscription.setIncludingBody(includeBody); + subscription.setPresenceStates(decodeWithComma(showValues)); + subscription.setType(NodeSubscription.Type.valueOf(subscriptionType)); + subscription.setDepth(subscriptionDepth); + subscription.setKeyword(keyword); + + subscription.setSavedToDB(true); + node.addSubscription(subscription); + } else { + log.warn("Node associated with affiliation not found " + owner + ", " + nodeId + + ". Will not be loaded."); + } + + } else { + log.warn("Node associated with subscription not found " + nodeId + ". Will not be loaded."); + } + } + + private static String encodeWithComma(Collection strings) { + StringBuilder sb = new StringBuilder(90); + + for (String string : strings) { + sb.append(string).append(","); + } + if (sb.length() > 0) { + sb.setLength(sb.length() - 1); + } + + return sb.toString(); + } + + private static Collection decodeWithComma(String strings) { + Collection decoded = new ArrayList(); + StringTokenizer tokenizer = new StringTokenizer(strings.trim(), ","); + + while (tokenizer.hasMoreTokens()) { + decoded.add(tokenizer.nextToken()); + } + + return decoded; + } + + private static DBCollection getDefaultCollection() { + return getCollection(COLLECTION_NAME); + } + + private static DBCollection getCollection(String collectionName) { + DB db = UnfortunateLackOfSpringSupportFactory.getOpenfiredb(); + + return db.getCollection(collectionName); + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoRemoteServerProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoRemoteServerProvider.java new file mode 100644 index 0000000000..c32db3f1f7 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoRemoteServerProvider.java @@ -0,0 +1,98 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.util.ArrayList; +import java.util.Collection; + +import org.jivesoftware.openfire.provider.RemoteServerProvider; +import org.jivesoftware.openfire.server.RemoteServerConfiguration; +import org.jivesoftware.openfire.server.RemoteServerConfiguration.Permission; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +public class MongoRemoteServerProvider extends BaseMongoProvider implements RemoteServerProvider { + private static final String COLLECTION_NAME = "ofRemoteServerConf"; + + public MongoRemoteServerProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection rSrvCollection = getDefaultCollection(); + + DBObject index = new BasicDBObject(); + index.put("xmppDomain", 1); + + rSrvCollection.ensureIndex(index); + } + + @Override + public void addConfiguration(RemoteServerConfiguration configuration) { + DBCollection rSrvCollection = getDefaultCollection(); + + DBObject toInsert = new BasicDBObject(); + toInsert.put("xmppDomain", configuration.getDomain()); + toInsert.put("remotePort", configuration.getRemotePort()); + toInsert.put("permission", configuration.getPermission().toString()); + + rSrvCollection.insert(toInsert); + } + + @Override + public RemoteServerConfiguration getConfiguration(String domain) { + DBCollection rSrvCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + query.put("xmppDomain", domain); + + RemoteServerConfiguration conf; + DBObject confObj = rSrvCollection.findOne(query); + + if (confObj != null) { + Integer remote = (Integer) confObj.get("remotePort"); + String permission = (String) confObj.get("permission"); + + conf = new RemoteServerConfiguration(domain); + conf.setPermission(Permission.valueOf(permission)); + conf.setRemotePort(remote); + } else { + conf = null; + } + + return conf; + } + + @Override + public Collection getConfigurations(Permission permission) { + Collection confs = new ArrayList(); + DBCollection rSrvCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + query.put("permission", permission.toString()); + + for (DBObject confObj : rSrvCollection.find(query)) { + String domain = (String) confObj.get("domain"); + Integer remote = (Integer) confObj.get("remotePort"); + + RemoteServerConfiguration conf = new RemoteServerConfiguration(domain); + conf.setPermission(permission); + conf.setRemotePort(remote); + confs.add(conf); + } + + return confs; + } + + @Override + public void deleteConfiguration(String domain) { + DBCollection rSrvCollection = getDefaultCollection(); + + DBObject toDelete = new BasicDBObject(); + toDelete.put("xmppDomain", domain); + + rSrvCollection.remove(toDelete); + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoRosterProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoRosterProvider.java new file mode 100755 index 0000000000..19b9ae1316 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoRosterProvider.java @@ -0,0 +1,158 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.jivesoftware.database.SequenceManager; +import org.jivesoftware.openfire.provider.RosterItemProvider; +import org.jivesoftware.openfire.roster.RosterItem; +import org.jivesoftware.openfire.user.UserAlreadyExistsException; +import org.jivesoftware.openfire.user.UserNotFoundException; +import org.jivesoftware.util.JiveConstants; +import org.xmpp.packet.JID; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +public class MongoRosterProvider extends BaseMongoProvider implements RosterItemProvider { + private static final String COLLECTION_NAME = "ofRoster"; + + public MongoRosterProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection rosterCollection = getDefaultCollection(); + + rosterCollection.ensureIndex("rosterID"); + rosterCollection.ensureIndex("jid"); + rosterCollection.ensureIndex("username"); + } + + /** + * {@inheritDoc} + */ + @Override + public RosterItem createItem(String username, RosterItem item) throws UserAlreadyExistsException { + DBCollection rosterCollection = getDefaultCollection(); + DBObject toInsert = new BasicDBObject(); + + toInsert.put("rosterID", SequenceManager.nextID(JiveConstants.ROSTER)); + toInsert.put("username", username); + toInsert.put("jid", item.getJid().toBareJID()); + toInsert.put("sub", item.getSubStatus().getValue()); + toInsert.put("ask", item.getAskStatus().getValue()); + toInsert.put("recv", item.getRecvStatus().getValue()); + toInsert.put("nick", item.getNickname()); + toInsert.put("groups", item.getGroups()); + + rosterCollection.insert(toInsert); + item.setID((Long) toInsert.get("rosterID")); + + return item; + } + + /** + * {@inheritDoc} + */ + @Override + public void deleteItem(String username, long rosterItemID) { + DBCollection rosterGrpCollection = getCollection("ofRosterGroups"); + DBObject grpToRemove = new BasicDBObject(); + grpToRemove.put("rosterID", rosterItemID); + + rosterGrpCollection.remove(grpToRemove); + + DBCollection rosterCollection = getDefaultCollection(); + DBObject toRemove = new BasicDBObject(); + toRemove.put("rosterID", rosterItemID); + + rosterCollection.remove(toRemove); + } + + /** + * {@inheritDoc} + */ + @Override + public int getItemCount(String username) { + DBCollection rosterCollection = getDefaultCollection(); + DBObject toQuery = new BasicDBObject(); + toQuery.put("username", username); + + return (int) rosterCollection.count(toQuery); + + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator getItems(String username) { + List items = new ArrayList(); + + DBCollection rosterCollection = getDefaultCollection(); + DBObject toQuery = new BasicDBObject(); + toQuery.put("username", username); + + for (DBObject row : rosterCollection.find(toQuery)) { + long id = (Long) row.get("rosterID"); + JID jid = new JID((String) row.get("jid")); + int subType = (Integer) row.get("sub"); + int askType = (Integer) row.get("ask"); + int recvType = (Integer) row.get("recv"); + String nick = (String) row.get("nick"); + + RosterItem item = new RosterItem(id, jid, RosterItem.SubType.getTypeFromInt(subType), + RosterItem.AskType.getTypeFromInt(askType), RosterItem.RecvType.getTypeFromInt(recvType), nick, + null); + + items.add(item); + } + + return items.iterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public Iterator getUsernames(String jid) { + List names = new ArrayList(); + + DBCollection rosterCollection = getDefaultCollection(); + DBObject toQuery = new BasicDBObject(); + toQuery.put("jid", jid); + + for (DBObject row : rosterCollection.find(toQuery)) { + names.add((String) row.get("username")); + } + + return names.iterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public void updateItem(String username, RosterItem item) throws UserNotFoundException { + DBCollection rosterCollection = getDefaultCollection(); + + DBObject query = new BasicDBObject(); + + query.put("rosterID", item.getID()); + + DBObject update = new BasicDBObject(); + + update.put("sub", item.getSubStatus().getValue()); + update.put("ask", item.getAskStatus().getValue()); + update.put("recv", item.getRecvStatus().getValue()); + update.put("nick", item.getNickname()); + update.put("groups", item.getGroups()); + + rosterCollection.findAndModify(query, new BasicDBObject("$set", update)); + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoSecurityAuditProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoSecurityAuditProvider.java new file mode 100644 index 0000000000..0043453fba --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoSecurityAuditProvider.java @@ -0,0 +1,134 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.jivesoftware.database.SequenceManager; +import org.jivesoftware.openfire.XMPPServer; +import org.jivesoftware.openfire.provider.SecurityAuditProvider; +import org.jivesoftware.openfire.security.EventNotFoundException; +import org.jivesoftware.openfire.security.SecurityAuditEvent; +import org.jivesoftware.util.JiveConstants; +import org.jivesoftware.util.StringUtils; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +public class MongoSecurityAuditProvider extends BaseMongoProvider implements SecurityAuditProvider { + private static final String COLLECTION_NAME = "ofSecurityAuditLog"; + + public MongoSecurityAuditProvider() { + setDefaultCollectionName(COLLECTION_NAME); + + DBCollection saLogCollection = getDefaultCollection(); + DBObject index = new BasicDBObject(); + + index.put("msgID", 1); + saLogCollection.ensureIndex(index); + } + + @Override + public void logEvent(String username, String summary, String details) { + DBCollection saLogCollection = getDefaultCollection(); + DBObject toInsert = new BasicDBObject(); + long msgID = SequenceManager.nextID(JiveConstants.SECURITY_AUDIT); + + toInsert.put("msgID", msgID); + toInsert.put("username", username); + toInsert.put("entryStamp", new Date().getTime()); + toInsert.put("summary", StringUtils.abbreviate(summary, 250)); + toInsert.put("node", XMPPServer.getInstance().getServerInfo().getHostname()); + toInsert.put("details", details); + + saLogCollection.insert(toInsert); + } + + @Override + public List getEvents(String username, Integer skipEvents, Integer numEvents, + Date startTime, Date endTime) { + List events = new ArrayList(); + DBCollection saLogCollection = getDefaultCollection(); + DBObject query = new BasicDBObject(); + + if (username != null) { + query.put("username", username); + } + if (startTime != null) { + query.put("entryStamp", username); + } + if (endTime != null) { + query.put("entryStamp", username); + } + + int skip = skipEvents != null ? skipEvents : 0; + int limit = numEvents != null ? numEvents : Integer.MAX_VALUE; + + for (DBObject evtObj : saLogCollection.find(query).skip(skip).limit(limit)) { + SecurityAuditEvent event = new SecurityAuditEvent(); + event.setMsgID((Long) evtObj.get("msgID")); + event.setUsername((String) evtObj.get("username")); + event.setEventStamp(new Date((Long) evtObj.get("entryStamp"))); + event.setSummary((String) evtObj.get("summary")); + event.setNode((String) evtObj.get("node")); + event.setDetails((String) evtObj.get("details")); + events.add(event); + } + + return events; + } + + @Override + public SecurityAuditEvent getEvent(Integer msgID) throws EventNotFoundException { + SecurityAuditEvent event = null; + DBCollection saLogCollection = getDefaultCollection(); + DBObject query = new BasicDBObject(); + + query.put("msgID", msgID); + DBObject evtObj = saLogCollection.findOne(query); + + if (evtObj != null) { + event = new SecurityAuditEvent(); + event.setMsgID(msgID); + event.setUsername((String) evtObj.get("username")); + event.setEventStamp(new Date((Long) evtObj.get("entryStamp"))); + event.setSummary((String) evtObj.get("summary")); + event.setNode((String) evtObj.get("node")); + event.setDetails((String) evtObj.get("details")); + } + + return event; + } + + @Override + public Integer getEventCount() { + return (int) getDefaultCollection().count(); + } + + @Override + public boolean isWriteOnly() { + return false; + } + + @Override + public String getAuditURL() { + return null; + } + + @Override + public boolean blockUserEvents() { + return false; + } + + @Override + public boolean blockGroupEvents() { + return false; + } + +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUIDProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUIDProvider.java new file mode 100644 index 0000000000..74d8dcb40b --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUIDProvider.java @@ -0,0 +1,54 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import org.jivesoftware.openfire.provider.UIDProvider; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +public class MongoUIDProvider extends BaseMongoProvider implements UIDProvider { + private static final String COLLECTION_NAME = "ofId"; + + public MongoUIDProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection idCollection = getDefaultCollection(); + DBObject index = new BasicDBObject(); + + index.put("idType", 1); + + idCollection.ensureIndex(index); + } + + @Override + public long[] getNextBlock(int type, int blockSize) { + long[] result = new long[2]; // we just return the min and max ids + DBCollection idCollection = getDefaultCollection(); + DBObject query = new BasicDBObject(); + + query.put("idType", type); + + DBObject idObj = idCollection.findOne(query); + + if (idObj != null) { + result[0] = (Long) idObj.get("id"); + } else { + result[0] = 1; + } + result[1] = result[0] + blockSize; + + DBObject update = new BasicDBObject(); + update.put("idType", type); + update.put("id", result[1]); + + // update if exists, otherwise insert + idCollection.findAndModify(query, null, null, false, new BasicDBObject("$set", update), false, true); + + return result; + } + +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUserPropertiesProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUserPropertiesProvider.java new file mode 100644 index 0000000000..04eed31bff --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUserPropertiesProvider.java @@ -0,0 +1,92 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.util.HashMap; +import java.util.Map; + +import org.jivesoftware.openfire.provider.UserPropertiesProvider; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; + +public class MongoUserPropertiesProvider extends BaseMongoProvider implements UserPropertiesProvider { + private static final String COLLECTION_NAME = "ofUserProp"; + + public MongoUserPropertiesProvider() { + setDefaultCollectionName(COLLECTION_NAME); + DBCollection usrPropsCollection = getDefaultCollection(); + DBObject index = new BasicDBObject(); + + index.put("username", 1); + index.put("name", 1); + usrPropsCollection.ensureIndex(index); + } + + @Override + public Map loadProperties(String username) { + Map props = new HashMap(); + DBCollection usrPropsCollection = getDefaultCollection(); + DBObject query = new BasicDBObject(); + + query.put("username", username); + + for (DBObject usrPropObj : usrPropsCollection.find(query)) { + String propName = (String) usrPropObj.get("name"); + String propValue = (String) usrPropObj.get("propValue"); + + props.put(propName, propValue); + } + + return props; + } + + @Override + public void insertProperty(String username, String propName, String propValue) { + // nothing to do + } + + @Override + public String getPropertyValue(String username, String propName) { + DBCollection usrPropsCollection = getDefaultCollection(); + DBObject usrPropObj = getPropObject(usrPropsCollection, username, propName); + String propValue = null; + + if (usrPropObj != null) { + propValue = (String) usrPropObj.get("propValue"); + } + + return propValue; + } + + @Override + public void updateProperty(String username, String propName, String propValue) { + // nothing to do + } + + @Override + public boolean deleteUserProperties(String username) { + // nothing to do + + return true; + } + + @Override + public void deleteProperty(String username, String propName) { + // nothing to do + } + + private static DBObject getPropObject(DBCollection usrPropsCollection, String username, String propName) { + DBObject query = new BasicDBObject(); + + query.put("username", username); + query.put("name", propName); + + DBObject usrPropObj = usrPropsCollection.findOne(query); + return usrPropObj; + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUserProviderAlt.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUserProviderAlt.java new file mode 100755 index 0000000000..ffb7a7224d --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoUserProviderAlt.java @@ -0,0 +1,261 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import static org.apache.commons.lang.StringUtils.defaultIfEmpty; +import static org.sipfoundry.commons.mongo.MongoConstants.ALT_IM_ID; +import static org.sipfoundry.commons.mongo.MongoConstants.EMAIL; +import static org.sipfoundry.commons.mongo.MongoConstants.IM_DISPLAY_NAME; +import static org.sipfoundry.commons.mongo.MongoConstants.IM_ID; +import static org.sipfoundry.commons.mongo.MongoConstants.UID; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.log4j.Logger; +import org.jivesoftware.openfire.XMPPServer; +import org.jivesoftware.openfire.provider.UserProvider; +import org.jivesoftware.openfire.user.User; +import org.jivesoftware.openfire.user.UserNotFoundException; +import org.sipfoundry.commons.userdb.ValidUsers; +import org.sipfoundry.commons.util.UnfortunateLackOfSpringSupportFactory; +import org.xmpp.packet.JID; + +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; +import com.mongodb.QueryBuilder; + +public class MongoUserProviderAlt implements UserProvider { + public static final String SIP_UID = "sipUid"; + private static final Logger log = Logger.getLogger(MongoUserProviderAlt.class); + + private static final String COLLECTION_NAME = "entity"; + + @Override + public User loadUser(String username) throws UserNotFoundException { + log.debug("load user: " + username); + String actualUsername = username; + if (username.contains("@")) { + if (!XMPPServer.getInstance().isLocal(new JID(username))) { + throw new UserNotFoundException("Cannot load user of remote server: " + username); + } + actualUsername = username.substring(0, username.lastIndexOf("@")); + } + + DBCollection userCollection = getCollection(); + DBObject query = new BasicDBObject(); + + query.put("ent", "user"); + query.put("imenbld", true); + query.put("imid", actualUsername); + + DBObject userObj = userCollection.findOne(query); + + if (userObj == null) { + // maybe it was looking for imbot + query.put("ent", "imbotsettings"); + userObj = userCollection.findOne(query); + log.debug("ImBot query: " + query); + + if (userObj == null) { + throw new UserNotFoundException(username); + } + } + + String id = (String) userObj.get("_id"); + String uid = (String) userObj.get("uid"); + CacheHolder.putUser(id, actualUsername); + CacheHolder.putImId(uid, actualUsername); + + return fromDBObject(userObj); + } + + @Override + public User createUser(String username, String password, String name, String email) { + User u = null; + + try { + u = loadUser(username); + } catch (UserNotFoundException e) { + log.error("User not find while trying to simulate a create [" + username + "]"); + } + + return u; + } + + @Override + public void deleteUser(String username) { + CacheHolder.removeUserByName(username); + // users are read-only, no further action needed + } + + @Override + public int getUserCount() { + DBCollection userCollection = getCollection(); + DBObject query = new BasicDBObject(); + + query.put("ent", "user"); + query.put("imenbld", true); + + return (int) userCollection.count(query); + } + + @Override + public Collection getUsers() { + return getUsers(0, Integer.MAX_VALUE); + } + + @Override + public Collection getUsernames() { + List usernames = new ArrayList(); + + for (User u : getUsers()) { + usernames.add(u.getName()); + } + + return usernames; + } + + @Override + public Collection getUsers(int startIndex, int numResults) { + List users = new ArrayList(); + DBCollection userCollection = getCollection(); + DBObject query = new BasicDBObject(); + + query.put("ent", "user"); + query.put("imenbld", true); + + for (DBObject userObj : userCollection.find(query).skip(startIndex).limit(numResults)) { + users.add(fromDBObject(userObj)); + } + + return users; + } + + @Override + public void setName(String username, String name) throws UserNotFoundException { + // user is read-only + } + + @Override + public void setEmail(String username, String email) throws UserNotFoundException { + // user is read-only + } + + @Override + public void setCreationDate(String username, Date creationDate) throws UserNotFoundException { + // user is read-only + } + + @Override + public void setModificationDate(String username, Date modificationDate) throws UserNotFoundException { + // user is read-only + } + + @Override + public Set getSearchFields() { + return new LinkedHashSet(Arrays.asList(ValidUsers.IM_USERNAME_FILTER, ValidUsers.IM_NAME_FILTER, + ValidUsers.IM_EMAIL_FILTER)); + } + + @Override + public Collection findUsers(Set fields, String query) { + return findUsers(fields, query, 0, Integer.MAX_VALUE); + } + + @Override + public Collection findUsers(Set fields, String query, int startIndex, int numResults) { + if (fields.isEmpty()) { + return Collections.emptyList(); + } + if (!getSearchFields().containsAll(fields)) { + throw new IllegalArgumentException("Search fields " + fields + " are not valid."); + } + if (query == null || "".equals(query)) { + return Collections.emptyList(); + } + + QueryBuilder mongoQuery = QueryBuilder.start(); + if (fields.contains("Username")) { + DBObject q = new BasicDBObject(); + q.put(IM_ID, query); + DBObject altQ = new BasicDBObject(); + altQ.put(ALT_IM_ID, query); + mongoQuery.or(q, altQ); + } + if (fields.contains("Name")) { + DBObject q = new BasicDBObject(); + q.put(IM_DISPLAY_NAME, query); + mongoQuery.or(q); + } + if (fields.contains("Email")) { + DBObject q = new BasicDBObject(); + q.put(EMAIL, query); + mongoQuery.or(q); + } + + List users = new ArrayList(); + DBCollection userCollection = getCollection(); + + for (DBObject userObj : userCollection.find(mongoQuery.get()).skip(startIndex).limit(numResults)) { + users.add(fromDBObject(userObj)); + } + + return users; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public boolean isNameRequired() { + return false; + } + + @Override + public boolean isEmailRequired() { + return false; + } + + private static User fromDBObject(DBObject userObj) { + User u = null; + + if (userObj != null) { + String username = (String) userObj.get("imid"); + String sipId = (String) userObj.get(UID); + String name = (String) userObj.get("imdn"); + String email = (String) userObj.get("email"); + Date creationDate = new Date(); + if (userObj.containsField("creationDate")) { + creationDate = new Date((Long) userObj.get("creationDate")); + } + Date modificationDate = new Date((Long) userObj.get("lastUpdated")); + + u = new User(username, name, email, creationDate, modificationDate); + u.setNameVisible(true); + u.getProperties().put(UID, defaultIfEmpty(sipId, "")); + u.getProperties().put(SIP_UID, sipId); + } + log.debug(String.format("Found in mongo: %s %s %s", u.getUsername(), u.getName(), u.getUID())); + return u; + } + + private static DBCollection getCollection() { + DB db = UnfortunateLackOfSpringSupportFactory.getImdb(); + + return db.getCollection(COLLECTION_NAME); + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoVCardProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoVCardProvider.java new file mode 100755 index 0000000000..abb1758025 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/MongoVCardProvider.java @@ -0,0 +1,434 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.provider; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathFactory; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringEscapeUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.jivesoftware.openfire.provider.VCardProvider; +import org.jivesoftware.util.AlreadyExistsException; +import org.jivesoftware.util.NotFoundException; +import org.sipfoundry.commons.mongo.MongoFactory; +import org.sipfoundry.commons.userdb.profile.Address; +import org.sipfoundry.commons.userdb.profile.UserProfile; +import org.sipfoundry.commons.userdb.profile.UserProfileService; +import org.sipfoundry.commons.userdb.profile.UserProfileServiceImpl; +import org.sipfoundry.openfire.vcard.ContactInfoHandlerImpl; +import org.sipfoundry.openfire.vcard.synchserver.VCardRpcServer; +import org.springframework.data.mongodb.core.MongoTemplate; + +import com.mongodb.Mongo; + +public class MongoVCardProvider implements VCardProvider { + private static Logger logger = Logger.getLogger(MongoVCardProvider.class); + private UserProfileService m_userProfileService; + private final XPath m_xpath = XPathFactory.newInstance().newXPath(); + + public MongoVCardProvider() throws Exception { + initUserProfileService(); + startVCardSynchServer(); + } + + private void initUserProfileService() throws Exception { + Mongo mongo = MongoFactory.fromConnectionFile(); + MongoTemplate profilesDb = new MongoTemplate(mongo, "profiles"); + m_userProfileService = new UserProfileServiceImpl(); + ((UserProfileServiceImpl) m_userProfileService).setProfilesDb(profilesDb); + } + + private void startVCardSynchServer() { + logger.info(this.getClass().getName() + " starting XML RPC server ..."); + try { + VCardRpcServer vcardRpcServer = new VCardRpcServer(ContactInfoHandlerImpl.class); + vcardRpcServer.start(); + logger.info(this.getClass().getName() + " initialized"); + } catch (Exception ex) { + logger.error(ex); + } + } + + @Override + public Element createVCard(String username, Element xcard) throws AlreadyExistsException { + // no need to create, users are created only via sipxconfig + return xcard; + } + + @Override + public void deleteVCard(String userName) { + CacheHolder.removeAvatarByImId(userName); + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public Element loadVCard(String userName) { + UserProfile profile = m_userProfileService.getUserProfileByImId(userName); + if (profile == null) { + logger.debug(String.format("Im ID %s not known by sipxecs, returning empty VCard", userName)); + return createEmptyVCard(); + } + return convertToVCard(profile); + } + + private static Element createEmptyVCard() { + StringBuilder xbuilder = new StringBuilder(""); + xbuilder.append(""); + return convertToElement(xbuilder.toString()); + } + + @Override + public Element updateVCard(String userName, Element xcard) throws NotFoundException { + UserProfile userProfile = m_userProfileService.getUserProfileByImId(userName); + if (userProfile == null) { + logger.warn(String.format("Im ID %s not known by sipxecs", userName)); + return xcard; + } + try { + logger.debug("Updating user profile for " + userName); + updateUserProfile(userProfile, xcard); + } catch (Exception ex) { + logger.error(String.format("failed to update avatar %s", ex.getMessage())); + } + return xcard; + } + + private static Element convertToElement(String vcard) { + try { + SAXReader sreader = new SAXReader(); + Document vcardDoc = sreader.read(new StringReader(vcard)); + return vcardDoc.getRootElement(); + } catch (Exception ex) { + logger.error(ex); + } + return null; + } + + private Element convertToVCard(UserProfile profile) { + logger.debug("convert vcard to element"); + StringBuilder xbuilder = new StringBuilder(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getImDisplayName())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getLastName())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getFirstName())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getCompanyName())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getJobDept())); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getJobTitle())); + xbuilder.append(""); + + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getFaxNumber())); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getOfficeAddress().getStreet())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getOfficeAddress().getCity())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getOfficeAddress().getState())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getOfficeAddress().getZip())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getOfficeAddress().getCountry())); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getHomePhoneNumber())); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getCellPhoneNumber())); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getHomeAddress().getStreet())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getHomeAddress().getCity())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getHomeAddress().getState())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getHomeAddress().getZip())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getHomeAddress().getCountry())); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getEmailAddress())); + xbuilder.append(""); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getImId())); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getUserName())); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getDidNumber())); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getAlternateEmailAddress())); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getAlternateImId())); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getManager())); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getSalutation())); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getAssistantName())); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getAssistantPhoneNumber())); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getLocation())); + xbuilder.append(""); + + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getTwiterName())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getFacebookName())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getLinkedinName())); + xbuilder.append(""); + xbuilder.append(""); + xbuilder.append(defaultValue(profile.getXingName())); + xbuilder.append(""); + xbuilder.append(""); + + String encodedStr = getEncodedAvatar(defaultValue(profile.getUserName())); + if (encodedStr != null) { + xbuilder.append(""); + xbuilder.append("image/png"); + xbuilder.append(""); + xbuilder.append(encodedStr); + xbuilder.append(""); + xbuilder.append(""); + } + + xbuilder.append(""); + return convertToElement(xbuilder.toString()); + } + + private String getEncodedAvatar(String userName) { + byte[] avatarContent = getAvatarAsByteArray(userName); + return avatarContent != null ? new String(new Base64().encode(avatarContent)) : null; + } + + private static String defaultValue(String value) { + return StringUtils.defaultString(StringEscapeUtils.escapeXml(value), StringUtils.EMPTY); + } + + @SuppressWarnings("resource") + public byte[] getAvatarAsByteArray(String userName) { + InputStream is = null; + try { + is = m_userProfileService.getAvatar(userName); + return is != null ? IOUtils.toByteArray(is) : null; + } catch (IOException ex) { + return null; + } finally { + IOUtils.closeQuietly(is); + } + } + + private void updateUserProfile(UserProfile profile, Element xcard) throws Exception { + DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = domFactory.newDocumentBuilder(); + org.w3c.dom.Document vcardDoc = builder.parse(IOUtils.toInputStream(xcard.asXML())); + profile.setLastName(extractField("//vCard/N/FAMILY/text()", vcardDoc, profile.getLastName())); + profile.setFirstName(extractField("//vCard/N/GIVEN/text()", vcardDoc, profile.getFirstName())); + profile.setImDisplayName(extractField("//vCard/FN/text()", vcardDoc, profile.getImDisplayName())); + profile.setCompanyName(extractField("//vCard/ORG/ORGNAME/text()", vcardDoc, profile.getCompanyName())); + profile.setJobDept(extractField("//vCard/ORG/ORGUNIT/text()", vcardDoc, profile.getJobDept())); + profile.setJobTitle(extractField("//vCard/TITLE/text()", vcardDoc, profile.getJobTitle())); + profile.setFaxNumber(extractField("//vCard/TEL[WORK][FAX]/NUMBER/text()", vcardDoc, profile.getFaxNumber())); + profile.setHomePhoneNumber(extractField("//vCard/TEL[HOME][VOICE]/NUMBER/text()", vcardDoc, + profile.getHomePhoneNumber())); + profile.setCellPhoneNumber(extractField("//vCard/TEL[HOME][CELL]/NUMBER/text()", vcardDoc, + profile.getCellPhoneNumber())); + + Address officeAddress = profile.getOfficeAddress(); + officeAddress + .setStreet(extractField("//vCard/ADR[WORK]/STREET/text()", vcardDoc, officeAddress.getStreet())); + officeAddress.setCity(extractField("//vCard/ADR[WORK]/LOCALITY/text()", vcardDoc, officeAddress.getCity())); + officeAddress.setState(extractField("//vCard/ADR[WORK]/REGION/text()", vcardDoc, officeAddress.getState())); + officeAddress.setZip(extractField("//vCard/ADR[WORK]/PCODE/text()", vcardDoc, officeAddress.getZip())); + officeAddress + .setCountry(extractField("//vCard/ADR[WORK]/CTRY/text()", vcardDoc, officeAddress.getCountry())); + profile.setOfficeAddress(officeAddress); + + Address homeAddress = profile.getHomeAddress(); + homeAddress.setStreet(extractField("//vCard/ADR[HOME]/STREET/text()", vcardDoc, homeAddress.getStreet())); + homeAddress.setCity(extractField("//vCard/ADR[HOME]/LOCALITY/text()", vcardDoc, homeAddress.getCity())); + homeAddress.setState(extractField("//vCard/ADR[HOME]/REGION/text()", vcardDoc, homeAddress.getState())); + homeAddress.setZip(extractField("//vCard/ADR[HOME]/PCODE/text()", vcardDoc, homeAddress.getZip())); + homeAddress.setCountry(extractField("//vCard/ADR[HOME]/CTRY/text()", vcardDoc, homeAddress.getCountry())); + profile.setHomeAddress(homeAddress); + + profile.setEmailAddress(extractField("//vCard/EMAIL/USERID/text()", vcardDoc, profile.getEmailAddress())); + profile.setSalutation(extractField("//vCard/X-SALUTATION/text()", vcardDoc, profile.getSalutation())); + profile.setDidNumber(extractField("//vCard/X-DID/text()", vcardDoc, profile.getDidNumber())); + profile.setAlternateEmailAddress(extractField("//vCard/X-ALT-EMAIL/text()", vcardDoc, + profile.getAlternateEmailAddress())); + profile.setAlternateImId(extractField("//vCard/X-ALT-JABBERID/text()", vcardDoc, profile.getAlternateImId())); + profile.setAssistantName(extractField("//vCard/X-ASSISTANT/text()", vcardDoc, profile.getAssistantName())); + profile.setAssistantPhoneNumber(extractField("//vCard/X-ASSISTANT-PHONE/text()", vcardDoc, + profile.getAssistantPhoneNumber())); + profile.setAssistantPhoneNumber(extractField("//vCard/X-ASSISTANT-PHONE/text()", vcardDoc, + profile.getAssistantPhoneNumber())); + profile.setLocation(extractField("//vCard/X-LOCATION/text()", vcardDoc, profile.getLocation())); + profile.setTwiterName(extractField("//vCard/X-TWITTER/text()", vcardDoc, profile.getTwiterName())); + profile.setLinkedinName(extractField("//vCard/X-LINKEDIN/text()", vcardDoc, profile.getLinkedinName())); + profile.setFacebookName(extractField("//vCard/X-FACEBOOK/text()", vcardDoc, profile.getFacebookName())); + profile.setXingName(extractField("//vCard/X-XING/text()", vcardDoc, profile.getXingName())); + + m_userProfileService.saveUserProfile(profile); + updateAvatar(profile.getUserName(), vcardDoc); + } + + private void updateAvatar(String username, org.w3c.dom.Document xcard) { + try { + String encodedAvatar = m_xpath.evaluate("//vCard/PHOTO/BINVAL/text()", xcard); + if (encodedAvatar == null) { + return; + } + byte[] avtContent = new Base64().decode(encodedAvatar.getBytes()); + logger.debug("Saving avatar for: " + username); + m_userProfileService.saveAvatar(username, new ByteArrayInputStream(avtContent), true); + } catch (Exception ex) { + logger.error("Cannot update avatar ", ex); + } + } + + private String extractField(String expression, org.w3c.dom.Document xcard, String defaultValue) { + try { + String value = m_xpath.evaluate(expression, xcard); + + if (StringUtils.isNotBlank(value)) { + return value; + } + + return defaultValue; + + } catch (Exception ex) { + logger.error(String.format("cannot extract field from vcard using %s", expression)); + return defaultValue; + } + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/package-info.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/package-info.java new file mode 100644 index 0000000000..77daf5f1c6 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/provider/package-info.java @@ -0,0 +1,7 @@ +/** + * This package contains implementations for all providers defined in Openfire. Uses MongoDB backend. + * Currently, each table in Openfire schema is replicated into a mongodb collection. In some cases, collections should + * be converted into embedded documents, for easier querying. + */ +package org.sipfoundry.openfire.provider; + diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/user/MongoUser.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/user/MongoUser.java new file mode 100755 index 0000000000..d1fe3fd13e --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/user/MongoUser.java @@ -0,0 +1,37 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.user; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.jivesoftware.openfire.user.User; + +/** + * Was only used by {@link MongoUserProvider} + */ +public class MongoUser extends User { + + private Map m_properties; + + public MongoUser() { + + } + + public MongoUser(String username, String name, String email, Date creationDate, Date modificationDate) { + super(username, name, email, creationDate, modificationDate); + } + + @Override + public Map getProperties() { + if (m_properties == null) { + m_properties = new HashMap(); + } + return m_properties; + } + +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/user/MongoUserProvider.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/user/MongoUserProvider.java new file mode 100755 index 0000000000..8346eacd18 --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/user/MongoUserProvider.java @@ -0,0 +1,187 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.user; + +import static org.apache.commons.lang.StringUtils.defaultIfEmpty; +import static org.apache.commons.lang.StringUtils.lowerCase; +import static org.sipfoundry.commons.mongo.MongoConstants.UID; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jivesoftware.openfire.XMPPServer; +import org.jivesoftware.openfire.provider.UserProvider; +import org.jivesoftware.openfire.user.User; +import org.jivesoftware.openfire.user.UserAlreadyExistsException; +import org.jivesoftware.openfire.user.UserCollection; +import org.jivesoftware.openfire.user.UserNotFoundException; +import org.sipfoundry.commons.userdb.ValidUsers; +import org.sipfoundry.commons.util.UnfortunateLackOfSpringSupportFactory; +import org.sipfoundry.openfire.provider.MongoAuthProvider; +import org.xmpp.packet.JID; + +/** + * Deprecated because of performance issues. Use {@link MongoUserpProviderAlt} instead + */ +@Deprecated +public class MongoUserProvider implements UserProvider { + public static final String SIP_UID = "sipUid"; + + private final Map m_userMapping = new HashMap(); + + public MongoUserProvider() { + // Executors.newFixedThreadPool(1).execute(new MongoOplogListener()); + } + + @Override + public User createUser(String username, String password, String name, String email) + throws UserAlreadyExistsException { + try { + return loadUser(username); + } catch (UserNotFoundException ex) { + throw new UnsupportedOperationException(); + } + } + + @Override + public void deleteUser(String username) { + m_userMapping.values().remove(username); + } + + @Override + public Collection findUsers(Set fields, String query) throws UnsupportedOperationException { + return findUsers(fields, query, 0, getUserCount()); + } + + @Override + public Collection findUsers(Set fields, String query, int startIndex, int numResults) + throws UnsupportedOperationException { + if (fields.isEmpty()) { + return Collections.emptyList(); + } + if (!getSearchFields().containsAll(fields)) { + throw new IllegalArgumentException("Search fields " + fields + " are not valid."); + } + if (query == null || "".equals(query)) { + return Collections.emptyList(); + } + List users = UnfortunateLackOfSpringSupportFactory.getValidUsers() + .getImUsersByFilter(fields, query, startIndex, numResults); + List ofUsers = new ArrayList(); + for (org.sipfoundry.commons.userdb.User user : users) { + User ofUser = getOfUser(user); + ofUser.setNameVisible(true); + ofUsers.add(ofUser); + } + return ofUsers; + } + + @Override + public Set getSearchFields() throws UnsupportedOperationException { + return new LinkedHashSet(Arrays.asList(ValidUsers.IM_USERNAME_FILTER, ValidUsers.IM_NAME_FILTER, + ValidUsers.IM_EMAIL_FILTER)); + } + + @Override + public int getUserCount() { + Long count = UnfortunateLackOfSpringSupportFactory.getValidUsers().getImUsersCount(); + return Integer.parseInt(Long.toString(count)); + } + + @Override + public Collection getUsernames() { + return getUsernames(0, getUserCount()); + } + + @Override + public Collection getUsers() { + Collection usernames = getUsernames(0, getUserCount()); + return new UserCollection(usernames.toArray(new String[usernames.size()])); + } + + @Override + public Collection getUsers(int start, int end) { + Collection usernames = getUsernames(start, end); + return new UserCollection(usernames.toArray(new String[usernames.size()])); + } + + private static Collection getUsernames(int startIndex, int numResults) { + return UnfortunateLackOfSpringSupportFactory.getValidUsers().getImUsernames(startIndex, numResults); + } + + @Override + public boolean isEmailRequired() { + return false; + } + + @Override + public boolean isNameRequired() { + return false; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public User loadUser(String username) throws UserNotFoundException { + if (username.contains("@")) { + if (!XMPPServer.getInstance().isLocal(new JID(username))) { + throw new UserNotFoundException("Cannot load user of remote server: " + username); + } + username = username.substring(0, username.lastIndexOf("@")); + } + org.sipfoundry.commons.userdb.User user = UnfortunateLackOfSpringSupportFactory.getValidUsers() + .getUserByInsensitiveJid(username); + if (user == null || (!user.getUserName().equals(MongoAuthProvider.SUPERADMIN) && !user.isImEnabled())) { + throw new UserNotFoundException("user not found"); + } + m_userMapping.put(user.getSysId(), user.getJid()); + return getOfUser(user); + } + + private static User getOfUser(org.sipfoundry.commons.userdb.User user) { + User ofUser = new MongoUser(lowerCase(user.getJid()), user.getImDisplayName(), user.getEmailAddress(), + new Date(), new Date()); + ofUser.setNameVisible(true); + ofUser.getProperties().put(UID, defaultIfEmpty(user.getUserName(), "")); + ofUser.getProperties().put(SIP_UID, user.getUserName()); + return ofUser; + } + + @Override + public void setCreationDate(String arg0, Date arg1) throws UserNotFoundException { + // user is read-only + } + + @Override + public void setEmail(String arg0, String arg1) throws UserNotFoundException { + // user is read-only + } + + @Override + public void setModificationDate(String arg0, Date arg1) throws UserNotFoundException { + // user is read-only + } + + @Override + public void setName(String arg0, String arg1) throws UserNotFoundException { + // user is read-only + } + + public String getUserName(String sysId) { + return m_userMapping.get(sysId); + } +} diff --git a/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/vcard/ContactInfoHandlerImpl.java b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/vcard/ContactInfoHandlerImpl.java new file mode 100755 index 0000000000..0b021133ae --- /dev/null +++ b/sipXopenfire/mongo-lib/src/org/sipfoundry/openfire/vcard/ContactInfoHandlerImpl.java @@ -0,0 +1,33 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.vcard; + +import org.apache.log4j.Logger; +import org.dom4j.Element; +import org.jivesoftware.openfire.provider.VCardProvider; +import org.jivesoftware.openfire.vcard.VCardManager; +import org.sipfoundry.openfire.vcard.synchserver.ContactInfoHandler; +import org.sipfoundry.openfire.vcard.synchserver.Util; + +public class ContactInfoHandlerImpl implements ContactInfoHandler { + private static Logger logger = Logger.getLogger(ContactInfoHandlerImpl.class); + + @Override + public void notifyContactChange(String userName) { + VCardProvider provider = VCardManager.getProvider(); + Element userVCard = provider.loadVCard(userName); + try { + logger.debug("Start synchronizing vcard"); + VCardManager.getInstance().setVCard(userName, userVCard); + Util.updateAvatar(userName, userVCard); + // Sending announcement to the client + Util.notify(userName); + logger.debug("Finished synchronizing vcard"); + } catch (Exception e) { + logger.error("Cannot synchronize VCard for: " + userName); + } + } +} diff --git a/sipXopenfire/mongo-lib/test/Makefile.am b/sipXopenfire/mongo-lib/test/Makefile.am new file mode 100644 index 0000000000..ad331bf9f9 --- /dev/null +++ b/sipXopenfire/mongo-lib/test/Makefile.am @@ -0,0 +1,6 @@ +include $(top_srcdir)/config/utility.am +include $(top_srcdir)/config/java.am +include $(top_srcdir)/common.am + +SUBDIRS = java + diff --git a/sipXopenfire/mongo-lib/test/java/Makefile.am b/sipXopenfire/mongo-lib/test/java/Makefile.am new file mode 100644 index 0000000000..d6a5bf10a2 --- /dev/null +++ b/sipXopenfire/mongo-lib/test/java/Makefile.am @@ -0,0 +1,34 @@ +include $(top_srcdir)/config/utility.am +include $(top_srcdir)/config/java.am +include $(top_srcdir)/common.am + +noinst_DATA = javac-test + +EXTRA_DIST = \ + $(test_SRC) \ + $(test_RESOURCES) + +test_SRC = $(shell cd $(srcdir); find . \( \ + -name '*.java' \ + \)) + +test_RESOURCES = $(shell cd $(srcdir); find . \( \ + -type f \ + -not -name '*.java' \ + -not -name 'README.TXT' \ + \)) + +test_PKGS = \ + $(plugin_PKGS) \ + junit + +test_DEPS = \ + $(srcdir) \ + $(JAVAROOT) \ + $(mongoplugin_JAVAROOT) \ + $(call JavaDep,@SIPX_JAVADIR@/sipXcommons,$(test_PKGS)) \ + @OPENFIRE_HOME@/lib/openfire.jar \ + @OPENFIRE_HOME@/lib/slf4j-log4j12.jar \ + @SIPX_JAVADIR@/sipXopenfire/lib/sipx-openfire-vcard-synchserver.jar + +precommit : check diff --git a/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/BaseMongoTest.java b/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/BaseMongoTest.java new file mode 100644 index 0000000000..3ad882f00b --- /dev/null +++ b/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/BaseMongoTest.java @@ -0,0 +1,64 @@ +package org.sipfoundry.openfire.provider; + +import java.net.UnknownHostException; + +import org.jivesoftware.openfire.XMPPServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import com.mongodb.DB; +import com.mongodb.MongoClient; + +/** + * Setup and teardown for all tests going to OpenfireDB is the same. + */ +public abstract class BaseMongoTest { + private static final String IM_DB_NAME = "imdb_TEST"; + private static final String OF_DB_NAME = "openfiredb_TEST"; + + private static MongoClient mongoClient; + private static DB openfiredb; + private static DB imdb; + private static XMPPServer server; + + @BeforeClass + public static void classSetup() throws UnknownHostException { + System.setProperty("mongo_ns", IM_DB_NAME); + System.setProperty("openfire_ns", OF_DB_NAME); + System.setProperty("openfireHome", "./mongo-lib/src/test/resources"); + System.setProperty("conf.dir", "./mongo-lib/src/test/resources"); + System.setProperty("provider.properties.className", "org.jivesoftware.util.FilePropertiesProvider"); + System.setProperty("configFile", "mongo-lib/src/test/resources/openfire.properties"); + + mongoClient = new MongoClient(); + imdb = mongoClient.getDB(IM_DB_NAME); + openfiredb = mongoClient.getDB(OF_DB_NAME); + // if (server == null) { + // synchronized (new byte[0]) { + // if (server == null) { + // server = new XMPPServer(); + // } + // } + // } + try { + server = new XMPPServer(); + } catch (IllegalStateException ex) { + server = XMPPServer.getInstance(); + } + } + + @AfterClass + public static void classTeardown() { + server.stop(); + mongoClient.dropDatabase(IM_DB_NAME); + mongoClient.dropDatabase(OF_DB_NAME); + } + + protected static DB getOpenfireDb() { + return openfiredb; + } + + protected static DB getImdb() { + return imdb; + } +} diff --git a/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/GroupProviderTest.java b/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/GroupProviderTest.java new file mode 100644 index 0000000000..fa3961278a --- /dev/null +++ b/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/GroupProviderTest.java @@ -0,0 +1,112 @@ +package org.sipfoundry.openfire.provider; + +import static org.junit.Assert.assertEquals; + +import java.util.Collection; + +import org.jivesoftware.openfire.group.Group; +import org.jivesoftware.openfire.group.GroupAlreadyExistsException; +import org.jivesoftware.openfire.provider.GroupProvider; +import org.junit.After; +import org.junit.Test; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; + +@SuppressWarnings("static-method") +public class GroupProviderTest extends BaseMongoTest { + @After + public void teardown() { + getImdb().getCollection("entity").remove(new BasicDBObject()); + } + + @Test + public void testCreateNonExistingGroup() throws GroupAlreadyExistsException { + GroupProvider provider = new MongoGroupProviderAlt(); + Group g = provider.createGroup("testGrp"); + + assertEquals(0L, getImdb().getCollection("entity").count()); + assertEquals(null, g); + } + + @Test + public void testCreateExistingGroup() throws GroupAlreadyExistsException { + GroupProvider provider = new MongoGroupProviderAlt(); + + insertGroup("testGrp"); + + Group g = provider.createGroup("testGrp"); + + assertEquals(1L, getImdb().getCollection("entity").count()); + assertEquals("testGrp", g.getName()); + } + + @Test + public void testSearchWildcardPrefix() { + GroupProvider provider = new MongoGroupProviderAlt(); + + insertGroup("testGrp"); + insertGroup("additional"); + + Collection names = provider.search("*Grp"); + + assertEquals(1L, names.size()); + for (String name : names) { + assertEquals("testGrp", name); + } + } + + @Test + public void testSearchWildcardSuffix() { + GroupProvider provider = new MongoGroupProviderAlt(); + + insertGroup("testGrp"); + insertGroup("additional"); + + Collection names = provider.search("test*"); + + assertEquals(1L, names.size()); + for (String name : names) { + assertEquals("testGrp", name); + } + } + + @Test + public void testSearchWildcardMiddle() { + GroupProvider provider = new MongoGroupProviderAlt(); + + insertGroup("testGrp"); + insertGroup("additional"); + + Collection names = provider.search("te*rp"); + + assertEquals(1L, names.size()); + for (String name : names) { + assertEquals("testGrp", name); + } + } + + @Test + public void testSearchWildcardAll() { + GroupProvider provider = new MongoGroupProviderAlt(); + + insertGroup("testGroup"); + insertGroup("additional"); + + Collection names = provider.search("*es*ro*"); + + assertEquals(1L, names.size()); + for (String name : names) { + assertEquals("testGroup", name); + } + } + + private static void insertGroup(String groupName) { + DBObject grpObj = new BasicDBObject(); + + grpObj.put("ent", "group"); + grpObj.put("uid", groupName); + + getImdb().getCollection("entity").insert(grpObj); + } +} diff --git a/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/OfflineMessageProviderTest.java b/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/OfflineMessageProviderTest.java new file mode 100644 index 0000000000..60dd4be28a --- /dev/null +++ b/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/OfflineMessageProviderTest.java @@ -0,0 +1,51 @@ +package org.sipfoundry.openfire.provider; + +import static org.junit.Assert.assertEquals; + +import java.util.Collection; + +import org.dom4j.io.SAXReader; +import org.jivesoftware.openfire.OfflineMessage; +import org.jivesoftware.openfire.provider.OfflineMessageProvider; +import org.junit.After; +import org.junit.Test; +import org.xmpp.packet.Message; + +import com.mongodb.BasicDBObject; + +@SuppressWarnings("static-method") +public class OfflineMessageProviderTest extends BaseMongoTest { + + @After + public void teardown() { + getOpenfireDb().getCollection("ofOffline").remove(new BasicDBObject()); + } + + @Test + public void testWrite() { + OfflineMessageProvider provider = new MongoOfflineMessageProvider(); + + provider.addMessage("gigel", 1, "gigel says 'hello world'"); + + assertEquals(1, getOpenfireDb().getCollection("ofOffline").find().count()); + } + + @Test + public void testRead() { + String testMessage = "gigel says 'hello world'"; + OfflineMessageProvider provider = new MongoOfflineMessageProvider(); + SAXReader reader = new SAXReader(); + Message message = new Message(); + message.setBody(testMessage); + + provider.addMessage("gigel", 1, message.getElement().asXML()); + + Collection messagesFromDB = provider.getMessages("gigel", false, reader); + + assertEquals(1, messagesFromDB.size()); + + OfflineMessage messageFromDB = messagesFromDB.iterator().next(); + + assertEquals(testMessage, messageFromDB.getBody()); + } +} diff --git a/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/PubsubProviderTest.java b/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/PubsubProviderTest.java new file mode 100644 index 0000000000..20853830da --- /dev/null +++ b/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/PubsubProviderTest.java @@ -0,0 +1,52 @@ +package org.sipfoundry.openfire.provider; + +import static org.junit.Assert.assertEquals; + +import org.jivesoftware.openfire.XMPPServer; +import org.jivesoftware.openfire.provider.PubSubProvider; +import org.jivesoftware.openfire.pubsub.DefaultNodeConfiguration; +import org.jivesoftware.openfire.pubsub.PubSubService; +import org.junit.After; +import org.junit.Test; + +import com.mongodb.BasicDBObject; + +@SuppressWarnings("static-method") +public class PubsubProviderTest extends BaseMongoTest { + private static final String TEST_LANGUAGE = "latin"; + + @After + public void teardown() { + getOpenfireDb().getCollection("ofPubsubDefaultConf").remove(new BasicDBObject()); + } + + @Test + public void testConfigWrite() { + PubSubProvider provider = new MongoPubSubProvider(); + PubSubService srv = XMPPServer.getInstance().getPubSubModule(); + DefaultNodeConfiguration config = new DefaultNodeConfiguration(true); + + config.setLanguage(TEST_LANGUAGE); + // some configurations are created on server initialization + long before = getOpenfireDb().getCollection("ofPubsubDefaultConf").count(); + provider.createDefaultConfiguration(srv, config); + + assertEquals(before + 1, getOpenfireDb().getCollection("ofPubsubDefaultConf").count()); + } + + @Test + public void testConfigRead() { + PubSubProvider provider = new MongoPubSubProvider(); + PubSubService srv = XMPPServer.getInstance().getPubSubModule(); + DefaultNodeConfiguration config = new DefaultNodeConfiguration(true); + + config.setLanguage(TEST_LANGUAGE); + provider.createDefaultConfiguration(srv, config); + + config.setLanguage(TEST_LANGUAGE); + + DefaultNodeConfiguration configFromDB = provider.loadDefaultConfiguration(srv, true); + + assertEquals(TEST_LANGUAGE, configFromDB.getLanguage()); + } +} diff --git a/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/UIDProviderTest.java b/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/UIDProviderTest.java new file mode 100644 index 0000000000..a49378a65a --- /dev/null +++ b/sipXopenfire/mongo-lib/test/java/org/sipfoundry/openfire/provider/UIDProviderTest.java @@ -0,0 +1,54 @@ +package org.sipfoundry.openfire.provider; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.jivesoftware.openfire.provider.UIDProvider; +import org.junit.After; +import org.junit.Test; + +import com.mongodb.BasicDBObject; + +@SuppressWarnings("static-method") +public class UIDProviderTest extends BaseMongoTest { + + @After + public void teardown() { + getOpenfireDb().getCollection("ofId").remove(new BasicDBObject()); + } + + @Test + public void testGetBlock() { + UIDProvider provider = new MongoUIDProvider(); + + long[] ids = provider.getNextBlock(1, 5); + + assertNotNull(ids); + assertEquals(2, ids.length); + assertEquals(1, ids[0]); // starting id for a type that's not in the DB yet + assertEquals(6, ids[1]); // next starting id + } + + @Test + public void testGetBlockAgain() { + UIDProvider provider = new MongoUIDProvider(); + + long[] ids = provider.getNextBlock(1, 3); + ids = provider.getNextBlock(1, 15); + + assertEquals(4, ids[0]); // starts where the first request ended + assertEquals(19, ids[1]); // next starting id + } + + @Test + public void testGetBlockHandleLong() { + UIDProvider provider = new MongoUIDProvider(); + + long[] ids = provider.getNextBlock(1, 7); + ids = provider.getNextBlock(1, Integer.MAX_VALUE); // that'll bring us into long territory + + assertEquals(8, ids[0]); // starts where the first request ended + assertEquals(Integer.MAX_VALUE + 8L, ids[1]); // next starting id + } + +} diff --git a/sipXopenfire/mongo-lib/test/java/resources/README.TXT b/sipXopenfire/mongo-lib/test/java/resources/README.TXT new file mode 100644 index 0000000000..c9a915cd4b --- /dev/null +++ b/sipXopenfire/mongo-lib/test/java/resources/README.TXT @@ -0,0 +1 @@ +Folders in this folder are copying Opefire's folder structure, as some classes will need to read configuration settings. diff --git a/sipXopenfire/mongo-lib/test/java/resources/conf/openfire.xml b/sipXopenfire/mongo-lib/test/java/resources/conf/openfire.xml new file mode 100644 index 0000000000..f08867cf6e --- /dev/null +++ b/sipXopenfire/mongo-lib/test/java/resources/conf/openfire.xml @@ -0,0 +1,47 @@ + + + + + + + + 9094 + 9095 + + + + + org.jivesoftware.database.DefaultConnectionProvider + + + + org.postgresql.Driver + jdbc:postgresql://localhost:5432/openfire + postgres + postgres + + + true + + + false + + + + true + + diff --git a/sipXopenfire/mongo-lib/test/java/resources/mongo-client.ini b/sipXopenfire/mongo-lib/test/java/resources/mongo-client.ini new file mode 100644 index 0000000000..7bc6ac6b33 --- /dev/null +++ b/sipXopenfire/mongo-lib/test/java/resources/mongo-client.ini @@ -0,0 +1,3 @@ +connectionUrl=mongodb://localhost:27017 +connectionString=something entirely different +BogusValue=bogus \ No newline at end of file diff --git a/sipXopenfire/mongo-lib/test/java/resources/openfire.properties b/sipXopenfire/mongo-lib/test/java/resources/openfire.properties new file mode 100644 index 0000000000..1bd56fb742 --- /dev/null +++ b/sipXopenfire/mongo-lib/test/java/resources/openfire.properties @@ -0,0 +1,52 @@ +admin.authorizedJIDs=admin@xmpptest.3zuce.com,superadmin@xmpptest.3zuce.com +cache.clustering.clustered.class=com.jivesoftware.util.cache.ClusteredCacheFactory +cache.group.size=40000000 +cache.lastActivity.size=1500000 +cache.offlinePresence.size=1500000 +cache.userCache.size=40000000 +cache.username2roster.size=12000000 +hazelcast.config.xml.filename=openuc-cache-config.xml +provider.admin.className=org.jivesoftware.openfire.admin.DefaultAdminProvider +provider.auth.className=org.sipfoundry.openfire.provider.MongoAuthProvider +provider.connectivity.className=org.sipfoundry.openfire.provider.MongoConnectivityProvider +provider.group.className=org.sipfoundry.openfire.provider.MongoGroupProviderAlt +provider.groupprops.className=org.sipfoundry.openfire.provider.MongoGroupPropertiesProvider +provider.lockout.className=org.sipfoundry.openfire.provider.MongoLockOutProvider +provider.muc.className=org.sipfoundry.openfire.provider.MongoMucProvider +provider.offline.className=org.sipfoundry.openfire.provider.MongoOfflineMessageProvider +provider.presence.className=org.sipfoundry.openfire.provider.MongoPresenceProvider +provider.privacylist.className=org.sipfoundry.openfire.provider.MongoPrivacyListProvider +provider.pubsub.className=org.sipfoundry.openfire.provider.MongoPubSubProvider +provider.roster.className=org.sipfoundry.openfire.provider.MongoRosterProvider +provider.securityAudit.className=org.sipfoundry.openfire.provider.MongoSecurityAuditProvider +provider.user.className=org.sipfoundry.openfire.provider.MongoUserProviderAlt +provider.vcard.className=org.sipfoundry.openfire.provider.MongoVCardProvider +register.inband=false +register.password=false +route.all-resources=false +rss.enabled=false +update.notify-admins=false +update.service-enabled=false +xmpp.audit.active=false +xmpp.audit.logdir=/opt/openfire/audit/ +xmpp.audit.messages=true +xmpp.audit.presence=true +xmpp.client.idle=0 +xmpp.domain=xmpptest.3zuce.com +xmpp.fqdn=openucload.xmpptest.3zuce.com +xmpp.offline.type=store +xmpp.pep.enabled=false +xmpp.privateStorageEnabled=true +xmpp.proxy.enabled=false +xmpp.proxy.externalip= +xmpp.proxy.port=7777 +xmpp.server.certificate.accept-selfsigned=true +xmpp.server.certificate.selfsigned=true +xmpp.server.permission=whitelist +xmpp.server.session.idle=-1 +xmpp.server.socket.active=false +xmpp.server.tls.enabled=true +xmpp.session.conflict-limit=0 +xmpp.socket.plain.active=true +xmpp.socket.ssl.active=true +xmpp.socket.ssl.keypass=changeit diff --git a/sipXopenfire/mongo-lib/test/java/resources/resources/security/keystore b/sipXopenfire/mongo-lib/test/java/resources/resources/security/keystore new file mode 100644 index 0000000000000000000000000000000000000000..582a741ba70e1e84ec099a088fd5036ca11a3e4c GIT binary patch literal 1749 zcmezO_TO6u1_mY|W&~rQirj*NlGNf7J>#m<gP;3Y%L^27kbU6MOsb zh$^wF#O&HElP403RqRRRg} zw|RyB&wslsB-U2t;kmx%>#WKwoTW4wEOe~z6S>%iG*0J|Mu69Ax_vP`iOPd%sICiU#^XR zF!@(R^oq}1q6Ux3Cv5ZhG;u-a?+uDyCpb=s+5C0d**^X1NSOo6LQm`$xsu80wJG+!Rrm^CGoMD~ z*S#ht{mUy?IB(_I|0K(2{mq;2+%4NDe7jgIp8Tpr_oV2wKOsMV3HfpkV$sTsYqcK%nmIpc`n zjksUcb6;dG`j{)H-x+w<{Mh3sbCUbYXY5Z6UFLk`dGOII@7!WU`;CfUB^BOXc{EGx zsKIh3&AGnUPk&7A=2>w1-ikx}zXr2DI4pQJ!E58wuF`!OJ-fWs#cGn4+~D85U+DUZ znq3Mr|4e-<{Oz^+YFqXzbC1>Cnc~wgEK!nmsqyg!?FajIW^bSISSk5(UegEe{E!#w z$AF0&n8H~j^h^yb85o#P7&I{-0Vd`(3z(T0nV47@!@Ud)c-c6$+C196^8!;fD}zDf zN<(e~PB!LH7B*p~&|pJB1AY*PgNMU0uQIqKu_V<{0GJnmLhL;3K%tQQ@;pOn14)nw z7Y~+<1XIq0EC7HKoAAwnj&!x8aLA3FCe#q{UV!RkeXMToRgoJLW!Rm=Of1mu%KaX>;=Y3 zCsSi1!*>0rf;X!=I(p@6k8E@Na_7pmc@2R9_j~z1#bzGer+16IICIR>&oKg;s5 zh_Q%p&aL=6N4I*~b_Qh`1?>d6`L!M2!Rbdcz`RWi82?t7(b$AP z@UUrBhePhXU!t454%|%o@RganS-DV>+kE$q=>gY_7Izsr+*=}e zcEV{hjzxmIYI5E+FvQv}QfB>@bb0&l?xZVefwFwB7<>&<{LXr+S7bD+-xnz0JoVV) zqDC-k2FR;##R-$i-9*O6If-Sq*pPaZQ=QJw9$ wXM5kC`xe=OJdbL)KP$`d@SAVhwsR&=>5~2};dSq4KHj&;P_aN=~ zEu(1ffX4aAer9B4U~cSXFlg*#YHVaU85240lXMrO$QFV7EDpCHhp*3ab?9&5;l5~m z^QPeWcT5rTs^7I%xUjBz!z^D>KX3BRSq!U`Yp<Od%kg&!s+0pt!>mKiwNIc_WKW)U`x+u4Vm;w`Ze2*{<{C+#ne5r z{W&@-0?!oRSaKlvgWol^17(cijg#UOKVIDU|M>;Qh_jjEk6mQuNV-^d#@w01z#rDj zH1qAE39dd*b_*=A{b0n*_wn417bgRIo+ZtHHvd5F_M$l_&8)KL`>pt&!kYq4aH7EE zRgqgzP?B0)qGw!Hnw+YaoSzF#Z8GrG#{9;hiTMRE#a&py%*4pV#KIWvrH||$%%u9!!fTixFoS8)lk5I4!|;V44g{v4%#51|SGdqQDf3%pFpG zX<}4DjtWrfZ(`(U0G170Oihf83|o>i|B8nQU#n&i_|(i@(pw~VcI}^^%Il{ct~N zW7gZ1EK4dMUW>^8`hB+C(h!>%b<4F}W-;IU^sJsX?g~BSe8K<9)8F-S2lv4Ya4E z%TtfL$)(r4dH0|H_OsWTTYNH;7inqlve+Y#U0srVeoK(^?`W2^JB@#O;wLzN{$zLf zO6rHKc)y>C{q;|if)86S(bbI%$!wWABhPC6i>0%-f-~S^F#{0;Az<2(6=r1o&%$BA z2Ber68IVH`n23QP$H)+P!{fSWu`+|OcHOJUqDO1L&MnNc4OpsB(>d>(;Y|sh#i|`? zzph5E`w;fkM5gjk$Uio=*)QkJ*l_(l(4aDTrvKzTLqT;%a3wd7{8w1 zcr)eQjGEl_Ih{*94z=gMu3Y?xr)dBBpq#w2yf=1U(NS;TZMlAHd#CmSCY5JaceiZQ zGo6w2_vOX<*!%T%ehT)Pew#PdR9ubmJRC7|s+jr5Cb1|E->UnOmqbM`Oiz7v?2qZ3 y*B(`=VPy|Z7TRh*TH*L9VSAYE_U{Zk+~e0}dL6y-X5z#-h9S4Dm$qq$9smHD3UZ+U literal 0 HcmV?d00001 diff --git a/sipXopenfire/mongo-lib/test/resources/README.TXT b/sipXopenfire/mongo-lib/test/resources/README.TXT new file mode 100644 index 0000000000..c9a915cd4b --- /dev/null +++ b/sipXopenfire/mongo-lib/test/resources/README.TXT @@ -0,0 +1 @@ +Folders in this folder are copying Opefire's folder structure, as some classes will need to read configuration settings. diff --git a/sipXopenfire/mongo-lib/test/resources/conf/openfire.xml b/sipXopenfire/mongo-lib/test/resources/conf/openfire.xml new file mode 100644 index 0000000000..f08867cf6e --- /dev/null +++ b/sipXopenfire/mongo-lib/test/resources/conf/openfire.xml @@ -0,0 +1,47 @@ + + + + + + + + 9094 + 9095 + + + + + org.jivesoftware.database.DefaultConnectionProvider + + + + org.postgresql.Driver + jdbc:postgresql://localhost:5432/openfire + postgres + postgres + + + true + + + false + + + + true + + diff --git a/sipXopenfire/mongo-lib/test/resources/mongo-client.ini b/sipXopenfire/mongo-lib/test/resources/mongo-client.ini new file mode 100644 index 0000000000..7bc6ac6b33 --- /dev/null +++ b/sipXopenfire/mongo-lib/test/resources/mongo-client.ini @@ -0,0 +1,3 @@ +connectionUrl=mongodb://localhost:27017 +connectionString=something entirely different +BogusValue=bogus \ No newline at end of file diff --git a/sipXopenfire/mongo-lib/test/resources/openfire.properties b/sipXopenfire/mongo-lib/test/resources/openfire.properties new file mode 100644 index 0000000000..1bd56fb742 --- /dev/null +++ b/sipXopenfire/mongo-lib/test/resources/openfire.properties @@ -0,0 +1,52 @@ +admin.authorizedJIDs=admin@xmpptest.3zuce.com,superadmin@xmpptest.3zuce.com +cache.clustering.clustered.class=com.jivesoftware.util.cache.ClusteredCacheFactory +cache.group.size=40000000 +cache.lastActivity.size=1500000 +cache.offlinePresence.size=1500000 +cache.userCache.size=40000000 +cache.username2roster.size=12000000 +hazelcast.config.xml.filename=openuc-cache-config.xml +provider.admin.className=org.jivesoftware.openfire.admin.DefaultAdminProvider +provider.auth.className=org.sipfoundry.openfire.provider.MongoAuthProvider +provider.connectivity.className=org.sipfoundry.openfire.provider.MongoConnectivityProvider +provider.group.className=org.sipfoundry.openfire.provider.MongoGroupProviderAlt +provider.groupprops.className=org.sipfoundry.openfire.provider.MongoGroupPropertiesProvider +provider.lockout.className=org.sipfoundry.openfire.provider.MongoLockOutProvider +provider.muc.className=org.sipfoundry.openfire.provider.MongoMucProvider +provider.offline.className=org.sipfoundry.openfire.provider.MongoOfflineMessageProvider +provider.presence.className=org.sipfoundry.openfire.provider.MongoPresenceProvider +provider.privacylist.className=org.sipfoundry.openfire.provider.MongoPrivacyListProvider +provider.pubsub.className=org.sipfoundry.openfire.provider.MongoPubSubProvider +provider.roster.className=org.sipfoundry.openfire.provider.MongoRosterProvider +provider.securityAudit.className=org.sipfoundry.openfire.provider.MongoSecurityAuditProvider +provider.user.className=org.sipfoundry.openfire.provider.MongoUserProviderAlt +provider.vcard.className=org.sipfoundry.openfire.provider.MongoVCardProvider +register.inband=false +register.password=false +route.all-resources=false +rss.enabled=false +update.notify-admins=false +update.service-enabled=false +xmpp.audit.active=false +xmpp.audit.logdir=/opt/openfire/audit/ +xmpp.audit.messages=true +xmpp.audit.presence=true +xmpp.client.idle=0 +xmpp.domain=xmpptest.3zuce.com +xmpp.fqdn=openucload.xmpptest.3zuce.com +xmpp.offline.type=store +xmpp.pep.enabled=false +xmpp.privateStorageEnabled=true +xmpp.proxy.enabled=false +xmpp.proxy.externalip= +xmpp.proxy.port=7777 +xmpp.server.certificate.accept-selfsigned=true +xmpp.server.certificate.selfsigned=true +xmpp.server.permission=whitelist +xmpp.server.session.idle=-1 +xmpp.server.socket.active=false +xmpp.server.tls.enabled=true +xmpp.session.conflict-limit=0 +xmpp.socket.plain.active=true +xmpp.socket.ssl.active=true +xmpp.socket.ssl.keypass=changeit diff --git a/sipXopenfire/mongo-lib/test/resources/resources/security/keystore b/sipXopenfire/mongo-lib/test/resources/resources/security/keystore new file mode 100644 index 0000000000000000000000000000000000000000..582a741ba70e1e84ec099a088fd5036ca11a3e4c GIT binary patch literal 1749 zcmezO_TO6u1_mY|W&~rQirj*NlGNf7J>#m<gP;3Y%L^27kbU6MOsb zh$^wF#O&HElP403RqRRRg} zw|RyB&wslsB-U2t;kmx%>#WKwoTW4wEOe~z6S>%iG*0J|Mu69Ax_vP`iOPd%sICiU#^XR zF!@(R^oq}1q6Ux3Cv5ZhG;u-a?+uDyCpb=s+5C0d**^X1NSOo6LQm`$xsu80wJG+!Rrm^CGoMD~ z*S#ht{mUy?IB(_I|0K(2{mq;2+%4NDe7jgIp8Tpr_oV2wKOsMV3HfpkV$sTsYqcK%nmIpc`n zjksUcb6;dG`j{)H-x+w<{Mh3sbCUbYXY5Z6UFLk`dGOII@7!WU`;CfUB^BOXc{EGx zsKIh3&AGnUPk&7A=2>w1-ikx}zXr2DI4pQJ!E58wuF`!OJ-fWs#cGn4+~D85U+DUZ znq3Mr|4e-<{Oz^+YFqXzbC1>Cnc~wgEK!nmsqyg!?FajIW^bSISSk5(UegEe{E!#w z$AF0&n8H~j^h^yb85o#P7&I{-0Vd`(3z(T0nV47@!@Ud)c-c6$+C196^8!;fD}zDf zN<(e~PB!LH7B*p~&|pJB1AY*PgNMU0uQIqKu_V<{0GJnmLhL;3K%tQQ@;pOn14)nw z7Y~+<1XIq0EC7HKoAAwnj&!x8aLA3FCe#q{UV!RkeXMToRgoJLW!Rm=Of1mu%KaX>;=Y3 zCsSi1!*>0rf;X!=I(p@6k8E@Na_7pmc@2R9_j~z1#bzGer+16IICIR>&oKg;s5 zh_Q%p&aL=6N4I*~b_Qh`1?>d6`L!M2!Rbdcz`RWi82?t7(b$AP z@UUrBhePhXU!t454%|%o@RganS-DV>+kE$q=>gY_7Izsr+*=}e zcEV{hjzxmIYI5E+FvQv}QfB>@bb0&l?xZVefwFwB7<>&<{LXr+S7bD+-xnz0JoVV) zqDC-k2FR;##R-$i-9*O6If-Sq*pPaZQ=QJw9$ wXM5kC`xe=OJdbL)KP$`d@SAVhwsR&=>5~2};dSq4KHj&;P_aN=~ zEu(1ffX4aAer9B4U~cSXFlg*#YHVaU85240lXMrO$QFV7EDpCHhp*3ab?9&5;l5~m z^QPeWcT5rTs^7I%xUjBz!z^D>KX3BRSq!U`Yp<Od%kg&!s+0pt!>mKiwNIc_WKW)U`x+u4Vm;w`Ze2*{<{C+#ne5r z{W&@-0?!oRSaKlvgWol^17(cijg#UOKVIDU|M>;Qh_jjEk6mQuNV-^d#@w01z#rDj zH1qAE39dd*b_*=A{b0n*_wn417bgRIo+ZtHHvd5F_M$l_&8)KL`>pt&!kYq4aH7EE zRgqgzP?B0)qGw!Hnw+YaoSzF#Z8GrG#{9;hiTMRE#a&py%*4pV#KIWvrH||$%%u9!!fTixFoS8)lk5I4!|;V44g{v4%#51|SGdqQDf3%pFpG zX<}4DjtWrfZ(`(U0G170Oihf83|o>i|B8nQU#n&i_|(i@(pw~VcI}^^%Il{ct~N zW7gZ1EK4dMUW>^8`hB+C(h!>%b<4F}W-;IU^sJsX?g~BSe8K<9)8F-S2lv4Ya4E z%TtfL$)(r4dH0|H_OsWTTYNH;7inqlve+Y#U0srVeoK(^?`W2^JB@#O;wLzN{$zLf zO6rHKc)y>C{q;|if)86S(bbI%$!wWABhPC6i>0%-f-~S^F#{0;Az<2(6=r1o&%$BA z2Ber68IVH`n23QP$H)+P!{fSWu`+|OcHOJUqDO1L&MnNc4OpsB(>d>(;Y|sh#i|`? zzph5E`w;fkM5gjk$Uio=*)QkJ*l_(l(4aDTrvKzTLqT;%a3wd7{8w1 zcr)eQjGEl_Ih{*94z=gMu3Y?xr)dBBpq#w2yf=1U(NS;TZMlAHd#CmSCY5JaceiZQ zGo6w2_vOX<*!%T%ehT)Pew#PdR9ubmJRC7|s+jr5Cb1|E->UnOmqbM`Oiz7v?2qZ3 y*B(`=VPy|Z7TRh*TH*L9VSAYE_U{Zk+~e0}dL6y-X5z#-h9S4Dm$qq$9smHD3UZ+U literal 0 HcmV?d00001 diff --git a/sipXopenfire/mongo-sync-plugin/Makefile.am b/sipXopenfire/mongo-sync-plugin/Makefile.am new file mode 100644 index 0000000000..84249160bf --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/Makefile.am @@ -0,0 +1,31 @@ +include $(top_srcdir)/config/utility.am +include $(top_srcdir)/config/java.am +include $(top_srcdir)/common.am + +EXTRA_DIST = \ + $(sync_SRC) \ + plugin.xml + +noinst_DATA = javac-sync + +sync_JAR = sipx-mongo-sync-plugin.jar +jar_DATA = $(sync_JAR) +jardir = @SIPX_JAVADIR@/sipXopenfire/plugins + +jar_PKGS = \ + log4j \ + commons-collections \ + spring-data-mongodb \ + mongo \ + sipxcommons + +sync_SRC = $(shell cd $(srcdir); find src -name '*.java') +sync_DEPS = \ + $(call JavaDep,@SIPX_JAVADIR@/sipXcommons,$(jar_PKGS)) \ + @OPENFIRE_HOME@/lib/openfire.jar \ + $(mongolib_JAVAROOT) + +$(sync_JAR) : javac-sync + jar -cf $@ \ + $(call JarInclude,$(mongosync_JAVAROOT),classes) \ + $(call JarInclude,$(srcdir),plugin.xml) diff --git a/sipXopenfire/mongo-sync-plugin/plugin.xml b/sipXopenfire/mongo-sync-plugin/plugin.xml new file mode 100644 index 0000000000..f63e28daec --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/plugin.xml @@ -0,0 +1,19 @@ + + + + org.sipfoundry.openfire.plugin.MongoSyncPlugin + + + MongoDB watcher + Uses mongo oplog to watch for DB changes + eZuce Inc + + 1.0 + 04/11/2013 + hazelcast + http://www.sipfoundry.org + 3.8.1 + agpl + + + diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/MongoOperation.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/MongoOperation.java new file mode 100644 index 0000000000..e044f184ef --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/MongoOperation.java @@ -0,0 +1,18 @@ +package org.sipfoundry.openfire.plugin; + +public enum MongoOperation { + INSERT, UPDATE, DELETE; + + public static MongoOperation fromString(String op) { + MongoOperation result = null; + if ("i".equalsIgnoreCase(op)) { + result = INSERT; + } else if ("u".equalsIgnoreCase(op)) { + result = UPDATE; + } else if ("d".equalsIgnoreCase(op)) { + result = DELETE; + } + + return result; + } +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/MongoSyncPlugin.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/MongoSyncPlugin.java new file mode 100644 index 0000000000..2a58e5af20 --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/MongoSyncPlugin.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 Avaya, certain elements licensed under a Contributor Agreement. + * Contributors retain copyright to elements licensed under a Contributor Agreement. + * Licensed to the User under the LGPL license. + */ +package org.sipfoundry.openfire.plugin; + +import java.io.File; + +import org.apache.log4j.Logger; +import org.jivesoftware.openfire.container.Plugin; +import org.jivesoftware.openfire.container.PluginManager; +import org.sipfoundry.openfire.plugin.listener.ImdbOplogListener; +import org.sipfoundry.openfire.plugin.listener.ProfilesOplogListener; + +public class MongoSyncPlugin implements Plugin { + private static Logger logger = Logger.getLogger(MongoSyncPlugin.class); + + @Override + public void initializePlugin(PluginManager manager, File pluginDirectory) { + logger.info("Mongo sync plugin initializing"); + ThreadGroup group = new ThreadGroup("mongoSync"); + Thread imdbOpLogThread = new Thread(group, new ImdbOplogListener(), "imdbOpLogListener"); + Thread profilesdbOpLogThread = new Thread(group, new ProfilesOplogListener(), "profilesOpLogListener"); + imdbOpLogThread.start(); + profilesdbOpLogThread.start(); + } + + @Override + public void destroyPlugin() { + logger.info("Mongo sync plugin stopping"); + QueueManager.shutdown(); + } +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/QueueManager.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/QueueManager.java new file mode 100644 index 0000000000..590796b726 --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/QueueManager.java @@ -0,0 +1,78 @@ +package org.sipfoundry.openfire.plugin; + +import java.util.LinkedList; +import java.util.Queue; + +import org.apache.log4j.Logger; +import org.sipfoundry.openfire.plugin.job.Job; + +public class QueueManager { + private static Logger logger = Logger.getLogger(QueueManager.class); + + // must preserve the order of events + private static final Queue QUEUE; + + private static final JobWatcher WATCHER; + + static { + QUEUE = initQueue(); + + WATCHER = new JobWatcher(); + // give it a distinctive name, but keep the number + WATCHER.setName(WATCHER.getName().replace("Thread", "watcher")); + WATCHER.start(); + } + + public static void submitJob(Job j) { + logger.debug(String.format("Submitting job %s", j)); + // try to avoid several nodes submitting the same change for processing + if (!QUEUE.contains(j)) { + QUEUE.add(j); + } else { + logger.debug(String.format("Job already queued: %s", j)); + } + } + + public static void shutdown() { + WATCHER.shutdown(); + } + + protected static Queue getQueue() { + return QUEUE; + } + + private static Queue initQueue() { + Queue jobQueue = new LinkedList(); + + return jobQueue; + } + + private static class JobWatcher extends Thread { + private boolean shutdown; + + @Override + public void run() { + while (!shutdown) { + Queue queueRef = getQueue(); + // work as hard as possible (no sleep) while there are queued jobs + while (!queueRef.isEmpty()) { + Job job = queueRef.iterator().next(); + logger.debug(String.format("Processing job %s", job.toString())); + job.process(); + queueRef.remove(job); + logger.debug(String.format("Finished processing job %s, removed from queue.", job.toString())); + } + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // ignore + } + } + } + + public void shutdown() { + shutdown = true; + } + } +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/Job.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/Job.java new file mode 100644 index 0000000000..8aa515a64c --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/Job.java @@ -0,0 +1,7 @@ +package org.sipfoundry.openfire.plugin.job; + +import java.io.Serializable; + +public interface Job extends Serializable { + void process(); +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/JobFactory.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/JobFactory.java new file mode 100644 index 0000000000..421d53e798 --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/JobFactory.java @@ -0,0 +1,224 @@ +package org.sipfoundry.openfire.plugin.job; + +import static org.sipfoundry.commons.mongo.MongoConstants.DESCR; +import static org.sipfoundry.commons.mongo.MongoConstants.EMAIL; +import static org.sipfoundry.commons.mongo.MongoConstants.GROUPS; +import static org.sipfoundry.commons.mongo.MongoConstants.IM_DISPLAY_NAME; +import static org.sipfoundry.commons.mongo.MongoConstants.IM_ENABLED; +import static org.sipfoundry.commons.mongo.MongoConstants.IM_GROUP; +import static org.sipfoundry.commons.mongo.MongoConstants.IM_ID; +import static org.sipfoundry.commons.mongo.MongoConstants.UID; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.bson.types.ObjectId; +import org.sipfoundry.commons.mongo.MongoFactory; +import org.sipfoundry.openfire.plugin.MongoOperation; +import org.sipfoundry.openfire.plugin.job.group.GroupDeleteJob; +import org.sipfoundry.openfire.plugin.job.group.GroupUpdateJob; +import org.sipfoundry.openfire.plugin.job.user.UserDeleteJob; +import org.sipfoundry.openfire.plugin.job.user.UserUpdateJob; +import org.sipfoundry.openfire.plugin.job.vcard.VcardUpdateJob; +import org.sipfoundry.openfire.provider.CacheHolder; + +import com.mongodb.BasicDBList; +import com.mongodb.BasicDBObject; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBObject; +import com.mongodb.Mongo; + +public class JobFactory { + private static Logger logger = Logger.getLogger(JobFactory.class); + + public static Job createJob(MongoOperation op, DBObject dbObj, Object id) { + Job job = null; + + if (id != null) { + if (id instanceof String) { + job = createJob(op, dbObj, (String) id); + } else if (id instanceof Long) { + job = createJob(op, dbObj, (Long) id); + /*Disable avatar operations due to problems + see UC-2765 + } else if (id instanceof ObjectId) { + // we assume all id's that are Mongo ObjectId + // are avatar operations + job = createJob(op, dbObj, (ObjectId) id); + */ + } else { + logger.warn(String.format("Unknown id type: %s", id.getClass().getName())); + } + } else { + logger.warn("Id was null."); + } + + return job; + } + + private static Job createJob(MongoOperation op, DBObject dbObj, String id) { + Job job = null; + + if (id.startsWith("User")) { + job = buildUserJob(op, dbObj, id); + } else if (id.startsWith("Group")) { + job = buildGroupJob(op, dbObj, id); + } + + return job; + } + + private static Job createJob(MongoOperation op, DBObject dbObj, ObjectId id) { + logger.debug("id: " + id.toString()); + logger.debug("op: " + op); + Job vcardUpdateJob = null; + + switch (op) { + case INSERT: + if (dbObj.containsField("filename")) { + String filename = dbObj.get("filename").toString(); + if (filename.startsWith("avatar_")) { + String uid = StringUtils.removeEnd(StringUtils.removeStart(filename, "avatar_"), ".png"); + logger.debug("uid: " + uid); + String imid = CacheHolder.getImId(uid); + CacheHolder.putAvatar(id.toString().toString(), uid); + vcardUpdateJob = imid != null ? new VcardUpdateJob(imid, dbObj) : null; + } + } + break; + case DELETE: + String imid = CacheHolder.getImIdByAvatar(id.toString()); + CacheHolder.removeAvatar(id.toString()); + vcardUpdateJob = imid != null ? new VcardUpdateJob(imid, dbObj) : null; + break; + } + return vcardUpdateJob; + } + + private static Job buildUserJob(MongoOperation op, DBObject dbObj, String id) { + logger.debug("User job obj: " + dbObj); + + Job userJob = null; + // get the im name from the update object; if it's not there (it has not been updated), + // use the cached value + String userImName = (String) dbObj.get(IM_ID); + if (userImName == null) { + userImName = CacheHolder.getUserName(id); + } + if (userImName == null) { + userImName = lookupImId(id); + } + + List groupNames = getGroupNames(dbObj); + if (!StringUtils.isBlank(userImName)) { + switch (op) { + case INSERT: + if (!(Boolean) dbObj.get(IM_ENABLED) == Boolean.TRUE) { + break; + } + //$FALL-THROUGH$ - only if IM is enabled, do an update for this user + case UPDATE: + String oldImName = CacheHolder.getUserName(id) == null ? userImName : CacheHolder.getUserName(id); + Boolean imUser = (Boolean) dbObj.get(IM_ENABLED); + // avoid querying the actual value; the value wasn't updated, but the user is + // cached, therefore it must have IM enabled + if (imUser == null) { + imUser = true; + } + String displayName = (String) dbObj.get(IM_DISPLAY_NAME); + String email = (String) dbObj.get(EMAIL); + String uid = (String) dbObj.get(UID); + userJob = new UserUpdateJob(userImName, oldImName, imUser, displayName, email, uid, groupNames); + break; + case DELETE: + userJob = new UserDeleteJob(userImName); + break; + default: + logger.warn(String.format("Unsupported user operation %s. Ignoring.", op)); + break; + } + } else { + logger.warn("Missing user name for update/delete operation"); + } + + return userJob; + } + + private static Job buildGroupJob(MongoOperation op, DBObject dbObj, String id) { + logger.debug("Group job obj: " + dbObj); + + Job groupJob = null; + String groupName = (String) dbObj.get(UID); + + if (!StringUtils.isBlank(groupName)) { + switch (op) { + case UPDATE: + String oldGroupName = CacheHolder.getGroupName(id); + // if it wasn't cached, it's not in use - no need to update + if (oldGroupName != null) { + String imGroupStr = (String) dbObj.get(IM_GROUP); + boolean imGroup; + if (imGroupStr != null) { + imGroup = "1".equals(imGroupStr); + } else { + // avoid querying the actual value; the value wasn't updated, but the + // group is cached, therefore it must have IM enabled + imGroup = true; + } + String description = (String) dbObj.get(DESCR); + groupJob = new GroupUpdateJob(groupName, oldGroupName, imGroup, description); + } else { + logger.debug(String.format("Skipping update of group %s. Not in cache.", groupName)); + } + break; + case DELETE: + groupJob = new GroupDeleteJob(groupName); + break; + default: + logger.warn(String.format("Unsupported group operation %s. Ignoring.", op)); + break; + } + } else { + logger.warn("Missing group name for update/delete operation"); + } + + return groupJob; + } + + private static List getGroupNames(DBObject userObject) { + List actualGroups = new ArrayList(); + List groupList = (BasicDBList) userObject.get(GROUPS); + + if (groupList == null) { + return null; + } + + for (int i = 0; i < groupList.size(); i++) { + actualGroups.add((String) groupList.get(i)); + } + + return actualGroups; + } + + private static String lookupImId(String uid) { + String imId = null; + Mongo m; + try { + m = MongoFactory.fromConnectionFile(); + DB db = m.getDB("imdb"); + DBCollection col = db.getCollection("entity"); + DBObject query = new BasicDBObject("_id", uid); + DBObject fields = new BasicDBObject("imid", 1); + DBObject user = col.findOne(query, fields); + imId = (String) user.get("imid"); + } catch (UnknownHostException e) { + logger.error("Could not get a mongodb connection: " + e.getMessage()); + } + + return imId; + } +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupDeleteJob.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupDeleteJob.java new file mode 100644 index 0000000000..c54182dc3c --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupDeleteJob.java @@ -0,0 +1,58 @@ +package org.sipfoundry.openfire.plugin.job.group; + +import org.apache.log4j.Logger; +import org.sipfoundry.openfire.plugin.job.Job; + +public class GroupDeleteJob implements Job { + private static Logger logger = Logger.getLogger(GroupDeleteJob.class); + + private static final long serialVersionUID = 1L; + + private final String groupName; + + public GroupDeleteJob(String groupName) { + this.groupName = groupName; + } + + @Override + public void process() { + logger.debug("processing " + toString()); + + GroupShared.removeGroup(groupName); + } + + @Override + public String toString() { + return "GroupDeleteJob [groupName=" + groupName + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((groupName == null) ? 0 : groupName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + GroupDeleteJob other = (GroupDeleteJob) obj; + if (groupName == null) { + if (other.groupName != null) { + return false; + } + } else if (!groupName.equals(other.groupName)) { + return false; + } + return true; + } +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupShared.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupShared.java new file mode 100644 index 0000000000..753b2a4025 --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupShared.java @@ -0,0 +1,22 @@ +package org.sipfoundry.openfire.plugin.job.group; + +import org.apache.log4j.Logger; +import org.jivesoftware.openfire.group.Group; +import org.jivesoftware.openfire.group.GroupManager; +import org.jivesoftware.openfire.group.GroupNotFoundException; + +public class GroupShared { + private static Logger logger = Logger.getLogger(GroupShared.class); + + public static void removeGroup(String groupName) { + try { + Group imGroup = GroupManager.getInstance().getGroup(groupName); + if (imGroup != null) { + logger.debug("deleting group: " + imGroup.getName()); + GroupManager.getInstance().deleteGroup(imGroup); + } + } catch (GroupNotFoundException ex) { + logger.error(String.format("Delete group: Group %s not found", groupName)); + } + } +} \ No newline at end of file diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupUpdateJob.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupUpdateJob.java new file mode 100644 index 0000000000..183acf8082 --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/group/GroupUpdateJob.java @@ -0,0 +1,130 @@ +package org.sipfoundry.openfire.plugin.job.group; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.jivesoftware.openfire.group.Group; +import org.jivesoftware.openfire.group.GroupAlreadyExistsException; +import org.jivesoftware.openfire.group.GroupManager; +import org.jivesoftware.openfire.group.GroupNotFoundException; +import org.sipfoundry.openfire.plugin.job.Job; + +public class GroupUpdateJob implements Job { + private static Logger logger = Logger.getLogger(GroupUpdateJob.class); + + private static final long serialVersionUID = 1L; + + private final String groupName; + private final String oldGroupName; + private final boolean isImGroup; + private final String description; + + public GroupUpdateJob(String groupName, String oldGroupName, boolean imGroup, String description) { + this.groupName = groupName; + this.oldGroupName = oldGroupName; + this.isImGroup = imGroup; + this.description = description; + } + + @Override + public void process() { + logger.debug("processing " + toString()); + + // not an imgroup but in cache: delete it + if (!isImGroup) { + GroupShared.removeGroup(oldGroupName); + return; + } + + // imgroup and not in cache: create it + if (isImGroup && StringUtils.isBlank(groupName)) { + createGroup(groupName); + return; + } + + // imgroup and in cache: update it + if (isImGroup && !StringUtils.isBlank(groupName)) { + // new name is not the same as the one from cache, delete old group, + // create new + // one + if (!StringUtils.equalsIgnoreCase(oldGroupName, groupName)) { + // group name changed: delete old one, create new with new name + GroupShared.removeGroup(oldGroupName); + createGroup(groupName); + return; + } + + // same group name: update existing one + try { + Group ofGroup = GroupManager.getInstance().getGroup(groupName); + ofGroup.setDescription(description); + } catch (GroupNotFoundException e) { + logger.error("Group not found trying to set new description " + groupName); + } + } + } + + private static void createGroup(String groupName) { + logger.debug("update group " + groupName); + try { + GroupManager.getInstance().createGroup(groupName); + } catch (GroupAlreadyExistsException e1) { + logger.error(String.format("Update group: Group %s already exists", groupName)); + } + } + + @Override + public String toString() { + return "GroupUpdateJob [groupName=" + groupName + ", oldGroupName=" + oldGroupName + ", isImGroup=" + + isImGroup + ", description=" + description + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((description == null) ? 0 : description.hashCode()); + result = prime * result + ((groupName == null) ? 0 : groupName.hashCode()); + result = prime * result + (isImGroup ? 1231 : 1237); + result = prime * result + ((oldGroupName == null) ? 0 : oldGroupName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + GroupUpdateJob other = (GroupUpdateJob) obj; + if (description == null) { + if (other.description != null) { + return false; + } + } else if (!description.equals(other.description)) { + return false; + } + if (groupName == null) { + if (other.groupName != null) { + return false; + } + } else if (!groupName.equals(other.groupName)) { + return false; + } + if (isImGroup != other.isImGroup) { + return false; + } + if (oldGroupName == null) { + if (other.oldGroupName != null) { + return false; + } + } else if (!oldGroupName.equals(other.oldGroupName)) { + return false; + } + return true; + } +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserDeleteJob.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserDeleteJob.java new file mode 100644 index 0000000000..c79b9f15ac --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserDeleteJob.java @@ -0,0 +1,59 @@ +package org.sipfoundry.openfire.plugin.job.user; + +import org.apache.log4j.Logger; +import org.sipfoundry.openfire.plugin.job.Job; + + +public class UserDeleteJob implements Job { + private static Logger logger = Logger.getLogger(UserDeleteJob.class); + + private static final long serialVersionUID = 1L; + + private final String userImName; + + public UserDeleteJob(String userImName) { + this.userImName = userImName; + } + + @Override + public void process() { + logger.debug("processing " + toString()); + + UserShared.removeUser(userImName); + } + + @Override + public String toString() { + return "UserDeleteJob [userImName=" + userImName + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((userImName == null) ? 0 : userImName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + UserDeleteJob other = (UserDeleteJob) obj; + if (userImName == null) { + if (other.userImName != null) { + return false; + } + } else if (!userImName.equals(other.userImName)) { + return false; + } + return true; + } +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserShared.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserShared.java new file mode 100644 index 0000000000..b5b0d23c5a --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserShared.java @@ -0,0 +1,53 @@ +package org.sipfoundry.openfire.plugin.job.user; + +import java.util.Collection; + +import org.apache.log4j.Logger; +import org.jivesoftware.openfire.SessionManager; +import org.jivesoftware.openfire.XMPPServer; +import org.jivesoftware.openfire.group.Group; +import org.jivesoftware.openfire.group.GroupManager; +import org.jivesoftware.openfire.session.ClientSession; +import org.jivesoftware.openfire.user.User; +import org.jivesoftware.openfire.user.UserManager; +import org.jivesoftware.openfire.user.UserNotFoundException; +import org.xmpp.packet.JID; +import org.xmpp.packet.StreamError; + +public class UserShared { + private static Logger logger = Logger.getLogger(UserShared.class); + + public static void removeUser(String userImName) { + try { + String jidAsString = appendDomain(userImName); + JID jid = new JID(jidAsString); + Collection groups = GroupManager.getInstance().getGroups(); + for (Group group : groups) { + group.getMembers().remove(jid); + group.getAdmins().remove(jid); + } + User user = UserManager.getInstance().getUser(jid.getNode()); + if (user != null) { + UserManager.getInstance().deleteUser(user); + final StreamError error = new StreamError(StreamError.Condition.not_authorized); + for (ClientSession sess : SessionManager.getInstance().getSessions(user.getUsername())) { + sess.deliverRawText(error.toXML()); + sess.close(); + } + logger.debug("deleting user: " + user.getName()); + GroupManager.getInstance().deleteUser(user); + } + } catch (UserNotFoundException ex) { + logger.error(String.format("Delete user: user %s not found", userImName)); + } + } + + public static String appendDomain(String userName) { + if (userName.indexOf("@") == -1) { + // No @ in the domain so assume this is our domain. + return userName + "@" + XMPPServer.getInstance().getServerInfo().getXMPPDomain(); + } + + return userName; + } +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserUpdateJob.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserUpdateJob.java new file mode 100644 index 0000000000..68b900fbf9 --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/user/UserUpdateJob.java @@ -0,0 +1,263 @@ +package org.sipfoundry.openfire.plugin.job.user; + +import static org.sipfoundry.commons.mongo.MongoConstants.UID; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.jivesoftware.openfire.group.Group; +import org.jivesoftware.openfire.group.GroupManager; +import org.jivesoftware.openfire.group.GroupNotFoundException; +import org.jivesoftware.openfire.user.User; +import org.jivesoftware.openfire.user.UserManager; +import org.jivesoftware.openfire.user.UserNotFoundException; +import org.sipfoundry.openfire.plugin.job.Job; +import org.xmpp.packet.JID; + +public class UserUpdateJob implements Job { + private static Logger logger = Logger.getLogger(UserUpdateJob.class); + + private static final long serialVersionUID = 1L; + + private final String userImName; + private final String oldUserImName; + private final boolean isImUser; + private final String displayName; + private final String email; + private final String uid; + private final List groups; + + public UserUpdateJob(String userImName, String oldUserImName, boolean isImUser, String displayName, + String email, String uid, List groups) { + this.userImName = userImName; + this.oldUserImName = oldUserImName; + this.isImUser = isImUser; + this.displayName = displayName; + this.email = email; + this.uid = uid; + this.groups = groups; + } + + @Override + public void process() { + logger.debug("processing " + toString()); + + // check if still an IM User, if not delete it + // not imuser and in cache: delete it + if (!isImUser && !StringUtils.isBlank(userImName)) { + UserShared.removeUser(userImName); + return; + } + + try { + User user = UserManager.getInstance().getUser(oldUserImName); + + // imuser and in cache: update it + if (isImUser && !StringUtils.isBlank(userImName)) { + if (!StringUtils.equalsIgnoreCase(oldUserImName, userImName)) { + logger.debug(String.format("im id changed to %s from %s", userImName, oldUserImName)); + // old name will be null for users that just had im enabled - nothing to + // remove + List actualGroups = new ArrayList(); + if (oldUserImName != null) { + Collection groups = GroupManager.getInstance().getGroups( + UserManager.getInstance().getUser(userImName)); + for (Group group : groups) { + actualGroups.add(group.getName()); + } + UserShared.removeUser(oldUserImName); + } + updateGroups(actualGroups, userImName); + return; + } + } + + // group update should go first + if (groups != null) { + updateGroups(groups, userImName); + } + + // update display name & email if changed + + boolean dnChanged = displayName != null && !StringUtils.equals(user.getName(), displayName); + boolean emailChanged = email != null && !StringUtils.equals(user.getEmail(), email); + boolean uidChanged = uid != null && !StringUtils.equals(user.getProperties().get(UID), uid); + boolean userChanged = dnChanged || emailChanged || uidChanged; + + if (dnChanged) { + logger.debug(String.format("im name changed to %s from %s", displayName, user.getName())); + user.setName(displayName); + userChanged = true; + } + if (emailChanged) { + logger.debug(String.format("email changed to %s from %s", email, user.getEmail())); + user.setEmail(email); + userChanged = true; + } + + if (uidChanged) { + logger.debug(String.format("uid changed to %s", uid)); + user.getProperties().put(UID, uid); + userChanged = true; + } + if (userChanged) { + logger.debug("User changed; updating roster: " + userImName); + // We need to remove user from all the groups and add it back in order for the + // rosters to be updated properly. + // More, we need to remove it first and then readd it, otherwise, if it is in more + // than 1 im group, the rosters are not updated. + JID jid = new JID(UserShared.appendDomain(userImName)); + Collection groups = GroupManager.getInstance().getGroups(jid); + for (Group g : groups) { + removeUserFromGroup(g.getName(), jid); + } + for (Group g : groups) { + addUserToGroup(g.getName(), jid); + } + } + } catch (UserNotFoundException e) { + logger.error("User not found trying to update " + oldUserImName); + } + } + + private static void updateGroups(List actualGroups, String imName) { + // rebuild groups in openfire + JID jid = new JID(UserShared.appendDomain(imName)); + + // get groups already assigned for current JID + List assignedGroups = new ArrayList(); + for (Group group : GroupManager.getInstance().getGroups()) { + if (group.getMembers().contains(jid) || group.getAdmins().contains(jid)) { + assignedGroups.add(group.getName()); + } + } + + // groups that were deleted + @SuppressWarnings("unchecked") + Collection groupsDeleted = CollectionUtils.subtract(assignedGroups, actualGroups); + for (String groupName : groupsDeleted) { + removeUserFromGroup(groupName, jid); + } + + // groups that were added + @SuppressWarnings("unchecked") + Collection groupsAdded = CollectionUtils.subtract(actualGroups, assignedGroups); + for (String groupName : groupsAdded) { + addUserToGroup(groupName, jid); + } + } + + private static void removeUserFromGroup(String groupName, JID jid) { + logger.debug(String.format("remove user %s from group %s", jid.toString(), groupName)); + try { + if (StringUtils.isNotBlank(groupName)) { + Group group = GroupManager.getInstance().getGroup(groupName); + group.getMembers().remove(jid); + // it's not supposed to be an admin, but just in case + group.getAdmins().remove(jid); + } + } catch (GroupNotFoundException ex) { + logger.error(String.format("Remove user %s from group %s: Group not found", jid.toString(), groupName)); + } + } + + private static void addUserToGroup(String groupName, JID jid) { + logger.debug(String.format("Add user %s to group %s", jid.toString(), groupName)); + try { + if (StringUtils.isNotBlank(groupName)) { + Group group = GroupManager.getInstance().getGroup(groupName); + // automatically add the user in group as admin. Openfire holds two collections + // members collection and admins collection. A user is contained either in members + // or in admins collection, not in both + group.getMembers().add(jid); + } + } catch (GroupNotFoundException ex) { + logger.error(String.format("Add user %s to group %s: Group not found", jid.toString(), groupName)); + } + } + + @Override + public String toString() { + return "UserUpdateJob [userImName=" + userImName + ", oldUserImName=" + oldUserImName + ", isImUser=" + + isImUser + ", displayName=" + displayName + ", email=" + email + ", uid=" + uid + ", groups=" + + groups + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((displayName == null) ? 0 : displayName.hashCode()); + result = prime * result + ((email == null) ? 0 : email.hashCode()); + result = prime * result + ((groups == null) ? 0 : groups.hashCode()); + result = prime * result + (isImUser ? 1231 : 1237); + result = prime * result + ((oldUserImName == null) ? 0 : oldUserImName.hashCode()); + result = prime * result + ((uid == null) ? 0 : uid.hashCode()); + result = prime * result + ((userImName == null) ? 0 : userImName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + UserUpdateJob other = (UserUpdateJob) obj; + if (displayName == null) { + if (other.displayName != null) { + return false; + } + } else if (!displayName.equals(other.displayName)) { + return false; + } + if (email == null) { + if (other.email != null) { + return false; + } + } else if (!email.equals(other.email)) { + return false; + } + if (groups == null) { + if (other.groups != null) { + return false; + } + } else if (!groups.equals(other.groups)) { + return false; + } + if (isImUser != other.isImUser) { + return false; + } + if (oldUserImName == null) { + if (other.oldUserImName != null) { + return false; + } + } else if (!oldUserImName.equals(other.oldUserImName)) { + return false; + } + if (uid == null) { + if (other.uid != null) { + return false; + } + } else if (!uid.equals(other.uid)) { + return false; + } + if (userImName == null) { + if (other.userImName != null) { + return false; + } + } else if (!userImName.equals(other.userImName)) { + return false; + } + return true; + } +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/vcard/VcardDeleteJob.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/vcard/VcardDeleteJob.java new file mode 100644 index 0000000000..290c0d719a --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/vcard/VcardDeleteJob.java @@ -0,0 +1,84 @@ +package org.sipfoundry.openfire.plugin.job.vcard; + +import org.apache.log4j.Logger; +import org.dom4j.Element; +import org.jivesoftware.openfire.provider.VCardProvider; +import org.jivesoftware.openfire.vcard.VCardManager; +import org.sipfoundry.openfire.provider.CacheHolder; + +/** + * We are not actually deleting the avatar, but setting it to + * the default image. However this job is processed in the event of + * a DELETE operation from OpLog + */ +public class VcardDeleteJob extends VcardUpdateJob { + private static Logger logger = Logger.getLogger(VcardDeleteJob.class); + /** + * + */ + private static final long serialVersionUID = 1L; + + public VcardDeleteJob(String userImName) { + super(userImName, null); + } + + @Override + public void process() { + try { + logger.info("Processing vcard delete job for " + toString()); + VCardProvider provider = VCardManager.getProvider(); + Element vcard = provider.loadVCard(userImName); + + logger.debug("delete vcard!"); + VCardManager.getInstance().setVCard(userImName, vcard); + CacheHolder.removeAvatarByImId(userImName); + + updateAvatar(userImName, vcard); + } catch (Exception e) { + logger.error(e); + } + } + + @Override + public String toString() { + return "VcardDeleteJob [userImName=" + userImName + ", oldMd5=" + oldMd5 + ", newMd5=" + newMd5 + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((newMd5 == null) ? 0 : newMd5.hashCode()); + result = prime * result + ((oldMd5 == null) ? 0 : oldMd5.hashCode()); + result = prime * result + ((userImName == null) ? 0 : userImName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VcardDeleteJob other = (VcardDeleteJob) obj; + if (newMd5 == null) { + if (other.newMd5 != null) + return false; + } else if (!newMd5.equals(other.newMd5)) + return false; + if (oldMd5 == null) { + if (other.oldMd5 != null) + return false; + } else if (!oldMd5.equals(other.oldMd5)) + return false; + if (userImName == null) { + if (other.userImName != null) + return false; + } else if (!userImName.equals(other.userImName)) + return false; + return true; + } + +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/vcard/VcardUpdateJob.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/vcard/VcardUpdateJob.java new file mode 100644 index 0000000000..ad15c7a796 --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/job/vcard/VcardUpdateJob.java @@ -0,0 +1,188 @@ +package org.sipfoundry.openfire.plugin.job.vcard; + +import java.io.StringReader; +import java.net.UnknownHostException; +import java.security.MessageDigest; + +import org.apache.log4j.Logger; +import org.dom4j.Document; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.jivesoftware.openfire.PresenceManager; +import org.jivesoftware.openfire.XMPPServer; +import org.jivesoftware.openfire.XMPPServerInfo; +import org.jivesoftware.openfire.handler.PresenceUpdateHandler; +import org.jivesoftware.openfire.provider.VCardProvider; +import org.jivesoftware.openfire.user.User; +import org.jivesoftware.openfire.user.UserManager; +import org.jivesoftware.openfire.vcard.VCardManager; +import org.sipfoundry.commons.mongo.MongoFactory; +import org.sipfoundry.commons.userdb.profile.UserProfileServiceImpl; +import org.sipfoundry.openfire.plugin.job.Job; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.xmpp.packet.Presence; + +import com.mongodb.DBObject; +import com.mongodb.Mongo; + +public class VcardUpdateJob implements Job { + private static Logger logger = Logger.getLogger(VcardUpdateJob.class); + /** + * + */ + private static final long serialVersionUID = 1L; + protected final String userImName; + protected final String oldMd5; + protected final String newMd5; + protected static UserProfileServiceImpl userProfileService; + + static { + Mongo mongo; + userProfileService = new UserProfileServiceImpl(); + try { + mongo = MongoFactory.fromConnectionFile(); + MongoTemplate profilesDb = new MongoTemplate(mongo, "profiles"); + userProfileService.setProfilesDb(profilesDb); + } catch (UnknownHostException e) { + logger.error("Error instantiating mongo"); + } + } + + public VcardUpdateJob(String userImName, DBObject dbObj) { + this.userImName = userImName; + this.oldMd5 = userProfileService.getAvatarDBFileMD5(userImName); + this.newMd5 = dbObj == null ? null : (dbObj.get("md5") != null ? dbObj.get("md5").toString() : null); + } + + @Override + public void process() { + try { + if (newMd5 != null && newMd5.equals(oldMd5)) { + logger.debug("Nothing changed!"); + return; + } + VCardProvider provider = VCardManager.getProvider(); + Element vcard = provider.loadVCard(userImName); + + logger.debug("update vcard!"); + VCardManager.getInstance().setVCard(userImName, vcard); + + updateAvatar(userImName, vcard); + } catch (Exception e) { + logger.error(e.getMessage(), e); + for (StackTraceElement el : e.getStackTrace()) { + logger.error(el.toString()); + } + } + } + + protected static void updateAvatar(String username, Element vCard) throws Exception { + if (vCard.element("PHOTO") == null) { + return; + } + Element binValElement = vCard.element("PHOTO").element("BINVAL"); + if (binValElement == null) { + return; + } + String avatarStr = binValElement.getText(); + XMPPServer server = XMPPServer.getInstance(); + XMPPServerInfo info = server.getServerInfo(); + String aor = username + "@" + info.getXMPPDomain(); + String itemId = getItemId(avatarStr.getBytes()); + Presence presenceAvatar = createPresenceAvatar(aor, itemId); + PresenceUpdateHandler puh = server.getPresenceUpdateHandler(); + logger.debug("processing " + presenceAvatar.toXML()); + puh.process(presenceAvatar); + } + + private static String getItemId(byte[] avatarBytes) throws Exception { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(avatarBytes); + byte[] digest = md.digest(); + return byteArrayToString(digest); + } + + private static String byteArrayToString(byte[] bytes) { + StringBuilder sb = new StringBuilder(bytes.length * 2); + for (int i = 0; i < bytes.length; i++) { + sb.append(String.format("%02x", bytes[i])); + } + return sb.toString(); + } + + private static Presence createPresenceAvatar(String aor, String itemId) throws Exception { + StringBuilder builder = new StringBuilder(""); + builder.append("1"); + builder.append(""); + builder.append(""); + builder.append(""); + builder.append(itemId); + builder.append(""); + + String xmlstr = builder.toString(); + SAXReader sreader = new SAXReader(); + + Document avatarDoc = sreader.read(new StringReader(xmlstr)); + Element rootElement = avatarDoc.getRootElement(); + + Presence avatar = new Presence(rootElement); + + avatar.setFrom(aor); + + XMPPServer.getInstance(); + UserManager um = XMPPServer.getInstance().getUserManager(); + User me = um.getUser(aor); + + PresenceManager pm = XMPPServer.getInstance().getPresenceManager(); + Presence mypresence = pm.getPresence(me); + + avatar.setType(mypresence.getType()); + avatar.setShow(mypresence.getShow()); + avatar.setStatus(mypresence.getStatus()); + avatar.setID(aor + "_presenceAvatar"); + return avatar; + } + + @Override + public String toString() { + return "VcardUpdateJob [userImName=" + userImName + ", oldMd5=" + oldMd5 + ", newMd5=" + newMd5 + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((newMd5 == null) ? 0 : newMd5.hashCode()); + result = prime * result + ((oldMd5 == null) ? 0 : oldMd5.hashCode()); + result = prime * result + ((userImName == null) ? 0 : userImName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VcardUpdateJob other = (VcardUpdateJob) obj; + if (newMd5 == null) { + if (other.newMd5 != null) + return false; + } else if (!newMd5.equals(other.newMd5)) + return false; + if (oldMd5 == null) { + if (other.oldMd5 != null) + return false; + } else if (!oldMd5.equals(other.oldMd5)) + return false; + if (userImName == null) { + if (other.userImName != null) + return false; + } else if (!userImName.equals(other.userImName)) + return false; + return true; + } + +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/ImdbOplogListener.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/ImdbOplogListener.java new file mode 100644 index 0000000000..07d48e9a53 --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/ImdbOplogListener.java @@ -0,0 +1,36 @@ +package org.sipfoundry.openfire.plugin.listener; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.regex.Pattern; + +import org.sipfoundry.openfire.plugin.MongoOperation; + +import com.mongodb.DBObject; +import com.mongodb.QueryBuilder; + +public class ImdbOplogListener extends MongoOplogListener { + private static final String WATCHED_NAMESPACE = "imdb.entity"; + private static final String ID = "_id"; + private static final Pattern USER_PATTERN = Pattern.compile("^User\\d+$"); + private static final Pattern GROUP_PATTERN = Pattern.compile("^Group\\d+$"); + private static final List WATCHED_ENTITIES = Arrays.asList(USER_PATTERN, GROUP_PATTERN); + private static final List WATCHED_OPERATIONS = Arrays.asList(MongoOperation.INSERT, + MongoOperation.UPDATE, MongoOperation.DELETE); + + @Override + protected DBObject buildOpLogQuery() { + DBObject nsQuery = QueryBuilder.start(NAMESPACE).is(WATCHED_NAMESPACE).get(); + DBObject ent1Query = QueryBuilder.start(RECORD + "." + ID).in(WATCHED_ENTITIES).get(); + DBObject ent2Query = QueryBuilder.start(RECORD2 + "." + ID).in(WATCHED_ENTITIES).get(); + DBObject entQuery = QueryBuilder.start().or(ent1Query, ent2Query).get(); + + return QueryBuilder.start().and(nsQuery, entQuery).get(); + } + + @Override + protected Collection getWatchedOperations() { + return WATCHED_OPERATIONS; + } +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/MongoOplogListener.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/MongoOplogListener.java new file mode 100755 index 0000000000..804edd875f --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/MongoOplogListener.java @@ -0,0 +1,102 @@ +/** + * + * + * Copyright (c) 2012 eZuce, Inc. All rights reserved. + */ +package org.sipfoundry.openfire.plugin.listener; + +import static org.sipfoundry.commons.mongo.MongoConstants.ID; + +import java.net.UnknownHostException; +import java.util.Collection; + +import org.apache.log4j.Logger; +import org.bson.types.BSONTimestamp; +import org.sipfoundry.commons.mongo.MongoFactory; +import org.sipfoundry.openfire.plugin.MongoOperation; +import org.sipfoundry.openfire.plugin.QueueManager; +import org.sipfoundry.openfire.plugin.job.Job; +import org.sipfoundry.openfire.plugin.job.JobFactory; + +import com.mongodb.Bytes; +import com.mongodb.DB; +import com.mongodb.DBCollection; +import com.mongodb.DBCursor; +import com.mongodb.DBObject; +import com.mongodb.Mongo; + +public abstract class MongoOplogListener implements Runnable { + private static Logger logger = Logger.getLogger(MongoOplogListener.class); + + protected static final String RECORD = "o"; + protected static final String RECORD2 = "o2"; + protected static final String NAMESPACE = "ns"; + private static final String OPERATION = "op"; + private static final String TIMESTAMP = "ts"; + private static final String SET_OP = "$set"; + + @Override + public void run() { + logger.debug("Running " + this.getClass()); + Mongo m = null; + try { + m = MongoFactory.fromConnectionFile(); + } catch (UnknownHostException ex) { + logger.error("Error running mongo opLog listener", ex); + return; + } + DB db = m.getDB("local"); + DBCollection col = db.getCollection("oplog.rs"); + long seconds = System.currentTimeMillis() / 1000; + DBObject query = buildOpLogQuery(); + System.out.println("Query: " + query); + DBCursor cur = col.find(query).addOption(Bytes.QUERYOPTION_TAILABLE).addOption(Bytes.QUERYOPTION_AWAITDATA); + + while (cur.hasNext()) { + DBObject object = cur.next(); + try { + int time = ((BSONTimestamp) object.get(TIMESTAMP)).getTime(); + + if (time > seconds) { + logger.debug(String.format("Got operation: %s", object)); + MongoOperation op = MongoOperation.fromString((String) object.get(OPERATION)); + + if (getWatchedOperations().contains(op)) { + DBObject record = (DBObject) object.get(RECORD); + Object id = record.get(ID); + + if (id == null) { + // this is an update, the id is in a different place + DBObject otherRecord = (DBObject) object.get(RECORD2); + id = otherRecord.get(ID); + // if it's a partial update, consider only the update part + DBObject partialUpdate = (DBObject) record.get(SET_OP); + if (partialUpdate != null) { + record = partialUpdate; + } + } + + Job j = JobFactory.createJob(op, record, id); + + // job can be null if the affected object is not in use (e.g. user + // update for an offline user - updated data will be read when the + // user comes online) + if (j != null) { + QueueManager.submitJob(j); + } + } + } + } catch (Exception ex) { + // this is a guard: no matter what fails, this worker loop must continue + // handling changes + logger.error("Error processing change: " + object, ex); + } + } + + cur.close(); + } + + protected abstract DBObject buildOpLogQuery(); + + protected abstract Collection getWatchedOperations(); +} diff --git a/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/ProfilesOplogListener.java b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/ProfilesOplogListener.java new file mode 100644 index 0000000000..9d0766df49 --- /dev/null +++ b/sipXopenfire/mongo-sync-plugin/src/org/sipfoundry/openfire/plugin/listener/ProfilesOplogListener.java @@ -0,0 +1,31 @@ +package org.sipfoundry.openfire.plugin.listener; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.apache.log4j.Logger; +import org.sipfoundry.openfire.plugin.MongoOperation; + +import com.mongodb.DBObject; +import com.mongodb.QueryBuilder; + +public class ProfilesOplogListener extends MongoOplogListener { + private static final Logger log = Logger.getLogger(ProfilesOplogListener.class); + private static final String WATCHED_NAMESPACE = "profiles.fs.files"; + private static final List WATCHED_OPERATIONS = Arrays.asList(MongoOperation.INSERT, + MongoOperation.DELETE); + + @Override + protected DBObject buildOpLogQuery() { + DBObject nsQuery = QueryBuilder.start(NAMESPACE).is(WATCHED_NAMESPACE).get(); + log.debug("Oplog query: " + nsQuery.toString()); + return nsQuery; + } + + @Override + protected Collection getWatchedOperations() { + return WATCHED_OPERATIONS; + } + +} diff --git a/sipXopenfire/vcard-provider/Makefile.am b/sipXopenfire/vcard-provider/Makefile.am deleted file mode 100644 index 4fefe5dee7..0000000000 --- a/sipXopenfire/vcard-provider/Makefile.am +++ /dev/null @@ -1,39 +0,0 @@ -include $(top_srcdir)/config/utility.am -include $(top_srcdir)/config/java.am -include $(top_srcdir)/common.am - -EXTRA_DIST = \ - $(provider_SRC) - -noinst_DATA = javac-provider - -jardir = @SIPX_JAVADIR@/sipXopenfire/lib - -jar_PKGS = \ - log4j \ - mongo \ - ws-commons-util \ - xmlrpc-common \ - xmlrpc-client \ - xmlrpc-server - -# copy jars into openfire lib dir. NOTE: I did not verify this is nec. but preserved from ant port -# but it wouldn't seem like is nec. Openfire integration i think would benefit from a full -# review. --douglas -# not necessary to copy jars into openfire lib - only sipxecs jars that need to be deployed in openfire lib (/opt/openfire/lib) should be put here -# but not jars from sipXcomons. sipxopenfire.in specifically creates symbolic links with needed jars from sipXcommons --mircea -jar_DATA = \ - $(provider_JAR) - -provider_JAR = sipx-openfire-vcard-provider.jar -provider_SRC = $(shell cd $(srcdir); find src -name '*.java') -provider_DEPS = \ - $(JAVAROOT) \ - $(call JavaDep,@SIPX_JAVADIR@/sipXcommons,$(provider_PKGS)) \ - @OPENFIRE_HOME@/lib/openfire.jar \ - ../vcard-synchserver/sipx-openfire-vcard-synchserver.jar - -$(provider_JAR) : javac-provider Manifest.txt Makefile - jar cfm $@ Manifest.txt \ - $(call JarInclude,$(sipxvcard_JAVAROOT),.) \ - $(call JarInclude,../presence-plugin,config.properties) diff --git a/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/ContactInfoHandlerImp.java b/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/ContactInfoHandlerImp.java deleted file mode 100644 index adbae10b66..0000000000 --- a/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/ContactInfoHandlerImp.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2010 Avaya, certain elements licensed under a Contributor Agreement. - * Contributors retain copyright to elements licensed under a Contributor Agreement. - * Licensed to the User under the LGPL license. - */ -package org.sipfoundry.openfire.vcard.provider; - -import org.apache.log4j.Logger; -import org.dom4j.Element; -import org.jivesoftware.openfire.vcard.VCardManager; -import org.sipfoundry.openfire.vcard.synchserver.ContactInfoHandler; -import org.sipfoundry.openfire.vcard.synchserver.Util; - -public class ContactInfoHandlerImp implements ContactInfoHandler { - private static Logger logger = Logger.getLogger(ContactInfoHandlerImp.class); - - @Override - public void notifyContactChange(String userName) { - if (VCardManager.getProvider() instanceof SipXVCardProvider) { - logger.info("Contact Change Notification received for user " + userName); - SipXVCardProvider prov = (SipXVCardProvider) VCardManager.getProvider(); - Element vCard = prov.cacheVCard(userName); - try { - VCardManager.getInstance().reset(); - Util.updateAvatar(userName, vCard); - // Sending announcement to the client - Util.notify(userName); - } catch (Exception e) { - logger.error("In ContactInfoHandlerImp set/update VCard failed! " + e.getMessage()); - } - } - } -} diff --git a/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/CustomSSLSocketFactory.java b/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/CustomSSLSocketFactory.java deleted file mode 100644 index 4c948864f7..0000000000 --- a/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/CustomSSLSocketFactory.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2010 Avaya, certain elements licensed under a Contributor Agreement. - * Contributors retain copyright to elements licensed under a Contributor Agreement. - * Licensed to the User under the LGPL license. - * $ - */ - -/** - * The CustomSSLSocketFacotry allows users to set timeout for the underneath socket. - * The timeout is in milliseconds. - */ - -package org.sipfoundry.openfire.vcard.provider; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.UnknownHostException; - -import javax.net.ssl.SSLSocketFactory; - -final public class CustomSSLSocketFactory extends SSLSocketFactory { - private SSLSocketFactory originalFactory; - private int socketTimeout; - - public CustomSSLSocketFactory(SSLSocketFactory originalFactory, int timeout) { - super(); - - this.originalFactory = originalFactory; - socketTimeout = timeout; - } - - @Override - public String[] getDefaultCipherSuites() { - return originalFactory.getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return originalFactory.getSupportedCipherSuites(); - } - - @Override - public Socket createSocket(String host, int port) throws IOException, UnknownHostException { - Socket socket = originalFactory.createSocket(); - socket.setSoTimeout(socketTimeout); - socket.connect(new InetSocketAddress(host, port)); - return socket; - } - - @Override - public Socket createSocket(InetAddress host, int port) throws IOException { - - Socket socket = originalFactory.createSocket(); - socket.setSoTimeout(socketTimeout); - socket.connect(new InetSocketAddress(host, port)); - return socket; - } - - @Override - public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) - throws IOException { - - Socket socket = originalFactory.createSocket(); - socket.setSoTimeout(socketTimeout); - socket.bind(new InetSocketAddress(localAddress, localPort)); - socket.connect(new InetSocketAddress(address, port)); - return socket; - } - - @Override - public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { - s.setSoTimeout(socketTimeout); - return this.originalFactory.createSocket(s, host, port, autoClose); - } - - @Override - public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, - UnknownHostException { - Socket socket = originalFactory.createSocket(); - socket.setSoTimeout(socketTimeout); - socket.bind(new InetSocketAddress(localHost, localPort)); - socket.connect(new InetSocketAddress(host, port)); - return socket; - } -} diff --git a/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/RestInterface.java b/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/RestInterface.java deleted file mode 100644 index c22231e052..0000000000 --- a/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/RestInterface.java +++ /dev/null @@ -1,560 +0,0 @@ -/* - * Copyright (C) 2010 Avaya, certain elements licensed under a Contributor Agreement. - * Contributors retain copyright to elements licensed under a Contributor Agreement. - * Licensed to the User under the LGPL license. - */ -package org.sipfoundry.openfire.vcard.provider; - -import java.awt.image.BufferedImage; -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.StringReader; -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.List; - -import javax.imageio.ImageIO; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.X509TrustManager; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.stream.StreamSource; - -import org.apache.log4j.Logger; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.lang.StringUtils; -import org.dom4j.Document; -import org.dom4j.DocumentHelper; -import org.dom4j.Element; -import org.dom4j.Node; -import org.dom4j.io.DocumentResult; -import org.dom4j.io.DocumentSource; -import org.dom4j.io.SAXReader; -import org.sipfoundry.commons.userdb.User; -import org.sipfoundry.openfire.vcard.provider.SipXVCardProvider; - -public class RestInterface { - public static final String REST_CALL_URL_CONTACT_INFO = System.getProperty("admin.rest.url") - + "/sipxconfig/rest/my/contact-information"; - public static final int SSL_CONNECTION_TIMEOUT = 10000; // milliseconds - public static final int CONNECTION_TIMEOUT = 30000; // milliseconds - public static final int READ_TIMEOUT = 30000; // milliseconds - - private static Logger logger = Logger.getLogger(RestInterface.class); - private static RestInterface instance = null; - - synchronized public static RestInterface getInstance() { - if (instance == null) - instance = new RestInterface(); - - return instance; - - } - - public static String sendRequest(String method, Element vcardElement) throws ConnectException { - try { - logger.debug("call REST URL " + REST_CALL_URL_CONTACT_INFO); - URL serverURL = new URL(REST_CALL_URL_CONTACT_INFO); - HttpURLConnection conn = (HttpURLConnection) serverURL.openConnection(); - conn.setReadTimeout(READ_TIMEOUT); - - conn.setDoOutput(true); - conn.setRequestMethod(method); - - if (vcardElement != null) { - conn.setDoInput(true); - conn.setRequestProperty("Content-type", "text/xml"); - OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream()); - String vcardXml = RestInterface.buildXMLContactInfo(vcardElement); - String contactXml = RestInterface.sendRequest(SipXVCardProvider.QUERY_METHOD, null); - if (contactXml != null) { - vcardXml = refillMissingContactInfo(vcardXml, contactXml); - } - - wr.write(vcardXml); - - wr.flush(); - wr.close(); - } - - conn.connect(); - - logger.debug("response code " + conn.getResponseCode() + " response message " + conn.getResponseMessage()); - - BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); - StringBuilder resp = new StringBuilder(); - String line; - while ((line = rd.readLine()) != null) { - resp.append(line); - } - rd.close(); - - if (conn.getResponseCode() >= 200 && conn.getResponseCode() < 300) { - return resp.toString(); - } else { - logger.error("Response code " + conn.getResponseCode() + ":" + conn.getResponseMessage() + " " - + resp.toString()); - return null; - } - } - - catch (ConnectException ex) { - logger.error("In sendRequest ConnectException " + ex.getMessage()); - throw ex; - } - - catch (IOException ex) { - logger.error("In sendRequest IOException " + ex.getMessage()); - return null; - } - - catch (Exception ex) { - logger.error("Exception " + ex.getMessage()); - return null; - } - } - - public String buildXMLContactInfoXSLT(Element e) { - try { - String x = e.asXML().replace("xmlns=\"vcard-temp\"", ""); // xmlns causes dom4j xpath - // not working somehow. - Document vcardDoc = DocumentHelper.parseText(x); - - logger.debug("before XSLT " + vcardDoc.getRootElement().asXML()); - InputStream inStream = this.getClass().getResourceAsStream("/contactInfo.xsl"); - Document contactDoc = styleDocument(vcardDoc, inStream); - - logger.debug("After XSLT " + contactDoc.getRootElement().asXML()); - return contactDoc.getRootElement().asXML(); - } catch (Exception ex) { - logger.error(ex.getMessage()); - return null; - } - - } - - public static String buildXMLContactInfo(Element e) { - try { - String x = e.asXML().replace("xmlns=\"vcard-temp\"", ""); // xmlns causes dom4j xpath - // not working somehow. - - logger.debug("In buildXMLContactInfo vcard string is " + x); - Document vcardDoc = DocumentHelper.parseText(x); - Element el = vcardDoc.getRootElement(); - - StringBuilder xbuilder = new StringBuilder(""); - - xbuilder.append(""); - xbuilder.append(getNodeText(el, "N/GIVEN")); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(getNodeText(el, "N/FAMILY")); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(getNodeText(el, "TITLE")); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(getNodeText(el, "ORG/ORGUNIT")); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(getNodeText(el, "ORG/ORGNAME")); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(getNodeText(el, "ADR/STREET")); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(getNodeText(el, "ADR/LOCALITY")); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(getNodeText(el, "ADR/CTRY")); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(getNodeText(el, "ADR/REGION")); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(getNodeText(el, "ADR/PCODE")); - xbuilder.append(""); - xbuilder.append(""); - - /* - * if (!(getNodeText(el, "JABBERID").equals("unknown"))) { xbuilder.append(""); - * xbuilder.append(getNodeText(el, "JABBERID"));xbuilder.append(""); } - */ - xbuilder.append(""); - xbuilder.append(getNodeText(el, "EMAIL/USERID")); - xbuilder.append(""); - xbuilder.append(""); - - logger.debug("contact info generated by buildXMLContactInfo is " + xbuilder.toString()); - - return xbuilder.toString(); - } catch (Exception ex) { - logger.error(ex.getMessage()); - return null; - } - - } - - public Element buildVCardFromXMLContactInfoXSLT(String xmlString) { - try { - Document contactDoc = DocumentHelper.parseText(xmlString); - InputStream inStream = this.getClass().getResourceAsStream("/vCardTemp.xsl"); - - logger.debug("before XSLT " + contactDoc.getRootElement().asXML()); - Document vcardDoc = styleDocument(contactDoc, inStream); - logger.debug("After XSLT " + vcardDoc.getRootElement().asXML()); - - return vcardDoc.getRootElement(); - } catch (Exception ex) { - logger.error(ex.getMessage()); - return null; - } - - } - - // - // Refill the contact information available in sipX but not mentioned in the vcard update - // request from XMM client. - // Otherwise, those information will be deleted by sipX. - // - public static String refillMissingContactInfo(String vcardXml, String contactXml) { - try { - SAXReader sreader = new SAXReader(); - - Document contactDoc = sreader.read(new StringReader(contactXml)); - Element contactRootElement = contactDoc.getRootElement(); - - Document vcardDoc = sreader.read(new StringReader(vcardXml)); - Element vcardRootElement = vcardDoc.getRootElement(); - - for (Element el : (List) contactRootElement.elements()) { - Element vElement; - if ((vElement = vcardRootElement.element(el.getName())) == null) { - logger.debug(" In refillMissingContactInfo Element = [" + el.getName() + "] not found!"); - vcardRootElement.add(el.createCopy()); - } else { - String newStr = refillMissingContactInfo(vElement.asXML(), el.asXML()); - if ((newStr.compareTo(vElement.asXML()) != 0)) { - Document vcardSubDoc = sreader.read(new StringReader(newStr)); - Element vcardSubRootElement = vcardSubDoc.getRootElement(); - vcardRootElement.remove(vElement); - vcardRootElement.add(vcardSubRootElement.createCopy()); - } - } - } - logger.debug("vcard XML string after refill is " + vcardRootElement.asXML()); - return vcardRootElement.asXML(); - } catch (Exception ex) { - logger.error(ex.getMessage()); - return null; - } - - } - - public static Element buildVCardFromXMLContactInfo(User user, Element avatarFromDB) { - try { - SAXReader sreader = new SAXReader(); - - StringBuilder xbuilder = new StringBuilder(""); - - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getDisplayName())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getLastName())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getFirstName())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getCompanyName())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getJobDepartment())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getJobTitle())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getUserName())); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getFaxNumber())); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getOfficeStreet())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getOfficeCity())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getOfficeState())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getOfficeZip())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getOfficeCountry())); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getHomeNum())); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getCellNum())); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getHomeStreet())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getHomeCity())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getHomeState())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getHomeZip())); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getHomeCountry())); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getEmailAddress())); - xbuilder.append(""); - xbuilder.append(""); - - xbuilder.append(""); - xbuilder.append(defaultContactValue(user.getJid())); - xbuilder.append(""); - xbuilder.append(""); - - if (avatarFromDB == null) { - String encodedStr = getEncodedAvatar(defaultContactValue(user.getAvatar())); - if (encodedStr != null) { - xbuilder.append(""); - xbuilder.append("image/png"); - xbuilder.append(""); - xbuilder.append(encodedStr); - xbuilder.append(""); - xbuilder.append(""); - } - } - - xbuilder.append(""); - - // The following are Not supported by XMPP vcard temp XEP-0054 so far. - /* - * adfafd - * afaf afdadf - * 201_IM_test afa - * afafd - * -bash-3.2$ - */ - - logger.debug("vcard generated by buildVCardFromXMLContactinfo is " + xbuilder.toString()); - Document vcardDoc = sreader.read(new StringReader(xbuilder.toString())); - Element vcardNode = vcardDoc.getRootElement(); - - if (avatarFromDB != null) { - vcardNode.add(avatarFromDB); - } - - return vcardNode; - } - - catch (Exception ex) { - logger.error(ex.getMessage()); - return null; - } - - } - - private static String defaultContactValue(String value) { - return StringUtils.defaultString(value, StringUtils.EMPTY); - } - - public static String getNodeText(Element element, String nodeName) { - Node node = element.selectSingleNode(nodeName); - if (node != null) { - return node.getText(); - } - - return ""; - } - - public static String getTextFromNodes(Element element, String nameNode, String criteriaNode, String valueNode) { - List nlist = element.selectNodes(nameNode); - for (int i = 0; i < nlist.size(); i++) { - Element el = (Element) (nlist.get(i)); - Node cNode = el.selectSingleNode(criteriaNode); - if (cNode != null) { - Node vNode = el.selectSingleNode(valueNode); - if (vNode != null) { - return vNode.getText(); - } - } - - } - - return ""; - } - - public static Document styleDocument(Document document, InputStream stylesheet) throws Exception { - - // load the transformer using JAXP - TransformerFactory factory = TransformerFactory.newInstance(); - Transformer transformer = factory.newTransformer(new StreamSource(stylesheet)); - - // now lets style the given document - DocumentSource source = new DocumentSource(document); - DocumentResult result = new DocumentResult(); - transformer.transform(source, result); - - // return the transformed document - Document transformedDoc = result.getDocument(); - return transformedDoc; - } - - public static String getEncodedAvatar(String avatarURL) { - logger.debug("Avatar URL " + avatarURL); - return getPngStringTimeout(avatarURL); - } - - public static String getPngString(URL url) { - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - BufferedImage image = ImageIO.read(url); - ImageIO.write(image, "png", os); - - return new String(new Base64().encode(os.toByteArray())); - } catch (IOException e) { - logger.error("In getPngString, error:" + e.getMessage()); - return null; - } catch (Exception e) { - logger.error(e.getMessage()); - return null; - } - } - - public static String getPngStringTimeout(String urlStr) { - try { - URL url = new URL(urlStr); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - if (url.getProtocol().equalsIgnoreCase("https")) { - TrustManager[] trustAllCerts = new TrustManager[] { - new X509TrustManager() { - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } - public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } - public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { - } - } - }; - - SSLContext sc = SSLContext.getInstance("TLS"); - sc.init(null, trustAllCerts, new java.security.SecureRandom()); - ((HttpsURLConnection) conn).setSSLSocketFactory(sc.getSocketFactory()); - } - conn.setConnectTimeout(CONNECTION_TIMEOUT); - conn.setReadTimeout(READ_TIMEOUT); - conn.setRequestMethod("GET"); - - conn.connect(); - - ByteArrayOutputStream os = new ByteArrayOutputStream(); - BufferedImage image = ImageIO.read(conn.getInputStream()); - ImageIO.write(image, "png", os); - - return new String(new Base64().encode(os.toByteArray())); - } catch (MalformedURLException e) { - logger.error("In getPngStringTimeout, MalformedURLException:" + e.getMessage()); - return null; - } catch (IOException e) { - logger.error("In getPngStringTimeout, IOException:" + e.getMessage()); - return null; - } catch (Exception e) { - logger.error("In getPngStringTimeout, Exception:" + e.getMessage()); - return null; - } - - } -} diff --git a/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/SipXVCardProvider.java b/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/SipXVCardProvider.java deleted file mode 100644 index 4f0ae36809..0000000000 --- a/sipXopenfire/vcard-provider/src/org/sipfoundry/openfire/vcard/provider/SipXVCardProvider.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (C) 2010 Avaya, certain elements licensed under a Contributor Agreement. - * Contributors retain copyright to elements licensed under a Contributor Agreement. - * Licensed to the User under the LGPL license. - */ -package org.sipfoundry.openfire.vcard.provider; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStream; -import java.net.ConnectException; -import java.util.Properties; - -import org.apache.commons.io.IOUtils; -import org.apache.log4j.Logger; -import org.dom4j.Element; -import org.jivesoftware.openfire.provider.VCardProvider; -import org.jivesoftware.openfire.vcard.DefaultVCardProvider; -import org.jivesoftware.openfire.vcard.VCardManager; -import org.jivesoftware.util.AlreadyExistsException; -import org.jivesoftware.util.NotFoundException; -import org.jivesoftware.util.cache.Cache; -import org.jivesoftware.util.cache.CacheFactory; -import org.sipfoundry.commons.userdb.User; -import org.sipfoundry.commons.util.UnfortunateLackOfSpringSupportFactory; -import org.sipfoundry.openfire.vcard.synchserver.Util; -import org.sipfoundry.openfire.vcard.synchserver.VCardRpcServer; - -/** - *

- * A vCard provider which uses sipX as user profile data source - *

- */ -public class SipXVCardProvider implements VCardProvider { - - /** - * The name of the avatar element (<PHOTO>) in the vCard XML. - */ - static final String PLUGIN_CONFIG_FILENAME = "/config.properties"; - static final String MONGO_CLIENT_CONFIG = "/mongo-client.ini"; - static final String PROP_SIPX_CONF_DIR = "sipxpbx.conf.dir"; - static final String PROP_CONFIG_HOST_NAME = "CONFIG_HOSTS"; - static final String PROP_SECRET = "SHARED_SECRET"; - static final String DEFAULT_DOMAIN_NAME = "localhost"; - static final String DEFAULT_SECRET = "unknown"; - static final String MODIFY_METHOD = "PUT"; - static final String QUERY_METHOD = "GET"; - static final String PA_USER = "mybuddy"; - static final String AVATAR_ELEMENT = "PHOTO"; - static final int MAX_ATTEMPTS = 12; // Try 12 times at most when connects to sipXconfig - static final int ATTEMPT_INTERVAL = 5000; // 5 seconds - static long ID_index = 0; - private static Logger logger = Logger.getLogger(SipXVCardProvider.class); - - private final Cache vcardCache; - private final DefaultVCardProvider defaultProvider; - - public SipXVCardProvider() { - super(); - - defaultProvider = new DefaultVCardProvider(); - - try { - UnfortunateLackOfSpringSupportFactory.initialize(); - if (new File("/tmp/sipx.properties").exists()) { - System.getProperties() - .load(new FileInputStream(new File("/tmp/sipx.properties"))); - } - - } catch (Exception e) { - logger.error(e); - } - - String cacheName = "SipXVCardCache"; - vcardCache = CacheFactory.createCache(cacheName); - - logger.info(this.getClass().getName() + " starting XML RPC server ..."); - try { - VCardRpcServer vcardRpcServer = new VCardRpcServer(ContactInfoHandlerImp.class); - vcardRpcServer.start(); - logger.info(this.getClass().getName() + " initialized"); - } catch (Exception ex) { - logger.error(ex); - } - - } - - synchronized public Element getVCard(String username) { - Element vcard = vcardCache.get(username); - if (vcard == null) { - return cacheVCard(username); - } - return vcard; - } - - /** - * SipXconfig Does NOT support "delete user profile". So only the big cache (database) record - * is removed. - */ - - @Override - synchronized public void deleteVCard(String username) { - vcardCache.remove(username); - defaultProvider.deleteVCard(username); - - // Refill the cache - Element vCard = cacheVCard(username); - try { - Util.updateAvatar(username, vCard); - } catch (Exception e) { - logger.error("Cannot send update Avatar notification", e); - } - } - - @Override - public Element createVCard(String username, Element element) { - Element vcard = null; - try { - defaultProvider.deleteVCard(username); - vcard = defaultProvider.createVCard(username, element); - } catch (AlreadyExistsException e) { - e.printStackTrace(); - logger.error("AlreadyExistsException even afer delete is called!"); - if (username.compareToIgnoreCase(PA_USER) == 0) { - return defaultProvider.loadVCard(username); - } - - } - - if (username.compareToIgnoreCase(PA_USER) == 0) { - return vcard; - } - - return updateVCard(username, element); - } - - synchronized Element cacheVCard(String username) { - Element vCardElement = null; - User user = UnfortunateLackOfSpringSupportFactory.getValidUsers().getUserByJid(username); - if (user != null) { - Element avatarFromDB = getAvatarCopy(defaultProvider.loadVCard(username)); - vCardElement = RestInterface.buildVCardFromXMLContactInfo(user, avatarFromDB); - if (vCardElement != null) { - vcardCache.remove(username); - vcardCache.put(username, vCardElement); - } else { - logger.error("In cacheVCard buildVCardFromXMLContactInfo failed! "); - } - } else { - logger.error("In cacheVCard Failed to find peer SIP user account for XMPP user " + username); - } - - return vCardElement; - } - - /** - * Loads the vCard using the SipX vCard Provider first On failure, attempt to load it from - * database. - * - */ - @Override - public Element loadVCard(String username) { - synchronized (username.intern()) { - if (username.compareToIgnoreCase(PA_USER) == 0) { - return defaultProvider.loadVCard(username); - } - - return getVCard(username); - } - } - - /** - * Updates the vCard both in SipX and in the database. - */ - @Override - public Element updateVCard(String username, Element vCardElement) { - if (username.compareToIgnoreCase(PA_USER) == 0) { - try { - return defaultProvider.updateVCard(username, vCardElement); - } catch (NotFoundException e) { - e.printStackTrace(); - logger.error("update " + PA_USER + "'s vcard failed!"); - return null; - } - } - - try { - defaultProvider.updateVCard(username, vCardElement); - } catch (NotFoundException e) { - try { - defaultProvider.createVCard(username, vCardElement); - } catch (AlreadyExistsException e1) { - logger.error("Failed to create vcard due to existing vcard found"); - e1.printStackTrace(); - } - e.printStackTrace(); - } - - try { - String sipUserName = getAORFromJABBERID(username); - if (sipUserName != null) { - - int attempts = 0; - boolean tryAgain; - do { - tryAgain = false; - try { - RestInterface.sendRequest(MODIFY_METHOD, vCardElement); - } catch (ConnectException e) { - try { - Thread.sleep(ATTEMPT_INTERVAL); - } catch (Exception e1) { - e1.printStackTrace(); - } - attempts++; - tryAgain = true; - } - } while (attempts < MAX_ATTEMPTS && tryAgain); - - if (attempts >= MAX_ATTEMPTS) { - logger.error("Failed to update contact info for user " + username + ", sipXconfig might be down"); - } - - Element vcardAfterUpdate = cacheVCard(username); - - //If client doesn't set local avatar, use the avatar from sipx/gravatar. - if (getAvatar(vCardElement) == null) { - VCardManager.getInstance().reset(); - Util.updateAvatar(username, vcardAfterUpdate); - } - - return vcardAfterUpdate; - - } - logger.error("Failed to find a valid SIP account for user " + username); - - return vCardElement; - } - - catch (Exception ex) { - logger.error("updateVCard failed! " + ex.getMessage()); - return vCardElement; - } - } - - /** - * Returns false to allow users to save vCards, even if only the avatar will be - * saved. - * - * @return false - */ - @Override - public boolean isReadOnly() { - return false; - } - - @SuppressWarnings("resource") - protected Properties loadProperties(String path_under_conf_dir) { - - Properties result = null; - - InputStream in = this.getClass().getResourceAsStream(PLUGIN_CONFIG_FILENAME); - Properties properties = new Properties(); - - try { - properties.load(in); - } catch (IOException ex) { - logger.error(ex); - } finally { - IOUtils.closeQuietly(in); - } - - String file_path = properties.getProperty(PROP_SIPX_CONF_DIR) + path_under_conf_dir; - logger.info("Domain config file path is " + file_path); - - InputStream fis = null; - try { - fis = new FileInputStream(file_path); - result = new Properties(); - result.load(fis); - - } catch (Exception e) { - logger.error("Failed to read '" + file_path + "':"); - System.err.println("Failed to read '" + file_path + "':"); - e.printStackTrace(System.err); - } finally { - IOUtils.closeQuietly(fis); - } - - return result; - } - - @SuppressWarnings("resource") - public String getConfDir() { - InputStream in = this.getClass().getResourceAsStream("/config.properties"); - Properties properties = new Properties(); - - try { - properties.load(in); - } catch (IOException ex) { - logger.error(ex); - } finally { - IOUtils.closeQuietly(in); - } - - return properties.getProperty("sipxpbx.conf.dir"); - } - - public static String getAORFromJABBERID(String jabberid) { - try { - User user = UnfortunateLackOfSpringSupportFactory.getValidUsers().getUserByJid(jabberid); - if (user != null) { - return user.getUserName(); - } - - return null; - } catch (Exception ex) { - logger.error("getAORFROMJABBERID exception " + ex.getMessage()); - return null; - } - } - - @SuppressWarnings("resource") - public static String readXML(File file) { - String contents = null; - - StringBuilder builder = new StringBuilder(); - BufferedReader reader = null; - - try { - reader = new BufferedReader(new FileReader(file)); - - String line; - while ((line = reader.readLine()) != null) { - builder.append(line); - } - - contents = builder.toString().replaceAll("xmlns=", "dummy="); // dom4j parser doesn't like - // xmlns in the root element - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - IOUtils.closeQuietly(reader); - } - - return contents; - } - - /** - * Loads the vCard using the LDAP vCard Provider and re-adds the avatar from the database. - * - * @param username - * @return LDAP vCard re-added avatar element - */ - synchronized static Element mergeAvatar(String username, Element vcardFromSipX, Element vcardFromDB) { - - logger.info("merge avatar for user '" + username + "' ..."); - - // get the vcard from ldap - // Element vCardElement = super.loadVCard(username); - - // only add avatar if it doesn't exist already - if (vcardFromDB != null && vcardFromDB.element(AVATAR_ELEMENT) != null) { - Element avatarElement = getAvatarCopy(vcardFromDB); - - if (avatarElement != null) { - if (getAvatar(vcardFromSipX) != null) { - vcardFromSipX.remove(getAvatar(vcardFromSipX)); - } - vcardFromSipX.add(avatarElement); - logger.info("Avatar merged from DB into sipX vCard"); - } else { - logger.info("No vCard found in database"); - } - } - - return vcardFromSipX; - } - - protected static Element getAvatarCopy(Element vcard) { - Element avatarElement = null; - if (vcard != null) { - Element photoElement = vcard.element(AVATAR_ELEMENT); - if (photoElement != null) { - avatarElement = photoElement.createCopy(); - } - } - - return avatarElement; - } - - protected static Element getAvatar(Element vcard) { - Element avatarElement = null; - if (vcard != null) { - return vcard.element(AVATAR_ELEMENT); - } - return avatarElement; - } - - - -} diff --git a/sipXopenfire/vcard-synchserver/Makefile.am b/sipXopenfire/vcard-synchserver/Makefile.am index 438696130c..4d1217cf72 100644 --- a/sipXopenfire/vcard-synchserver/Makefile.am +++ b/sipXopenfire/vcard-synchserver/Makefile.am @@ -21,4 +21,4 @@ synchserver_DEPS = \ $(synchserver_JAR) : javac-synchserver Manifest.txt Makefile jar cfm $@ Manifest.txt \ - $(call JarInclude,$(vcardsynchserver_JAVAROOT),.) + $(call JarInclude,$(vcardsynchserver_JAVAROOT)/classes,.)