Extending the User Service plugin

Hi,

I noticed the User Service plugin and it would fit my needs well however I’‘d like to message my jabber server from my web app’'s backend through the jabber protocol not via http. What would be the best way to do this? I imagine using some sort of IQ based protocol, but tips on best way to do this are welcome.

Has anyone already done this by chance?

Also, I’'d like to suggest that the user and group managers be exposed vie JNDI from the webapp distribution. That way, other webapps on the same servlet container/application container could manage the WF server without any hustle.

Hi,

http://www.jivesoftware.org/community/thread.jspa?messageID=120965 should be interesting for you (JEP-0133: Service Administration). I’'m not sure whether it is fully implemented yet.

Group management could be implemented according to JEP-0050 Ad-Hoc Commands.

LG

I find the JEP 133 lacks the aspect of managing also the users groups, as these users already belong to certain groups like society, technician, operator etc. We’‘d need also something for managing that too. Is there another JEP that handles that aspect? Also on the topic of JNDI, could I write a plugin that doesn’‘t do anything but that contains the User and Group manager and that exposes itself via JNDI? I would then use it from my other webapp to get the references but I’‘m not sure what’'s the visibility on jndi resources that live in a application container. Could anyone give a clue on this?

I’‘m sorry if I’'m a bit pushi but we started integrating WF this weekend and we need to integrate it by the end of the week so if I could avoid the process of developing my own iq protocol that would save us a lot of trouble (and make it easier to use, more secure and more reliable).

Hi Srgjan,

JEP-0133 is indeed a poor JEP, so I did mention also JEP-0050.

You could indeed write your own plugin, the User Service plugin could be a good one to start with (source should be included in the wildfire.src release) but it handles so far only basic user functions. HTTP GET / POST methods could be fine to manage users and groups depending on your needs.

LG

At the moment I’‘m focused on trying to do something like the User Service Plugin. Would I be correct it would need to extend IQHandler and implement the Plugin interface? Also one thing I noticed is that there is no getGroupManager on a XMPPServer instance, how could I manage groups and add users to them if I don’'t have access to it?

I would really[/b] like if all this functionality was already exposed via JNDI in the webapp. That way I could bundle the wildfire server as a war in my ear and I could use it from my ejb business layer. Could I theoretically register programmatically the plugin via JNDI and expose the managers via geters? That way I could inject them to the ejb module. I’'ve looked at JNDI today but it was much harder to do then I originally thought.

I’‘d appreciate if anyone could give me help on these issues (by help I mean programming advice not program for me, plus I’'d be glad to donate the result back to the community).

I would like to explain my ostination with the JNDI stuff.I would prefer it over developing my own protocol for managing these aspects as I like to code java code in my backend, not develop new proprietary protocols where I’'d be pressed on issues like transactions, realibility and security, parsing messages (or even exposing the user service as http? no thanks) etc.

Message was edited by: schrepfler

I’‘ve had this alternative idea that might play out even better. Perhaps exposing the methods as Web Service? What are the limits imposed on the web app distribution (I haven’‘t worked with sitemesh and I’‘m not sure if I’'m supposed to put the webservice in a plugin or in the webapp itself?)

Thanks!

As I’‘m new to this community I don’‘t know any other channel so I’'ll post here my code for peer review. First thing I did was to add the group manager to the UserServicePlugin:


/**

  • $Revision: 1722 $

  • $Date: 2005-07-28 15:19:16 -0700 (Thu, 28 Jul 2005) $

  • Copyright © 2006 Jive Software. All rights reserved.
  • This software is published under the terms of the GNU Public License (GPL),

  • a copy of which is included in this distribution.

*/

package org.jivesoftware.wildfire.plugin;

import org.jivesoftware.wildfire.container.Plugin;

import org.jivesoftware.wildfire.container.PluginManager;

import org.jivesoftware.wildfire.group.Group;

import org.jivesoftware.wildfire.group.GroupAlreadyExistsException;

import org.jivesoftware.wildfire.group.GroupManager;

import org.jivesoftware.wildfire.group.GroupNotFoundException;

import org.jivesoftware.wildfire.user.UserManager;

import org.jivesoftware.wildfire.user.UserNotFoundException;

import org.jivesoftware.wildfire.user.UserAlreadyExistsException;

import org.jivesoftware.wildfire.user.User;

import org.jivesoftware.wildfire.XMPPServer;

import org.jivesoftware.util.JiveGlobals;

import org.jivesoftware.util.StringUtils;

import org.xmpp.packet.JID;

import java.io.File;

/**

  • Plugin that allows the administration of users via HTTP requests.
  • @author Justin Hunt

*/

public class UserServicePlugin implements Plugin {

private UserManager userManager;

private GroupManager groupManager;

private String hostname;

private String secret;

private boolean enabled;

public void initializePlugin(PluginManager manager, File pluginDirectory) {

XMPPServer server = XMPPServer.getInstance();

userManager = server.getUserManager();

groupManager = GroupManager.getInstance();

hostname = server.getServerInfo().getName();

secret = JiveGlobals.getProperty(“plugin.userservice.secret”, “”);

// If no secret key has been assigned to the user service yet, assign a random one.

if (secret.equals("")){

secret = StringUtils.randomString(8);

setSecret(secret);

}

// See if the service is enabled or not.

enabled = JiveGlobals.getBooleanProperty(“plugin.userservice.enabled”, false);

}

public void destroyPlugin() {

userManager = null;

}

public void createUser(String username, String password, String name, String email)

throws UserAlreadyExistsException

{

userManager.createUser(username, password, name, email);

}

public void deleteUser(String jid) throws UserNotFoundException{

User user = getUser(jid);

userManager.deleteUser(user);

}

public void updateUser(String jid, String password, String name, String email)

throws UserNotFoundException

{

User user = getUser(jid);

user.setPassword(password);

user.setName(name);

user.setEmail(email);

}

/**

  • Returns the the requested user or null if there are any

  • problems that don’'t throw an error.

  • @param jid the bare JID of the entity whose presence is being probed.

  • @return the requested user.

  • @throws UserNotFoundException if the requested user

  • does not exist in the local server.

*/

private User getUser(String jid) throws UserNotFoundException {

JID targetJID = new JID(jid);

// Check that the sender is not requesting information of a remote server entity

if (targetJID.getDomain() == null || XMPPServer.getInstance().isRemote(targetJID)) {

throw new UserNotFoundException(“Domain does not matches local server domain”);

}

if (!hostname.equals(targetJID.getDomain())) {

// Sender is requesting information about component presence

// TODO Implement this

throw new UserNotFoundException(“Presence of components not supported yet!”);

}

if (targetJID.getNode() == null) {

// Sender is requesting presence information of an anonymous user

throw new UserNotFoundException(“Username is null”);

}

return userManager.getUser(targetJID.getNode());

}

/**

  • Returns the secret key that only valid requests should know.
  • @return the secret key.

*/

public String getSecret() {

return secret;

}

/**

  • Sets the secret key that grants permission to use the userservice.
  • @param secret the secret key.

*/

public void setSecret(String secret) {

JiveGlobals.setProperty(“plugin.userservice.secret”, secret);

this.secret = secret;

}

/**

  • Returns true if the user service is enabled. If not enabled, it will not accept

  • requests to create new accounts.

  • @return true if the user service is enabled.

*/

public boolean isEnabled() {

return enabled;

}

/**

  • Enables or disables the user service. If not enabled, it will not accept

  • requests to create new accounts.

  • @param enabled true if the user service should be enabled.

*/

public void setEnabled(boolean enabled) {

this.enabled = enabled;

JiveGlobals.setProperty(“plugin.userservice.enabled”, enabled ? “true” : “false”);

}

public void createGroup(String name) throws GroupAlreadyExistsException{

groupManager.createGroup(name);

}

public void deleteGroup(String name) throws GroupNotFoundException{

Group group = groupManager.getGroup(name);

groupManager.deleteGroup(group);

}

}


Next my first idea was to create a webservice and as I’‘m under a deadline I wanted to use the new JSR 181 annotations to create one (I’'m deploying under JBoss 4.0.4, it has support for the JSR 181 (that is, I hope so, docs say it does) and expose the manager functions through it.


package org.jivesoftware.wildfire.plugin.userService;

import javax.jws.WebMethod;

import javax.jws.WebService;

import javax.jws.soap.SOAPBinding;

import org.jivesoftware.wildfire.XMPPServer;

import org.jivesoftware.wildfire.group.GroupAlreadyExistsException;

import org.jivesoftware.wildfire.group.GroupNotFoundException;

import org.jivesoftware.wildfire.plugin.UserServicePlugin;

import org.jivesoftware.wildfire.user.UserAlreadyExistsException;

import org.jivesoftware.wildfire.user.UserNotFoundException;

@WebService(name = “EndpointInterface”,targetNamespace = “http://org.jboss.ws/samples/jsr181pojo",serviceName="TestService”)

@SOAPBinding(style = SOAPBinding.Style.RPC)

public class UserServiceWS {

private UserServicePlugin plugin;

public UserServiceWS(){

plugin = (UserServicePlugin) XMPPServer.getInstance().getPluginManager().getPlugin(“userservice”);

}

@WebMethod

public void createUser(String username, String password, String name, String email){

try {

plugin.createUser(username, password, name, email);

} catch (UserAlreadyExistsException e) {

e.printStackTrace();

}

}

@WebMethod

public void deleteUser(String jid) {

try {

plugin.deleteUser(jid);

} catch (UserNotFoundException e) {

e.printStackTrace();

}

}

@WebMethod

public void updateUser(String jid, String password, String name, String email){

try {

plugin.updateUser(jid, password, name, email);

} catch (UserNotFoundException e) {

e.printStackTrace();

}

}

@WebMethod

public void createGroup(String name){

try {

plugin.createGroup(name);

} catch (GroupAlreadyExistsException e) {

e.printStackTrace();

}

}

@WebMethod

public void deleteGroup(String name){

try {

plugin.deleteGroup(name);

} catch (GroupNotFoundException e) {

e.printStackTrace();

}

}

}


I’‘ve also changed the web-custom.xml to this (also i switched from the dtd to the j2ee 2.4 schema, perhaps it’‘s a bad mix with the main webapp’'s xml?)


I have built the plugins and on deployment the Warn log said:

2006.05.27 02:33:55 Could not load userservice/*: not a servlet.

I’‘m aware that I’‘ve mixed a bunch of new fraemeworks, perhaps incompatible with WildFire. However this could bring a whole new level of integration of java (actually any WS aware language, .net, php … ) apps with the wildfire server. Can I have some of the developers comment on this solution and what can I do to make it work? I’‘m under a deadline and we must integrate this as a solution for the next week so if someone is willing to help it’‘ll save our butt’‘s . If we integrate this I’'ll try to push the issue to the manager on getting support from jive.

I’‘m re-releasing this code based on gpl under gpl, I didn’‘t put my name anywhere. Also, various approaches could have been made on exception handling on the web service (exceptions yes/no, should I return something or void so that I return immediately). I’‘ve chosen void as it’‘s the simplest and my other app’‘s thread won’‘t get blocked but the user should have various choices/implementations. I don’‘t mind if anyone that want’‘s to help chooses another framework (axis, axis2, xfire… ) or servlet/application server (glassfish… ). I chose the new annotation model as I don’'t have experience with axis based web services. If you guys know to do the service with axis go at it.

I have created the web service below. It’‘ll successfully execute those methods. I’‘ve used xfire as a ws stack so it was trivial to expose it. I wouldn’‘t qualify it as production quality or feature complete but the functionality is there. It would be nice if the web app module would be a separate one. Could you use maven 2 to moduleise your project? It’'s a nightmare to create projects and use wildfire with tools like maven 2. With libs included the war rose to 19MB but the service it gives is a great plus. If you like it include it and improve it with my permission!

Srgjan Srepfler

package org.jivesoftware.wildfire.plugin.userService;

import javax.jws.WebMethod;

import javax.jws.WebService;

import org.jivesoftware.wildfire.XMPPServer;

import org.jivesoftware.wildfire.group.Group;

import org.jivesoftware.wildfire.group.GroupAlreadyExistsException;

import org.jivesoftware.wildfire.group.GroupManager;

import org.jivesoftware.wildfire.group.GroupNotFoundException;

import org.jivesoftware.wildfire.user.User;

import org.jivesoftware.wildfire.user.UserAlreadyExistsException;

import org.jivesoftware.wildfire.user.UserManager;

import org.jivesoftware.wildfire.user.UserNotFoundException;

@WebService

public class UserWS {

private UserManager userManager;

private GroupManager groupManager;

public UserWS(){

XMPPServer server = XMPPServer.getInstance();

userManager = server.getUserManager();

groupManager = GroupManager.getInstance();

}

@WebMethod

public void createUser(String username, String password, String name, String email){

try {

userManager.createUser(username, password, name, email);

} catch (UserAlreadyExistsException e) {

e.printStackTrace();

}

}

@WebMethod

public void deleteUser(String jid) {

User theUser;

try {

theUser = userManager.getUser(jid);

userManager.deleteUser(theUser);

} catch (UserNotFoundException e) {

e.printStackTrace();

}

}

@WebMethod

public void updateUser(String jid, String password, String name, String email){

try {

User theUser = userManager.getUser(jid);

theUser.setPassword(password);

theUser.setEmail(email);

theUser.setName(name);

} catch (UserNotFoundException e) {

e.printStackTrace();

}

}

@WebMethod

public void createGroup(String name){

try {

groupManager.createGroup(name);

} catch (GroupAlreadyExistsException e) {

e.printStackTrace();

}

}

@WebMethod

public void deleteGroup(String name){

try {

Group theGroup = groupManager.getGroup(name);

groupManager.deleteGroup(theGroup);

} catch (GroupNotFoundException e) {

e.printStackTrace();

}

}

}

Hi,

It would be great for User Service plugin to also have group management abilities. I’‘ve tried dealing with groups in database, but changes are not instant due to wildfire’'s group cache. Is your modified version (with group management) working and available?

This was just a proof of concept that I suggested to the developers. To my knowledge it’‘s not integrated nor tested and in chat I sensed they were more oriented in using the jabber protocol itself in order to enable these features (which is a shame, they dont understand that people need this feature - yesterday, SOA was invented for this kind of integration and wildfire is not the center of our universe, “No man is an island…”). I’‘ve modified my own web-app wildfire distribution in order to make it work. If you think its usefull make it an issue with the dev’‘s. Also to be fair they are perhaps already working on it but wondering which WS stack to use and how to expose the services. It’'s no easy task.

I imagined created a plugin for wildfire that could accept the presence protocol. For example to join a group you could do,

and to leave a group you could do

Has anyone done this yet? Looking at the broadcast plugin I think this will be an easy task.

What I was proposing was a way to allow external applications to include users to the user database and groups, not jabber clients. This is because we have a web application that already has a user database and roles, so we wanted to expose the user manager to WebSevices calls so our app could manage the jabber server users. That said I have nothing against the jabber approach except this one, I want only authorized clients to be in certain groups on this server, so allowing clients to register themselves kind of beats my use case. Imagine a technician that register himself to a admin group. Beats the whole purpose, but in a system with not so rigorous rules I guess your use case is valid.

Good work on the plugin, Schrepfler!

I wrote the UserService plugin, because like you, I was in a hurry and needed to perform user admin. from a web app on the same server as Wildfire. The http way, was the just fastest and most flexible way to do that. Having said that, now there is the ability to direct Wildfire to use a user database that you specify. This kind of takes away from the usefulness of the UserService plugin.

Adding group management and other functions to the UserService plugin, is quite simple. Probably you can’'t really expect the Jive Software guys to do that though, because it is not really the direction they are going in, and they were not involved in the dev. of the plugin in the first place. I am sure they are more interested in adding features by way of JEPs that are part of a community process, than plugins which just apply to wildfire. And after all, this is open source.

Given that, I guess it is my “job.” Let me just investigate, what functions we should add to it, and how best to manage the process.

Could you or anyone who is interested, post here their recommendations, suggestions?

Thanks

Justin Hunt

My approach would be to make two sets of methods, one with a return to void so there is no blocking on the client side and another with some sort of feedback. To me what makes sense is to try and expose as many methods that are available to the managers but also perhaps “batch” methods where a large amount of users might be registered in one shot, and added/removed from groups.

As you rightly addressed the issue there is now the custom database integration, but for me it fails.

Justin,

I went through the work of manually updating the Roster in the database to match some externally enforced presence rules. When I came up against Wildfire’'s cacheing I realized I would need something more integrated in order to flush the cache or, even better, do the roster updates properly.

It would be great to have the User Service plugin accept roster management commands.

Something like:

modify = Roster

operation=Add|Update|Remove (required)

Owner = XXX (required)

Jid = XXX@XXX (required)

Nickname = XXXX

substatus = n

ask = n

recv = n

OK thanks for your input guys. I have been waiting for more ideas to come through, but I think I will just get an updated version out. Should be done this month.