/**
*
* geo-platform
* Rich webgis framework
* http://geo-platform.org
* ====================================================================
*
* Copyright (C) 2008-2017 geoSDI Group (CNR IMAA - Potenza - ITALY).
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. This program is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License
* for more details. You should have received a copy of the GNU General
* Public License along with this program. If not, see http://www.gnu.org/licenses/
*
* ====================================================================
*
* Linking this library statically or dynamically with other modules is
* making a combined work based on this library. Thus, the terms and
* conditions of the GNU General Public License cover the whole combination.
*
* As a special exception, the copyright holders of this library give you permission
* to link this library with independent modules to produce an executable, regardless
* of the license terms of these independent modules, and to copy and distribute
* the resulting executable under terms of your choice, provided that you also meet,
* for each linked independent module, the terms and conditions of the license of
* that module. An independent module is a module which is not derived from or
* based on this library. If you modify this library, you may extend this exception
* to your version of the library, but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version.
*/
package org.geosdi.geoplatform.services;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.jws.WebService;
import org.geosdi.geoplatform.core.dao.GPAccountProjectDAO;
import org.geosdi.geoplatform.core.dao.GPProjectDAO;
import org.geosdi.geoplatform.core.model.GPAccount;
import org.geosdi.geoplatform.core.model.GPAccountProject;
import org.geosdi.geoplatform.core.model.GPProject;
import org.geosdi.geoplatform.exception.ResourceNotFoundFault;
import org.geosdi.geoplatform.gui.shared.XMPPSubjectEnum;
import org.geosdi.geoplatform.response.collection.XmppAttributesMap;
import org.geosdi.geoplatform.services.development.EntityCorrectness;
import org.geosdi.geoplatform.services.development.EntityCorrectnessException;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.quartz.CalendarIntervalScheduleBuilder;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author Nazzareno Sileno - CNR IMAA geoSDI Group
* @email nazzareno.sileno@geosdi.org
*/
@WebService(endpointInterface = "org.geosdi.geoplatform.services.GPTrackingService")
public class GPTrackingServiceImpl implements GPTrackingService, InitializingBean {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private XMPPConnection connection;
private ConnectionConfiguration config;
//
@Autowired
private TrackingScheduler scheduler;
//
@Autowired
private GPProjectDAO projectDao;
//
@Autowired
private GPAccountProjectDAO accountProjectDao;
public GPTrackingServiceImpl(String host_xmpp_server, String port_xmpp_server,
String username_xmpp_server, String password_xmpp_server) {
int xmppPort = Integer.parseInt(port_xmpp_server);
config = new ConnectionConfiguration(host_xmpp_server, xmppPort);
connection = new XMPPConnection(config);
this.login(username_xmpp_server, password_xmpp_server);
}
private void login(String userName, String password) {
try {
logger.debug("Triyng to connect to the XMPP server");
connection.connect();
logger.debug("Triyng to login to the XMPP Server");
connection.login(userName, password, UUID.randomUUID().toString());
} catch (XMPPException ex) {
logger.error(ex.toString());
}
}
@Override
public void afterPropertiesSet() throws Exception {
}
@Override
public boolean unscribeLayerNotification(String username, String layerUUID) {
TriggerKey triggerKey = new TriggerKey(username + ":" + layerUUID,
TrackingScheduler.TRACKING_GROUP);
return this.unscribeLayerNotification(triggerKey);
}
public boolean unscribeLayerNotification(TriggerKey triggerKey) {
boolean result = Boolean.FALSE;
try {
result = this.scheduler.getScheduler().unscheduleJob(triggerKey);
logger.debug("Job unscheduled: " + result);
} catch (SchedulerException ex) {
logger.error("Error unscheduling publisher cleaner job: " + ex);
}
return result;
}
@Override
public void subscribeLayerNotification(String username, String emiteResource,
String layerUUID, int secondToRefresh) {
TriggerKey triggerKey = new TriggerKey(username + ":" + layerUUID,
TrackingScheduler.TRACKING_GROUP);
GregorianCalendar calendar = new GregorianCalendar();
calendar.add(Calendar.SECOND, secondToRefresh);
Trigger trigger = TriggerBuilder.newTrigger().forJob(
this.scheduler.getSendMessageJobDetail()).
withIdentity(triggerKey).
withDescription("Runs after " + secondToRefresh + " seconds").
startAt(calendar.getTime()).
withSchedule(CalendarIntervalScheduleBuilder.calendarIntervalSchedule().
withIntervalInSeconds(secondToRefresh).
withMisfireHandlingInstructionFireAndProceed()).
build();
trigger.getJobDataMap().put(SendTrackingMessageJob.GP_TRACKING_SERVICE, this);
String receiver = username + "@" + this.connection.getHost();
String messageReceiver = receiver + '/' + emiteResource;
// RosterGroup rosterGroup = connection.getRoster().getGroup("sitdpc-share");
// if (rosterGroup != null) {
// boolean found = false;
// for (RosterEntry entry : rosterGroup.getEntries()) {
// System.out.println("Stampa nome entry: " + entry.getUser());
// if (entry.getUser().equalsIgnoreCase(receiver)) {
// logger.info("The RosterGroup already contains the user");
// found = true;
// break;
// }
// }
// if (!found) {
// try {
// connection.getRoster().createEntry(receiver, username, new String[]{rosterGroup.getName()});
// System.out.println("created entry");
// } catch (XMPPException ex) {
// logger.error("Failed to add user: " + username + " to the service roster: " + ex);
// }
// }
// }
Message message = new Message(messageReceiver, Message.Type.normal);
message.setSubject(XMPPSubjectEnum.LAYER_RELOAD.toString());
message.setBody(layerUUID);
trigger.getJobDataMap().put(SendTrackingMessageJob.MESSAGE, message);
this.scheduleTrigger(triggerKey, trigger);
logger.debug("Subscribed layer: " + layerUUID + " notification");
}
private void scheduleTrigger(TriggerKey triggerKey, Trigger trigger) {
try {
if (this.scheduler.getScheduler().checkExists(triggerKey)) {
this.scheduler.getScheduler().rescheduleJob(triggerKey, trigger);
logger.debug("Re-Scheduled layer notification");
} else {
this.scheduler.getScheduler().scheduleJob(trigger);
logger.debug("Scheduled layer notification");
}
} catch (SchedulerException ex) {
logger.error("Error adding tracking job: " + ex);
}
}
protected XMPPConnection getConnection() {
return this.connection;
}
/**
* @see GPTrackingService#sendSharedProjectNotification(java.lang.Long,
* org.geosdi.geoplatform.services.XMPPSubjectServerEnum, java.lang.String,
* org.geosdi.geoplatform.responce.collection.XmppAttributesMap)
*/
@Override
public void sendSharedProjectNotification(Long projectID, XMPPSubjectEnum subject,
String text, XmppAttributesMap attributesMap) throws ResourceNotFoundFault {
GPProject project = projectDao.find(projectID);
if (project == null) {
throw new ResourceNotFoundFault("Project not found", projectID);
}
EntityCorrectness.checkProjectLog(project); // TODO assert
if (!project.isShared()) { // TODO assert
throw new EntityCorrectnessException("The Project must be shared.");
}
List<GPAccountProject> accounts = accountProjectDao.findNotOwnersByProjectID(projectID);
EntityCorrectness.checkAccountProjectListLog(accounts); // TODO assert
Roster roster = this.connection.getRoster();
Message message = this.createUnknowMessage(subject, text, attributesMap);
for (GPAccountProject accountProject : accounts) {
GPAccount account = accountProject.getAccount();
EntityCorrectness.checkAccountLog(account); // TODO assert
// If user have this project as default
if (accountProject.isDefaultProject()) {
String naturalID = account.getNaturalID(); // Username for User
String recipient = this.createXmppUri(naturalID);
logger.trace("\n\n*** Recipient XMPP uri: {} ***", recipient);
// If user is online send the message
Presence presence = roster.getPresence(recipient);
if (presence.isAvailable()) {
logger.info("\n*** Send Message to online user \"{}\" ***", naturalID);
message.setTo(recipient);
connection.sendPacket(message);
}
}
}
}
/**
* Create an XMPP URI (node@domain).
*/
private String createXmppUri(String username) {
return username + "@" + this.connection.getHost();
}
/**
* Create an XMPP Message less than recipient.
*/
private Message createUnknowMessage(XMPPSubjectEnum subject, String text,
XmppAttributesMap attributesMap) {
Message message = new Message();
message.setType(Message.Type.normal);
// message.setFrom(text); // TODO Is this necessary?
message.setSubject(subject.toString());
message.setBody(text);
for (Map.Entry<String, String> entry : attributesMap.getAttributesMap().entrySet()) {
logger.trace("Attribute - {}: {}", entry.getKey(), entry.getValue());
message.setProperty(entry.getKey(), entry.getValue());
}
return message;
}
}