Java tutorial
/* * Copyright 2010 Vodafone Group Services Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.onesocialweb.openfire.manager; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.Query; import org.dom4j.dom.DOMDocument; import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.user.UserNotFoundException; import org.onesocialweb.model.atom.DefaultAtomHelper; import org.onesocialweb.model.relation.Relation; import org.onesocialweb.openfire.OswPlugin; import org.onesocialweb.openfire.exception.InvalidRelationException; import org.onesocialweb.openfire.model.relation.PersistentRelation; import org.onesocialweb.xml.dom.RelationDomWriter; import org.onesocialweb.xml.dom.imp.DefaultRelationDomWriter; import org.onesocialweb.xml.namespace.Onesocialweb; import org.w3c.dom.Element; import org.xmpp.packet.Message; /** * The relation manager is a singleton class taking care of all the business * logic related to querying, creating, updating and deleting relations. * * @author eschenal * */ public class RelationManager { public static final String NODE = "http://onesocialweb.org/spec/1.0/relations"; /** * Singleton: keep a static reference to teh only instance */ private static RelationManager instance; public static RelationManager getInstance() { if (instance == null) { // Carefull, we are in a threaded environment ! synchronized (RelationManager.class) { instance = new RelationManager(); } } return instance; } /** * Retrieves the relation of the target user as can be seen by the requesting user. * * TODO ACL is not yet implemented. All relations are returned at this stage. * * @param requestorJID the entity requesting the relations. * @param targetJID the entity whose relations are requested. * @return the list of relations of the target user, as can be seen by the requestor * @throws UserNotFoundException */ @SuppressWarnings("unchecked") public List<Relation> getRelations(String requestorJID, String targetJID) throws UserNotFoundException { final EntityManager em = OswPlugin.getEmFactory().createEntityManager(); Query query = em .createQuery("SELECT DISTINCT relation FROM Relation relation WHERE relation.owner = :target"); // Parametrize the query query.setParameter("target", targetJID); List<Relation> result = query.getResultList(); em.close(); return result; } /** * Setup a new relation. * * @param userJID the user seting up the new relation * @param relation the relation to setup * @throws InvalidRelationException */ public void setupRelation(String userJID, PersistentRelation relation) throws InvalidRelationException { // Validate the relation request // TODO More should be validated here if (!relation.hasFrom() || !relation.hasTo() || !relation.hasNature()) { throw new InvalidRelationException("Relation is missing required elements"); } // Verify that the from or to is the user making the request if (!(relation.getFrom().equals(userJID) || relation.getTo().equals(userJID))) { throw new InvalidRelationException("Must be part of the relation to create it"); } // Assign a unique ID to this new relation relation.setId(DefaultAtomHelper.generateId()); // Set the request status relation.setStatus(Relation.Status.REQUEST); // We store the relation for requestor relation.setOwner(userJID); // Persist the relation final EntityManager em = OswPlugin.getEmFactory().createEntityManager(); em.getTransaction().begin(); em.persist(relation); em.getTransaction().commit(); em.close(); // We cleanup and notifiy the relation for the recipient relation.setAclRules(null); relation.setComment(null); notify(userJID, relation); } /** * Update an existing relation. * * @param userJID the user seting up the new relation * @param relation the relation to setup * @throws InvalidRelationException */ @SuppressWarnings("unchecked") public void updateRelation(String userJID, PersistentRelation relation) throws InvalidRelationException { // Validate the relation request // TODO More should be validated here if (!relation.hasId() || !relation.hasStatus()) { throw new InvalidRelationException("Relation is missing required elements"); } // Search for an existing relation with the given ID final EntityManager em = OswPlugin.getEmFactory().createEntityManager(); Query query = em.createQuery("SELECT x FROM Relation x WHERE x.owner = ?1 AND x.guid = ?2"); query.setParameter(1, userJID); query.setParameter(2, relation.getId()); List<PersistentRelation> relations = query.getResultList(); // If no match, or more than one, we have an issue if (relations.size() != 1) { throw new InvalidRelationException("Could not find relationship with id " + relation.getId()); } // We update the persisted relation em.getTransaction().begin(); PersistentRelation storedRelation = relations.get(0); storedRelation.setStatus(relation.getStatus()); em.getTransaction().commit(); em.close(); // We cleanup and notifiy the relation for the recipient storedRelation.setAclRules(null); storedRelation.setComment(null); notify(userJID, storedRelation); } /** * Handles a relation notification message. * * @param remoteJID the entity sending the message * @param localJID the entity having received the message * @param relation the relation being notified * @throws InvalidRelationException */ public void handleMessage(String remoteJID, String localJID, Relation relation) throws InvalidRelationException { // We need at least a status field if (!relation.hasStatus()) { throw new InvalidRelationException("Relation is missing a status field"); } // Is this a new request ? if (relation.getStatus().equals(Relation.Status.REQUEST)) { handleRequestMessage(remoteJID, localJID, relation); } else { handleUpdateMessage(remoteJID, localJID, relation); } } @SuppressWarnings("unchecked") private void handleRequestMessage(String remoteJID, String localJID, Relation relation) throws InvalidRelationException { // Are required fields for a new relation setup present ? if (!relation.hasNature() || !relation.hasStatus() || !relation.hasFrom() || !relation.hasTo() || !relation.hasId()) { throw new InvalidRelationException("Relation is missing required elements"); } // The relation should be between the sender and the receiver if (getDirection(relation, remoteJID, localJID) == 0) { throw new InvalidRelationException("Relation from/to do not match message from/to"); } // Cannot add a relation to yourself if (relation.getFrom().equals(relation.getTo())) { throw new InvalidRelationException("Cannot add relation to yourself"); } // Verify that this relation is not already here final EntityManager em = OswPlugin.getEmFactory().createEntityManager(); Query query = em.createQuery("SELECT x FROM Relation x WHERE x.owner = ?1 AND x.guid = ?2"); query.setParameter(1, localJID); query.setParameter(2, relation.getId()); List<PersistentRelation> relations = query.getResultList(); // If there is a match, we give up // TODO Not that fast. The other end may jut have not received any // answer and wants to try again // we should deal with all these recovery features. if (relations.size() > 0) { throw new InvalidRelationException("This relation has already been requested"); } // Save the relation PersistentRelation persistentRelation = (PersistentRelation) relation; persistentRelation.setOwner(localJID); em.getTransaction().begin(); em.persist(persistentRelation); em.getTransaction().commit(); em.close(); } @SuppressWarnings("unchecked") private void handleUpdateMessage(String remoteJID, String localJID, Relation relation) throws InvalidRelationException { // Search for the stored relation final EntityManager em = OswPlugin.getEmFactory().createEntityManager(); Query query = em.createQuery("SELECT x FROM Relation x WHERE x.owner = ?1 AND x.guid = ?2"); query.setParameter(1, localJID); query.setParameter(2, relation.getId()); List<PersistentRelation> relations = query.getResultList(); // If no match, or more than one, we have an issue if (relations.size() != 1) { throw new InvalidRelationException("Could not find matching relationship"); } // We update the persisted relation em.getTransaction().begin(); PersistentRelation previous = relations.get(0); previous.setStatus(relation.getStatus()); em.getTransaction().commit(); em.close(); } private void notify(String localJID, Relation relation) { final DOMDocument domDocument = new DOMDocument(); final Element entryElement = (Element) domDocument .appendChild(domDocument.createElementNS(Onesocialweb.NAMESPACE, Onesocialweb.RELATION_ELEMENT)); final RelationDomWriter writer = new DefaultRelationDomWriter(); writer.write(relation, entryElement); domDocument.removeChild(entryElement); final Message message = new Message(); message.setFrom(localJID); message.setType(Message.Type.headline); org.dom4j.Element eventElement = message.addChildElement("event", "http://jabber.org/protocol/pubsub#event"); org.dom4j.Element itemsElement = eventElement.addElement("items"); itemsElement.addAttribute("node", NODE); org.dom4j.Element itemElement = itemsElement.addElement("item"); itemElement.addAttribute("id", relation.getId()); itemElement.add((org.dom4j.Element) entryElement); // Send to this user message.setTo(getOtherEnd(relation, localJID)); server.getMessageRouter().route(message); } private String getOtherEnd(Relation relation, String userJID) { if (!relation.hasFrom() || !relation.hasTo()) { return null; } if (relation.getFrom().equals(userJID)) { return relation.getTo(); } if (relation.getTo().equals(userJID)) { return relation.getFrom(); } // The given JID is no part of this relation return null; } private int getDirection(Relation relation, String fromJID, String toJID) { if (!relation.hasFrom() || !relation.hasTo()) { return 0; } if (relation.getFrom().equals(fromJID) && relation.getTo().equals(toJID)) { return 1; } if (relation.getFrom().equals(toJID) && relation.getTo().equals(fromJID)) { return -1; } // If we are here, the relation does not concern from & to return 0; } /** * Class dependencies (should be true dependency injection someday) */ private final XMPPServer server; /** * Private constructor to enforce the singleton */ private RelationManager() { server = XMPPServer.getInstance(); } }