Custom String datatype

Hi,

I’m trying to create a custom datatype for a phone number. I’ve been able to get a Long datatype to work but am stumped with String. What I’ve done so far:

  1. Create a datatype class:

package com.examples.datatypes;
import ...
public class PhoneDatatype extends StringDatatype implements Datatype<String> {

        // This field is required for Studio even if you don't use it in code
        public static final String NAME = "string";

        @Nonnull
        @Override
        public String format(Object value) {
        // input is raw numbers, output +1 2345 6789
            return value == null ? "" : ((String)value).replaceFirst("(\\d+)(\\d{4})(\\d{4})", "+$1 $2 $3");
        }

        @Override
        public String parse(String value) {
                // strip out non-numeric chars
                StringBuilder parsed = new StringBuilder();
                for (int i=0;i<value.length();i++) {
                        if (Character.isDigit(value.charAt(i))) {
                                parsed.append(value.charAt(i));
                        }
                }
                return parsed.toString();
        }  
}
  1. Register this in datatypes.xml

<datatype class="com.examples.datatypes.PhoneDatatype"
              javaType="java.lang.String"/>
  1. Tag my Entity column with the custom datatype

    @MetaProperty(datatype = PhoneDatatype.NAME)
    @Column(name = "PHONE")
    protected String phone;

Everything compiles and server starts fine, no errors that I can see in the logs. Values are saved and retrieved but just not being formatted and parsed. Any idea what I’m missing?

Thanks,
Eddy

Hi Eddy,

Maybe your datatype doesn’t work because you have given the existing name “string” to it.

I’ve just updated the sample application here and added your PhoneDatatype. There is a problem with using a String-based datatype in FieldGroup so I had to create a custom field, see CustomerEdit.java.

Consider also using MaskedField for phone numbers - it can be more convenient for users.

Hi Konstantin,

Thanks for the tip on MaskedField, that actually does the trick for me.

On the original question, I had tried naming it uniquely but my app wouldn’t load hence I tried setting it to “string”. I’ve tried it in two different projects (in case I some how messed up my other project) but still doesn’t load. As I mentioned, no problems with other non-string datatypes.

Fyi, the error in the logs are:


23:03:25.928 ERROR c.h.cuba.core.sys.AppContextLoader - Error initializing application
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException: java.lang.IllegalArgumentException: Datatype phone is not found
	at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:431) ~[spring-orm-4.3.3.RELEASE.jar:4.3.3.RELEASE]
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE]
	at com.haulmont.cuba.core.sys.TransactionImpl.<init>(TransactionImpl.java:58) ~[cuba-core-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.PersistenceImpl.createTransaction(PersistenceImpl.java:121) ~[cuba-core-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.security.app.LoginWorkerBean.loginAnonymous(LoginWorkerBean.java:213) ~[cuba-core-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.security.app.LoginWorkerBean.initializeAnonymousSession(LoginWorkerBean.java:511) ~[cuba-core-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.security.app.LoginWorkerBean.applicationStarted(LoginWorkerBean.java:521) ~[cuba-core-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.AppContext.startContext(AppContext.java:234) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.AppContext$Internals.startContext(AppContext.java:289) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.AbstractWebAppContextLoader.contextInitialized(AbstractWebAppContextLoader.java:74) ~[cuba-global-6.4.2.jar:6.4.2]
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4813) [catalina.jar:8.0.35]
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5272) [catalina.jar:8.0.35]
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147) [catalina.jar:8.0.35]
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:725) [catalina.jar:8.0.35]
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:701) [catalina.jar:8.0.35]
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717) [catalina.jar:8.0.35]
	at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1092) [catalina.jar:8.0.35]
	at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1834) [catalina.jar:8.0.35]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_66]
	at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_66]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_66]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_66]
	at java.lang.Thread.run(Thread.java:745) [na:1.8.0_66]
Caused by: javax.persistence.PersistenceException: java.lang.IllegalArgumentException: Datatype phone is not found
	at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:815) ~[eclipselink-2.6.2.cuba12.jar:2.6.2.cuba12]
	at org.eclipse.persistence.internal.jpa.EntityManagerFactoryDelegate.getAbstractSession(EntityManagerFactoryDelegate.java:205) ~[eclipselink-2.6.2.cuba12.jar:2.6.2.cuba12]
	at org.eclipse.persistence.internal.jpa.EntityManagerFactoryDelegate.createEntityManagerImpl(EntityManagerFactoryDelegate.java:305) ~[eclipselink-2.6.2.cuba12.jar:2.6.2.cuba12]
	at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManagerImpl(EntityManagerFactoryImpl.java:337) ~[eclipselink-2.6.2.cuba12.jar:2.6.2.cuba12]
	at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:303) ~[eclipselink-2.6.2.cuba12.jar:2.6.2.cuba12]
	at org.springframework.orm.jpa.JpaTransactionManager.createEntityManagerForTransaction(JpaTransactionManager.java:449) ~[spring-orm-4.3.3.RELEASE.jar:4.3.3.RELEASE]
	at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:369) ~[spring-orm-4.3.3.RELEASE.jar:4.3.3.RELEASE]
	... 22 common frames omitted
Caused by: java.lang.IllegalArgumentException: Datatype phone is not found
	at com.haulmont.chile.core.datatypes.Datatypes.get(Datatypes.java:160) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.MetaModelLoader.getDatatype(MetaModelLoader.java:676) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.MetaModelLoader.loadProperty(MetaModelLoader.java:269) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.MetaModelLoader.initProperties(MetaModelLoader.java:193) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.MetaModelLoader.loadClass(MetaModelLoader.java:120) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.MetaModelLoader.loadModel(MetaModelLoader.java:91) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.MetadataLoader.loadMetadata(MetadataLoader.java:103) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.MetadataImpl.initMetadata(MetadataImpl.java:202) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.MetadataImpl.getSession(MetadataImpl.java:69) ~[cuba-global-6.4.2.jar:6.4.2]
	at com.haulmont.cuba.core.sys.persistence.EclipseLinkSessionEventListener.preLogin(EclipseLinkSessionEventListener.java:66) ~[cuba-core-6.4.2.jar:6.4.2]
	at org.eclipse.persistence.sessions.SessionEventManager.preLogin(SessionEventManager.java:620) ~[eclipselink-2.6.2.cuba12.jar:2.6.2.cuba12]
	at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.preConnectDatasource(DatabaseSessionImpl.java:797) ~[eclipselink-2.6.2.cuba12.jar:2.6.2.cuba12]
	at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.login(DatabaseSessionImpl.java:773) ~[eclipselink-2.6.2.cuba12.jar:2.6.2.cuba12]
	at org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider.login(EntityManagerFactoryProvider.java:267) ~[eclipselink-2.6.2.cuba12.jar:2.6.2.cuba12]
	at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:731) ~[eclipselink-2.6.2.cuba12.jar:2.6.2.cuba12]
	... 28 common frames omitted