1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.contentapp;
35
36 import static com.vaadin.shared.data.sort.SortDirection.ASCENDING;
37 import static info.magnolia.ui.contentapp.JcrDataProviderUtils.streamJcrItems;
38
39 import info.magnolia.jcr.RuntimeRepositoryException;
40 import info.magnolia.ui.contentapp.observation.JcrDataSourceObservation;
41 import info.magnolia.ui.datasource.jcr.JcrDatasourceDefinition;
42 import info.magnolia.ui.datasource.jcr.JcrSessionManager;
43 import info.magnolia.ui.framework.ioc.Destructible;
44
45 import java.util.Optional;
46 import java.util.stream.Collectors;
47 import java.util.stream.Stream;
48
49 import javax.inject.Inject;
50 import javax.jcr.Item;
51 import javax.jcr.Node;
52 import javax.jcr.NodeIterator;
53 import javax.jcr.RepositoryException;
54 import javax.jcr.Session;
55
56 import org.apache.commons.collections4.CollectionUtils;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 import com.vaadin.data.provider.AbstractBackEndDataProvider;
61 import com.vaadin.data.provider.Query;
62 import com.vaadin.data.provider.SortOrder;
63
64
65
66
67 public class JcrDataProvider extends AbstractBackEndDataProvider<Node, DataFilter> implements Destructible {
68 private static final Logger log = LoggerFactory.getLogger(JcrDataProvider.class);
69 private final JcrDatasourceDefinition definition;
70 private final JcrSessionManager contentDecorator;
71 private final JcrDataSourceObservation observation;
72
73 @Inject
74 public JcrDataProvider(JcrDatasourceDefinition definition, JcrDataSourceObservation observation, JcrSessionManager contentDecorator) {
75 this.definition = definition;
76 this.contentDecorator = contentDecorator;
77 this.observation = observation;
78 observation.register(definition, this);
79 }
80
81 @Override
82 public void destroy() {
83 observation.destroy();
84 }
85
86 @Override
87 public boolean isInMemory() {
88 return false;
89 }
90
91 @Override
92 public Stream<Node> fetchFromBackEnd(Query<Node, DataFilter> query) {
93 try {
94 javax.jcr.query.Query q = createJcrQuery(query);
95 q.setLimit(query.getLimit());
96 q.setOffset(query.getOffset());
97 final NodeIterator result = q.execute().getNodes();
98
99 return streamJcrItems(result).map(item -> contentDecorator.wrapItem((Item) item));
100 } catch (RepositoryException e) {
101 throw new RuntimeRepositoryException(e);
102 }
103 }
104
105 @Override
106 public int sizeInBackEnd(Query<Node, DataFilter> query) {
107 return (int) fetchFromBackEnd(query).count();
108 }
109
110 private Session getSession() throws RepositoryException {
111 return contentDecorator.getJCRSession();
112 }
113
114 private javax.jcr.query.Query createJcrQuery(Query<Node, DataFilter> query) throws RepositoryException {
115 final StringBuilder statement = new StringBuilder("select * from [nt:base] as t ");
116
117 createWhereClause().ifPresent(clause -> statement.append(clause));
118 createOrderByClause(query).ifPresent(clause -> statement.append(clause));
119
120 log.debug("JCR query statement is {}", statement);
121
122 return getSession()
123 .getWorkspace()
124 .getQueryManager()
125 .createQuery(statement.toString(), javax.jcr.query.Query.JCR_SQL2);
126 }
127
128 private Optional<String> createWhereClause() {
129 if (CollectionUtils.isEmpty(definition.getAllowedNodeTypes())) {
130 return Optional.empty();
131 }
132
133
134 final String types = definition.getAllowedNodeTypes()
135 .stream()
136 .map(type -> String.format("t.[jcr:primaryType] = '%s'", type))
137 .collect(Collectors.joining(" or "));
138 return Optional.of(String.format(" where (%s)", types));
139 }
140
141 private Optional<String> createOrderByClause(Query<Node, DataFilter> query) {
142 if (!definition.isSortable()) {
143 return Optional.empty();
144 }
145 StringBuilder sortOrder = new StringBuilder();
146
147
148 if (CollectionUtils.isNotEmpty(definition.getSortByProperties())) {
149 sortOrder.append(definition.getSortByProperties()
150 .stream()
151 .map(o -> String.format("t.[%s] asc", o))
152 .collect(Collectors.joining(",")));
153 }
154
155 sortOrder.append(query.getSortOrders()
156 .stream()
157 .map(so -> resolveProperty(so))
158 .collect(Collectors.joining(",")));
159
160 return sortOrder.length() == 0 ? Optional.empty() : Optional.of(String.format(" order by %s", sortOrder.toString()));
161 }
162
163 private String resolveProperty(SortOrder so) {
164 return String.format("%s %s", "jcrName".equals(so.getSorted()) ? "lower(name(t))" :
165 String.format("t.[%s]", so.getSorted()), so.getDirection() == ASCENDING ? "asc" : "desc");
166 }
167 }