Mark Youwanes created OPENJPA-2237:
--------------------------------------
Summary: ElementCollection of Embeddable objects is resulting in a
ClassCastException
Key: OPENJPA-2237
URL: https://issues.apache.org/jira/browse/OPENJPA-2237
Project: OpenJPA
Issue Type: Bug
Components: jpa
Affects Versions: 2.2.0
Environment: Latest Spring Data JPA for defining repository (dao) to
retrieve the data. MySQL 5.5 database. Spring MVC.
Reporter: Mark Youwanes
I have a model that is very similar to the following, which taken straight from
the Pro JPA 2 book by Mike Keith (page 108):
package emp;
import java.util.Collection;
import java.util.Set;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
@Entity
public class Employee {
@Id
private int id;
private String name;
private long salary;
@ElementCollection(targetClass = VacationEntry.class, fetch =
FetchType.EAGER)
private Collection VacationBookings;
public Collection getVacationBookings() {
return VacationBookings;
}
}
package com.kryterion.wa.core.model.emp;
import java.util.Calendar;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Embeddable
public class VacationEntry {
@Temporal(TemporalType.DATE)
private Calendar startDate;
@Column(name = "DAYS")
private int daysTaken;
}
CREATE TABLE IF NOT EXISTS`EMPLOYEE`(
`ID` int(11) NOT NULL AUTO_INCREMENT,
`NAME` varchar(255) NOT NULL,
`SALARY` bigint(15),
PRIMARY KEY (`ID`)
);
CREATE TABLE IF NOT EXISTS`EMPLOYEE_VACATIONBOOKINGS`(
`EMPLOYEE_ID` int(11) NOT NULL,
`STARTDATE` DATE,
`DAYS` int(11),
PRIMARY KEY (`EMPLOYEE_ID`,`STARTDATE`,`DAYS`)
);
ALTER TABLE EMPLOYEE_VACATIONBOOKINGS ADD CONSTRAINT
EMPLOYEE_VACATIONBOOKINGS_FKC FOREIGN KEY (EMPLOYEE_ID) REFERENCES EMPLOYEE
(ID);
Simply insert an employee and then some sample vacation entries for that
employee to test it out.
When I retrieve an Employee object, as follows:
//...within my spring controller annotated with @Controller
@Inject
private EmployeeRepository empRepo;
....
Employee emp = empRepo.findOne(1);
I end up with the following exception:
org.springframework.orm.jpa.JpaSystemException: [Ljava.lang.Object; cannot be
cast to org.apache.openjpa.enhance.PersistenceCapable; nested exception is
<openjpa-2.2.0-r422266:1244990 nonfatal general error>
org.apache.openjpa.persistence.PersistenceException: [Ljava.lang.Object; cannot
be cast to org.apache.openjpa.enhance.PersistenceCapable
FailedObject: 1 [org.apache.openjpa.util.IntId] [java.lang.String]
at
org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:326)
~[spring-orm-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:404)
~[spring-orm-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
~[spring-tx-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
~[spring-tx-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
~[spring-tx-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
~[spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:91)
~[spring-data-jpa-1.1.0.RELEASE.jar:na]
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
~[spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
~[spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
~[spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
~[spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at $Proxy387.findOne(Unknown Source) ~[na:na]
at
com.kryterion.wa.importexport.controller.mapper.DataMappingController.setUpForm(DataMappingController.java:109)
~[DataMappingController.class:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
~[na:1.7.0_05]
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
~[na:1.7.0_05]
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
~[na:1.7.0_05]
at java.lang.reflect.Method.invoke(Method.java:601) ~[na:1.7.0_05]
at
org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
~[spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
~[spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
~[spring-webmvc-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
~[spring-webmvc-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
~[spring-webmvc-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
~[spring-webmvc-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
[spring-webmvc-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
[spring-webmvc-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
[spring-webmvc-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
[spring-webmvc-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
[javax.servlet-api.jar:3.0.1]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:770)
[javax.servlet-api.jar:3.0.1]
at
org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:322)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:116)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:182)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:76)
[cas-client-core-3.2.1.jar:3.2.1]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:184)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:155)
[spring-security-web-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at
org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
[spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
[spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
[spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
[spring-web-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:217)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:279)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
[web-core.jar:3.1.2.1-SNAPSHOT]
at
com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317)
[kernel.jar:3.1.2.1-SNAPSHOT]
at
com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
[kernel.jar:3.1.2.1-SNAPSHOT]
at
com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
[grizzly-http.jar:1.9.50]
at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
[grizzly-http.jar:1.9.50]
at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
[grizzly-http.jar:1.9.50]
at
com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
[grizzly-http.jar:1.9.50]
at
com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
[grizzly-framework.jar:1.9.50]
at
com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
[grizzly-framework.jar:1.9.50]
at
com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
[grizzly-framework.jar:1.9.50]
at
com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
[grizzly-http.jar:1.9.50]
at
com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
[grizzly-framework.jar:1.9.50]
at
com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
[grizzly-framework.jar:1.9.50]
at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
[grizzly-framework.jar:1.9.50]
at
com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
[grizzly-utils.jar:1.9.50]
at
com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
[grizzly-utils.jar:1.9.50]
at java.lang.Thread.run(Thread.java:722) [na:1.7.0_05]
Caused by: org.apache.openjpa.persistence.PersistenceException:
[Ljava.lang.Object; cannot be cast to
org.apache.openjpa.enhance.PersistenceCapable
at org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:1017)
~[openjpa-all-2.2.0.jar:2.2.0]
at org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:911)
~[openjpa-all-2.2.0.jar:2.2.0]
at
org.apache.openjpa.kernel.DelegatingBroker.find(DelegatingBroker.java:231)
~[openjpa-all-2.2.0.jar:2.2.0]
at
org.apache.openjpa.persistence.EntityManagerImpl.find(EntityManagerImpl.java:487)
~[openjpa-all-2.2.0.jar:2.2.0]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
~[na:1.7.0_05]
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
~[na:1.7.0_05]
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
~[na:1.7.0_05]
at java.lang.reflect.Method.invoke(Method.java:601) ~[na:1.7.0_05]
at
org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
~[spring-orm-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at $Proxy357.find(Unknown Source) ~[na:na]
at
org.springframework.data.jpa.repository.support.SimpleJpaRepository.findOne(SimpleJpaRepository.java:206)
~[spring-data-jpa-1.1.0.RELEASE.jar:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
~[na:1.7.0_05]
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
~[na:1.7.0_05]
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
~[na:1.7.0_05]
at java.lang.reflect.Method.invoke(Method.java:601) ~[na:1.7.0_05]
at
org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:334)
~[spring-data-commons-core-1.3.0.RELEASE.jar:na]
at
org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:319)
~[spring-data-commons-core-1.3.0.RELEASE.jar:na]
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
~[spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
~[spring-tx-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
~[spring-aop-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at
org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
~[spring-tx-3.1.2.RELEASE.jar:3.1.2.RELEASE]
... 84 common frames omitted
Caused by: java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to
org.apache.openjpa.enhance.PersistenceCapable
at
org.apache.openjpa.jdbc.kernel.JDBCStoreManager.setInverseRelation(JDBCStoreManager.java:444)
~[openjpa-all-2.2.0.jar:2.2.0]
at
org.apache.openjpa.jdbc.kernel.JDBCStoreManager.initializeState(JDBCStoreManager.java:411)
~[openjpa-all-2.2.0.jar:2.2.0]
at
org.apache.openjpa.jdbc.kernel.JDBCStoreManager.initialize(JDBCStoreManager.java:304)
~[openjpa-all-2.2.0.jar:2.2.0]
at
org.apache.openjpa.kernel.DelegatingStoreManager.initialize(DelegatingStoreManager.java:112)
~[openjpa-all-2.2.0.jar:2.2.0]
at
org.apache.openjpa.kernel.ROPStoreManager.initialize(ROPStoreManager.java:57)
~[openjpa-all-2.2.0.jar:2.2.0]
at
org.apache.openjpa.kernel.BrokerImpl.initialize(BrokerImpl.java:1036)
~[openjpa-all-2.2.0.jar:2.2.0]
at org.apache.openjpa.kernel.BrokerImpl.find(BrokerImpl.java:994)
~[openjpa-all-2.2.0.jar:2.2.0]
... 104 common frames omitted
--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators:
https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira