1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.opencastproject.assetmanager.impl.query;
22
23 import static com.entwinemedia.fn.Stream.$;
24 import static java.lang.String.format;
25
26 import org.opencastproject.assetmanager.api.query.ADeleteQuery;
27 import org.opencastproject.assetmanager.api.query.Predicate;
28 import org.opencastproject.assetmanager.api.storage.AssetStore;
29 import org.opencastproject.assetmanager.api.storage.DeletionSelector;
30 import org.opencastproject.assetmanager.impl.AssetManagerImpl;
31 import org.opencastproject.assetmanager.impl.RuntimeTypes;
32 import org.opencastproject.assetmanager.impl.VersionImpl;
33 import org.opencastproject.assetmanager.impl.persistence.Conversions;
34 import org.opencastproject.assetmanager.impl.persistence.EntityPaths;
35 import org.opencastproject.assetmanager.impl.persistence.QPropertyDto;
36 import org.opencastproject.assetmanager.impl.persistence.QSnapshotDto;
37 import org.opencastproject.util.data.Function;
38
39 import com.entwinemedia.fn.Fn;
40 import com.entwinemedia.fn.data.SetB;
41 import com.mysema.query.Tuple;
42 import com.mysema.query.jpa.JPASubQuery;
43 import com.mysema.query.jpa.impl.JPADeleteClause;
44 import com.mysema.query.jpa.impl.JPAQueryFactory;
45 import com.mysema.query.support.Expressions;
46 import com.mysema.query.types.EntityPath;
47 import com.mysema.query.types.expr.BooleanExpression;
48
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import java.util.Collections;
53 import java.util.List;
54 import java.util.Set;
55
56 public abstract class AbstractADeleteQuery implements ADeleteQuery, DeleteQueryContributor, EntityPaths {
57 private static final Logger logger = LoggerFactory.getLogger(AbstractADeleteQuery.class);
58
59 private AssetManagerImpl am;
60 private String owner;
61
62 public AbstractADeleteQuery(AssetManagerImpl am, String owner) {
63 this.am = am;
64 this.owner = owner;
65 }
66
67 @Override public ADeleteQuery name(final String queryName) {
68 return new AbstractADeleteQuery(am, owner) {
69 @Override public DeleteQueryContribution contributeDelete(String owner) {
70 final DeleteQueryContribution cParent = AbstractADeleteQuery.this.contributeDelete(owner);
71 return DeleteQueryContribution.mk(cParent).name(queryName);
72 }
73 };
74 }
75
76 @Override public ADeleteQuery where(final Predicate predicate) {
77 return new AbstractADeleteQuery(am, owner) {
78 @Override public DeleteQueryContribution contributeDelete(String owner) {
79 final DeleteQueryContribution cParent = AbstractADeleteQuery.this.contributeDelete(owner);
80 final DeleteQueryContribution cPredicate = RuntimeTypes.convert(predicate).contributeDelete(owner);
81 return DeleteQueryContribution.mk()
82 .from(cParent.from.append(cPredicate.from))
83 .targetPredicate(cParent.targetPredicate)
84 .where(JpaFns.allOf(cParent.where, cPredicate.where));
85 }
86
87 @Override public String toString() {
88 return "where " + predicate;
89 }
90 };
91 }
92
93 public long run(DeleteEpisodeHandler deleteEpisodeHandler) {
94
95 final long startTime = System.nanoTime();
96
97 final DeleteQueryContribution c = contributeDelete(owner);
98
99 final DeletionResult deletion = am.getDatabase().run(new Function<JPAQueryFactory, DeletionResult>() {
100 @Override public DeletionResult apply(final JPAQueryFactory jpa) {
101 return runQueries(jpa, c);
102 }
103 });
104 logger.debug("Pure query ms " + (System.nanoTime() - startTime) / 1000000);
105
106 for (Tuple t : deletion.deletedSnapshots) {
107
108 final String orgId = t.get(Q_SNAPSHOT.organizationId);
109 final String mpId = t.get(Q_SNAPSHOT.mediaPackageId);
110 final VersionImpl version = Conversions.toVersion(t.get(Q_SNAPSHOT.version));
111 final DeletionSelector deletionSelector = DeletionSelector.delete(orgId, mpId, version);
112 am.getLocalAssetStore().delete(deletionSelector);
113 for (AssetStore as : am.getRemoteAssetStores()) {
114 as.delete(deletionSelector);
115 }
116 }
117 for (String mpId : deletion.deletedEpisodes) {
118 deleteEpisodeHandler.handleDeletedEpisode(mpId);
119 }
120 final long searchTime = (System.nanoTime() - startTime) / 1000000;
121 logger.debug("Complete query ms " + searchTime);
122 return deletion.deletedItemsCount;
123 }
124
125
126 private DeletionResult runQueries(JPAQueryFactory jpa, DeleteQueryContribution c) {
127
128
129
130 final EntityPath<?> from;
131 {
132 final Set<EntityPath<?>> f = c.from.toSet(SetB.MH);
133 if (f.size() == 1) {
134 from = $(f).head2();
135 } else {
136 throw new RuntimeException("Only one entity is allowed in the from clause");
137 }
138 }
139
140 if (from instanceof QSnapshotDto) {
141
142
143 final BooleanExpression where = Expressions.allOf(
144 c.targetPredicate.orNull(),
145 c.where.apply(Q_SNAPSHOT));
146
147
148
149
150 final List<Tuple> deletedSnapshots = jpa.query()
151 .from(Q_SNAPSHOT)
152 .where(where)
153 .list(Q_SNAPSHOT.organizationId, Q_SNAPSHOT.mediaPackageId, Q_SNAPSHOT.version);
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184 final JPADeleteClause qMain = jpa.delete(Q_SNAPSHOT).where(where);
185 am.getDatabase().logDelete(formatQueryName(c.name, "main"), qMain);
186 final long deletedItems = qMain.execute();
187
188
189
190
191
192 final Set<String> deletedEpisodes;
193 {
194 final List<String> remainingSnapshots = jpa.query()
195 .from(Q_SNAPSHOT)
196 .distinct()
197 .list(Q_SNAPSHOT.mediaPackageId);
198 final Set<String> d = $(deletedSnapshots).map(new Fn<Tuple, String>() {
199 @Override public String apply(Tuple tuple) {
200 return tuple.get(Q_SNAPSHOT.mediaPackageId);
201 }
202 }).toSet(SetB.MH);
203 d.removeAll(remainingSnapshots);
204 deletedEpisodes = Collections.unmodifiableSet(d);
205 }
206
207 return new DeletionResult(deletedItems, deletedSnapshots, deletedEpisodes);
208 } else if (from instanceof QPropertyDto) {
209
210
211 final BooleanExpression where;
212 {
213 final BooleanExpression w = c.where.apply(Q_PROPERTY);
214 if (w != null) {
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234 where = Q_PROPERTY.mediaPackageId.in(
235 new JPASubQuery()
236 .from(Q_PROPERTY)
237 .join(Q_SNAPSHOT)
238
239
240 .where(Q_PROPERTY.mediaPackageId.eq(Q_SNAPSHOT.mediaPackageId).and(w))
241 .distinct()
242 .list(Q_PROPERTY.mediaPackageId));
243 } else {
244 where = null;
245 }
246 }
247 final JPADeleteClause qProperties = jpa.delete(from).where(Expressions.allOf(c.targetPredicate.orNull(), where));
248 am.getDatabase().logDelete(formatQueryName(c.name, "main"), qProperties);
249 final long deletedItems = qProperties.execute();
250 return new DeletionResult(deletedItems, Collections.<Tuple>emptyList(), Collections.<String>emptySet());
251 } else {
252
253 throw new RuntimeException("[Bug]");
254 }
255 }
256
257 @Override public long run() {
258 return run(DELETE_EPISODE_HANDLER);
259 }
260
261 private static String formatQueryName(String name, String subQueryName) {
262 return format("[%s] [%s]", name, subQueryName);
263 }
264
265
266
267
268 public interface DeleteEpisodeHandler {
269
270 void handleDeletedEpisode(String mpId);
271 }
272
273 public static final DeleteEpisodeHandler DELETE_EPISODE_HANDLER = new DeleteEpisodeHandler() {
274
275 @Override public void handleDeletedEpisode(String mpId) {
276 }
277 };
278
279 public final class DeletionResult {
280
281 public final long deletedItemsCount;
282 public final List<Tuple> deletedSnapshots;
283 public final Set<String> deletedEpisodes;
284
285
286 public DeletionResult(
287 long deletedItemsCount, List<Tuple> deletedSnapshots, Set<String> deletedEpisodes) {
288 this.deletedItemsCount = deletedItemsCount;
289 this.deletedSnapshots = deletedSnapshots;
290 this.deletedEpisodes = deletedEpisodes;
291 }
292 }
293 }