users@glassfish.java.net

[gf-users] UUIDs in foreign keys yields postgres error

From: David Wheeler <david_at_inomial.com>
Date: Thu, 29 Jan 2015 13:56:39 +1100

Hi,

I'm hoping you can help me resolve an error I'm getting when trying to persist an entity with a null UUID reference to another entity.

Persisting the SubscriptionIpRange works fine when the subscriptionReservationLink field is non-null, however when the reference is null postgres complains with "org.postgresql.util.PSQLException: ERROR: column "subscriptionreservationlink" is of type uuid but expression is of type character varying". Interestingly, the UUIDConverter is never called to convert the null UUID into db column, so I guess eclipselink is trying to convert it itself because the reference is null but using string "null" or something like that. I'm unable to see exactly what is being set because it's a parameterised query so it just has ?s.

Here's my set up:

Glassfish 4.1
Postgres 9.3, JDBC4 driver

Two entities:

SubscriptionIpRange entity

  @Id
  @javax.persistence.Convert(converter = UUIDConverter.class)
  @Column(name = "subscriptioniprange", columnDefinition = "uuid")
  private UUID subscriptionIpRange = UUID.randomUUID();

  @ManyToOne
  @JoinColumn(name = "uid", nullable = false, referencedColumnName = "uid")
  private Basic basic;
  
  @Convert(converter = PGInetAddressConverter.class)
  @Column(name = "fromaddress", nullable = false)
  private InetAddress fromAddress;
  
  @OneToOne(optional=true)
  @JoinColumn(name="subscriptionreservationlink", columnDefinition = "uuid", nullable=true)
  private SubscriptionReservationLink subscriptionReservationLink;
  
  @Convert(converter = PGInetAddressConverter.class)
  @Column(name = "toaddress", nullable = false)
  private InetAddress toAddress;

SubscriptionReservationLink entity

  @Id
  @Convert(converter = UUIDConverter.class)
  @Column(columnDefinition = "uuid")
  private UUID subscriptionReservationLink = UUID.randomUUID();
  
  @ManyToOne()
  @JoinColumn(name="uid", referencedColumnName="uid", nullable=false)
  private Basic basicBean;
  
  @Convert(converter = UUIDConverter.class)
  @Column(columnDefinition = "uuid")
  private UUID linkId;
  
  @Convert(converter = UUIDConverter.class)
  @Column(columnDefinition = "uuid")
  private UUID reservationId;
  
  @OneToOne(mappedBy="subscriptionReservationLink")
  private SubscriptionIpRange subscriptionIpRange;

Converter:

@Converter(autoApply=true)
public class UUIDConverter implements javax.persistence.AttributeConverter<UUID, Object>
{
  @Override
  public Object convertToDatabaseColumn(UUID attribute)
  {
    PGobject object = new PGobject();
    object.setType("uuid");
    try
    {
        if (attribute == null)
        {
            object.setValue(null);
        }
        else
        {
            object.setValue(attribute.toString());
        }
    }
    catch (SQLException e)
    {
        throw new IllegalArgumentException("Error when creating Postgres uuid", e);
    }
    
    return object;
  }

  @Override
  public UUID convertToEntityAttribute(Object dbData)
  {
    return (UUID) dbData;
  }

}

full error message:

[2015-01-29T10:19:15.552+1100] [glassfish 4.1] [SEVERE] [] [com.sun.xml.ws.server.sei.TieHandler] [tid: _ThreadID=29 _ThreadName=http-listener-1(5)] [timeMillis: 1422487155552] [levelValue: 1000] [[
  
javax.ejb.EJBException
        at com.sun.ejb.containers.EJBContainerTransactionManager.processSystemException(EJBContainerTransactionManager.java:748)
        at com.sun.ejb.containers.EJBContainerTransactionManager.completeNewTx(EJBContainerTransactionManager.java:698)
        at com.sun.ejb.containers.EJBContainerTransactionManager.postInvokeTx(EJBContainerTransactionManager.java:503)
        at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4566)
        at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2074)
        at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2044)
        at com.sun.ejb.containers.EJBObjectInvocationHandler.invoke(EJBObjectInvocationHandler.java:212)
        at com.sun.ejb.containers.EJBObjectInvocationHandlerDelegate.invoke(EJBObjectInvocationHandlerDelegate.java:79)
        at com.sun.proxy.$Proxy393.addIpRange(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:239)
        at com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:150)
        at com.sun.corba.ee.impl.presentation.rmi.codegen.CodegenStubBase.invoke(CodegenStubBase.java:226)
        at com.inomial.smile.api.__Subscription_Remote_DynamicStub.addIpRange(com/inomial/smile/api/__Subscription_Remote_DynamicStub.java)
        at com.inomial.smile.api._Subscription_Wrapper.addIpRange(com/inomial/smile/api/_Subscription_Wrapper.java)
        at com.inomial.smile.ws.v2.subscription.WSSubscription.addIpRange(WSSubscription.java:1122)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.glassfish.webservices.InstanceResolverImpl$1.invoke(InstanceResolverImpl.java:144)
        at com.sun.xml.ws.server.InvokerTube$2.invoke(InvokerTube.java:149)
        at com.sun.xml.ws.server.sei.SEIInvokerTube.processRequest(SEIInvokerTube.java:88)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1136)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:1050)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:1019)
        at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:877)
        at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java:136)
        at org.glassfish.webservices.MonitoringPipe.process(MonitoringPipe.java:142)
        at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:119)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1136)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:1050)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:1019)
        at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:877)
        at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java:136)
        at com.sun.enterprise.security.webservices.CommonServerSecurityPipe$1.run(CommonServerSecurityPipe.java:221)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAsPrivileged(Subject.java:536)
        at com.sun.enterprise.security.webservices.CommonServerSecurityPipe.processRequest(CommonServerSecurityPipe.java:216)
        at com.sun.enterprise.security.webservices.CommonServerSecurityPipe.process(CommonServerSecurityPipe.java:141)
        at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:119)
        at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1136)
        at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:1050)
        at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:1019)
        at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:877)
        at com.sun.xml.ws.server.WSEndpointImpl$2.process(WSEndpointImpl.java:419)
        at com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:868)
        at com.sun.xml.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:422)
        at com.sun.xml.ws.transport.http.servlet.ServletAdapter.handle(ServletAdapter.java:169)
        at org.glassfish.webservices.JAXWSServlet.doPost(JAXWSServlet.java:169)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
        at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:344)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
        at com.inomial.smile.ws.SoapAccessFilter.doFilter(SoapAccessFilter.java:67)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:214)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:316)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
        at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
        at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
        at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
        at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:415)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:282)
        at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:459)
        at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:167)
        at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:201)
        at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:175)
        at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:235)
        at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:284)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:201)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:133)
        at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:112)
        at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
        at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:561)
        at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:112)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:117)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:56)
        at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:137)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:565)
        at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:545)
        at java.lang.Thread.run(Thread.java:745)
Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: column "subscriptionreservationlink" is of type uuid but expression is of type character varying
  Hint: You will need to rewrite or cast the expression.
  Position: 137
Error Code: 0
Call: INSERT INTO subscriptioniprange (subscriptioniprange, fromaddress, toaddress, uid, subscriptionreservationlink) VALUES (?, ?, ?, ?, ?)
        bind => [5 parameters bound]
Query: InsertObjectQuery(SubscriptionIpRange [subscriptionIpRange=dca2b68c-c8e0-4fa6-9a7f-04817168feb0, basic=Basic [uid=1111, service=com.inomial.smile.domainObjects.core.Service_at_40f73552, parent=1110, fqun=Fqun [uid=1111, company=1, serviceType=3, username=test1422428588258_at_example.com], usn=2142425772], fromAddress=/127.1.1.9, toAddress=/127.1.1.10, subscriptionReservationLink=null])
        at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:868)
        at com.sun.enterprise.container.common.impl.EntityManagerWrapper.flush(EntityManagerWrapper.java:437)
        at com.inomial.smile.api.impl.SubscriptionBean.addIpRange(SubscriptionBean.java:2182)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.glassfish.ejb.security.application.EJBSecurityManager$5.run(EJBSecurityManager.java:1159)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.glassfish.ejb.security.application.EJBSecurityManager.doAsPrivileged(EJBSecurityManager.java:1057)
        at org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1164)
        at com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:4786)
        at com.sun.ejb.EjbInvocation.invokeBeanMethod(EjbInvocation.java:656)
        at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
        at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:608)
        at org.jboss.weld.ejb.AbstractEJBRequestScopeActivationInterceptor.aroundInvoke(AbstractEJBRequestScopeActivationInterceptor.java:46)
        at org.jboss.weld.ejb.SessionBeanInterceptor.aroundInvoke(SessionBeanInterceptor.java:52)
        at sun.reflect.GeneratedMethodAccessor126.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor$2.run(InterceptorManager.java:878)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:875)
        at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
        at com.sun.ejb.EjbInvocation.proceed(EjbInvocation.java:608)
        at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.doCall(SystemInterceptorProxy.java:163)
        at com.sun.ejb.containers.interceptors.SystemInterceptorProxy.aroundInvoke(SystemInterceptorProxy.java:140)
        at sun.reflect.GeneratedMethodAccessor61.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor$2.run(InterceptorManager.java:878)
        at java.security.AccessController.doPrivileged(Native Method)
        at com.sun.ejb.containers.interceptors.AroundInvokeInterceptor.intercept(InterceptorManager.java:875)
        at com.sun.ejb.containers.interceptors.AroundInvokeChainImpl.invokeNext(InterceptorManager.java:822)
        at com.sun.ejb.containers.interceptors.InterceptorManager.intercept(InterceptorManager.java:369)
        at com.sun.ejb.containers.BaseContainer.__intercept(BaseContainer.java:4758)
        at com.sun.ejb.containers.BaseContainer.intercept(BaseContainer.java:4746)
        at com.sun.ejb.containers.EJBObjectInvocationHandler.invoke(EJBObjectInvocationHandler.java:205)
        ... 81 more
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: ERROR: column "subscriptionreservationlink" is of type uuid but expression is of type character varying
  Hint: You will need to rewrite or cast the expression.
  Position: 137
Error Code: 0
        at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:340)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.processExceptionForCommError(DatabaseAccessor.java:1611)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:898)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeNoSelect(DatabaseAccessor.java:962)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.basicExecuteCall(DatabaseAccessor.java:631)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeCall(DatabaseAccessor.java:558)
        at org.eclipse.persistence.internal.sessions.AbstractSession.basicExecuteCall(AbstractSession.java:2002)
        at org.eclipse.persistence.sessions.server.ClientSession.executeCall(ClientSession.java:298)
        at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:242)
        at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.executeCall(DatasourceCallQueryMechanism.java:228)
        at org.eclipse.persistence.internal.queries.DatasourceCallQueryMechanism.insertObject(DatasourceCallQueryMechanism.java:377)
        at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:165)
        at org.eclipse.persistence.internal.queries.StatementQueryMechanism.insertObject(StatementQueryMechanism.java:180)
        at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.insertObjectForWrite(DatabaseQueryMechanism.java:489)
        at org.eclipse.persistence.queries.InsertObjectQuery.executeCommit(InsertObjectQuery.java:80)
        at org.eclipse.persistence.queries.InsertObjectQuery.executeCommitWithChangeSet(InsertObjectQuery.java:90)
        at org.eclipse.persistence.internal.queries.DatabaseQueryMechanism.executeWriteWithChangeSet(DatabaseQueryMechanism.java:301)
        at org.eclipse.persistence.queries.WriteObjectQuery.executeDatabaseQuery(WriteObjectQuery.java:58)
        at org.eclipse.persistence.queries.DatabaseQuery.execute(DatabaseQuery.java:899)
        at org.eclipse.persistence.queries.DatabaseQuery.executeInUnitOfWork(DatabaseQuery.java:798)
        at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWorkObjectLevelModifyQuery(ObjectLevelModifyQuery.java:108)
        at org.eclipse.persistence.queries.ObjectLevelModifyQuery.executeInUnitOfWork(ObjectLevelModifyQuery.java:85)
        at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.internalExecuteQuery(UnitOfWorkImpl.java:2896)
        at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1804)
        at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1786)
        at org.eclipse.persistence.internal.sessions.AbstractSession.executeQuery(AbstractSession.java:1737)
        at org.eclipse.persistence.internal.sessions.CommitManager.commitNewObjectsForClassWithChangeSet(CommitManager.java:226)
        at org.eclipse.persistence.internal.sessions.CommitManager.commitAllObjectsWithChangeSet(CommitManager.java:125)
        at org.eclipse.persistence.internal.sessions.AbstractSession.writeAllObjectsWithChangeSet(AbstractSession.java:4207)
        at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabase(UnitOfWorkImpl.java:1441)
        at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithPreBuiltChangeSet(UnitOfWorkImpl.java:1587)
        at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.writeChanges(RepeatableWriteUnitOfWork.java:452)
        at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:863)
        ... 118 more
Caused by: org.postgresql.util.PSQLException: ERROR: column "subscriptionreservationlink" is of type uuid but expression is of type character varying
  Hint: You will need to rewrite or cast the expression.
  Position: 137
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2198)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1927)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:255)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:561)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:419)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeUpdate(AbstractJdbc2Statement.java:365)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.postgresql.ds.jdbc23.AbstractJdbc23PooledConnection$StatementHandler.invoke(AbstractJdbc23PooledConnection.java:453)
        at com.sun.proxy.$Proxy268.executeUpdate(Unknown Source)
        at com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:125)
        at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:890)
        ... 148 more
]]


Any help you could offer would be much appreciated.


Cheers,

-- David




David Wheeler • lead engineer
Inomial Pty Ltd • Automatic Billing
p +61 3 9663 3554