Skip to content

Commit 1402cd6

Browse files
committed
Merge branch 'release/2021-01-rc1'
2 parents 8f21c5c + 58d7c6a commit 1402cd6

147 files changed

Lines changed: 1520 additions & 4504 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/controllers/ElementController.java

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
package controllers;
2323

2424
import com.fasterxml.jackson.databind.JsonNode;
25+
import com.google.common.primitives.Bytes;
2526
import config.MetamodelProvider;
2627
import jackson.JacksonHelper;
2728
import jackson.JsonLdMofObjectAdornment;
@@ -36,10 +37,8 @@
3637
import services.ElementService;
3738

3839
import javax.inject.Inject;
39-
import java.util.List;
40-
import java.util.Optional;
41-
import java.util.Set;
42-
import java.util.UUID;
40+
import java.time.Instant;
41+
import java.util.*;
4342
import java.util.stream.Collectors;
4443

4544
import static jackson.JsonLdMofObjectAdornment.JSONLD_MIME_TYPE;
@@ -83,22 +82,70 @@ public Result create(Http.Request request) {
8382
}
8483

8584
public Result getElementsByProjectIdCommitId(UUID projectId, UUID commitId, Http.Request request) {
86-
Set<Element> elements = elementService.getElementsByProjectIdCommitId(projectId, commitId);
85+
UUID pageAfter = Optional.ofNullable(request.getQueryString("page[after]"))
86+
.map(ElementController::fromCursor)
87+
//.map(UUID::fromString)
88+
.orElse(null);
89+
UUID pageBefore = Optional.ofNullable(request.getQueryString("page[before]"))
90+
.map(ElementController::fromCursor)
91+
//.map(UUID::fromString)
92+
.orElse(null);
93+
int pageSize = Optional.ofNullable(request.getQueryString("page[size]"))
94+
.map(Integer::parseInt)
95+
.orElse(100);
96+
if (pageSize <= 0) {
97+
return Results.badRequest("Page size must be greater than zero.");
98+
}
99+
100+
List<Element> elements = elementService.getElementsByProjectIdCommitId(projectId, commitId, pageAfter, pageBefore, pageSize);
87101
boolean respondWithJsonLd = respondWithJsonLd(request);
102+
final String linkHeaderValue;
103+
if (elements.size() > 0) {
104+
boolean pageFull = elements.size() == pageSize;
105+
boolean hasNext = pageFull || pageBefore != null;
106+
boolean hasPrev = pageFull && pageBefore != null || pageAfter != null;
107+
// hasPrev -> !pageFull || pageAfter != null
108+
StringBuilder linkHeaderValueBuilder = new StringBuilder();
109+
if (hasNext) {
110+
linkHeaderValueBuilder.append(String.format("<http://%s/projects/%s/commits/%s/elements?page[after]=%s&page[size]=%s>; rel=\"next\"",
111+
request.host(),
112+
projectId,
113+
commitId,
114+
toCursor(elements.get(elements.size() - 1).getIdentifier()),
115+
pageSize));
116+
if (hasPrev) {
117+
linkHeaderValueBuilder.append(", ");
118+
}
119+
}
120+
if (hasPrev) {
121+
linkHeaderValueBuilder.append(String.format("<http://%s/projects/%s/commits/%s/elements?page[before]=%s&page[size]=%s>; rel=\"prev\"",
122+
request.host(),
123+
projectId,
124+
commitId,
125+
toCursor(elements.get(0).getIdentifier()),
126+
pageSize));
127+
}
128+
linkHeaderValue = linkHeaderValueBuilder.length() > 0 ? linkHeaderValueBuilder.toString() : null;
129+
}
130+
else {
131+
linkHeaderValue = null;
132+
}
133+
88134
return Optional.of(
89135
elements.stream()
90136
.map(e -> respondWithJsonLd ?
91137
adornMofObject(e, request, metamodelProvider, environment, projectId, commitId) :
92138
e
93139
)
94-
.collect(Collectors.toSet())
140+
.collect(Collectors.toList())
95141
)
96-
.map(set -> JacksonHelper.collectionToTree(set, Set.class, respondWithJsonLd ?
142+
.map(collection -> JacksonHelper.collectionToTree(collection, List.class, respondWithJsonLd ?
97143
JsonLdMofObjectAdornment.class :
98144
metamodelProvider.getImplementationClass(Element.class))
99145
)
100146
.map(Results::ok)
101147
.map(result -> respondWithJsonLd ? result.as(JSONLD_MIME_TYPE) : result)
148+
.map(result -> linkHeaderValue != null ? result.withHeader("Link", linkHeaderValue) : result)
102149
.orElseThrow();
103150
}
104151

@@ -123,7 +170,7 @@ static JsonLdMofObjectAdornment adornMofObject(MofObject mof, Http.Request reque
123170
}
124171

125172
public Result getRootsByProjectIdCommitId(UUID projectId, UUID commitId, Http.Request request) {
126-
Set<Element> roots = elementService.getRootsByProjectIdCommitId(projectId, commitId);
173+
List<Element> roots = elementService.getRootsByProjectIdCommitId(projectId, commitId);
127174
boolean respondWithJsonLd = respondWithJsonLd(request);
128175
return ok(JacksonHelper.collectionToTree(roots.stream()
129176
.map(e -> respondWithJsonLd ? adornMofObject(e, request, metamodelProvider, environment, projectId, commitId) : e)
@@ -135,4 +182,24 @@ public Result getRootsByProjectIdCommitId(UUID projectId, UUID commitId, Http.Re
135182
static boolean respondWithJsonLd(Http.Request request) {
136183
return request.accepts(JSONLD_MIME_TYPE);
137184
}
185+
186+
protected static char CURSOR_SEPARATOR = '|';
187+
188+
protected static UUID fromCursor(String cursor) throws IllegalArgumentException {
189+
byte[] decoded = Base64.getUrlDecoder().decode(cursor);
190+
int separatorIndex = Bytes.indexOf(decoded, (byte) CURSOR_SEPARATOR);
191+
if (separatorIndex < 0) {
192+
throw new IllegalArgumentException("Cursor separator missing");
193+
}
194+
return UUID.fromString(
195+
new String(decoded, separatorIndex + 1, decoded.length - separatorIndex - 1)
196+
);
197+
}
198+
199+
protected static String toCursor(UUID id) throws IllegalArgumentException {
200+
String unencoded = String.valueOf(Instant.now().toEpochMilli()) +
201+
CURSOR_SEPARATOR +
202+
id.toString();
203+
return Base64.getUrlEncoder().withoutPadding().encodeToString(unencoded.getBytes());
204+
}
138205
}

app/controllers/QueryController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public Result getQueryResultsByProjectIdQuery(UUID projectId, @SuppressWarnings(
106106
}
107107

108108
private Result buildResponse(QueryService.QueryResults result, UUID projectId, Http.Request request) {
109-
Set<Element> elements = result.getElements();
109+
List<Element> elements = result.getElements();
110110
AllowedPropertyFilter filter = result.getPropertyFilter();
111111
boolean respondWithJsonLd = ElementController.respondWithJsonLd(request);
112112
JsonNode json = JacksonHelper.collectionToTree(elements.stream()

app/dao/ElementDao.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,17 @@
2525
import org.omg.sysml.metamodel.Element;
2626
import org.omg.sysml.query.Query;
2727

28+
import java.util.List;
2829
import java.util.Optional;
29-
import java.util.Set;
3030
import java.util.UUID;
3131

3232
public interface ElementDao extends Dao<Element> {
3333

34-
Set<Element> findAllByCommit(Commit commit);
34+
List<Element> findAllByCommit(Commit commit, UUID after, UUID before, int maxResults);
3535

3636
Optional<Element> findByCommitAndId(Commit commit, UUID id);
3737

38-
Set<Element> findRootsByCommit(Commit commit);
38+
List<Element> findRootsByCommit(Commit commit);
3939

40-
Set<Element> findByCommitAndQuery(Commit commit, Query query);
40+
List<Element> findByCommitAndQuery(Commit commit, Query query);
4141
}

app/dao/impl/jpa/JpaElementDao.java

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import javax.persistence.EntityManager;
4848
import javax.persistence.EntityTransaction;
4949
import javax.persistence.NoResultException;
50+
import javax.persistence.TypedQuery;
5051
import javax.persistence.criteria.*;
5152
import java.beans.PropertyDescriptor;
5253
import java.util.*;
@@ -101,7 +102,9 @@ public List<Element> findAll() {
101102
CriteriaQuery<MofObjectImpl> query = builder.createQuery(MofObjectImpl.class);
102103
Root<MofObjectImpl> root = query.from(MofObjectImpl.class);
103104
query.select(root).where(getTypeExpression(builder, root));
104-
return em.createQuery(query).getResultStream().map(o -> (Element) o).collect(Collectors.toList());
105+
return em.createQuery(query).getResultStream()
106+
.map(o -> (Element) o)
107+
.collect(Collectors.toList());
105108
});
106109
}
107110

@@ -112,21 +115,50 @@ public void deleteAll() {
112115
CriteriaDelete<MofObjectImpl> query = builder.createCriteriaDelete(MofObjectImpl.class);
113116
Root<MofObjectImpl> root = query.from(MofObjectImpl.class);
114117
query.where(getTypeExpression(builder, root));
115-
return ((Stream<?>) em.createQuery(query).getResultStream()).map(o -> (Element) o).collect(Collectors.toList());
118+
return ((Stream<?>) em.createQuery(query).getResultStream())
119+
.map(o -> (Element) o)
120+
.collect(Collectors.toList());
116121
});
117122
}
118123

119-
@Override
120-
public Set<Element> findAllByCommit(Commit commit) {
124+
public List<Element> findAllByCommit(Commit commit, UUID after, UUID before, int maxResults) {
121125
return jpaManager.transact(em -> {
122126
// 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.
123127
Commit c = em.contains(commit) ? commit : em.find(metamodelProvider.getImplementationClass(Commit.class), commit.getId());
124-
return getCommitIndex(c, em).getWorkingElementVersions().stream()
128+
CommitIndex commitIndex = getCommitIndex(c, em);
129+
130+
CriteriaBuilder builder = em.getCriteriaBuilder();
131+
CriteriaQuery<ElementVersionImpl> query = builder.createQuery(ElementVersionImpl.class);
132+
Root<CommitIndexImpl> commitIndexRoot = query.from(CommitIndexImpl.class);
133+
SetJoin<CommitIndexImpl, ElementVersionImpl> workingElementVersionsJoin = commitIndexRoot.join(CommitIndexImpl_.workingElementVersions);
134+
Join<ElementVersionImpl, ElementIdentityImpl> elementIdentityJoin = workingElementVersionsJoin.join(ElementVersionImpl_.identity);
135+
query.select(workingElementVersionsJoin);
136+
Expression<Boolean> whereExpression = builder.equal(commitIndexRoot.get(CommitIndexImpl_.id), commitIndex.getId());
137+
if (after != null) {
138+
whereExpression = builder.and(whereExpression, builder.greaterThan(elementIdentityJoin.get(ElementIdentityImpl_.id), after));
139+
}
140+
if (before != null) {
141+
whereExpression = builder.and(whereExpression, builder.lessThan(elementIdentityJoin.get(ElementIdentityImpl_.id), before));
142+
}
143+
query.where(whereExpression);
144+
boolean flip = after == null && before != null;
145+
Function<Path<UUID>, Order> orderFunction = flip ? builder::desc : builder::asc;
146+
query.orderBy((orderFunction).apply(elementIdentityJoin.get(ElementIdentityImpl_.id)));
147+
TypedQuery<ElementVersionImpl> typedQuery = em.createQuery(query);
148+
if (maxResults >= 0) {
149+
typedQuery.setMaxResults(maxResults);
150+
}
151+
List<Element> result = typedQuery
152+
.getResultStream()
125153
.map(ElementVersion::getData)
126154
.filter(mof -> mof instanceof Element)
127155
.map(mof -> (Element) mof)
128156
.map(PROXY_RESOLVER)
129-
.collect(Collectors.toSet());
157+
.collect(Collectors.toList());
158+
if (flip) {
159+
Collections.reverse(result);
160+
}
161+
return result;
130162
});
131163
}
132164

@@ -159,7 +191,7 @@ public Optional<Element> findByCommitAndId(Commit commit, UUID id) {
159191
}
160192

161193
@Override
162-
public Set<Element> findRootsByCommit(Commit commit) {
194+
public List<Element> findRootsByCommit(Commit commit) {
163195
return jpaManager.transact(em -> {
164196
// 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.
165197
Commit c = em.contains(commit) ? commit : em.find(metamodelProvider.getImplementationClass(Commit.class), commit.getId());
@@ -169,12 +201,12 @@ public Set<Element> findRootsByCommit(Commit commit) {
169201
.map(mof -> (Element) mof)
170202
.filter(element -> element.getOwner() == null)
171203
.map(PROXY_RESOLVER)
172-
.collect(Collectors.toSet());
204+
.collect(Collectors.toList());
173205
});
174206
}
175207

176208
@Override
177-
public Set<Element> findByCommitAndQuery(Commit commit, Query query) {
209+
public List<Element> findByCommitAndQuery(Commit commit, Query query) {
178210
return jpaManager.transact(em -> {
179211
// 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.
180212
Commit c = em.contains(commit) ? commit : em.find(metamodelProvider.getImplementationClass(Commit.class), commit.getId());
@@ -186,7 +218,7 @@ public Set<Element> findByCommitAndQuery(Commit commit, Query query) {
186218
.map(mof -> (Element) mof)
187219
.filter(constrain(q.getWhere()))
188220
.map(PROXY_RESOLVER)
189-
.collect(Collectors.toSet());
221+
.collect(Collectors.toList());
190222
});
191223
}
192224

app/org/omg/sysml/metamodel/Association.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@
2525
import java.util.List;
2626
import java.util.Set;
2727

28-
public interface Association extends Class, Relationship, MofObject {
28+
public interface Association extends Classifier, Relationship, MofObject {
2929
List<? extends Type> getRelatedType();
3030

31-
Collection<? extends Feature> getAssociationEnd();
32-
3331
Type getSourceType();
3432

3533
Collection<? extends Type> getTargetType();
34+
35+
Collection<? extends Feature> getAssociationEnd();
3636
}

app/org/omg/sysml/metamodel/MetadataReferenceExpression.java renamed to app/org/omg/sysml/metamodel/AssociationStructure.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@
2525
import java.util.List;
2626
import java.util.Set;
2727

28-
public interface MetadataReferenceExpression extends FeatureReferenceExpression, MetadataExpression, MofObject {
28+
public interface AssociationStructure extends Structure, Association, MofObject {
2929

3030
}

app/org/omg/sysml/metamodel/Behavior.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import java.util.List;
2626
import java.util.Set;
2727

28-
public interface Behavior extends Classifier, MofObject {
28+
public interface Behavior extends Class, MofObject {
2929
Collection<? extends Step> getStep();
3030

3131
Collection<? extends Feature> getParameter();

app/org/omg/sysml/metamodel/ConnectionDefinition.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@
2525
import java.util.List;
2626
import java.util.Set;
2727

28-
public interface ConnectionDefinition extends Association, PartDefinition, MofObject {
28+
public interface ConnectionDefinition extends AssociationStructure, PartDefinition, MofObject {
2929
Collection<? extends Usage> getConnectionEnd();
3030
}

app/org/omg/sysml/metamodel/ConnectionUsage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@
2626
import java.util.Set;
2727

2828
public interface ConnectionUsage extends Connector, PartUsage, MofObject {
29-
Collection<? extends Association> getConnectionDefinition();
29+
Collection<? extends AssociationStructure> getConnectionDefinition();
3030
}

app/org/omg/sysml/metamodel/ElementFilter.java

Lines changed: 0 additions & 32 deletions
This file was deleted.

0 commit comments

Comments
 (0)