use of won.owner.model.UserAtom in project webofneeds by researchstudio-sat.
the class WonWebSocketHandler method afterConnectionClosed.
@Override
public void afterConnectionClosed(final WebSocketSession session, final CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
User user = getUserForSession(session);
if (user != null) {
logger.debug("session closed, removing session bindings to user {}", user.getId());
this.webSocketSessionService.removeMapping(user, session);
for (UserAtom userAtom : user.getUserAtoms()) {
logger.debug("removing session bindings to atom {}", userAtom.getUri());
this.webSocketSessionService.removeMapping(userAtom.getUri(), session);
}
} else {
logger.debug("connection closed, but no user found in session, no bindings removed");
}
}
use of won.owner.model.UserAtom in project webofneeds by researchstudio-sat.
the class OwnerPersistenceTest method test_delete_UserAtom.
@Test
public void test_delete_UserAtom() throws Exception {
URI atomUri = URI.create("some:/atom.uri");
String email = "user@example.com";
createUserWithAtom(atomUri, email);
Thread t1 = new Thread(() -> helper.doInSeparateTransaction(() -> createUserWithAtom(atomUri, email)));
Thread t2 = new Thread(() -> helper.doInSeparateTransaction(() -> {
User sameUser = userRepository.findByUsername(email);
UserAtom sameAtom = userAtomRepository.findByAtomUri(atomUri);
sameUser.removeUserAtom(sameAtom);
userAtomRepository.delete(sameAtom);
}));
t1.start();
t1.join();
t2.start();
t2.join();
}
use of won.owner.model.UserAtom in project webofneeds by researchstudio-sat.
the class WonWebSocketHandler method notifyPerPush.
private void notifyPerPush(final User user, final URI atomUri, final WonMessage wonMessage, URI connectionUri) {
if (wonMessage.getFocalMessage().getMessageType().isResponseMessage()) {
// we assume that this message, coming from the server here, can only be an
// echoed message. don't send by email.
logger.debug("not sending notification to user: message {} looks like an echo from the server", wonMessage.getMessageURI());
return;
}
if (user == null) {
logger.info("not sending notification to user: user not specified");
return;
}
UserAtom userAtom = getAtomOfUser(user, atomUri);
if (userAtom == null) {
logger.debug("not sending notification to user: atom uri not specified");
return;
}
String textMsg = WonRdfUtils.MessageUtils.getTextMessage(wonMessage);
String iconUrl = uriService.getOwnerProtocolOwnerURI().toString() + "/skin/current/images/logo.png";
switch(wonMessage.getMessageType()) {
case CONNECTION_MESSAGE:
if (userAtom.isConversations()) {
ObjectMapper mapper = new ObjectMapper();
ObjectNode rootNode = mapper.createObjectNode();
rootNode.put("type", "MESSAGE");
rootNode.put("atomUri", userAtom.getUri().toString());
rootNode.put("connectionUri", connectionUri.toString());
rootNode.put("icon", iconUrl);
if (textMsg != null) {
rootNode.put("message", StringUtils.abbreviate(textMsg, 50));
}
String stringifiedJson;
try {
stringifiedJson = mapper.writer().writeValueAsString(rootNode);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
pushSender.sendNotification(user, stringifiedJson);
}
return;
case SOCKET_HINT_MESSAGE:
if (userAtom.isMatches()) {
if (!isConnectionInSuggestedState(connectionUri)) {
// found the connection previously and we don't want to notify them
return;
}
ObjectMapper mapper = new ObjectMapper();
ObjectNode rootNode = mapper.createObjectNode();
rootNode.put("type", "HINT");
rootNode.put("atomUri", userAtom.getUri().toString());
rootNode.put("connectionUri", connectionUri.toString());
rootNode.put("icon", iconUrl);
String stringifiedJson;
try {
stringifiedJson = mapper.writer().writeValueAsString(rootNode);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
pushSender.sendNotification(user, stringifiedJson);
}
return;
case CONNECT:
if (userAtom.isRequests()) {
ObjectMapper mapper = new ObjectMapper();
ObjectNode rootNode = mapper.createObjectNode();
rootNode.put("type", "CONNECT");
rootNode.put("atomUri", userAtom.getUri().toString());
rootNode.put("connectionUri", connectionUri.toString());
rootNode.put("icon", iconUrl);
if (textMsg != null) {
rootNode.put("message", StringUtils.abbreviate(textMsg, 50));
}
String stringifiedJson;
try {
stringifiedJson = mapper.writer().writeValueAsString(rootNode);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
pushSender.sendNotification(user, stringifiedJson);
}
return;
default:
return;
}
}
use of won.owner.model.UserAtom in project webofneeds by researchstudio-sat.
the class WonWebSocketHandler method process.
/**
* Sends a message coming from the WoN node to the client.
*/
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
public WonMessage process(final WonMessage wonMessage) {
try {
logger.debug("processing message {} incoming from node", wonMessage.getMessageURI());
String wonMessageJsonLdString = WonMessageEncoder.encodeAsJsonLd(wonMessage);
WebSocketMessage<String> webSocketMessage = new TextMessage(wonMessageJsonLdString);
logger.debug("determining which owned atom is to be informed of message {} ", wonMessage.getMessageURI());
URI atomUri = getOwnedAtomURIForMessageFromNode(wonMessage);
logger.debug("obtaining WebSocketSessions for message {} ", wonMessage.getMessageURI());
Set<WebSocketSession> webSocketSessions = webSocketSessionService.getWebSocketSessions(atomUri);
Optional<User> userOpt = webSocketSessions == null ? Optional.empty() : webSocketSessions.stream().filter(s -> s.isOpen()).findFirst().map(s -> getUserForSession(s));
logger.debug("found {} sessions for message {} ", webSocketSessions.size(), wonMessage.getMessageURI());
logger.debug("found user for message {} via session: {} ", wonMessage.getMessageURI(), userOpt.isPresent());
if (!userOpt.isPresent()) {
userOpt = Optional.ofNullable(userRepository.findByAtomUri(atomUri));
}
logger.debug("found user for message {} atom uri: {} ", wonMessage.getMessageURI(), userOpt.isPresent());
// it's quite possible that we don't find the user object this way.
User user = userOpt.orElse(null);
// Methods below can handle that.
logger.debug("updating user-atom association for message {}, user has been found:{} ", wonMessage.getMessageURI(), userOpt.isPresent());
userAtomService.updateUserAtomAssociation(wonMessage, user);
logger.debug("trying to find WebSocketSessions for message{}, atom {}, user has been found:{}", new Object[] { wonMessage.getMessageURI(), atomUri, userOpt.isPresent() });
webSocketSessions = webSocketSessionService.findWebSocketSessionsForAtomAndUser(atomUri, user);
// check if we can deliver the message. If not, send email.
if (webSocketSessions.size() == 0) {
if (logger.isDebugEnabled()) {
logger.debug("cannot deliver message {}: no websocket session found. Trying to send message by email.", wonMessage.toShortStringForDebug());
}
notifyUserOnDifferentChannel(wonMessage, atomUri, user);
return wonMessage;
}
// we can send it - pre-cache the delivery chain:
logger.debug("put message {} into cache before sending on websocket", wonMessage.getMessageURI());
eagerlyCachePopulatingProcessor.process(wonMessage);
// send to owner webapp
int successfullySent = 0;
for (WebSocketSession session : webSocketSessions) {
logger.debug("sending message {} via websocket session", wonMessage.getMessageURI());
successfullySent += sendMessageForSession(wonMessage, webSocketMessage, session, atomUri, user) ? 1 : 0;
}
logger.debug("sent message {} via {} websocket sessions", wonMessage.getMessageURI(), successfullySent);
if (successfullySent == 0) {
// we did not manage to send the message via the websocket, send it by email.
if (logger.isDebugEnabled()) {
logger.debug("cannot deliver message {}: none of the associated websocket sessions worked. Trying to send message by webpush and email.", wonMessage.toShortStringForDebug());
}
// TODO: ideally in this case
// 1. collect multiple events occurring in close succession
// 2. try to push
// 3. email only if push was not successful
notifyUserOnDifferentChannel(wonMessage, atomUri, user);
} else {
// Always send possible pushNotifications:
// - maybe session is active -> message was send, but Tab is not focused
// - Browser is running in background -> user needs to get push notification
Optional<URI> connectionURI = WonLinkedDataUtils.getConnectionURIForIncomingMessage(wonMessage, linkedDataSource);
if (connectionURI.isPresent()) {
logger.debug("notifying user per web push for message {}", wonMessage.getMessageURI());
UserAtom userAtom = getAtomOfUser(user, atomUri);
if (userAtom == null) {
userOpt = Optional.ofNullable(userRepository.findByAtomUri(atomUri));
user = userOpt.orElse(null);
}
notifyPerPush(user, atomUri, wonMessage, connectionURI.get());
}
logger.debug("cannot notify user: cannot determine connection URI");
}
return wonMessage;
} finally {
// in any case, let the serversideactionservice do its work, if there is any to
// do:
logger.debug("processing server side actions for message {} if any are registered", wonMessage.getMessageURI());
serverSideActionService.process(wonMessage);
}
}
use of won.owner.model.UserAtom in project webofneeds by researchstudio-sat.
the class WonWebSocketHandler method notifyPerEmail.
private void notifyPerEmail(final User user, final URI atomUri, final WonMessage wonMessage, URI connectionUri) {
if (wonMessage.getFocalMessage().getMessageType().isResponseMessage()) {
// we assume that this message, coming from the server here, can only be an
// echoed message. don't send by email.
logger.debug("not sending email to user: message {} looks like an echo from the server", wonMessage.getMessageURI());
return;
}
if (user == null) {
logger.info("not sending email to user: user not specified");
return;
}
if (user.isAnonymous()) {
logger.debug("not sending email to user: user is anonymous");
return;
}
if (!user.isEmailVerified()) {
logger.debug("not sending email to user: email address not yet verified");
return;
}
UserAtom userAtom = getAtomOfUser(user, atomUri);
if (userAtom == null) {
logger.debug("not sending email to user: atom uri not specified");
return;
}
UserAtom senderAtom = getAtomOfUser(user, wonMessage.getSenderAtomURI());
if (senderAtom != null) {
logger.debug("not sending email to user: sender and recipient atoms are controlled by same user.");
return;
}
String textMsg = WonRdfUtils.MessageUtils.getTextMessage(wonMessage);
try {
switch(wonMessage.getMessageType()) {
case CONNECTION_MESSAGE:
if (userAtom.isConversations()) {
emailSender.sendConversationNotificationMessage(user.getEmail(), atomUri.toString(), wonMessage.getSenderAtomURI().toString(), connectionUri.toString(), wonMessage.getRecipientSocketURIRequired().toString(), wonMessage.getSenderSocketURIRequired().toString(), textMsg);
}
return;
case CONNECT:
if (userAtom.isRequests()) {
emailSender.sendConnectNotificationMessage(user.getEmail(), atomUri.toString(), wonMessage.getSenderAtomURI().toString(), connectionUri.toString(), wonMessage.getRecipientSocketURIRequired().toString(), wonMessage.getSenderSocketURIRequired().toString(), textMsg);
}
return;
case ATOM_HINT_MESSAGE:
case SOCKET_HINT_MESSAGE:
if (userAtom.isMatches()) {
Optional<URI> targetAtomUri = WonLinkedDataUtils.getAtomOfSocket(wonMessage.getHintTargetSocketURI(), linkedDataSource);
if (!isConnectionInSuggestedState(connectionUri)) {
// found the connection previously and we don't want to notify them
return;
}
if (targetAtomUri.isPresent()) {
// user a hash of the user's email address for the key, so as not to hold
// users emails in memory all the time
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(user.getEmail().getBytes(StandardCharsets.UTF_8));
String key = "HINT" + Base64.getEncoder().encodeToString(hash);
String[] args = new String[] { user.getEmail(), atomUri.toString(), targetAtomUri.get().toString(), connectionUri.toString() };
// only count 1 item per atom/atom combination per batch key.
String deduplicationKey = atomUri.toString() + targetAtomUri.toString();
// set the configuration
BatchingConsumer.Config config = new BatchingConsumer.ConfigBuilder().consumeFirst(// send the first mail immediately
true).maxBatchAge(// empty batch at least once a day
Duration.ofHours(24)).maxItemInterval(// wait 10 minutes after the last
Duration.ofMinutes(10)).minChunkInterval(// send at most 1 mail every 6 hours
Duration.ofHours(6)).maxBatchSize(// as soon as we reach 50 hints, send mail
50).build();
batchingConsumer.accept(key, args, deduplicationKey, batch -> {
if (batch.size() == 1) {
String[] a = batch.iterator().next();
emailSender.sendHintNotificationMessage(a[0], a[1], a[2], a[3]);
} else if (batch.size() > 0) {
String[] a = batch.iterator().next();
Map<String, Long> hintCounts = batch.stream().collect(Collectors.groupingBy(item -> item[1], Collectors.counting()));
emailSender.sendMultipleHintsNotificationMessage(a[0], hintCounts);
}
}, config);
} else {
logger.info("received socket hint to {} but could not identify corresponding atom - no mail sent.", wonMessage.getHintTargetSocketURI());
}
}
return;
case CLOSE:
// do not send emails for a close
return;
case DEACTIVATE:
// a deactivate message, coming from the WoN node. Always deliverd by email.
emailSender.sendSystemDeactivateNotificationMessage(user.getEmail(), atomUri.toString(), textMsg);
return;
case ATOM_MESSAGE:
// an atom message, coming from the WoN node. Always deliverd by email.
emailSender.sendAtomMessageNotificationMessage(user.getEmail(), atomUri.toString(), textMsg);
return;
default:
return;
}
} catch (MailException | NoSuchAlgorithmException ex) {
logger.error("Email could not be sent", ex);
}
}
Aggregations