Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Table of Contents
maxLevel2

 


...

Custom Media Items and the Is Used in Widget
Anchor
custom_media_items_and_the_is_used_in_widget
custom_media_items_and_the_is_used_in_widget

...

Credentials Service Provider

...


Panel
borderColor#0081C0
titleColor#0081C0

The following applies to XperienCentral versions 10.19 and higher.

...

The implementation of the CustomCalendarEditor looks like this:

 


Code Block
themeEclipse
public class CustomCalendarEditor extends PropertyEditorSupport {
	private final DateFormat myDateFormat;
	private final static Logger LOG =
Logger.getLogger(CustomCalendarEditor.class.getName());

	public CustomCalendarEditor(DateFormat dateFormat) {
		myDateFormat = dateFormat;
	}
	public void setAsText(String text) {
		if (text == null) {
			setValue(null);
		} else {
			try {
				myDateFormat.parse(text);
				setValue(myDateFormat.getCalendar());
			} catch (ParseException ex) {
				LOG.log(Level.WARNING, "Could not parse the date '" + text + "'" + "\nThe exception\n: " + ex);
			}
		}
	}

	public String getAsText() {
		Calendar value = (Calendar) getValue();
		if (value == null) {
			return "";
		} else {
			return myDateFormat.format(value.getTime());
		}
	}
}

...

Now that the custom Property Editor is implemented, it has to be registered with Spring MVC. This is done in the initBinder method; this method is placed in the controller of your component (for example, CustomElementController.java). The registration looks like this:

 


Code Block
themeEclipse
@Override
public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
super.initBinder(request, binder);
if (myValidator == null) {
		myValidator = new CustomCalendarEditor(
new SimpleDateFormat("dd/MM/yyyy");
addValidator(myValidator);
}
}

...

Code in the edit-JSP (e.g. editCustomElement.jspf):

 


Code Block
themeEclipse
 <fmt:message key="helloworldelement.inputfieldLabel.birthdate" />:

...

Code Block
themeEclipse
<wmedit:datePicker path="birthdate"/>

 


Code in the FormBacking object (for example,  CustomElementFBO.java):

 


Code Block
themeEclipse
private Calendar myBirthdate;

public Calendar getBirthdate() {
	return myBirthdate;
}

public void setBirthdate(Calendar bdate) {
	myBirthdate = bdate;
}

...


The result as seen in XperienCentral: 


 

 



Back to Top 


...

Validators

Validators can be used to validate user input and to generate client side error messages when invalid input is provided by a user. Validators prevent users from entering invalid data. Validators are Java classes that implement the org.springframework.validation.Validator interface. This interface provides two methods: 


Code Block
themeEclipse
boolean supports(Class clazz);
void validate(Object target, Errors errors);

 


The supports method indicates which classes the validators can handle. The validate method performs the actual validation. The Sprint MVC framework provides API methods that make it easy to perform this validation, for example org.springframework.validation.ValidationUtils and org.springframework.validation.Errors.

...

The code snippets below provide an example of using a text validator for a custom element that rejects any empty text value or value that equals “not empty”:

 


Code Block
themeEclipse
public void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
	if (myValidator == null) {
		myValidator = new TextValidator();
		addValidator(myValidator);
	}
}

 


The validator class:

 


Code Block
themeEclipse
public class TextValidator implements Validator {

	public void validate(Object target, Errors errors) {
		CustomTextElementFBO element = (CustomTextElementFBO) target;
		ValidationUtils.rejectIfEmpty(errors, "text", "maynotbeempty");
		if (element.getText() != null 
			&& element.getText().equals("not empty")) {
			errors.rejectValue("text", "maynotbenotempty");
		}
	}

	public boolean supports(Class clazz) {
		if (CustomElementFBO.class.isAssignableFrom(clazz)) {
			return true;
		}
		else {
			return false;
		}
	}
}

 


In messages_en_US.properties: 


Code Block
themeEclipse
maynotbeempty=Text may not be empty
maynotbenotempty=Text may not be “not empty”

 


As a result the message “Text may not be empty” will be displayed if the user input was invalid: 


 


Back to Top 


...

HTTP Client


The java.net package part of the Java 1.6 API provides a basic set of classes which can be used to handle HTTP GET and POST requests. However, in some cases a more powerful HTTP client API is needed. The commons HTTP client (org.apache.commons.httpclient.HttpClient) may be a better alternative for those cases.

To incorporate the commons HTTPp client jar files into a plugin, modify the pom.xml file in order to define a dependency with the commons HTTPclient artifact. For example, define the following dependency:

 


Code Block
themeEclipse
<dependency>
	<groupId>commons-httpclient</groupId>
	<artifactId>commons-httpclient</artifactId>
	<version>3.0</version>
</dependency>

 


Without further changes however, the plugin will throw a runtime error upon the invocation of the HTTP client because of a conflict in commons logging:

...

In order to resolve this issue, an additional dependency with the commons-logging artifact must be defined with a scope provided so that at runtime the commons-logging classes exported by the framework will be used instead:

 


Code Block
themeEclipse
<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.0.4</version>
	<scope>provided</scope>
</dependency>

 


XperienCentral offers standard HTTP proxy configuration settings. These settings are configured using the XperienCentral Setup Tool (/web/setup). The settings are made available through the default system networking properties. For complete information about these properties, go to the URL http://docs.oracle.com/javase/8/. The example below shows how you can apply these settings in XperienCentral in combination with HTTP clients version 2 and 3.

...


Code Block
themeEclipse
private static void configureHttpClient2Proxy(HttpClient httpClient, HttpMethod method) {
	String proxyHost = System.getProperty("http.proxyHost");
	if (proxyHost == null || "".equals(proxyHost)) {
		return;
	}
	try {
		org.apache.commons.httpclient.URI apacheUri = method.getURI();
		java.net.URI javaUri = null;
		if (apacheUri.isAbsoluteURI()==false) {
			javaUri = new 
			java.net.URI(httpClient.getHostConfiguration().getHostURL());
	} else {
		javaUri = new java.net.URI(apacheUri.toString());
	}
	// ProxySelector
	List<Proxy> selectedProxy = 
	ProxySelector.getDefault().select(javaUri);
	if (selectedProxy.size()==0 || 
	selectedProxy.get(0).type()==Proxy.Type.DIRECT) {
		// No proxy needed.
		return;
	}
	HostConfiguration hc = httpClient.getHostConfiguration();
	hc.setHost(javaUri.getHost(), javaUri.getPort(), 
	Protocol.getProtocol(javaUri.getScheme()));
	Proxy proxy = selectedProxy.get(0);
	InetSocketAddress proxyAddress = (InetSocketAddress) proxy.address();
	hc.setProxy(proxyAddress.getHostName(),proxyAddress.getPort());
	if (System.getProperty("http.proxyUserName") != null) {
		httpClient.getState().setProxyCredentials(null, null,
			new UsernamePasswordCredentials(System.getProperty("http.proxyUserName"), System.getProperty("http.proxyPassword")));
		httpClient.getState().setAuthenticationPreemptive(true);
	}


  } catch (URISyntaxException ex) {
		LOG.log(Level.WARNING, null, ex);
  } catch (URIException ex) {
		lOG.log(Level.WARNING, null, ex);
  }
}

 


Back to Top 


...

 

Anchor
adding_custom_indicators_to_the_performance_dashboard
adding_custom_indicators_to_the_performance_dashboard

...

  • SystemHealthIndicator
  • SystemHealthIndicator.ValueStatus

...


Note
  • The indicators are called “System health” indicators instead of “Performance indicators”. The reason for this is that although the current focus is on implementing performance indicators, XperienCentral intends to support more generic system health indicators in the future.
  • For complete information on the methods in these classes, see the XperienCentral API Javadoc. For complete information about plugin extensibility, see Extensibility.

...


The following sample adds a category named “My Category” containing an indicator named “test custom PI” to the “Custom System Performance Indicators” tab.

 


Code Block
themeEclipse
public class CustomServiceImpl extends SimpleServiceComponent implements SystemHealthIndicatorExtensionPoint {
	// Private logger for this class
	private static final Logger LOG = Logger.getLogger(CustomServiceImpl.class.getName());
	public SystemHealthIndicator[] getPerformanceIndicators() {
	return new SystemHealthIndicator[] {new CustomPI()};
	}

	class CustomPI implements SystemHealthIndicator{
		public String getId() {return "testPI";};
		public String getCategory() {return "my category";}
		public String getType() {return TYPE_CUSTOM;};
		public String getName(Language language) {return "test custom PI";}
		public Object getValue() {return myCache.getCacheRatio;}
		public ValueStatus getValueState() {return myValueStatus;}
		public String getMessage(Language language) {return "test custom PI";}
		public void reset() {}
	}
}

 


Back to Top

 


...

Creating an Extension for the User Profiles Component

...

As a result, the following folder structure is created containing the source code for the customprofile sample plugin:

 


 


UserManagement Interface Methods

In order to create a custom extension for the user profiles functionality, you must use the following XperienCentral API methods to implement the Profile Extension Provider interface. The following code is taken from the file ProfileExtensionProvider.java: 


Code Block
themeEclipse
/**
	* Call back method that will be invoked by the framework when the
	* given {@link User} is created.
	* @param user the {@link User} instance that just has been created
	*/
	void onCreate(User user);

	/**
	* Call back method that will be invoked by the framework when the given {@link User} is deleted.
	* 
	* @param user the {@link User} instance that just has been deleted
	*/
	void onDelete(User user);

	/**
	* Gets the profile for the given user. If the profile part exists, then it is returned, otherwise a default profile part is 
	* returned which can be modified and updated.
	* 
	* @param user the {@link User} to get the profile for
	* @return the user's profile
	* @throws UserManagementException on error
	*/
	T getProfileFor(User user) throws UserManagementException;

	/**
	* Updates the given profile.
	* 
	* @param profile the profile to update
	* @throws UserManagementException on error
	*/
	void update(T profile) throws UserManagementException;

...


The Profile Extension provider contains the following:

...

Code Block
themeEclipse
	/**
	* This method should return a constant list of strings which are the column headers of the values that
	* are exported by this profile extension.
	* 
	* @return the headers of the fields that are present in an export
	*/
	List<String> getExportColumnHeaders();

    /**
	* This method should return a list of values whose size is equal to the size
	* of the list returned by
	* {@link #getExportColumnHeaders()}. Each entry in the returned 
	* list is a list of strings and represents a single profile property. Single-valued profile properties are 
	* to be represented by a list of length
	* 1. Multi-valued profile properties can be represented by a list of appropriate size.
	* 
	* @param user the user whose profile to export
	* @return the values for the exportable fields of the given profile
	* @throws UserManagementException on error
	*/
	List<List<String>> export(User user) throws UserManagementException;
}

 


Add your own methods that implement this interface. In the file src\main\java\nl\gx\product\customprofile\profileprovider\CustomProfileProviderImpl.java, the following code makes the profile provider available to the framework:

 


Code Block
themeEclipse
public final class CustomProfileProviderImpl extends SimpleProfileProviderComponent implements
	CustomProfileProvider {

...

In the file src\main\java\nl\gx\product\customprofile\profileprovider\CustomProfileImpl.java, the following code implements the custom profile and initializes the new fields: 


Code Block
themeEclipse
customprfileimpl - extended profile - declaration:

public final class CustomProfileImpl implements CustomProfile {

	private final User myUser;

		private String myMsnAddress;
		private String myIcqAddress;

		/**
		* @param usr The owner of this profile part
		*/
		public CustomProfileImpl(User usr) {
			myUser = usr;
			String userString = "";
			if (usr != null) {
				userString = "" + usr.getId();
			}
			myMsnAddress = userString + "msn@hotmail.com";
			myIcqAddress = userString + "icq@hotmail.com";

...


The following code sets and retrieves the data in the new custom fields:

 


Code Block
themeEclipse
/**
	* {@inheritDoc}
	*/
	public void setMsnAddress(String newMsnAddress) {
		myMsnAddress = newMsnAddress;
	}

	/**
	* {@inheritDoc}
	*/
	public String getMsnAddress() {
		return myMsnAddress;
	}

	/**
	* {@inheritDoc}
	*/
	public void setIcqAddress(String newIcqAddress) {
		myIcqAddress = newIcqAddress;
	}

	/**
	* {@inheritDoc}
	*/
	public String getIcqAddress() {
	return myIcqAddress;

 


Adding a Custom Tab to the User Profiles Component

The following code shows how to add a custom tab to ‘User Profiles’ component. The code is taken from the file src\main\java\nl\gx\product\customprofile\subtab\CustomSubTab.java:

 


Code Block
themeEclipse
public final Class<?> getDelegatedControllerClass() {
	return CustomSubTabController.class;
	}

	/**
	* {@inheritDoc}
	*/
	public final String getLabelId() {
		return "nl.gx.product.customprofile.subtab.msntitle";
	}

	/**
	* {@inheritDoc}
	* Note: You must implement your own jspf in order for the presentation to be rendered.
	*/
	public final String getEditPresentation() {
		return "customprofilesubtab.jspf";   }

	/**
	* {@inheritDoc}
	*/
	public final void initialize(ComponentController controller, FormBackingObject fbo, HttpServletRequest request, String tabId) {
		fbo.getNestedPath();

		CustomSubTabFBO concreteFBO = (CustomSubTabFBO) fbo;

		// Set the nested path
		concreteFBO.setTabId(tabId);

		// Store the selected user in the form backing object
		ExtensiblePanelTabParentFBO parentFBO;
		try {
			parentFBO = (ExtensiblePanelTabParentFBO) controller.getParentController().formBackingObject(request);
			concreteFBO.setSelectedUser(parentFBO.getSelectedUser());
			concreteFBO.isNewUserMode(parentFBO.isNewUserMode());
		} catch (ServletException e) {
			LOG.log(Level.SEVERE, "Failed to retrieve parent fbo.", e);
		}

		// Store reference to services
		if (controller instanceof ExtensionSubTabController) {
			ExtensionSubTabController extensionSubTabController = ((ExtensionSubTabController) controller);
			extensionSubTabController.setSessionManager(mySessionManager);
			extensionSubTabController.setRealmManager(myRealmManager);
		}
	}

	/**
	* {@inheritDoc}
	*/
	public final int getRank() {
		return MY_RANK;
	}
}

 

 



Note

If you add more than one sub-tab, the order that they appear from left to right is controlled by the custom tab’s rank. The sub-tab with the lowest rank value appears first and the rest follow in order from left to right.

...


For example:

 


Code Block
themeEclipse
public class CustomSubTab extends SimpleServiceComponent implements ExtensionSubTab {
	private static final int MY_RANK = 10;

...


The following code, taken from the file src\main\java\nl\gx\product\customprofile\viewextension\impl\CustomSubTabFBO.java, shows how to extend the panel tab’s form backing object to include the new field:

...


Code Block
themeEclipse
package nl.gx.product.customprofile.subtab;

import nl.gx.product.wmpuserprofiles.api.ExtensionSubTabFBO;

/**
* The FBO for the customprofile sub tab.
*/
public class CustomSubTabFBO extends ExtensionSubTabFBO {

	private String myMsnAddress;

	/**
	* Returns the msn address.
	* 
	* @return the msn address
	*/
	public String getMsnAddress() {
		return myMsnAddress;
	}

	/**
	* Sets the msn address.
	* 
	* @param msnAddress the msn address
	*/
	public void setMsnAddress(String msnAddress) {
		this.myMsnAddress = msnAddress;
	}
}

...


The following code, taken from the file src\main\resources\editpresentation\customprofilesubtab.jspf, implements the rendering of the new field:

 


Code Block
themeEclipse
<%@ page language="java" session="false" buffer="none" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/taglib/spring" %>
<%@ taglib prefix="wmedit" uri="http://www.gx.nl/taglib/wmedit"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>

<fieldset>

	<div class="wm_style">
		<table class="widgetgrid">
			<tr>
				<td class="label_left"><fmt:message key="nl.gx.product.customprofile.subtab.msnaddress"/>:</td>
				<td class="datefield"><wmedit:input path="msnAddress" size="25" /></td>
			</tr>
		</table>
	</div>
</fieldset>

 


Adding a Custom Field to the User Details Tab

Custom data fields can be added to the [User Details] tab in order to extend the detailed information you want to store for each user. The following sample code, taken from the file src\main\java\nl\gx\product\customprofile\profileprovider\impl\CustomProfileImpl.java, shows how to add a new field, "ICQ Address" to the [User Details] tab. 

 


Note

The same custom profile is used for the custom tab extension and extending the [User Details] tab.

 

 



Code Block
themeEclipse
/**
* A custom profile extension which adds a single text field to the profile.
*/
public final class CustomProfileImpl implements CustomProfile {

	private final User myUser;
	private String myMsnAddress;
	private String myIcqAddress;
	/**
	* @param usr the owner of this profile part
	*/
	public CustomProfileImpl(User usr) {
		myUser = usr;
		String userString = "";
		if (usr != null) {
			userString = "" + usr.getId();
		}
		myMsnAddress = userString + "msn@hotmail.com";
		myIcqAddress = userString + "icq@hotmail.com";
	}

/**
	* {@inheritDoc}
	*/
	public String getIcqAddress() {
		return myIcqAddress;
	}
/**
	* {@inheritDoc}
	*/
	@Override
	public String toString() {
		return "CustomProfile[User = " + myUser + ", MSN address = " + myMsnAddress + ", ICQ address = " + myIcqAddress + "]";
    }
}

...


The following code, taken from the file src\main\java\nl\gx\product\customprofile\viewextension\impl\CustomFBO.java, shows how to extend the panel tab’s form backing object to include the new field:

...


Code Block
themeEclipse
/**
* Class customprofileFBO.
* 
* Form backing object of the customprofileController
*/
public class CustomFBO extends PanelTabFBO implements FormBackingObject {

	private String myIcqAddress;

	/**
	* {@inheritDoc}
	*/
	public final String getUUID() {
	    return WCBConstants.USER_PROFILES_PROFILEDETAILS_EXTENSION_PATH;
	}

	/**
	* Returns the icq address.
	* 
	* @return the icq address
	*/
	public String getIcqAddress() {
		return myIcqAddress;
	}

	/**
	* Set the icq address.
	* 
	* @param icqAddress the icq address
	*/
	public void setMsnAddress(String icqAddress) {
		this.myIcqAddress = icqAddress;
	}
}

 


The following code, taken from the file src\main\resources\editpresentation\customprofileview.jspf, implements the rendering of the new field:

 


Code Block
themeEclipse
%@ page language="java" session="false" buffer="none" %>
<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/taglib/spring" %>
<%@ taglib prefix="wmedit" uri="http://www.gx.nl/taglib/wmedit" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>

<tr>
	<td class="label_left"><fmt:message key="nl.gx.product.customprofile.profileview.icqaddress"/>:</td>
	<td class="datefield"><wmedit:input path="icqAddress" size="20" /></td>
</tr>

...


Exporting Data from Custom Fields

In order for XperienCentral to be able to export data from custom fields, you must declare the new headers that need to be retrieved during the export. For example: 


Code Block
themeEclipse
public List<String> getExportColumnHeaders() {
	return Collections.unmodifiableList(Arrays.asList(new String[]{"msn address", "icq address"}));
    }

 


The following code passes the values retrieved from the custom fields to the export function:

 


Code Block
themeEclipse
public List<List<String>> export(User user) throws UserManagementException {
	return Collections.unmodifiableList(Arrays.asList((List<String>[]) new List[]{
Collections.singletonList(getProfileFor(user).getMsnAddress()), Collections.singletonList(getProfileFor(user).getIcqAddress())

 


This implementation comes from the CustomProfileProviderImpl interface. The methods are part of the ProfileExtensionProvider interface.

 


Back to Top

 


...

Creating Workflow Action Constraints

...

Developers can create their own workflow action constraints by implementing and registering the WorkflowActionConstraint interface as a service. The interface is implemented as follows: 


Code Block
themeEclipse
public interface WorkflowActionConstraint {

	/**
	 * Returns the domain prefixes for which the workflow action constraint is scoped to. Domain prefixes must
     * correspond with the available concrete implementations of the {@link ContentDomain} interface, though
	 * limited to those whose's default domain type is an instance of {@link WorkflowEnabled}.
	 * <p>
	 * Allowed standard domain prefixes:
	 *
	 <ul>
	 * <li>pageversion</li>
	 * <li>pagesectionversion</li>
	 * <li>articleversion</li>
	 * <li>downloadversion</li>
	 * <li>flashversion</li>
	 * <li>imageversion</li>
	 * <li>multimediaversion</li>
	 * </ul>
	 * Note that the domain prefix <code>mediapageversion</code> which corresponds to the content domain
	 * representing {@link MediaItemPageVersion} is NOT allowed. A media item page version is a media item
     * which references a page version, and in context of its workflow it should be seen as a page version.
	 * 
	 * @return Array of domain prefixes. An empty array represents that the constraint is designed for all
	 *  known workflow enabled content domains. Must not return <code>null</code>.
	 */
	String[] getDomainPrefixes();
	/**
	 * Returns the per locale title of the workflow action constraint.
	 * 
	 * @return The title of the workflow action constraint.
	 */
	Map<Locale, String> getTitleMessages();

	/**
	 * Returns the identifier of the workflow action constraint. This must be unique among other workflow action
	 * constraints available in the system. If not it will be denied (as logged) and therefore unavailable.
	 * 
	 * @return The identifier of the workflow action constraint.
	 */
	String getIdentifier();

	/**
	 * Validates the workflow action constraint.
	 * 
	 * @param webEnabled A workflowEnabled object that is the subject of the workflow action.
	 * @return A {@link WorkflowActionResult} that contains the validation  status (true/false) and info
	 * messages.
	 */

WorkflowActionConstraintResult validate(WorkflowEnabled workflowEnabled);
}

 


The pagemetadata and mediaitem archetypes provide an example workflow action constraint. This constraint prevents a content item from being published if it has no title.

 


Code Block
themeEclipse
public class TitleWorkflowActionConstraint extends SimpleServiceComponent implements WorkflowActionConstraint {

	private static final Logger LOG = Logger.getLogger(TitleWorkflowActionConstraint.class.getName());

	private static final String TITLEKEY = "pagemetadata.constraint.title";

	private static final Map<Locale, String> TITLE_MESSAGES = new HashMap<Locale, String>();

	private static final Map<Locale, String> VALIDATION_MESSAGES = new HashMap<Locale, String>();

	static {
		Locale localeNL = new Locale("nl", "NL"); 
		ResourceBundle bundle_nl = ResourceBundle.getBundle("messages/messages", new Locale("nl", "NL"));
		ResourceBundle bundle_en = ResourceBundle.getBundle("messages/messages", Locale.US);

		TITLE_MESSAGES.put(localeNL, bundle_nl.getString(TITLEKEY));
		TITLE_MESSAGES.put(Locale.US, bundle_en.getString(TITLEKEY));

		VALIDATION_MESSAGES.put(localeNL, bundle_nl.getString(TITLEKEY));
		VALIDATION_MESSAGES.put(Locale.US, bundle_en.getString(TITLEKEY));
	}

	@Override
	public Map<Locale, String> getTitleMessages() {
		return TITLE_MESSAGES;
	}

@Override
	public String getIdentifier() {
		return WCBConstants.BUNDLE_ID + ".titleWorkflowActionConstraint";
	}

	@Override
	public WorkflowActionConstraintResult validate(final WorkflowEnabled workflowEnabled) {
		final boolean isValid;

		// Validate
		final String title;
		if (workflowEnabled instanceof PageVersion) {
			title = ((PageVersion) workflowEnabled).getTitle();
		} else if (workflowEnabled instanceof MediaItemVersion) {
			title = ((MediaItemVersion) workflowEnabled).getTitle();
		} else {
			title = null;
		}

		isValid = !"".equals(title);
		return new WorkflowActionConstraintResult() {

			@Override
				public String getIdentifier() {
					return TitleWorkflowActionConstraint.this.getIdentifier();
			}

			@Override
			public Map<Locale, String> getMessages() {
				if (isValid) {
					return null;
				} else {
					return VALIDATION_MESSAGES;
				}
			}

			@Override
			public boolean isValid() {
				return isValid;
			}
		}
	}

 

 



Back to Top