Skip to content

Commit 57c89df

Browse files
authored
Merge pull request #16 from ModelDriven/feature/ST5AS-41
ST5AS-41
2 parents 9365a38 + 1e0a319 commit 57c89df

7 files changed

Lines changed: 265 additions & 19 deletions

File tree

app/dao/impl/jpa/JpaCommitDao.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ protected JPAManager getJpaManager() {
4242
return jpa;
4343
}
4444

45-
private JpaElementDao elementDao = new JpaElementDao();
45+
@Inject
46+
private JpaElementDao elementDao;
4647

4748
@Override
4849
public Optional<Commit> persist(Commit commit) {

app/dao/impl/jpa/JpaElementDao.java

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,27 @@
44
import dao.ElementDao;
55
import javabean.JavaBeanHelper;
66
import jpa.manager.JPAManager;
7+
import org.omg.sysml.internal.CommitIndex;
8+
import org.omg.sysml.internal.impl.CommitIndexImpl;
9+
import org.omg.sysml.internal.impl.CommitIndexImpl_;
710
import org.omg.sysml.lifecycle.Commit;
11+
import org.omg.sysml.lifecycle.ElementVersion;
12+
import org.omg.sysml.lifecycle.impl.ElementIdentityImpl;
13+
import org.omg.sysml.lifecycle.impl.ElementIdentityImpl_;
14+
import org.omg.sysml.lifecycle.impl.ElementVersionImpl;
15+
import org.omg.sysml.lifecycle.impl.ElementVersionImpl_;
816
import org.omg.sysml.metamodel.Element;
917
import org.omg.sysml.metamodel.impl.MofObjectImpl;
1018
import org.omg.sysml.metamodel.impl.MofObjectImpl_;
1119

1220
import javax.inject.Inject;
1321
import javax.inject.Singleton;
22+
import javax.persistence.EntityManager;
23+
import javax.persistence.EntityTransaction;
1424
import javax.persistence.NoResultException;
1525
import javax.persistence.criteria.*;
1626
import java.util.*;
27+
import java.util.concurrent.ConcurrentHashMap;
1728
import java.util.function.Consumer;
1829
import java.util.function.Function;
1930
import java.util.stream.Collectors;
@@ -22,7 +33,8 @@
2233
@Singleton
2334
public class JpaElementDao extends JpaDao<Element> implements ElementDao {
2435
// TODO Explore alternative to serializing lazy entity attributes that doesn't involve resolving all proxies one level.
25-
static Consumer<Element> PROXY_RESOLVER = element -> JavaBeanHelper.getBeanPropertyValues(element).values().stream().flatMap(o -> o instanceof Collection ? ((Collection<?>) o).stream() : Stream.of(o)).forEach(o -> {});
36+
static Consumer<Element> PROXY_RESOLVER = element -> JavaBeanHelper.getBeanPropertyValues(element).values().stream().flatMap(o -> o instanceof Collection ? ((Collection<?>) o).stream() : Stream.of(o)).forEach(o -> {
37+
});
2638

2739
@Inject
2840
private MetamodelProvider metamodelProvider;
@@ -80,17 +92,31 @@ public Set<Element> findAllByCommit(Commit commit) {
8092
return jpa.transact(em -> {
8193
// TODO Commit is detached at this point. This ternary mitigates by requerying for the Commit in this transaction. A better solution would be moving transaction handling up to service layer (supported by general wisdom) and optionally migrating to using Play's @Transactional/JPAApi. Pros would include removal of repetitive transaction handling at the DAO layer and ability to interface with multiple DAOs in the same transaction (consistent view). Cons include increased temptation to keep transaction open for longer than needed, e.g. during JSON serialization due to the convenience of @Transactional (deprecated in >= 2.8.x), and the service, a higher level of abstraction, becoming aware of transactions. An alternative would be DAO-to-DAO calls (generally discouraged) and delegating to non-transactional versions of methods.
8294
Commit c = em.contains(commit) ? commit : em.find(metamodelProvider.getImplementationClass(Commit.class), commit.getId());
83-
return streamFlattenedElements(c).peek(PROXY_RESOLVER).collect(Collectors.toSet());
95+
return getCommitIndex(c, em).getWorkingElementVersions().stream().map(ElementVersion::getData).filter(mof -> mof instanceof Element).map(mof -> (Element) mof).collect(Collectors.toSet());
8496
});
8597
}
8698

8799
@Override
88100
public Optional<Element> findByCommitAndId(Commit commit, UUID id) {
89101
return jpa.transact(em -> {
90-
return queryCommitTree(em.contains(commit) ? commit : em.find(metamodelProvider.getImplementationClass(Commit.class), commit.getId()), c ->
91-
c.getChanges().stream().filter(record -> record.getIdentity() != null && record.getIdentity().getId() != null && record.getData() instanceof Element).filter(record -> id.equals(record.getIdentity().getId())).map(record -> (Element) record.getData()).findAny(),
92-
Optional::isPresent)
93-
.values().stream().filter(Optional::isPresent).map(Optional::get).peek(PROXY_RESOLVER).findAny();
102+
// TODO Commit is detached at this point. This ternary mitigates by requerying for the Commit in this transaction. A better solution would be moving transaction handling up to service layer (supported by general wisdom) and optionally migrating to using Play's @Transactional/JPAApi. Pros would include removal of repetitive transaction handling at the DAO layer and ability to interface with multiple DAOs in the same transaction (consistent view). Cons include increased temptation to keep transaction open for longer than needed, e.g. during JSON serialization due to the convenience of @Transactional (deprecated in >= 2.8.x), and the service, a higher level of abstraction, becoming aware of transactions. An alternative would be DAO-to-DAO calls (generally discouraged) and delegating to non-transactional versions of methods.
103+
Commit c = em.contains(commit) ? commit : em.find(metamodelProvider.getImplementationClass(Commit.class), commit.getId());
104+
CommitIndex commitIndex = getCommitIndex(c, em);
105+
106+
CriteriaBuilder builder = em.getCriteriaBuilder();
107+
CriteriaQuery<ElementVersionImpl> query = builder.createQuery(ElementVersionImpl.class);
108+
Root<CommitIndexImpl> commitIndexRoot = query.from(CommitIndexImpl.class);
109+
SetJoin<CommitIndexImpl, ElementVersionImpl> workingElementVersionsJoin = commitIndexRoot.join(CommitIndexImpl_.workingElementVersions);
110+
Join<ElementVersionImpl, ElementIdentityImpl> elementIdentityJoin = workingElementVersionsJoin.join(ElementVersionImpl_.identity);
111+
query.select(workingElementVersionsJoin).where(
112+
builder.equal(commitIndexRoot.get(CommitIndexImpl_.id), commitIndex.getId()),
113+
builder.equal(elementIdentityJoin.get(ElementIdentityImpl_.id), id)
114+
);
115+
try {
116+
return Optional.of(em.createQuery(query).getSingleResult()).map(ElementVersion::getData).filter(mof -> mof instanceof Element).map(mof -> (Element) mof);
117+
} catch (NoResultException e) {
118+
return Optional.empty();
119+
}
94120
});
95121
}
96122

@@ -114,13 +140,37 @@ protected <T> Map<Commit, T> queryCommitTree(Commit commit, Function<Commit, T>
114140
return results;
115141
}
116142

117-
protected Stream<Element> streamFlattenedElements(Commit commit) {
118-
Set<UUID> visitedElements = new HashSet<>();
119-
Map<Commit, Stream<Element>> results = queryCommitTree(commit,
120-
c -> c.getChanges().stream().filter(record -> record.getIdentity() != null && record.getIdentity().getId() != null && record.getData() instanceof Element).filter(record -> !visitedElements.contains(record.getIdentity().getId())).peek(record -> visitedElements.add(record.getIdentity().getId())).map(record -> (Element) record.getData()));
143+
protected Stream<ElementVersion> streamWorkingElementVersions(Commit commit) {
144+
Set<UUID> visitedElements = ConcurrentHashMap.newKeySet();
145+
Map<Commit, Stream<ElementVersion>> results = queryCommitTree(commit,
146+
c -> c.getChanges().stream().filter(record -> record.getIdentity() != null && record.getIdentity().getId() != null && record.getData() != null).filter(record -> !visitedElements.contains(record.getIdentity().getId())).peek(record -> visitedElements.add(record.getIdentity().getId())));
121147
return results.values().stream().flatMap(Function.identity());
122148
}
123149

150+
protected CommitIndex getCommitIndex(Commit commit, EntityManager em) {
151+
CriteriaBuilder builder = em.getCriteriaBuilder();
152+
CriteriaQuery<CommitIndexImpl> query = builder.createQuery(CommitIndexImpl.class);
153+
Root<CommitIndexImpl> root = query.from(CommitIndexImpl.class);
154+
query.select(root).where(builder.equal(root.get(CommitIndexImpl_.id), commit.getId()));
155+
CommitIndex commitIndex = null;
156+
try {
157+
commitIndex = em.createQuery(query).getSingleResult();
158+
} catch (NoResultException ignored) {
159+
}
160+
if (commitIndex != null) {
161+
return commitIndex;
162+
}
163+
164+
commitIndex = new CommitIndexImpl();
165+
commitIndex.setCommit(commit);
166+
commitIndex.setWorkingElementVersions(streamWorkingElementVersions(commit).collect(Collectors.toSet()));
167+
EntityTransaction transaction = em.getTransaction();
168+
transaction.begin();
169+
em.persist(commitIndex);
170+
transaction.commit();
171+
return commitIndex;
172+
}
173+
124174
private Expression<Boolean> getTypeExpression(CriteriaBuilder builder, Root<?> root) {
125175
return builder.or(metamodelProvider.getAllImplementationClasses().stream().filter(Element.class::isAssignableFrom).map(c -> builder.equal(root.type(), c)).toArray(Predicate[]::new));
126176
}

app/dao/impl/jpa/JpaRelationshipDao.java

Lines changed: 107 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
package dao.impl.jpa;
22

33
import config.MetamodelProvider;
4-
import dao.ElementDao;
54
import dao.RelationshipDao;
65
import jpa.manager.JPAManager;
7-
import org.hibernate.Session;
8-
import org.hibernate.SessionFactory;
96
import org.omg.sysml.lifecycle.Commit;
10-
import org.omg.sysml.lifecycle.Project;
7+
import org.omg.sysml.lifecycle.ElementVersion;
118
import org.omg.sysml.metamodel.Element;
129
import org.omg.sysml.metamodel.Relationship;
1310
import org.omg.sysml.metamodel.impl.MofObjectImpl;
@@ -17,7 +14,10 @@
1714
import javax.inject.Singleton;
1815
import javax.persistence.NoResultException;
1916
import javax.persistence.criteria.*;
20-
import java.util.*;
17+
import java.util.List;
18+
import java.util.Optional;
19+
import java.util.Set;
20+
import java.util.UUID;
2121
import java.util.stream.Collectors;
2222
import java.util.stream.Stream;
2323

@@ -79,11 +79,110 @@ public void deleteAll() {
7979
@Override
8080
public Set<Relationship> findAllByCommitRelatedElement(Commit commit, Element relatedElement) {
8181
return jpa.transact(em -> {
82-
// TODO Commit is detached at this point. This ternary mitigates by requerying for the Commit in this transaction. A better solution would be moving transaction handling up to service layer (supported by general wisdom) and optionally migrating to using Play's @Transactional/JPAApi. Pros would include removal of repetitive transaction handling at the DAO layer and ability to interface with multiple DAOs in the same transaction (consistent view). Cons include increased temptation to keep transaction open for longer than needed, e.g. during JSON serialization due to the convenience of @Transactional (deprecated in >= 2.8.x), and the service, a higher level of abstraction, becoming aware of transactions. An alternative would be DAO-to-DAO calls (generally discouraged) and delegating to non-transactional versions of methods.
83-
Commit c = em.contains(commit) ? commit : em.find(metamodelProvider.getImplementationClass(Commit.class), commit.getId());
84-
return elementDao.streamFlattenedElements(c).peek(System.out::println).filter(e -> e instanceof Relationship).map(e -> (Relationship) e)
82+
/* Commit c = em.contains(commit) ? commit : em.find(metamodelProvider.getImplementationClass(Commit.class), commit.getId());
83+
return elementDao.streamFlattenedElements(c).filter(e -> e instanceof Relationship).map(e -> (Relationship) e)
8584
.filter(relationship -> Stream.concat(relationship.getSource().stream(), relationship.getTarget().stream()).map(Element::getIdentifier).anyMatch(id -> id.equals(relatedElement.getIdentifier())))
8685
.collect(Collectors.toSet());
86+
});*/
87+
88+
/*return jpa.transact(em -> {
89+
CommitIndex commitIndex = elementDao.getCommitIndex(commit, em);
90+
91+
CriteriaBuilder builder = em.getCriteriaBuilder();
92+
CriteriaQuery<ElementVersionImpl> query = builder.createQuery(ElementVersionImpl.class);
93+
Root<CommitIndexImpl> commitIndexRoot = query.from(CommitIndexImpl.class);
94+
SetJoin<CommitIndexImpl, ElementVersionImpl> workingElementVersionsJoin = commitIndexRoot.join(CommitIndexImpl_.workingElementVersions);
95+
Join<ElementVersionImpl, MofObjectImpl> dataJoin = workingElementVersionsJoin.join(ElementVersionImpl_.identity);
96+
query.select(workingElementVersionsJoin).where(
97+
builder.equal(commitIndexRoot.get(CommitIndexImpl_.id), commitIndex.getId()),
98+
builder.equal(elementIdentityJoin.get(ElementIdentityImpl_.id), id)
99+
);
100+
try {
101+
return Optional.of(em.createQuery(query).getSingleResult()).map(ElementVersion::getData).filter(mof -> mof instanceof Element).map(mof -> (Element) mof);
102+
} catch (NoResultException e) {
103+
return Optional.empty();
104+
}
105+
});*/
106+
/* Commit c = em.contains(commit) ? commit : em.find(metamodelProvider.getImplementationClass(Commit.class), commit.getId());
107+
CommitIndex commitIndex = elementDao.getCommitIndex(c, em);
108+
109+
// Switching from Criteria API to HQL, because JPA 2 can't handle @Any for ElementVersion#data and no static metamodel is generated.
110+
// https://hibernate.atlassian.net/browse/HHH-6589
111+
Session session = em.unwrap(Session.class);
112+
113+
Query<?> query2 = session.createQuery(
114+
"select relationship, source, target " +
115+
"from RelationshipImpl as relationship " +
116+
"join relationship.source source " +
117+
"join relationship.target target " +
118+
"where relationship.id in (" +
119+
"select elementVersion.data.id " +
120+
"from org.omg.sysml.internal.impl.CommitIndexImpl as commitIndex " +
121+
"join commitIndex.workingElementVersions as elementVersion " +
122+
"where commitIndex.id = :commitIndexId" +
123+
")");
124+
query2.setParameter("commitIndexId", commitIndex.getId());
125+
query2.setParameter("relatedElementIdentifier", relatedElement.getIdentifier());
126+
query2.list().forEach(System.out::println);*/
127+
128+
// Polymorphism ruins the day again. Attributes of @Any and @ManyToAny attributes can't be used in where or join clauses as their type is not known. Mitigating with nested queries.
129+
// Note that nested queries can only be used in select (but not from) and where clauses.
130+
131+
/* Query<Relationship> query = session.createQuery(
132+
"select relationship " +
133+
"from org.omg.sysml.internal.impl.CommitIndexImpl as commitIndex, RelationshipImpl relationship " +
134+
"join commitIndex.workingElementVersions as elementVersion " +
135+
"join elementVersion.data as data " +
136+
"join relationship.source as source " +
137+
"join relationship.target as target " +
138+
"where commitIndex.id = :commitIndexId " +
139+
"and relationship.id = data.id " +
140+
"and relationship.class in :types " +
141+
"and (source.identifier = :relatedElementIdentifier or target.identifier = :relatedElementIdentifier)",
142+
Relationship.class);
143+
Query<Relationship> query = session.createQuery(
144+
"select relationship " +
145+
"from CommitIndexImpl commitIndex, ElementVersionImpl elementVersion, RelationshipImpl relationship " +
146+
"where commitIndex.id = :commitIndexId " +
147+
"and elementVersion. = ",
148+
Relationship.class);
149+
);
150+
query.setParameter("commitIndexId", commitIndex.getId());
151+
query.setParameter("types", getTypeStream().map(Class::getCanonicalName).collect(Collectors.toList()));
152+
query.setParameter("relatedElementIdentifier", relatedElement.getIdentifier());
153+
return new LinkedHashSet<>(query.list());*/
154+
155+
/* Session session = em.unwrap(Session.class);
156+
Query<UUID> uuidQuery = session.createQuery(
157+
"select elementVersion.data.id " +
158+
"from org.omg.sysml.internal.impl.CommitIndexImpl as commitIndex " +
159+
"join commitIndex.workingElementVersions as elementVersion " +
160+
"where commitIndex.id = :commitIndexId",
161+
UUID.class);
162+
uuidQuery.setParameter("commitIndexId", commitIndex.getId());
163+
List<UUID> uuids = uuidQuery.list();
164+
165+
CriteriaBuilder builder = em.getCriteriaBuilder();
166+
CriteriaQuery<RelationshipImpl> query = builder.createQuery(RelationshipImpl.class);
167+
Root<RelationshipImpl> relationshipRoot = query.from(RelationshipImpl.class);
168+
// java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [source] on this ManagedType [org.omg.sysml.metamodel.impl.MofObjectImpl]
169+
// Polymorphism strikes again...
170+
CollectionJoin<RelationshipImpl, Element> sourceJoin = relationshipRoot.joinCollection(RelationshipImpl_.SOURCE, JoinType.LEFT);
171+
CollectionJoin<RelationshipImpl, Element> targetJoin = relationshipRoot.joinCollection(RelationshipImpl_.TARGET, JoinType.LEFT);
172+
173+
query.select(relationshipRoot).where(
174+
relationshipRoot.get(RelationshipImpl_.id).in(uuids),
175+
builder.or(
176+
builder.equal(sourceJoin.get(ElementImpl_.IDENTIFIER), relatedElement.getIdentifier()),
177+
builder.equal(targetJoin.get(ElementImpl_.IDENTIFIER), relatedElement.getIdentifier())
178+
)
179+
);
180+
return new LinkedHashSet<>(em.createQuery(query).getResultList());*/
181+
182+
// Reverting to non-relational streaming
183+
// TODO Commit is detached at this point. This ternary mitigates by requerying for the Commit in this transaction. A better solution would be moving transaction handling up to service layer (supported by general wisdom) and optionally migrating to using Play's @Transactional/JPAApi. Pros would include removal of repetitive transaction handling at the DAO layer and ability to interface with multiple DAOs in the same transaction (consistent view). Cons include increased temptation to keep transaction open for longer than needed, e.g. during JSON serialization due to the convenience of @Transactional (deprecated in >= 2.8.x), and the service, a higher level of abstraction, becoming aware of transactions. An alternative would be DAO-to-DAO calls (generally discouraged) and delegating to non-transactional versions of methods.
184+
Commit c = em.contains(commit) ? commit : em.find(metamodelProvider.getImplementationClass(Commit.class), commit.getId());
185+
return elementDao.getCommitIndex(c, em).getWorkingElementVersions().stream().map(ElementVersion::getData).filter(mof -> mof instanceof Relationship).map(mof -> (Relationship) mof).filter(relationship -> Stream.concat(relationship.getSource().stream(), relationship.getTarget().stream()).map(Element::getIdentifier).anyMatch(id -> id.equals(relatedElement.getIdentifier()))).collect(Collectors.toSet());
87186
});
88187
}
89188

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.omg.sysml.internal;
2+
3+
import org.omg.sysml.lifecycle.Commit;
4+
import org.omg.sysml.lifecycle.ElementVersion;
5+
import org.omg.sysml.lifecycle.Record;
6+
7+
import java.util.Set;
8+
9+
public interface CommitIndex extends Record {
10+
11+
Commit getCommit();
12+
13+
void setCommit(Commit commit);
14+
15+
Set<ElementVersion> getWorkingElementVersions();
16+
17+
void setWorkingElementVersions(Set<ElementVersion> elements);
18+
}

0 commit comments

Comments
 (0)