diff --git a/build/build.xml b/build/build.xml index a93e74c18..7d5d73acd 100644 --- a/build/build.xml +++ b/build/build.xml @@ -29,7 +29,7 @@ - + @@ -170,54 +170,63 @@ + - + + + includes="org/jivesoftware/smack/**/*.class, **/smack-config.xml, **/core.providers, **/jul.properties"> - - + + - - + + - - + + - + + + - - - + + - + @@ -225,14 +234,14 @@ - - + + - + diff --git a/build/release.xml b/build/release.xml index b4a0eef4b..667b71d2f 100644 --- a/build/release.xml +++ b/build/release.xml @@ -43,10 +43,14 @@ + + + + @@ -54,12 +58,20 @@ + + + + + + + + @@ -110,7 +122,10 @@ - + + + + diff --git a/build/resources/META-INF/core.providers b/build/resources/META-INF/core.providers new file mode 100644 index 000000000..614a2d8d7 --- /dev/null +++ b/build/resources/META-INF/core.providers @@ -0,0 +1,19 @@ + + + + + + + query + jabber:iq:privacy + org.jivesoftware.smack.provider.PrivacyProvider + + + + + ping + urn:xmpp:ping + org.jivesoftware.smack.ping.provider.PingProvider + + + diff --git a/build/resources/META-INF/smack.providers b/build/resources/META-INF/extension.providers similarity index 97% rename from build/resources/META-INF/smack.providers rename to build/resources/META-INF/extension.providers index 452e3df55..a0e420c20 100644 --- a/build/resources/META-INF/smack.providers +++ b/build/resources/META-INF/extension.providers @@ -1,663 +1,649 @@ - - - - - - - query - jabber:iq:private - org.jivesoftware.smackx.PrivateDataManager$PrivateDataIQProvider - - - - - query - jabber:iq:time - org.jivesoftware.smackx.packet.Time - - - - - x - jabber:x:roster - org.jivesoftware.smackx.provider.RosterExchangeProvider - - - - - x - jabber:x:event - org.jivesoftware.smackx.provider.MessageEventProvider - - - - - active - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.packet.ChatStateExtension$Provider - - - - composing - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.packet.ChatStateExtension$Provider - - - - paused - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.packet.ChatStateExtension$Provider - - - - inactive - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.packet.ChatStateExtension$Provider - - - - gone - http://jabber.org/protocol/chatstates - org.jivesoftware.smackx.packet.ChatStateExtension$Provider - - - - - html - http://jabber.org/protocol/xhtml-im - org.jivesoftware.smackx.provider.XHTMLExtensionProvider - - - - - x - jabber:x:conference - org.jivesoftware.smackx.GroupChatInvitation$Provider - - - - - query - http://jabber.org/protocol/disco#items - org.jivesoftware.smackx.provider.DiscoverItemsProvider - - - - - query - http://jabber.org/protocol/disco#info - org.jivesoftware.smackx.provider.DiscoverInfoProvider - - - - - x - jabber:x:data - org.jivesoftware.smackx.provider.DataFormProvider - - - - - x - http://jabber.org/protocol/muc#user - org.jivesoftware.smackx.provider.MUCUserProvider - - - - - query - http://jabber.org/protocol/muc#admin - org.jivesoftware.smackx.provider.MUCAdminProvider - - - - - query - http://jabber.org/protocol/muc#owner - org.jivesoftware.smackx.provider.MUCOwnerProvider - - - - - x - jabber:x:delay - org.jivesoftware.smackx.provider.DelayInformationProvider - - - - delay - urn:xmpp:delay - org.jivesoftware.smackx.provider.DelayInfoProvider - - - - - query - jabber:iq:version - org.jivesoftware.smackx.packet.Version - - - - - vCard - vcard-temp - org.jivesoftware.smackx.provider.VCardProvider - - - - - offline - http://jabber.org/protocol/offline - org.jivesoftware.smackx.packet.OfflineMessageRequest$Provider - - - - - offline - http://jabber.org/protocol/offline - org.jivesoftware.smackx.packet.OfflineMessageInfo$Provider - - - - - query - jabber:iq:last - org.jivesoftware.smackx.packet.LastActivity$Provider - - - - - query - jabber:iq:search - org.jivesoftware.smackx.search.UserSearch$Provider - - - - - sharedgroup - http://www.jivesoftware.org/protocol/sharedgroup - org.jivesoftware.smackx.packet.SharedGroupsInfo$Provider - - - - - addresses - http://jabber.org/protocol/address - org.jivesoftware.smackx.provider.MultipleAddressesProvider - - - - - si - http://jabber.org/protocol/si - org.jivesoftware.smackx.provider.StreamInitiationProvider - - - - query - http://jabber.org/protocol/bytestreams - org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider - - - - open - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider - - - - data - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider - - - - close - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.CloseIQProvider - - - - data - http://jabber.org/protocol/ibb - org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider - - - - - query - jabber:iq:privacy - org.jivesoftware.smack.provider.PrivacyProvider - - - - - command - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider - - - - bad-action - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadActionError - - - - malformed-actionn - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$MalformedActionError - - - - bad-locale - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadLocaleError - - - - bad-payload - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadPayloadError - - - - bad-sessionid - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadSessionIDError - - - - session-expired - http://jabber.org/protocol/commands - org.jivesoftware.smackx.provider.AdHocCommandDataProvider$SessionExpiredError - - - - - - offer - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OfferRequestProvider - - - - offer-revoke - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OfferRevokeProvider - - - - agent-status-request - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentStatusRequest$Provider - - - - transcripts - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptsProvider - - - - transcript - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptProvider - - - - workgroups - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentWorkgroups$Provider - - - - agent-info - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentInfo$Provider - - - - transcript-search - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.TranscriptSearch$Provider - - - - occupants-info - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.OccupantsInfo$Provider - - - - chat-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.ChatSettings$InternalProvider - - - - chat-notes - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.notes.ChatNotes$Provider - - - - chat-sessions - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.history.AgentChatHistory$InternalProvider - - - - offline-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.OfflineSettings$InternalProvider - - - - sound-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.SoundSettings$InternalProvider - - - - workgroup-properties - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.WorkgroupProperties$InternalProvider - - - - - search-settings - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.SearchSettings$InternalProvider - - - - workgroup-form - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm$InternalProvider - - - - macros - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.macros.Macros$InternalProvider - - - - chat-metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.ext.history.ChatMetadata$Provider - - - - generic-metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.settings.GenericSettings$InternalProvider - - - - monitor - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.MonitorPacket$InternalProvider - - - - - queue-status - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueUpdate$Provider - - - - workgroup - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.WorkgroupInformation$Provider - - - - metadata - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.MetaDataProvider - - - - session - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.SessionID$Provider - - - - user - http://jivesoftware.com/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.UserID$Provider - - - - agent-status - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.AgentStatus$Provider - - - - notify-queue-details - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueDetails$Provider - - - - notify-queue - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.QueueOverview$Provider - - - - invite - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.RoomInvitation$Provider - - - - transfer - http://jabber.org/protocol/workgroup - org.jivesoftware.smackx.workgroup.packet.RoomTransfer$Provider - - - - - headers - http://jabber.org/protocol/shim - org.jivesoftware.smackx.provider.HeadersProvider - - - - header - http://jabber.org/protocol/shim - org.jivesoftware.smackx.provider.HeaderProvider - - - - - pubsub - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.PubSubProvider - - - - create - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider - - - - items - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.ItemsProvider - - - - item - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.ItemProvider - - - - subscriptions - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.SubscriptionsProvider - - - - subscription - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider - - - - affiliations - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.AffiliationsProvider - - - - affiliation - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.AffiliationProvider - - - - options - http://jabber.org/protocol/pubsub - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - - pubsub - http://jabber.org/protocol/pubsub#owner - org.jivesoftware.smackx.pubsub.provider.PubSubProvider - - - - configure - http://jabber.org/protocol/pubsub#owner - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - default - http://jabber.org/protocol/pubsub#owner - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - - event - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.EventProvider - - - - configuration - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.ConfigEventProvider - - - - delete - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider - - - - options - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.FormNodeProvider - - - - items - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.ItemsProvider - - - - item - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.ItemProvider - - - - retract - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.RetractEventProvider - - - - purge - http://jabber.org/protocol/pubsub#event - org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider - - - - - nick - http://jabber.org/protocol/nick - org.jivesoftware.smackx.packet.Nick$Provider - - - - - attention - urn:xmpp:attention:0 - org.jivesoftware.smackx.packet.AttentionExtension$Provider - - - - - received - urn:xmpp:receipts - org.jivesoftware.smackx.receipts.DeliveryReceipt$Provider - - - request - urn:xmpp:receipts - org.jivesoftware.smackx.receipts.DeliveryReceiptRequest$Provider - - - - - c - http://jabber.org/protocol/caps - org.jivesoftware.smackx.entitycaps.provider.CapsExtensionProvider - - - - - ping - urn:xmpp:ping - org.jivesoftware.smack.ping.provider.PingProvider - - - + + + + + + + query + jabber:iq:private + org.jivesoftware.smackx.PrivateDataManager$PrivateDataIQProvider + + + + + query + jabber:iq:time + org.jivesoftware.smackx.packet.Time + + + + + x + jabber:x:roster + org.jivesoftware.smackx.provider.RosterExchangeProvider + + + + + x + jabber:x:event + org.jivesoftware.smackx.provider.MessageEventProvider + + + + + active + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.packet.ChatStateExtension$Provider + + + + composing + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.packet.ChatStateExtension$Provider + + + + paused + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.packet.ChatStateExtension$Provider + + + + inactive + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.packet.ChatStateExtension$Provider + + + + gone + http://jabber.org/protocol/chatstates + org.jivesoftware.smackx.packet.ChatStateExtension$Provider + + + + + html + http://jabber.org/protocol/xhtml-im + org.jivesoftware.smackx.provider.XHTMLExtensionProvider + + + + + x + jabber:x:conference + org.jivesoftware.smackx.GroupChatInvitation$Provider + + + + + query + http://jabber.org/protocol/disco#items + org.jivesoftware.smackx.provider.DiscoverItemsProvider + + + + + query + http://jabber.org/protocol/disco#info + org.jivesoftware.smackx.provider.DiscoverInfoProvider + + + + + x + jabber:x:data + org.jivesoftware.smackx.provider.DataFormProvider + + + + + x + http://jabber.org/protocol/muc#user + org.jivesoftware.smackx.provider.MUCUserProvider + + + + + query + http://jabber.org/protocol/muc#admin + org.jivesoftware.smackx.provider.MUCAdminProvider + + + + + query + http://jabber.org/protocol/muc#owner + org.jivesoftware.smackx.provider.MUCOwnerProvider + + + + + x + jabber:x:delay + org.jivesoftware.smackx.provider.DelayInformationProvider + + + + delay + urn:xmpp:delay + org.jivesoftware.smackx.provider.DelayInfoProvider + + + + + query + jabber:iq:version + org.jivesoftware.smackx.packet.Version + + + + + vCard + vcard-temp + org.jivesoftware.smackx.provider.VCardProvider + + + + + offline + http://jabber.org/protocol/offline + org.jivesoftware.smackx.packet.OfflineMessageRequest$Provider + + + + + offline + http://jabber.org/protocol/offline + org.jivesoftware.smackx.packet.OfflineMessageInfo$Provider + + + + + query + jabber:iq:last + org.jivesoftware.smackx.packet.LastActivity$Provider + + + + + query + jabber:iq:search + org.jivesoftware.smackx.search.UserSearch$Provider + + + + + sharedgroup + http://www.jivesoftware.org/protocol/sharedgroup + org.jivesoftware.smackx.packet.SharedGroupsInfo$Provider + + + + + addresses + http://jabber.org/protocol/address + org.jivesoftware.smackx.provider.MultipleAddressesProvider + + + + + si + http://jabber.org/protocol/si + org.jivesoftware.smackx.provider.StreamInitiationProvider + + + + query + http://jabber.org/protocol/bytestreams + org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider + + + + open + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider + + + + data + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider + + + + close + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.CloseIQProvider + + + + data + http://jabber.org/protocol/ibb + org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider + + + + + command + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider + + + + bad-action + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadActionError + + + + malformed-actionn + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$MalformedActionError + + + + bad-locale + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadLocaleError + + + + bad-payload + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadPayloadError + + + + bad-sessionid + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$BadSessionIDError + + + + session-expired + http://jabber.org/protocol/commands + org.jivesoftware.smackx.provider.AdHocCommandDataProvider$SessionExpiredError + + + + + + offer + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OfferRequestProvider + + + + offer-revoke + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OfferRevokeProvider + + + + agent-status-request + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentStatusRequest$Provider + + + + transcripts + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptsProvider + + + + transcript + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptProvider + + + + workgroups + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentWorkgroups$Provider + + + + agent-info + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentInfo$Provider + + + + transcript-search + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.TranscriptSearch$Provider + + + + occupants-info + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.OccupantsInfo$Provider + + + + chat-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.ChatSettings$InternalProvider + + + + chat-notes + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.notes.ChatNotes$Provider + + + + chat-sessions + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.history.AgentChatHistory$InternalProvider + + + + offline-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.OfflineSettings$InternalProvider + + + + sound-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.SoundSettings$InternalProvider + + + + workgroup-properties + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.WorkgroupProperties$InternalProvider + + + + + search-settings + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.SearchSettings$InternalProvider + + + + workgroup-form + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.forms.WorkgroupForm$InternalProvider + + + + macros + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.macros.Macros$InternalProvider + + + + chat-metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.ext.history.ChatMetadata$Provider + + + + generic-metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.settings.GenericSettings$InternalProvider + + + + monitor + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.MonitorPacket$InternalProvider + + + + + queue-status + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueUpdate$Provider + + + + workgroup + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.WorkgroupInformation$Provider + + + + metadata + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.MetaDataProvider + + + + session + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.SessionID$Provider + + + + user + http://jivesoftware.com/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.UserID$Provider + + + + agent-status + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.AgentStatus$Provider + + + + notify-queue-details + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueDetails$Provider + + + + notify-queue + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.QueueOverview$Provider + + + + invite + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.RoomInvitation$Provider + + + + transfer + http://jabber.org/protocol/workgroup + org.jivesoftware.smackx.workgroup.packet.RoomTransfer$Provider + + + + + headers + http://jabber.org/protocol/shim + org.jivesoftware.smackx.provider.HeadersProvider + + + + header + http://jabber.org/protocol/shim + org.jivesoftware.smackx.provider.HeaderProvider + + + + + pubsub + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.PubSubProvider + + + + create + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider + + + + items + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.ItemsProvider + + + + item + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.ItemProvider + + + + subscriptions + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.SubscriptionsProvider + + + + subscription + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider + + + + affiliations + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.AffiliationsProvider + + + + affiliation + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.AffiliationProvider + + + + options + http://jabber.org/protocol/pubsub + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + + pubsub + http://jabber.org/protocol/pubsub#owner + org.jivesoftware.smackx.pubsub.provider.PubSubProvider + + + + configure + http://jabber.org/protocol/pubsub#owner + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + default + http://jabber.org/protocol/pubsub#owner + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + + event + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.EventProvider + + + + configuration + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.ConfigEventProvider + + + + delete + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider + + + + options + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.FormNodeProvider + + + + items + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.ItemsProvider + + + + item + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.ItemProvider + + + + retract + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.RetractEventProvider + + + + purge + http://jabber.org/protocol/pubsub#event + org.jivesoftware.smackx.pubsub.provider.SimpleNodeProvider + + + + + nick + http://jabber.org/protocol/nick + org.jivesoftware.smackx.packet.Nick$Provider + + + + + attention + urn:xmpp:attention:0 + org.jivesoftware.smackx.packet.AttentionExtension$Provider + + + + + received + urn:xmpp:receipts + org.jivesoftware.smackx.receipts.DeliveryReceipt$Provider + + + request + urn:xmpp:receipts + org.jivesoftware.smackx.receipts.DeliveryReceiptRequest$Provider + + + + + c + http://jabber.org/protocol/caps + org.jivesoftware.smackx.entitycaps.provider.CapsExtensionProvider + + + diff --git a/build/resources/META-INF/jul.properties b/build/resources/META-INF/jul.properties new file mode 100644 index 000000000..fe90575ec --- /dev/null +++ b/build/resources/META-INF/jul.properties @@ -0,0 +1,3 @@ +# Java Util Logging configuration for Smack. +handlers = java.util.logging.ConsoleHandler +.level = WARNING \ No newline at end of file diff --git a/build/resources/META-INF/sample.providers b/build/resources/META-INF/sample.providers new file mode 100644 index 000000000..b13ddbdaa --- /dev/null +++ b/build/resources/META-INF/sample.providers @@ -0,0 +1,17 @@ + + + + + + element + ns + com.myco.MyIQProvider + + + + elem + http://jabber.org/protocol/whoknows + com.myco.MyExtProvider + + + diff --git a/build/resources/META-INF/smack-config.xml b/build/resources/META-INF/smack-config.xml index a4df4764c..f3cad304e 100644 --- a/build/resources/META-INF/smack-config.xml +++ b/build/resources/META-INF/smack-config.xml @@ -22,9 +22,13 @@ - org.jivesoftware.smackx.ServiceDiscoveryManager + org.jivesoftware.smack.LoggingInitializer + org.jivesoftware.smack.provider.CoreInitializer + org.jivesoftware.smack.provider.VmArgInitializer org.jivesoftware.smack.PrivacyListManager org.jivesoftware.smack.keepalive.KeepAliveManager + org.jivesoftware.smackx.provider.ExtensionInitializer + org.jivesoftware.smackx.ServiceDiscoveryManager org.jivesoftware.smackx.XHTMLManager org.jivesoftware.smackx.muc.MultiUserChat org.jivesoftware.smackx.bytestreams.ibb.InBandBytestreamManager diff --git a/documentation/gettingstarted.html b/documentation/gettingstarted.html index c6dd7d670..7c6d5725d 100644 --- a/documentation/gettingstarted.html +++ b/documentation/gettingstarted.html @@ -40,6 +40,37 @@ over which features applications require: +

Configuration

+Smack has an initialization process that involves 2 phases. +
    +
  • Initializing system properties - Initializing all the system properties accessible through the class +SmackConfiguration. These properties are retrieve by the getXXX methods on that class. +
  • Initializing startup classes - Initializing any classes meant to be active at startup by instantiating +the class, and then calling the initialize method on that class if it extends SmackInitializer. +If it does not extend this interface, then initialization will have to take place in a static block of code +which is automatically executed when the class is loaded. +
+

+Initialization is accomplished via a configuration file. By default, Smack will load the one embedded in +the Smack jar at META-INF/smack-config.xml. This particular configuration contains all the default +property values as well as a list of initializer classes to load. All manager type classes are contained +in this list of initializers. If your application does not use all the features provided by Smack via the +aforementioned managers, you may want to 'turn them off' by providing a custom config file that does not +include that feature. +

+If you want to change the configuration file used, you have two options: +

    +
  • Programmatically - Call the setConfigFileUrl method of SmackConfiguration with the location +of a new config file. +
    SmackConfiguration.setConfigFileUrl("classpath:test/smack-config.xml", null)
    +
  • VM Argument - Set the VM argument smack.config.file to the location of a new config file. +
    -Dsmack.config.file=file:///c:/com/myco/provider/myco_custom_config.xml
    +
+ +

+Please note, there is a copy of the smack-config.xml in the samples directory of the deployment +archive file (zip or tar). +

Establishing a Connection

diff --git a/documentation/providers.html b/documentation/providers.html index 229df7f8b..21f441b64 100644 --- a/documentation/providers.html +++ b/documentation/providers.html @@ -15,44 +15,68 @@ Provider Architecture: Packet Extensions and Custom IQ's

+

Introduction

The Smack provider architecture is a system for plugging in custom XML parsing of packet extensions and IQ packets. The standard Smack Extensions -are built using the provider architecture. Two types of -providers exist:
    +are built using the provider architecture. There are two types of +providers:
    • IQProvider -- parses IQ requests into Java objects. -
    • PacketExtension -- parses XML sub-documents attached to +
    • Extension Provider -- parses XML sub-documents attached to packets into PacketExtension instances.
    -

    IQProvider

    - -By default, Smack only knows how to process IQ packets with sub-packets that -are in a few namespaces such as:
      +By default, Smack only knows how to process a few standard packets and sub-packets +that are in a few namespaces such as:
      • jabber:iq:auth
      • jabber:iq:roster
      • jabber:iq:register
      -Because many more IQ types are part of XMPP and its extensions, a pluggable IQ parsing -mechanism is provided. IQ providers are registered programatically or by creating a -smack.providers file in the META-INF directory of your JAR file. The file is an XML -document that contains one or more iqProvider entries, as in the following example: +There are many more IQ types and extensions that are part of XMPP standards, and of +course an endless number that can be added as custom extensions. To support this, an +extensible parsing mechanism is provided via Smack and user build providers. +

      +Whenever a packet extension is found in a packet, parsing will +be passed to the correct provider. Each provider can either implement the +PacketExtensionProvider interface or be a standard Java Bean. In the +former case, each extension provider is responsible for parsing the raw +XML stream, via the XML Pull Parser, to contruct an object. In the latter case, bean introspection +is used to try to automatically set the properties of the class using +the values in the packet extension sub-element. +

      +When no extension provider is registered for an element name and +namespace combination, Smack will store all top-level elements of the +sub-packet in the DefaultPacketExtension object and then attach it to the packet. + +

      +Management of these providers is accomplished via the ProviderManager +class. There are multiple ways to add providers to the manager.

        +
      • Call addXXProvider methods - You can call the appropriate add methods directly. +
        +    ProviderManager.getInstance().addIQProvider("element", "namespace", new MyIQProvider());
        +    ProviderManager.getInstance().addExtensionProvider("element", "namespace", new MyExtProvider());
        +
        +
      • Add a loader - You can add a ProviderLoader which will inject a means of loading multiple +providers (both types) into the manager. This is the mechanism used by Smack to load from the +Smack specific file format (via ProviderFileLoader). Implementers can provide the means to load +providers from any source they wish, or simply reuse the ProviderFileLoader to load from +their own provider files. +
        +    ProviderManager.getInstance().addLoader(new ProviderFileLoader(FileUtils.getStreamForUrl("classpath:com/myco/provider/myco_custom.providers", null)));
        +
        +
      • VM Argument - You can add a provider file via the VM argument smack.provider.file. +This will load the file at the specified URL during startup when Smack initializes. +This also assumes the default configuration, since it requires that the VmArgInitializer was +part of the startup configuration.
        - <?xml version="1.0"?>
        - <smackProviders>
        -     <iqProvider>
        -         <elementName>query</elementName>
        -         <namespace>jabber:iq:time</namespace>
        -         <className>org.jivesoftware.smack.packet.Time</className>
        -     </iqProvider>
        - </smackProviders>
        + -Dsmack.provider.file=classpath:com/myco/provider/myco_custom.providers + or + -Dsmack.provider.file=file:///c:/myco/provider/myco_custom.providers + +
      -Each IQ provider is associated with an element name and a namespace. In the -example above, the element name is query and the namespace is -abber:iq:time. If multiple provider entries attempt to register to -handle the same namespace, the first entry loaded from the classpath will -take precedence.

      +

      IQ Providers

      The IQ provider class can either implement the IQProvider interface, or extend the IQ class. In the former case, each IQProvider is @@ -61,54 +85,191 @@ the latter case, bean introspection is used to try to automatically set properties of the IQ instance using the values found in the IQ packet XML. For example, an XMPP time packet resembles the following: +

      +Introspection +

      +Time Packet

      -<iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'>
      -    <query xmlns='jabber:iq:time'>
      -        <utc>20020910T17:58:35</utc>
      -        <tz>MDT</tz>
      -        <display>Tue Sep 10 12:58:35 2002</display>
      -    </query>
      -</iq>
      + <iq type='result' to='joe@example.com' from='mary@example.com' id='time_1'> + <query xmlns='jabber:iq:time'> + <utc>20020910T17:58:35</utc> + <tz>MDT</tz> + <display>Tue Sep 10 12:58:35 2002</display> + </query> + </iq> + -In order for this packet to be automatically mapped to the Time object listed in the -providers file above, it must have the methods setUtc(String), setTz(String), and -setDisplay(String). The introspection service will automatically try to convert the String +

      +Time IQ Class +

      +    class Time extends IQ {
      +        private Date utc;
      +        private TimeZone timeZone;
      +        private String display;
      +
      +        @Override
      +        public String getChildElementXML() {
      +            return null;
      +        }
      +
      +        public void setUtc(String utcString) {
      +            try {
      +                utc = StringUtils.parseDate(utcString);
      +            } catch (ParseException e) {
      +            }
      +        }
      +
      +        public void setTimeZone(String zone) {
      +            timeZone = TimeZone.getTimeZone(zone);
      +        }
      +
      +        public void setDisplay(String timeDisplay) {
      +            display = timeDisplay;
      +        }
      +    }
      +
      + +The introspection service will automatically try to convert the String value from the XML into a boolean, int, long, float, double, or Class depending on the -type the IQ instance expects.

      +type the IQ instance expects. -

      PacketExtensionProvider

      +

      +IQProvider Implementation +

      +Disco Items Packet +

      +    <iq type='result' from='shakespeare.lit' to='romeo@montague.net/orchard' id='items1'>
      +       <query xmlns='http://jabber.org/protocol/disco#items'>
      +           <item jid='people.shakespeare.lit' name='Directory of Characters'/>
      +           <item jid='plays.shakespeare.lit' name='Play-Specific Chatrooms'/>
      +           <item jid='mim.shakespeare.lit' name='Gateway to Marlowe IM'/>
      +           <item jid='words.shakespeare.lit' name='Shakespearean Lexicon'/>
      +           <item jid='globe.shakespeare.lit' name='Calendar of Performances'/>
      +           <item jid='headlines.shakespeare.lit' name='Latest Shakespearean News'/>
      +           <item jid='catalog.shakespeare.lit' name='Buy Shakespeare Stuff!'/>
      +           <item jid='en2fr.shakespeare.lit' name='French Translation Service'/>
      +       </query>
      +    </iq>
      +
      -Packet extension providers provide a pluggable system for -packet extensions, which are child elements in a custom namespace -of IQ, message and presence packets. -Each extension provider is registered with an element name and namespace -in the smack.providers file as in the following example: +

      +Disco Items IQProvider +

      +    public class DiscoverItemsProvider implements IQProvider {
      +
      +        public IQ parseIQ(XmlPullParser parser) throws Exception {
      +            DiscoverItems discoverItems = new DiscoverItems();
      +            boolean done = false;
      +            DiscoverItems.Item item;
      +            String jid = "";
      +            String name = "";
      +            String action = "";
      +            String node = "";
      +            discoverItems.setNode(parser.getAttributeValue("", "node"));
      +            while (!done) {
      +                int eventType = parser.next();
      +	
      +                if (eventType == XmlPullParser.START_TAG && "item".equals(parser.getName())) {
      +                    // Initialize the variables from the parsed XML
      +                    jid = parser.getAttributeValue("", "jid");
      +                    name = parser.getAttributeValue("", "name");
      +                    node = parser.getAttributeValue("", "node");
      +                    action = parser.getAttributeValue("", "action");
      +                }
      +                else if (eventType == XmlPullParser.END_TAG && "item".equals(parser.getName())) {
      +                    // Create a new Item and add it to DiscoverItems.
      +                    item = new DiscoverItems.Item(jid);
      +                    item.setName(name);
      +                    item.setNode(node);
      +                    item.setAction(action);
      +                    discoverItems.addItem(item);
      +                }
      +                else if (eventType == XmlPullParser.END_TAG && "query".equals(parser.getName())) {
      +                    done = true;
      +                }
      +            }
      +            return discoverItems;
      +        }
      +    }
      +
      + +

      Extension Providers

      + +Packet extension providers are responsible for parsing packet extensions, which are +child elements in a custom namespace of IQ, message and presence packets. +

      +Pubsub Subscription Packet

      -<?xml version="1.0"?>
      -<smackProviders>
      +   <iq type='result' from='pubsub.shakespeare.lit' to='francisco@denmark.lit/barracks' id='sub1'>
      +       <pubsub xmlns='http://jabber.org/protocol/pubsub'>
      +           <subscription node='princely_musings' jid='francisco@denmark.lit' subscription='unconfigured'>
      +               <subscribe-options>
      +                   <required/>
      +               </subscribe-options>
      +           </subscription>
      +       </pubsub>
      +    </iq>
      +
      + +

      +Subscription PacketExtensionProvider Implementation + +

      +    public class SubscriptionProvider implements PacketExtensionProvider {
      +        public PacketExtension parseExtension(XmlPullParser parser) throws Exception {
      +            String jid = parser.getAttributeValue(null, "jid");
      +            String nodeId = parser.getAttributeValue(null, "node");
      +            String subId = parser.getAttributeValue(null, "subid");
      +            String state = parser.getAttributeValue(null, "subscription");
      +            boolean isRequired = false;
      +
      +            int tag = parser.next();
      +
      +            if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("subscribe-options")) {
      +                tag = parser.next();
      +	
      +                if ((tag == XmlPullParser.START_TAG) && parser.getName().equals("required"))
      +                    isRequired = true;
      +			
      +                while (parser.next() != XmlPullParser.END_TAG && parser.getName() != "subscribe-options");
      +            }
      +            while (parser.getEventType() != XmlPullParser.END_TAG) parser.next();
      +            return new Subscription(jid, nodeId, subId, (state == null ? null : Subscription.State.valueOf(state)), isRequired);
      +        }
      +    }
      +
      + +

      Provider file format

      + +This is the format for a provider file which can be parsed by the ProviderFileLoader. +
      + <?xml version="1.0"?>
      + <smackProviders>
      +     <iqProvider>
      +         <elementName>query</elementName>
      +         <namespace>jabber:iq:time</namespace>
      +         <className>org.jivesoftware.smack.packet.Time</className>
      +     </iqProvider>
      +     
      +     <iqProvider>
      +         <elementName>query</elementName>
      +         <namespace>http://jabber.org/protocol/disco#items</namespace>
      +         <className>org.jivesoftware.smackx.provider.DiscoverItemsProvider</className>
      +     </iqProvider>
      +
           <extensionProvider>
      -        <elementName>x</elementName>
      -        <namespace>jabber:iq:event</namespace>
      -        <className>org.jivesoftware.smack.packet.MessageEvent</className>
      +        <elementName>subscription</elementName>
      +        <namespace>http://jabber.org/protocol/pubsub</namespace>
      +        <className>org.jivesoftware.smackx.pubsub.provider.SubscriptionProvider</className>
           </extensionProvider>
      -</smackProviders>
      + </smackProviders> -If multiple provider entries attempt to register to handle the same element -name and namespace, the first entry loaded from the classpath will take -precedence.

      +Each provider is associated with an element name and a namespace. If multiple provider entries attempt to register to +handle the same namespace, the last entry added to the ProviderManager will overwrite any other that was loaded +before it. +

      -Whenever a packet extension is found in a packet, parsing will -be passed to the correct provider. Each provider can either implement the -PacketExtensionProvider interface or be a standard Java Bean. In the -former case, each extension provider is responsible for parsing the raw -XML stream to contruct an object. In the latter case, bean introspection -is used to try to automatically set the properties of the class using -the values in the packet extension sub-element.

      - -When an extension provider is not registered for an element name and -namespace combination, Smack will store all top-level elements of the -sub-packet in DefaultPacketExtension object and then attach it to the packet.


      diff --git a/source/org/jivesoftware/smack/LoggingInitializer.java b/source/org/jivesoftware/smack/LoggingInitializer.java new file mode 100644 index 000000000..ddc49d461 --- /dev/null +++ b/source/org/jivesoftware/smack/LoggingInitializer.java @@ -0,0 +1,28 @@ +package org.jivesoftware.smack; + +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +import org.jivesoftware.smack.util.FileUtils; + +/** + * Initializes the Java logging system. + * + * @author Robin Collier + * + */ +public class LoggingInitializer implements SmackInitializer { + + private static Logger log = Logger.getLogger(LoggingInitializer.class.getName()); + + @Override + public void initialize() { + try { + LogManager.getLogManager().readConfiguration(FileUtils.getStreamForUrl("classpath:META-INF/jul.properties", null)); + } + catch (Exception e) { + log .log(Level.WARNING, "Could not initialize Java Logging from default file.", e); + } + } +} diff --git a/source/org/jivesoftware/smack/SmackConfiguration.java b/source/org/jivesoftware/smack/SmackConfiguration.java index 02f1d2115..77ffb3b84 100644 --- a/source/org/jivesoftware/smack/SmackConfiguration.java +++ b/source/org/jivesoftware/smack/SmackConfiguration.java @@ -20,16 +20,18 @@ package org.jivesoftware.smack; +import java.io.IOException; import java.io.InputStream; -import java.net.URL; import java.util.ArrayList; import java.util.Collection; -import java.util.Enumeration; +import java.util.Collections; import java.util.List; -import java.util.Vector; +import java.util.logging.Level; +import java.util.logging.Logger; -import org.jivesoftware.smack.parsing.ParsingExceptionCallback; import org.jivesoftware.smack.parsing.ExceptionThrowingCallback; +import org.jivesoftware.smack.parsing.ParsingExceptionCallback; +import org.jivesoftware.smack.util.FileUtils; import org.xmlpull.mxp1.MXParser; import org.xmlpull.v1.XmlPullParser; @@ -49,16 +51,22 @@ import org.xmlpull.v1.XmlPullParser; * @author Gaston Dombiak */ public final class SmackConfiguration { - private static final String SMACK_VERSION = "3.3.1"; - + private static final String DEFAULT_CONFIG_FILE = "classpath:META-INF/smack-config.xml"; + + private static final Logger log = Logger.getLogger(SmackConfiguration.class.getName()); + + private static InputStream configFileStream; + private static int packetReplyTimeout = 5000; private static int keepAliveInterval = 30000; - private static Vector defaultMechs = new Vector(); + private static List defaultMechs = new ArrayList(); private static boolean localSocks5ProxyEnabled = true; private static int localSocks5ProxyPort = 7777; private static int packetCollectorSize = 5000; + + private static boolean initialized = false; /** * The default parsing exception callback is {@link ExceptionThrowingCallback} which will @@ -81,72 +89,39 @@ public final class SmackConfiguration { * 1) a set of classes will be loaded in order to execute their static init block * 2) retrieve and set the current Smack release */ - static { + + /** + * Sets the location of the config file on the classpath. Only required if changing from the default location of classpath:META-INF/smack-config.xml. + * + *

      + * This method must be called before accessing any other class in Smack. + * + * @param configFileUrl The location of the config file. + * @param loader The classloader to use if the URL has a protocol of classpath and the file is not located on the default classpath. + * This can be set to null to use defaults and is ignored for all other protocols. + * @throws IllegalArgumentException If the config URL is invalid in that it cannot open an {@link InputStream} + */ + public static void setConfigFileUrl(String configFileUrl, ClassLoader loader) { try { - // Get an array of class loaders to try loading the providers files from. - ClassLoader[] classLoaders = getClassLoaders(); - for (ClassLoader classLoader : classLoaders) { - Enumeration configEnum = classLoader.getResources("META-INF/smack-config.xml"); - while (configEnum.hasMoreElements()) { - URL url = configEnum.nextElement(); - InputStream systemStream = null; - try { - systemStream = url.openStream(); - XmlPullParser parser = new MXParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(systemStream, "UTF-8"); - int eventType = parser.getEventType(); - do { - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals("className")) { - // Attempt to load the class so that the class can get initialized - parseClassToLoad(parser); - } - else if (parser.getName().equals("packetReplyTimeout")) { - packetReplyTimeout = parseIntProperty(parser, packetReplyTimeout); - } - else if (parser.getName().equals("keepAliveInterval")) { - keepAliveInterval = parseIntProperty(parser, keepAliveInterval); - } - else if (parser.getName().equals("mechName")) { - defaultMechs.add(parser.nextText()); - } - else if (parser.getName().equals("localSocks5ProxyEnabled")) { - localSocks5ProxyEnabled = Boolean.parseBoolean(parser.nextText()); - } - else if (parser.getName().equals("localSocks5ProxyPort")) { - localSocks5ProxyPort = parseIntProperty(parser, localSocks5ProxyPort); - } - else if (parser.getName().equals("packetCollectorSize")) { - packetCollectorSize = parseIntProperty(parser, packetCollectorSize); - } - else if (parser.getName().equals("autoEnableEntityCaps")) { - autoEnableEntityCaps = Boolean.parseBoolean(parser.nextText()); - } - } - eventType = parser.next(); - } - while (eventType != XmlPullParser.END_DOCUMENT); - } - catch (Exception e) { - e.printStackTrace(); - } - finally { - try { - systemStream.close(); - } - catch (Exception e) { - // Ignore. - } - } - } - } - } + configFileStream = FileUtils.getStreamForUrl(configFileUrl, loader); + } catch (Exception e) { - e.printStackTrace(); - } + throw new IllegalArgumentException("Failed to create input stream from specified file URL ["+ configFileUrl + "]", e); + } + initialize(); } - + + /** + * Sets the {@link InputStream} representing the smack configuration file. This can be used to override the default with something that is not on the classpath. + *

      + * This method must be called before accessing any other class in Smack. + * @param configFile + */ + public static void setConfigFileStream(InputStream configFile) { + configFileStream = configFile; + initialize(); + } + /** * Returns the Smack version information, eg "1.3.0". * @@ -163,6 +138,8 @@ public final class SmackConfiguration { * @return the milliseconds to wait for a response from the server */ public static int getPacketReplyTimeout() { + initialize(); + // The timeout value must be greater than 0 otherwise we will answer the default value if (packetReplyTimeout <= 0) { packetReplyTimeout = 5000; @@ -177,6 +154,8 @@ public final class SmackConfiguration { * @param timeout the milliseconds to wait for a response from the server */ public static void setPacketReplyTimeout(int timeout) { + initialize(); + if (timeout <= 0) { throw new IllegalArgumentException(); } @@ -192,6 +171,7 @@ public final class SmackConfiguration { * no keep-alive should be sent. */ public static int getKeepAliveInterval() { + initialize(); return keepAliveInterval; } @@ -204,6 +184,7 @@ public final class SmackConfiguration { * or -1 if no keep-alive should be sent. */ public static void setKeepAliveInterval(int interval) { + initialize(); keepAliveInterval = interval; } @@ -214,6 +195,7 @@ public final class SmackConfiguration { * @return The number of packets to queue before deleting older packets. */ public static int getPacketCollectorSize() { + initialize(); return packetCollectorSize; } @@ -224,6 +206,7 @@ public final class SmackConfiguration { * @param The number of packets to queue before deleting older packets. */ public static void setPacketCollectorSize(int collectorSize) { + initialize(); packetCollectorSize = collectorSize; } @@ -233,6 +216,8 @@ public final class SmackConfiguration { * @param mech the SASL mechanism to be added */ public static void addSaslMech(String mech) { + initialize(); + if(! defaultMechs.contains(mech) ) { defaultMechs.add(mech); } @@ -244,6 +229,8 @@ public final class SmackConfiguration { * @param mechs the Collection of SASL mechanisms to be added */ public static void addSaslMechs(Collection mechs) { + initialize(); + for(String mech : mechs) { addSaslMech(mech); } @@ -255,9 +242,8 @@ public final class SmackConfiguration { * @param mech the SASL mechanism to be removed */ public static void removeSaslMech(String mech) { - if( defaultMechs.contains(mech) ) { - defaultMechs.remove(mech); - } + initialize(); + defaultMechs.remove(mech); } /** @@ -266,9 +252,8 @@ public final class SmackConfiguration { * @param mechs the Collection of SASL mechanisms to be removed */ public static void removeSaslMechs(Collection mechs) { - for(String mech : mechs) { - removeSaslMech(mech); - } + initialize(); + defaultMechs.removeAll(mechs); } /** @@ -279,7 +264,7 @@ public final class SmackConfiguration { * @return the list of SASL mechanisms to be used. */ public static List getSaslMechs() { - return defaultMechs; + return Collections.unmodifiableList(defaultMechs); } /** @@ -288,6 +273,7 @@ public final class SmackConfiguration { * @return if the local Socks5 proxy should be started */ public static boolean isLocalSocks5ProxyEnabled() { + initialize(); return localSocks5ProxyEnabled; } @@ -297,6 +283,7 @@ public final class SmackConfiguration { * @param localSocks5ProxyEnabled if the local Socks5 proxy should be started */ public static void setLocalSocks5ProxyEnabled(boolean localSocks5ProxyEnabled) { + initialize(); SmackConfiguration.localSocks5ProxyEnabled = localSocks5ProxyEnabled; } @@ -306,6 +293,7 @@ public final class SmackConfiguration { * @return the port of the local Socks5 proxy */ public static int getLocalSocks5ProxyPort() { + initialize(); return localSocks5ProxyPort; } @@ -316,6 +304,7 @@ public final class SmackConfiguration { * @param localSocks5ProxyPort the port of the local Socks5 proxy to set */ public static void setLocalSocks5ProxyPort(int localSocks5ProxyPort) { + initialize(); SmackConfiguration.localSocks5ProxyPort = localSocks5ProxyPort; } @@ -324,6 +313,7 @@ public final class SmackConfiguration { * @return */ public static boolean autoEnableEntityCaps() { + initialize(); return autoEnableEntityCaps; } @@ -333,6 +323,7 @@ public final class SmackConfiguration { * @param true if Entity Caps should be auto enabled, false if not */ public static void setAutoEnableEntityCaps(boolean b) { + initialize(); autoEnableEntityCaps = b; } @@ -343,6 +334,7 @@ public final class SmackConfiguration { * @see ParsingExceptionCallback */ public static void setDefaultParsingExceptionCallback(ParsingExceptionCallback callback) { + initialize(); defaultCallback = callback; } @@ -353,6 +345,7 @@ public final class SmackConfiguration { * @see ParsingExceptionCallback */ public static ParsingExceptionCallback getDefaultParsingExceptionCallback() { + initialize(); return defaultCallback; } @@ -360,11 +353,15 @@ public final class SmackConfiguration { String className = parser.nextText(); // Attempt to load the class so that the class can get initialized try { - Class.forName(className); + Class initClass = Class.forName(className); + + if (SmackInitializer.class.isAssignableFrom(initClass)) { + SmackInitializer initializer = (SmackInitializer) initClass.newInstance(); + initializer.initialize(); + } } catch (ClassNotFoundException cnfe) { - System.err.println("Error! A startup class specified in smack-config.xml could " + - "not be loaded: " + className); + log.log(Level.WARNING, "A startup class [" + className + "] specified in smack-config.xml could not be loaded: "); } } @@ -375,27 +372,93 @@ public final class SmackConfiguration { return Integer.parseInt(parser.nextText()); } catch (NumberFormatException nfe) { - nfe.printStackTrace(); + log.log(Level.SEVERE, "Could not parse integer", nfe); return defaultValue; } } - /** - * Returns an array of class loaders to load resources from. - * - * @return an array of ClassLoader instances. + /* + * Order of precedence for config file is VM arg, setConfigXXX methods and embedded default file location. */ - private static ClassLoader[] getClassLoaders() { - ClassLoader[] classLoaders = new ClassLoader[2]; - classLoaders[0] = SmackConfiguration.class.getClassLoader(); - classLoaders[1] = Thread.currentThread().getContextClassLoader(); - // Clean up possible null values. Note that #getClassLoader may return a null value. - List loaders = new ArrayList(); - for (ClassLoader classLoader : classLoaders) { - if (classLoader != null) { - loaders.add(classLoader); + private static void initialize() { + if (initialized) { + return; + } + initialized = true; + + String configFileLocation = System.getProperty("smack.config.file"); + + if (configFileLocation != null) { + try { + configFileStream = FileUtils.getStreamForUrl(configFileLocation, null); + } + catch (Exception e) { + log.log(Level.SEVERE, "Error creating input stream for config file [" + configFileLocation + "] from VM argument", e); + } + } + + if (configFileStream == null) { + try { + configFileStream = FileUtils.getStreamForUrl(DEFAULT_CONFIG_FILE, null); + } + catch (Exception e) { + log.log(Level.INFO, "Could not create input stream for default config file [" + DEFAULT_CONFIG_FILE + "]", e); + } + } + + if (configFileStream != null) { + readFile(configFileStream); + } + else { + log.log(Level.INFO, "No configuration file found"); + } + } + + private static void readFile(InputStream cfgFileStream) { + XmlPullParser parser = new MXParser(); + try { + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(cfgFileStream, "UTF-8"); + int eventType = parser.getEventType(); + do { + if (eventType == XmlPullParser.START_TAG) { + if (parser.getName().equals("className")) { + // Attempt to load the class so that the class can get initialized + parseClassToLoad(parser); + } + else if (parser.getName().equals("packetReplyTimeout")) { + packetReplyTimeout = parseIntProperty(parser, packetReplyTimeout); + } + else if (parser.getName().equals("keepAliveInterval")) { + keepAliveInterval = parseIntProperty(parser, keepAliveInterval); + } + else if (parser.getName().equals("mechName")) { + defaultMechs.add(parser.nextText()); + } + else if (parser.getName().equals("localSocks5ProxyEnabled")) { + localSocks5ProxyEnabled = Boolean.parseBoolean(parser.nextText()); + } + else if (parser.getName().equals("localSocks5ProxyPort")) { + localSocks5ProxyPort = parseIntProperty(parser, localSocks5ProxyPort); + } + else if (parser.getName().equals("packetCollectorSize")) { + packetCollectorSize = parseIntProperty(parser, packetCollectorSize); + } + else if (parser.getName().equals("autoEnableEntityCaps")) { + autoEnableEntityCaps = Boolean.parseBoolean(parser.nextText()); + } + } + eventType = parser.next(); + } while (eventType != XmlPullParser.END_DOCUMENT); + } catch (Exception e) { + log.log(Level.SEVERE, "Error occurred while reading config file", e); + } + finally { + try { + cfgFileStream.close(); + } catch (IOException e) { + log.log(Level.INFO, "Error while closing config file input stream", e); } } - return loaders.toArray(new ClassLoader[loaders.size()]); } } diff --git a/source/org/jivesoftware/smack/SmackInitializer.java b/source/org/jivesoftware/smack/SmackInitializer.java new file mode 100644 index 000000000..d22aebc4b --- /dev/null +++ b/source/org/jivesoftware/smack/SmackInitializer.java @@ -0,0 +1,14 @@ +package org.jivesoftware.smack; + +/** + * Defines an initialization class that will be instantiated and invoked by the {@link SmackConfiguration} class during initialization. + * + *

      + * Any implementation of this class MUST have a default constructor. + * + * @author Robin Collier + * + */ +public interface SmackInitializer { + void initialize(); +} diff --git a/source/org/jivesoftware/smack/provider/AbstractProviderInfo.java b/source/org/jivesoftware/smack/provider/AbstractProviderInfo.java new file mode 100644 index 000000000..487f29463 --- /dev/null +++ b/source/org/jivesoftware/smack/provider/AbstractProviderInfo.java @@ -0,0 +1,26 @@ +package org.jivesoftware.smack.provider; + + +abstract class AbstractProviderInfo { + private String element; + private String ns; + private Object provider; + + AbstractProviderInfo(String elementName, String namespace, Object iqOrExtProvider) { + element = elementName; + ns = namespace; + provider = iqOrExtProvider; + } + + public String getElementName() { + return element; + } + + public String getNamespace() { + return ns; + } + + Object getProvider() { + return provider; + } +} diff --git a/source/org/jivesoftware/smack/provider/CoreInitializer.java b/source/org/jivesoftware/smack/provider/CoreInitializer.java new file mode 100644 index 000000000..b39aa0f82 --- /dev/null +++ b/source/org/jivesoftware/smack/provider/CoreInitializer.java @@ -0,0 +1,15 @@ +package org.jivesoftware.smack.provider; + +import org.jivesoftware.smack.SmackInitializer; + +/** + * Loads the default provider file for the Smack core on initialization. + * + * @author Robin Collier + * + */ +public class CoreInitializer extends UrlProviderFileInitializer implements SmackInitializer { + protected String getFilePath() { + return "classpath:META-INF/core.providers"; + } +} diff --git a/source/org/jivesoftware/smack/provider/ExtensionProviderInfo.java b/source/org/jivesoftware/smack/provider/ExtensionProviderInfo.java new file mode 100644 index 000000000..4d6333cd9 --- /dev/null +++ b/source/org/jivesoftware/smack/provider/ExtensionProviderInfo.java @@ -0,0 +1,33 @@ +package org.jivesoftware.smack.provider; + +/** + * Defines the information required to register a packet extension Provider with the {@link ProviderManager} when using the + * {@link ProviderLoader}. + * + * @author Robin Collier + * + */ +public final class ExtensionProviderInfo extends AbstractProviderInfo { + + /** + * Defines an extension provider which implements the PacketExtensionProvider interface. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param extProvider The provider implementation. + */ + public ExtensionProviderInfo(String elementName, String namespace, PacketExtensionProvider extProvider) { + super(elementName, namespace, extProvider); + } + + /** + * Defines an extension provider which is adheres to the JavaBean spec for parsing the extension. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param beanClass The provider bean class. + */ + public ExtensionProviderInfo(String elementName, String namespace, Class beanClass) { + super(elementName, namespace, beanClass); + } +} diff --git a/source/org/jivesoftware/smack/provider/IQProviderInfo.java b/source/org/jivesoftware/smack/provider/IQProviderInfo.java new file mode 100644 index 000000000..c707e61d3 --- /dev/null +++ b/source/org/jivesoftware/smack/provider/IQProviderInfo.java @@ -0,0 +1,35 @@ +package org.jivesoftware.smack.provider; + +import org.jivesoftware.smack.packet.IQ; + +/** + * Defines the information required to register an IQ Provider with the {@link ProviderManager} when using the + * {@link ProviderLoader}. + * + * @author Robin Collier + * + */ +public final class IQProviderInfo extends AbstractProviderInfo { + + /** + * Defines an IQ provider which implements the IQProvider interface. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param iqProvider The provider implementation. + */ + public IQProviderInfo(String elementName, String namespace, IQProvider iqProvider) { + super(elementName, namespace, iqProvider); + } + + /** + * Defines an IQ class which can be used as a provider via introspection. + * + * @param elementName Element that provider parses. + * @param namespace Namespace that provider parses. + * @param iqProviderClass The IQ class being parsed. + */ + public IQProviderInfo(String elementName, String namespace, Class iqProviderClass) { + super(elementName, namespace, iqProviderClass); + } +} diff --git a/source/org/jivesoftware/smack/provider/ProviderFileLoader.java b/source/org/jivesoftware/smack/provider/ProviderFileLoader.java new file mode 100644 index 000000000..57c9916fb --- /dev/null +++ b/source/org/jivesoftware/smack/provider/ProviderFileLoader.java @@ -0,0 +1,146 @@ +package org.jivesoftware.smack.provider; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.packet.PacketExtension; +import org.xmlpull.mxp1.MXParser; +import org.xmlpull.v1.XmlPullParser; + +/** + * Loads the {@link IQProvider} and {@link PacketExtensionProvider} information from a standard provider file in preparation + * for loading into the {@link ProviderManager}. + * + * @author Robin Collier + * + */ +public class ProviderFileLoader implements ProviderLoader { + private final static Logger log = Logger.getLogger(ProviderFileLoader.class.getName()); + + private Collection iqProviders; + private Collection extProviders; + private InputStream providerStream; + + public ProviderFileLoader(InputStream providerFileInputStream) { + setInputStream(providerFileInputStream); + } + + public ProviderFileLoader() { + } + + @Override + public Collection getIQProviderInfo() { + initialize(); + return iqProviders; + } + + @Override + public Collection getExtensionProviderInfo() { + initialize(); + return extProviders; + } + + @SuppressWarnings("unchecked") + private synchronized void initialize() { + // Check to see if already initialized + if (iqProviders != null) { + return; + } + + if (providerStream == null) { + throw new IllegalArgumentException("No input stream set for loader"); + } + iqProviders = new ArrayList(); + extProviders = new ArrayList(); + + // Load processing providers. + try { + XmlPullParser parser = new MXParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(providerStream, "UTF-8"); + int eventType = parser.getEventType(); + do { + if (eventType == XmlPullParser.START_TAG) { + String typeName = parser.getName(); + + try { + if (!"smackProviders".equals(typeName)) { + parser.next(); + parser.next(); + String elementName = parser.nextText(); + parser.next(); + parser.next(); + String namespace = parser.nextText(); + parser.next(); + parser.next(); + String className = parser.nextText(); + + try { + // Attempt to load the provider class and then create + // a new instance if it's an IQProvider. Otherwise, if it's + // an IQ class, add the class object itself, then we'll use + // reflection later to create instances of the class. + if ("iqProvider".equals(typeName)) { + // Add the provider to the map. + Class provider = Class.forName(className); + + if (IQProvider.class.isAssignableFrom(provider)) { + iqProviders.add(new IQProviderInfo(elementName, namespace, (IQProvider) provider.newInstance())); + } + else if (IQ.class.isAssignableFrom(provider)) { + iqProviders.add(new IQProviderInfo(elementName, namespace, (Class)provider)); + } + } + else { + // Attempt to load the provider class and then create + // a new instance if it's an ExtensionProvider. Otherwise, if it's + // a PacketExtension, add the class object itself and + // then we'll use reflection later to create instances + // of the class. + Class provider = Class.forName(className); + if (PacketExtensionProvider.class.isAssignableFrom(provider)) { + extProviders.add(new ExtensionProviderInfo(elementName, namespace, (PacketExtensionProvider) provider.newInstance())); + } + else if (PacketExtension.class.isAssignableFrom(provider)) { + extProviders.add(new ExtensionProviderInfo(elementName, namespace, provider)); + } + } + } + catch (ClassNotFoundException cnfe) { + log.log(Level.SEVERE, "Could not find provider class", cnfe); + } + } + } + catch (IllegalArgumentException illExc) { + log.log(Level.SEVERE, "Invalid provider type found [" + typeName + "] when expecting iqProvider or extensionProvider", illExc); + } + } + eventType = parser.next(); + } + while (eventType != XmlPullParser.END_DOCUMENT); + } + catch (Exception e){ + log.log(Level.SEVERE, "Unknown error occurred while parsing provider file", e); + } + finally { + try { + providerStream.close(); + } + catch (Exception e) { + // Ignore. + } + } + } + + public void setInputStream(InputStream providerFileInput) { + if (providerFileInput == null) { + throw new IllegalArgumentException("InputStream cannot be null"); + } + providerStream = providerFileInput; + initialize(); + } +} diff --git a/source/org/jivesoftware/smack/provider/ProviderLoader.java b/source/org/jivesoftware/smack/provider/ProviderLoader.java new file mode 100644 index 000000000..fd813b74b --- /dev/null +++ b/source/org/jivesoftware/smack/provider/ProviderLoader.java @@ -0,0 +1,23 @@ +package org.jivesoftware.smack.provider; + +import java.util.Collection; + +/** + * Used to load providers into the {@link ProviderManager}. + * + * @author Robin Collier + */ +public interface ProviderLoader { + + /** + * Provides the IQ provider info for the creation of IQ providers to be added to the ProviderManager. + * @return The IQ provider info to load. + */ + Collection getIQProviderInfo(); + + /** + * Provides the extension providers for the creation of extension providers to be added to the ProviderManager. + * @return The extension provider info to load. + */ + Collection getExtensionProviderInfo(); +} diff --git a/source/org/jivesoftware/smack/provider/ProviderManager.java b/source/org/jivesoftware/smack/provider/ProviderManager.java index fda6b1b81..fb1ba19b6 100644 --- a/source/org/jivesoftware/smack/provider/ProviderManager.java +++ b/source/org/jivesoftware/smack/provider/ProviderManager.java @@ -20,16 +20,13 @@ package org.jivesoftware.smack.provider; -import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.PacketExtension; -import org.xmlpull.mxp1.MXParser; -import org.xmlpull.v1.XmlPullParser; - -import java.io.InputStream; -import java.net.URL; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.jivesoftware.smack.packet.IQ; + /** * Manages providers for parsing custom XML sub-documents of XMPP packets. Two types of * providers exist:

        @@ -102,20 +99,14 @@ import java.util.concurrent.ConcurrentHashMap; * can either implement the PacketExtensionProvider interface or be a standard Java Bean. In * the former case, each extension provider is responsible for parsing the raw XML stream to * contruct an object. In the latter case, bean introspection is used to try to automatically - * set the properties of the class using the values in the packet extension sub-element. When an + * set the properties of th class using the values in the packet extension sub-element. When an * extension provider is not registered for an element name and namespace combination, Smack will * store all top-level elements of the sub-packet in DefaultPacketExtension object and then * attach it to the packet.

        * - * It is possible to provide a custom provider manager instead of the default implementation - * provided by Smack. If you want to provide your own provider manager then you need to do it - * before creating any {@link org.jivesoftware.smack.Connection} by sending the static - * {@link #setInstance(ProviderManager)} message. Trying to change the provider manager after - * an Connection was created will result in an {@link IllegalStateException} error. - * * @author Matt Tucker */ -public class ProviderManager { +public final class ProviderManager { private static ProviderManager instance; @@ -123,9 +114,7 @@ public class ProviderManager { private Map iqProviders = new ConcurrentHashMap(); /** - * Returns the only ProviderManager valid instance. Use {@link #setInstance(ProviderManager)} - * to configure your own provider manager. If non was provided then an instance of this - * class will be used. + * Returns the ProviderManager instance. * * @return the only ProviderManager valid instance. */ @@ -136,130 +125,28 @@ public class ProviderManager { return instance; } - /** - * Sets the only ProviderManager valid instance to be used by all Connections. If you - * want to provide your own provider manager then you need to do it before creating - * any Connection. Otherwise an IllegalStateException will be thrown. - * - * @param providerManager the only ProviderManager valid instance to be used. - * @throws IllegalStateException if a provider manager was already configued. - */ - public static synchronized void setInstance(ProviderManager providerManager) { - if (instance != null) { - throw new IllegalStateException("ProviderManager singleton already set"); - } - instance = providerManager; + private ProviderManager() { + super(); } - protected void initialize() { - // Load IQ processing providers. - try { - // Get an array of class loaders to try loading the providers files from. - ClassLoader[] classLoaders = getClassLoaders(); - for (ClassLoader classLoader : classLoaders) { - Enumeration providerEnum = classLoader.getResources( - "META-INF/smack.providers"); - while (providerEnum.hasMoreElements()) { - URL url = providerEnum.nextElement(); - InputStream providerStream = null; - try { - providerStream = url.openStream(); - XmlPullParser parser = new MXParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - parser.setInput(providerStream, "UTF-8"); - int eventType = parser.getEventType(); - do { - if (eventType == XmlPullParser.START_TAG) { - if (parser.getName().equals("iqProvider")) { - parser.next(); - parser.next(); - String elementName = parser.nextText(); - parser.next(); - parser.next(); - String namespace = parser.nextText(); - parser.next(); - parser.next(); - String className = parser.nextText(); - // Only add the provider for the namespace if one isn't - // already registered. - String key = getProviderKey(elementName, namespace); - if (!iqProviders.containsKey(key)) { - // Attempt to load the provider class and then create - // a new instance if it's an IQProvider. Otherwise, if it's - // an IQ class, add the class object itself, then we'll use - // reflection later to create instances of the class. - try { - // Add the provider to the map. - Class provider = Class.forName(className); - if (IQProvider.class.isAssignableFrom(provider)) { - iqProviders.put(key, provider.newInstance()); - } - else if (IQ.class.isAssignableFrom(provider)) { - iqProviders.put(key, provider); - } - } - catch (ClassNotFoundException cnfe) { - cnfe.printStackTrace(); - } - } - } - else if (parser.getName().equals("extensionProvider")) { - parser.next(); - parser.next(); - String elementName = parser.nextText(); - parser.next(); - parser.next(); - String namespace = parser.nextText(); - parser.next(); - parser.next(); - String className = parser.nextText(); - // Only add the provider for the namespace if one isn't - // already registered. - String key = getProviderKey(elementName, namespace); - if (!extensionProviders.containsKey(key)) { - // Attempt to load the provider class and then create - // a new instance if it's a Provider. Otherwise, if it's - // a PacketExtension, add the class object itself and - // then we'll use reflection later to create instances - // of the class. - try { - // Add the provider to the map. - Class provider = Class.forName(className); - if (PacketExtensionProvider.class.isAssignableFrom( - provider)) { - extensionProviders.put(key, provider.newInstance()); - } - else if (PacketExtension.class.isAssignableFrom( - provider)) { - extensionProviders.put(key, provider); - } - } - catch (ClassNotFoundException cnfe) { - cnfe.printStackTrace(); - } - } - } - } - eventType = parser.next(); - } - while (eventType != XmlPullParser.END_DOCUMENT); - } - finally { - try { - providerStream.close(); - } - catch (Exception e) { - // Ignore. - } - } - } + public void addLoader(ProviderLoader loader) { + if (loader == null) { + throw new IllegalArgumentException("loader cannot be null"); + } + + if (loader.getIQProviderInfo() != null) { + for (IQProviderInfo info : loader.getIQProviderInfo()) { + iqProviders.put(getProviderKey(info.getElementName(), info.getNamespace()), info.getProvider()); } } - catch (Exception e) { - e.printStackTrace(); + + if (loader.getExtensionProviderInfo() != null) { + for (ExtensionProviderInfo info : loader.getExtensionProviderInfo()) { + extensionProviders.put(getProviderKey(info.getElementName(), info.getNamespace()), info.getProvider()); + } } } - + /** * Returns the IQ provider registered to the specified XML element name and namespace. * For example, if a provider was registered to the element name "query" and the @@ -411,28 +298,4 @@ public class ProviderManager { buf.append("<").append(elementName).append("/><").append(namespace).append("/>"); return buf.toString(); } - - /** - * Returns an array of class loaders to load resources from. - * - * @return an array of ClassLoader instances. - */ - private ClassLoader[] getClassLoaders() { - ClassLoader[] classLoaders = new ClassLoader[2]; - classLoaders[0] = ProviderManager.class.getClassLoader(); - classLoaders[1] = Thread.currentThread().getContextClassLoader(); - // Clean up possible null values. Note that #getClassLoader may return a null value. - List loaders = new ArrayList(); - for (ClassLoader classLoader : classLoaders) { - if (classLoader != null) { - loaders.add(classLoader); - } - } - return loaders.toArray(new ClassLoader[loaders.size()]); - } - - private ProviderManager() { - super(); - initialize(); - } } \ No newline at end of file diff --git a/source/org/jivesoftware/smack/provider/UrlProviderFileInitializer.java b/source/org/jivesoftware/smack/provider/UrlProviderFileInitializer.java new file mode 100644 index 000000000..68cbfe5c6 --- /dev/null +++ b/source/org/jivesoftware/smack/provider/UrlProviderFileInitializer.java @@ -0,0 +1,54 @@ +package org.jivesoftware.smack.provider; + +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jivesoftware.smack.SmackInitializer; +import org.jivesoftware.smack.util.FileUtils; + +/** + * Loads the provider file defined by the URL returned by {@link #getFilePath()}. This file will be loaded on Smack initialization. + * + * @author Robin Collier + * + */ +public abstract class UrlProviderFileInitializer implements SmackInitializer { + private static final Logger log = Logger.getLogger(UrlProviderFileInitializer.class.getName()); + + @Override + public void initialize() { + String filePath = getFilePath(); + + try { + InputStream is = FileUtils.getStreamForUrl(filePath, getClassLoader()); + + if (is != null) { + log.log(Level.INFO, "Loading providers for file [" + filePath + "]"); + ProviderManager.getInstance().addLoader(new ProviderFileLoader(is)); + } + else { + log.log(Level.WARNING, "No input stream created for " + filePath); + } + } + catch (Exception e) { + log.log(Level.SEVERE, "Error trying to load provider file " + filePath, e); + } + } + + protected abstract String getFilePath(); + + /** + * Returns an array of class loaders to load resources from. + * + * @return an array of ClassLoader instances. + */ + protected ClassLoader getClassLoader() { + return null; + } +} diff --git a/source/org/jivesoftware/smack/provider/VmArgInitializer.java b/source/org/jivesoftware/smack/provider/VmArgInitializer.java new file mode 100644 index 000000000..54e1be9cf --- /dev/null +++ b/source/org/jivesoftware/smack/provider/VmArgInitializer.java @@ -0,0 +1,23 @@ +package org.jivesoftware.smack.provider; + + +/** + * Looks for a provider file location based on the VM argument smack.provider.file. If it is supplied, its value will + * be used as a file location for a providers file and loaded into the {@link ProviderManager} on Smack initialization. + * + * @author Robin Collier + * + */ +public class VmArgInitializer extends UrlProviderFileInitializer { + + protected String getFilePath() { + return System.getProperty("smack.provider.file"); + } + + @Override + public void initialize() { + if (getFilePath() != null) { + super.initialize(); + } + } +} diff --git a/source/org/jivesoftware/smack/util/FileUtils.java b/source/org/jivesoftware/smack/util/FileUtils.java new file mode 100644 index 000000000..e400230e3 --- /dev/null +++ b/source/org/jivesoftware/smack/util/FileUtils.java @@ -0,0 +1,59 @@ +package org.jivesoftware.smack.util; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +public final class FileUtils { + + private FileUtils() { + } + + public static InputStream getStreamForUrl(String url, ClassLoader loader) throws MalformedURLException, IOException { + URI fileUri = URI.create(url); + + if (fileUri.getScheme() == null) { + throw new MalformedURLException("No protocol found in file URL: " + url); + } + + if (fileUri.getScheme().equals("classpath")) { + // Get an array of class loaders to try loading the providers files from. + ClassLoader[] classLoaders = getClassLoaders(); + for (ClassLoader classLoader : classLoaders) { + InputStream is = classLoader.getResourceAsStream(fileUri.getSchemeSpecificPart()); + + if (is != null) { + return is; + } + } + } + else { + return fileUri.toURL().openStream(); + } + return null; + } + + /** + * Returns default classloaders. + * + * @return an array of ClassLoader instances. + */ + public static ClassLoader[] getClassLoaders() { + ClassLoader[] classLoaders = new ClassLoader[2]; + classLoaders[0] = FileUtils.class.getClassLoader(); + classLoaders[1] = Thread.currentThread().getContextClassLoader(); + // Clean up possible null values. Note that #getClassLoader may return a null value. + List loaders = new ArrayList(); + + for (ClassLoader classLoader : classLoaders) { + if (classLoader != null) { + loaders.add(classLoader); + } + } + return loaders.toArray(new ClassLoader[loaders.size()]); + } + +} diff --git a/source/org/jivesoftware/smackx/provider/ExtensionInitializer.java b/source/org/jivesoftware/smackx/provider/ExtensionInitializer.java new file mode 100644 index 000000000..3a907ddba --- /dev/null +++ b/source/org/jivesoftware/smackx/provider/ExtensionInitializer.java @@ -0,0 +1,16 @@ +package org.jivesoftware.smackx.provider; + +import org.jivesoftware.smack.provider.UrlProviderFileInitializer; + +/** + * Loads the default provider file for the Smack extensions on initialization. + * + * @author Robin Collier + * + */ +public class ExtensionInitializer extends UrlProviderFileInitializer { + @Override + protected String getFilePath() { + return "classpath:META-INF/extension.providers"; + } +} diff --git a/test-unit/org/jivesoftware/smackx/provider/ProviderConfigTest.java b/test-unit/org/jivesoftware/smackx/provider/ProviderConfigTest.java new file mode 100644 index 000000000..473a1f19e --- /dev/null +++ b/test-unit/org/jivesoftware/smackx/provider/ProviderConfigTest.java @@ -0,0 +1,55 @@ +package org.jivesoftware.smackx.provider; + +import java.util.ArrayList; +import java.util.Collection; + +import junit.framework.Assert; + +import org.jivesoftware.smack.packet.IQ; +import org.jivesoftware.smack.provider.ExtensionProviderInfo; +import org.jivesoftware.smack.provider.IQProvider; +import org.jivesoftware.smack.provider.IQProviderInfo; +import org.jivesoftware.smack.provider.ProviderFileLoader; +import org.jivesoftware.smack.provider.ProviderLoader; +import org.jivesoftware.smack.provider.ProviderManager; +import org.jivesoftware.smack.util.FileUtils; +import org.junit.Test; +import org.xmlpull.v1.XmlPullParser; + +public class ProviderConfigTest { + + @Test + public void addGenericLoaderProvider() { + ProviderManager.getInstance().addLoader(new ProviderLoader() { + + @Override + public Collection getIQProviderInfo() { + ArrayList l = new ArrayList(1); + l.add(new IQProviderInfo("provider", "test:provider", new TestIQProvider())); + return l; + } + + @Override + public Collection getExtensionProviderInfo() { + return null; + } + }); + + Assert.assertNotNull(ProviderManager.getInstance().getIQProvider("provider", "test:provider")); + } + + @Test + public void addClasspathFileLoaderProvider() throws Exception{ + ProviderManager.getInstance().addLoader(new ProviderFileLoader(FileUtils.getStreamForUrl("classpath:org/jivesoftware/smackx/provider/test.providers", null))); + Assert.assertNotNull(ProviderManager.getInstance().getIQProvider("provider", "test:file_provider")); + } + + public static class TestIQProvider implements IQProvider { + + @Override + public IQ parseIQ(XmlPullParser parser) throws Exception { + return null; + } + + } +} diff --git a/test-unit/org/jivesoftware/smackx/provider/test.providers b/test-unit/org/jivesoftware/smackx/provider/test.providers new file mode 100644 index 000000000..90bafc4f7 --- /dev/null +++ b/test-unit/org/jivesoftware/smackx/provider/test.providers @@ -0,0 +1,11 @@ + + + + + + provider + test:file_provider + org.jivesoftware.smackx.provider.ProviderConfigTest$TestIQProvider + + +