View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  package org.opencastproject.assetmanager.impl.persistence;
22  
23  import static org.opencastproject.util.data.functions.Functions.chuck;
24  
25  import org.opencastproject.assetmanager.api.Availability;
26  import org.opencastproject.assetmanager.api.Snapshot;
27  import org.opencastproject.assetmanager.impl.SnapshotImpl;
28  import org.opencastproject.assetmanager.impl.VersionImpl;
29  import org.opencastproject.mediapackage.MediaPackage;
30  import org.opencastproject.mediapackage.MediaPackageElement;
31  import org.opencastproject.mediapackage.MediaPackageParser;
32  
33  import org.eclipse.persistence.annotations.CascadeOnDelete;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  import java.util.Arrays;
38  import java.util.Date;
39  import java.util.Set;
40  import java.util.function.Function;
41  
42  import javax.persistence.CascadeType;
43  import javax.persistence.Column;
44  import javax.persistence.Entity;
45  import javax.persistence.EntityManager;
46  import javax.persistence.FetchType;
47  import javax.persistence.GeneratedValue;
48  import javax.persistence.GenerationType;
49  import javax.persistence.Id;
50  import javax.persistence.Index;
51  import javax.persistence.Lob;
52  import javax.persistence.NamedQueries;
53  import javax.persistence.NamedQuery;
54  import javax.persistence.OneToMany;
55  import javax.persistence.Table;
56  import javax.persistence.TableGenerator;
57  import javax.persistence.Temporal;
58  import javax.persistence.TemporalType;
59  import javax.persistence.TypedQuery;
60  import javax.persistence.UniqueConstraint;
61  
62  /** JPA DTO. */
63  @Entity(name = "Snapshot")
64  @Table(name = "oc_assets_snapshot", indexes = {
65      @Index(name = "IX_oc_assets_snapshot_archival_date", columnList = ("archival_date")),
66      @Index(name = "IX_oc_assets_snapshot_mediapackage_id", columnList = ("mediapackage_id")),
67      @Index(name = "IX_oc_assets_snapshot_organization_id", columnList = ("organization_id")),
68      @Index(name = "IX_oc_assets_snapshot_owner", columnList = ("owner")),
69      @Index(name = "IX_oc_assets_snapshot_series", columnList = ("series_id, version"))
70      }, uniqueConstraints = {
71      @UniqueConstraint(columnNames = {"mediapackage_id", "version"}) })
72  @NamedQueries({
73          @NamedQuery(name = "Snapshot.countOrgEvents", query = "select count(distinct s.mediaPackageId) from Snapshot s "
74                  + "where s.organizationId = :organizationId"),
75          @NamedQuery(name = "Snapshot.countEvents", query = "select count(distinct s.mediaPackageId) from Snapshot s"),
76          @NamedQuery(name = "Snapshot.countByMediaPackage", query = "select count(s) from Snapshot s "
77                  + "where s.mediaPackageId = :mediaPackageId"),
78          @NamedQuery(name = "Snapshot.countByMediaPackageAndOrg", query = "select count(s) from Snapshot s "
79                  + "where s.mediaPackageId = :mediaPackageId and s.organizationId = :organizationId")})
80  // Maintain own generator to support database migrations from Archive to AssetManager
81  // The generator's initial value has to be set after the data migration.
82  // Otherwise duplicate key errors will most likely happen.
83  @TableGenerator(name = "seq_oc_assets_snapshot", initialValue = 0, allocationSize = 50)
84  public class SnapshotDto {
85    private static final Logger logger = LoggerFactory.getLogger(SnapshotDto.class);
86  
87    @Id
88    @Column(name = "id")
89    @GeneratedValue(strategy = GenerationType.TABLE, generator = "seq_oc_assets_snapshot")
90    private Long id;
91  
92    @Column(name = "mediapackage_id", length = 128, nullable = false)
93    private String mediaPackageId;
94  
95    @Column(name = "version", nullable = false)
96    private Long version;
97  
98    @Column(name = "series_id", length = 128)
99    private String seriesId;
100 
101   @Column(name = "organization_id", length = 128, nullable = false)
102   private String organizationId;
103 
104   @Column(name = "archival_date", nullable = false)
105   @Temporal(TemporalType.TIMESTAMP)
106   private Date archivalDate;
107 
108   @Column(name = "availability", nullable = false, length = 32)
109   private String availability;
110 
111   @Column(name = "storage_id", nullable = false, length = 256)
112   private String storageId;
113 
114   @Column(name = "owner", nullable = false, length = 256)
115   private String owner;
116 
117   @Lob
118   @Column(name = "mediapackage_xml", length = 65535, nullable = false)
119   private String mediaPackageXml;
120 
121   @CascadeOnDelete
122   @OneToMany(targetEntity = AssetDto.class, fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "snapshot")
123   private Set<AssetDto> assets;
124 
125   public static SnapshotDto mk(
126           MediaPackage mediaPackage,
127           VersionImpl version,
128           String organization,
129           Date archivalDate,
130           Availability availability,
131           String storageId,
132           String owner) {
133     try {
134       final SnapshotDto dto = new SnapshotDto();
135       dto.mediaPackageId = mediaPackage.getIdentifier().toString();
136       dto.version = version.value();
137       dto.seriesId = mediaPackage.getSeries();
138       dto.organizationId = organization;
139       dto.archivalDate = archivalDate;
140       dto.mediaPackageXml = MediaPackageParser.getAsXml(mediaPackage);
141       dto.availability = availability.name();
142       dto.storageId = storageId;
143       dto.owner = owner;
144       return dto;
145     } catch (Exception e) {
146       return chuck(e);
147     }
148   }
149 
150   public static SnapshotDto mk(Snapshot snapshot) {
151     try {
152       return mk(snapshot.getMediaPackage(),
153               VersionImpl.mk(Long.parseLong(snapshot.getVersion().toString())),
154               snapshot.getOrganizationId(),
155               snapshot.getArchivalDate(),
156               snapshot.getAvailability(),
157               snapshot.getStorageId(),
158               snapshot.getOwner());
159     } catch (Exception e) {
160       return chuck(e);
161     }
162   }
163 
164   public Long getId() {
165     return Database.insidePersistenceContextCheck(id);
166   }
167 
168   public VersionImpl getVersion() {
169     return Conversions.toVersion(version);
170   }
171 
172   public String getMediaPackageId() {
173     return mediaPackageId;
174   }
175 
176   public String getStorageId() {
177     return storageId;
178   }
179 
180   void setAvailability(Availability a) {
181     this.availability = a.name();
182   }
183 
184   void setStorageId(String id) {
185     this.storageId = id;
186   }
187 
188   public boolean addAsset(AssetDto asset) {
189     return this.assets.add(asset);
190   }
191 
192   public boolean removeAsset(AssetDto asset) {
193     return this.assets.remove(asset);
194   }
195 
196   public Snapshot toSnapshot() {
197     MediaPackage mediaPackage = Conversions.toMediaPackage(mediaPackageXml);
198     // ensure elements are tagged `archive`
199     for (MediaPackageElement element: mediaPackage.getElements()) {
200       if (!Arrays.asList(element.getTags()).contains("archive")) {
201         logger.debug("Adding additional tag `archive` to element {} retrieved from asset manager", element);
202         element.addTag("archive");
203       }
204     }
205     return new SnapshotImpl(
206             id,
207             Conversions.toVersion(version),
208             organizationId,
209             archivalDate,
210             Availability.valueOf(availability),
211             storageId,
212             owner,
213             mediaPackage);
214   }
215 
216   /**
217    * Check if any snapshot with the given media package exists.
218    *
219    * @param mediaPackageId
220    *          The media package identifier to check for
221    * @return If a snapshot exists for the given media package
222    */
223   public static Function<EntityManager, Boolean> existsQuery(final String mediaPackageId) {
224     return existsQuery(mediaPackageId, null);
225   }
226 
227   /**
228    * Check if any snapshot with the given media package exists.
229    *
230    * @param mediaPackageId
231    *          The media package identifier to check for
232    * @param organization
233    *          An organization to limit the check for
234    * @return If a snapshot exists for the given media package
235    */
236   public static Function<EntityManager, Boolean> existsQuery(final String mediaPackageId, final String organization) {
237     return em -> {
238       TypedQuery<Long> query;
239       if (organization == null) {
240         query = em.createNamedQuery("Snapshot.countByMediaPackage", Long.class)
241             .setParameter("mediaPackageId", mediaPackageId);
242       } else {
243         query = em.createNamedQuery("Snapshot.countByMediaPackageAndOrg", Long.class)
244             .setParameter("mediaPackageId", mediaPackageId)
245             .setParameter("organizationId", organization);
246       }
247       logger.debug("Executing query {}", query);
248       return query.getSingleResult() > 0;
249     };
250   }
251 
252   /**
253    * Count events with snapshots in the asset manager
254    *
255    * @param organization
256    *          An organization to count in
257    * @return Number of events
258    */
259   public static Function<EntityManager, Long> countEventsQuery(final String organization) {
260     return em -> {
261       TypedQuery<Long> query;
262       if (null != organization) {
263         query = em.createNamedQuery("Snapshot.countOrgEvents", Long.class)
264             .setParameter("organizationId", organization);
265       } else {
266         query = em.createNamedQuery("Snapshot.countEvents", Long.class);
267       }
268       logger.debug("Executing query {}", query);
269       return query.getSingleResult();
270     };
271   }
272 }