Когда я использую UUID#randomUUID()
(который использует SecureRandom) или RandomStringUtils#randomAlphabetic(int)
(который использует Random) для генерации идентификатора компонента для проверки работоспособности HtmlInputText. Если вместо этого я устанавливаю идентификатор компонента с использованием произвольной жестко запрограммированной строки (например, «C5d682a6f»), то проверка выполняется так, как ожидалось. Вот код:JSF Validators не работают, если Random или SecureRandom используются для генерации идентификатора компонента.
import org.apache.commons.lang3.RandomStringUtils;
import java.util.UUID;
import javax.faces.component.html.HtmlInputText;
import javax.faces.component.html.HtmlMessage;
import javax.faces.component.html.HtmlPanelGrid;
@Model
public class LoginBean
{
private HtmlPanelGrid panelGrid;
private String email;
@PostConstruct void initialize()
{
FacesContext facesContext = FacesContext.getCurrentInstance();
String componentId;
componentId = "C" + UUID.randomUUID().toString().substring(0, 8); // Yields something like "C5d682a6f", which should be fine, yet breaks validation.
//componentId = RandomStringUtils.randomAlphabetic(8); // Yields something like "zxYBcUYM", which should be fine, yet breaks validation.
//componentId = "C5d682a6f"; // Hard-coding the same exact kind of string generated by UUID#randomUUID() works fine.
//componentId = "zxYBcUYM"; // Hard-coding the same exact kind of string generated by RandomStringUtils#randomAlphabetic(int)
HtmlInputText emailFieldComponent = (HtmlInputText)facesContext.getApplication().createComponent(
facesContext,
HtmlInputText.COMPONENT_TYPE,
"javax.faces.Text"
);
emailFieldComponent.setId(componentId);
emailFieldComponent.setValueExpression(
"value",
facesContext.getApplication().getExpressionFactory().createValueExpression(
facesContext.getELContext(),
"#{loginBean.email}",
String.class
)
);
// The following validators stop working if UUID#randomUUID() or
// RandomStringUtils#randomAlphabetic(int) are used to generate componentId.
emailFieldComponent.setRequired(true);
emailFieldComponent.addValidator(new EmailValidator());
HtmlMessage message = (HtmlMessage)facesContext.getApplication().createComponent(
facesContext,
HtmlMessage.COMPONENT_TYPE,
"javax.faces.Message"
);
message.setFor(componentId);
panelGrid = (HtmlPanelGrid)facesContext.getApplication().createComponent(
facesContext,
HtmlPanelGrid.COMPONENT_TYPE,
"javax.faces.Grid"
);
panelGrid.setColumns(2);
panelGrid.getChildren().add(emailFieldComponent);
panelGrid.getChildren().add(message);
}
}
Любые идеи о том, почему это так? Я просто нужно ComponentID, чтобы быть произвольная строка генерируется во время выполнения и в соответствии со следующими конвенциями (от UIComponent#setId(String)
JavaDoc):
компонентов идентификаторы должны соблюдать следующие ограничения синтаксиса:
Must not be a zero-length String. First character must be a letter or an underscore ('_'). Subsequent characters must be a letter, a digit, an underscore ('_'), or a dash ('-').
компонентов идентификаторы должны также соблюдать следующие семантические ограничения (заметим, что это ограничение не является обязательным для реализации SETID()):
The specified identifier must be unique among all the components (including facets) that are descendents of the nearest ancestor UIComponent that is a NamingContainer, or within the scope of the entire component tree if there is no such ancestor that is a NamingContainer.
Моя среда разработки Mojarra 2.2.6-jbossorg-4 на Wildfly 8.1.0.Final.
EDIT:
Таким образом, кажется, что любая попытка создать идентификатор компонента во время выполнения вызывает проверка не произойдет.
componentId = "C" + Long.toHexString(Double.doubleToLongBits(Math.random()));
componentId = "C" + Long.toHexString(System.currentTimeMillis());
componentId = "C" + Long.toHexString(new Date().getTime());
componentId = "C" + new Date().hashCode();
Принимая во внимание, что идентификатор компонента известен во время компиляции, проверка достоверности происходит просто отлично.
componentId = "C" + Long.toHexString(Double.doubleToLongBits(Double.MAX_VALUE));
Я бы очень хотел понять, почему это так.
EDIT # 2:
следующие работы просто отлично (спасибо, BalusC), ComponentID генерируется во время выполнения, что это именно то, что мне нужно:
setId(facesContext.getViewRoot().createUniqueId());
Я последовал совету BalusC в и посмотрел на UIViewRoot#createUniqueId()
, который выглядит, как это под капотом:
public String createUniqueId() {
return createUniqueId(getFacesContext(), null);
}
public String createUniqueId(FacesContext context, String seed) {
if (seed != null) {
return UIViewRoot.UNIQUE_ID_PREFIX + seed;
} else {
Integer i = (Integer) getStateHelper().get(PropertyKeys.lastId);
int lastId = ((i != null) ? i : 0);
getStateHelper().put(PropertyKeys.lastId, ++lastId);
return UIViewRoot.UNIQUE_ID_PREFIX + lastId;
}
}
Но я запутался потому, что он что указанный выше метод сохраняет новый идентификатор клиента в состоянии просмотра JSF. Он только увеличивает lastId и обновляет lastId в состоянии просмотра.
Похоже, что 'UINamingContainer # createUniqueId()' получает lastId из 'StateHelper', добавляет его в j_id и увеличивает lastId помощника состояния. Я обновил свой вопрос в запросе для разъяснения (см. Редактировать № 2). –
Идентификатор компонента/клиента также не должен сохраняться в состоянии просмотра. Это время, основанное на построении времени, а не на основе времени выполнения (например, теги-манипуляторы и т. Д.). – BalusC