К основному контенту

XPages. Диалог с пользователем. Собственные уведомления

Итогом полученных ограничений, которые получились в итоге стало появление "собственной" системы сообщений. Итак, по-порядку:

1. FlashScope
Сегодняшняя работа будет очень тесна связана в возможностью показать сообщение после редиректа страницы. Поэтому с механизма, который позволит это сделать и начнем.
Метод, позволяющий отображать сообщения (речь идет о FacesMessage) после смены страницы описан тут. Но будем использовать, в некотором смысле, более универсальный механизм, который позволит передавать параметры между запросами и не занимать sessionScope или applicationScope - это FlashScope. Я использую немного не эту реализацию, а реализацию от Jesse Gallagher. Еще нужно в faces-config.xml прописать:
<managed-bean>
  <managed-bean-name>flashScope</managed-bean-name>
  <managed-bean-class>java.util.HashMap</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

и

<lifecycle>
  <phase-listener>frostillicus.FlashScopePhaseListener</phase-listener>
</lifecycle>

А так же иметь метод по получению flashScope, например:
@SuppressWarnings("unchecked")
public static Map<Object, Object> getFlashScope() {
return (Map<Object, Object>)resolveVariable("flashScope");
}
public static Object resolveVariable(final String varName) {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getVariableResolver().resolveVariable(context, varName);
}

2. Добавляем класс под свои уведомления:
import java.io.Serializable;
import javax.faces.application.FacesMessage;
public class Message implements Serializable {
private static final long serialVersionUID = 1L;
private static final String TYPE_INFO_NAME = "INFO";
public static final MessageType TYPE_INFO = new MessageType(TYPE_INFO_NAME);
private static final String TYPE_WARNING_NAME = "WARNING";
public static final MessageType TYPE_WARNING = new MessageType(TYPE_WARNING_NAME);
private static final String TYPE_SUCCESS_NAME = "SUCCESS";
public static final MessageType TYPE_SUCCESS = new MessageType(TYPE_SUCCESS_NAME);
private static final String TYPE_ERROR_NAME = "ERROR";
public static final MessageType TYPE_ERROR = new MessageType(TYPE_ERROR_NAME);
private static final String TYPE_FATAL_NAME = "FATAL";
public static final MessageType TYPE_FATAL = new MessageType(TYPE_FATAL_NAME);
public static final MessageType[] messagesTypes = {TYPE_FATAL, TYPE_ERROR, TYPE_WARNING, TYPE_SUCCESS, TYPE_INFO};
private String summary;
private String detail;
public Message(final String summary, final String detail) {
this.summary = summary;
this.detail = detail;
}
public String getSummary() {
return summary;
}
public String getDetail() {
return detail;
}
public static class MessageType {
String typeName;
protected MessageType(final String typeName) {
this.typeName = typeName;
}
public String getTypeName() {
return typeName;
}
}
}

3. Учим MessageBean отправлять "наши" уведомления:
- для каждого типа сообщения будет свой канал, например infoMessages
- канал это просто список (List) сообщений, который лежит вo flashScope (а это Map) с ключом, соответствующем названию канала
- flashScope очищается после, условно говоря, смены страницы, поэтому, если через него отображать сообщения в рамках одной страницы - они будут накапливаться. Иногда это может быть нужно, мне кажется, а иногда нет. И
- для того, чтобы наши уведомления отправлялись единообразно как в рамках одной страницы, так и при ее смене, добавим 2 вспомогательным метода для очистки стека сообщений.

@SuppressWarnings("unchecked")
public static void postMessage(final Message.MessageType messageType, final String summary, final String detail) {  
 Message message = new Message(summary, detail);
 String channelName = messageType.getTypeName().toLowerCase();
 Map<Object, Object> flashScope = getFlashScope();
 
 List<Object> messages = (List<Object>) flashScope.get(channelName + "Messages");
 if (messages == null) {
  messages = new ArrayList<Object>();
  flashScope.put(channelName + "Messages", messages);
 }
 messages.add(message);
}

public static void clearMessages(final MessageType messageType) {
 Map<Object, Object> flashScope = getFlashScope();
 String channelName = messageType.getTypeName().toLowerCase();
 flashScope.put(channelName + "Messages", null);  
}

public static void clearMessages() {
 for (MessageType type : Message.messagesTypes) {
  clearMessages(type);
 }
}

4. CustomControl для отображения сообщений
Основной:
<xp:div id="msgDisplay">
<xp:div rendered="#{javascript:flashScope.fatalMessages != null}">
<xp:repeat id="repeat2" rows="30" var="message"
disableOutputTag="true" value="#{flashScope.fatalMessages}">
<xc:cc_Core_MessageEntry messageStyleClass="alert-danger"></xc:cc_Core_MessageEntry>
</xp:repeat>
</xp:div>
<xp:div rendered="#{javascript:flashScope.errorMessages != null}">
<xp:repeat id="repeat1" rows="30" var="message"
disableOutputTag="true" value="#{flashScope.errorMessages}">
<xc:cc_Core_MessageEntry messageStyleClass="alert-danger"></xc:cc_Core_MessageEntry>
</xp:repeat>
</xp:div>
<xp:div rendered="#{javascript:flashScope.warningMessages != null}">
<xp:repeat id="repeat3" rows="30" var="message"
disableOutputTag="true" value="#{flashScope.warningMessages}">
<xc:cc_Core_MessageEntry messageStyleClass="alert-warning"></xc:cc_Core_MessageEntry>
</xp:repeat>
</xp:div>
<xp:div rendered="#{javascript:flashScope.successMessages != null}">
<xp:repeat id="repeat4" rows="30" var="message"
disableOutputTag="true" value="#{flashScope.successMessages}">
<xc:cc_Core_MessageEntry messageStyleClass="alert-success"></xc:cc_Core_MessageEntry>
</xp:repeat>
</xp:div>
<xp:div rendered="#{javascript:flashScope.infoMessages != null}">
<xp:repeat id="repeat5" rows="30" var="message"
disableOutputTag="true" value="#{flashScope.infoMessages}">
<xc:cc_Core_MessageEntry></xc:cc_Core_MessageEntry>
</xp:repeat>
</xp:div>
</xp:div>

cc_Core_MessageEntry для отображения индивидуального сообщения:
<div class="alert fade in #{compositeData.messageStyleClass}">
<a class="close" data-dismiss="alert" href="#">&#215;</a>
<xp:text escape="false" id="computedField1"
value="#{message.summary}" style="font-weight:bold"
rendered="#{not empty message.summary}">
</xp:text>
<xp:br></xp:br>
<xp:text escape="false" id="computedField2"
value="#{message.detail}" rendered="#{not empty message.detail}">
</xp:text>
<br />
</div>

Для этого Custom Control добавлено строковое свойство messageStyleClass. Оно не обязательно


Итоговый результат можно пощупать и рассмотреть в Demo-приложении.

Если вам понравился пост, жмите +1делитесь в соц. сетях и форумах! #xPages-по-русски

Комментарии

Популярные сообщения из этого блога

Занимательные алгоритмы. Поиск цикла в односвязном списке

И снова про тараканов, которые иногда возникают в голове. Как-то раз, засыпая, я задумался на курьезными задачками из своей сферы деятельности (Lotus Notes), которые можно было бы задать на собеседовании, плавно перешел к воспоминаниям о своих первых собеседования, когда опыта работы еще не было. Опыт самих собеседований у меня не велик а места, где задавались действительно интересные задачи (а не задачки типа: написать сортировку массива любым известным способом) вообще равны одному - это ABBYY. Как минимум одна задачка в списке на знание и понимание классических алгоритмов, описанных в книге Дональда Кнута -  Искусство программирования .

Unit-testing object validation when validator has DI

Summary Unit test object validation when validator(s) has a dependency. For instance, we have some custom field and cross-field validators. Want to test their combination. Additionally some of validators have dependencies, injected through constructor or setters. You're not using property injection, right? Shortcut If you are just searching for an answer, here's the fast way: Declare CustomConstraintValidatorFactory that implements javax.validation.ConstraintValidatorFactory Override getInstance method and on facing your constraint validator class instantiate it Otherwise delegate validator construction to org.hibernate.validator.internal.engine.constraintvalidation.ConstraintValidatorFactoryImpl Build validator factory and provide it your CustomConstraintValidatorFactory Build validator, using that factory... Go to demo project on GitHub for details:  https://github.com/MrArtemAA/blog-demos/blob/master/test-validator-with-injection/src/test/java/ru/artemaa/d

Lotus Notes FAQ. 8/9 Eclipse. Как настроить уведомления о Sametime сообщениях

Н а написание данной "инструкцию" натолкнул мой коллега. Помню, первый раз сам долго искал, как отключить постоянно выпрыгивающие уведомления о новых сообщениях в Sametime. И так, речь идет о клиентах IBM Notes 8+ версии Standart (Eclipse based). Как настроить уведомления о Sametime сообщениях?