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  
22  package org.opencastproject.assetmanager.aws.persistence;
23  
24  import static org.opencastproject.db.Queries.namedQuery;
25  
26  import org.opencastproject.assetmanager.api.storage.StoragePath;
27  
28  import org.apache.commons.lang3.tuple.Pair;
29  
30  import java.util.Date;
31  import java.util.List;
32  import java.util.Optional;
33  import java.util.function.Consumer;
34  import java.util.function.Function;
35  
36  import javax.persistence.Column;
37  import javax.persistence.Entity;
38  import javax.persistence.EntityManager;
39  import javax.persistence.GeneratedValue;
40  import javax.persistence.GenerationType;
41  import javax.persistence.Id;
42  import javax.persistence.Index;
43  import javax.persistence.NamedQueries;
44  import javax.persistence.NamedQuery;
45  import javax.persistence.Table;
46  import javax.persistence.Temporal;
47  import javax.persistence.TemporalType;
48  import javax.persistence.TypedQuery;
49  import javax.persistence.UniqueConstraint;
50  import javax.xml.bind.annotation.XmlAttribute;
51  
52  @Entity(name = "AwsAssetMapping")
53  @Table(
54      name = "oc_aws_asset_mapping",
55      indexes = {
56          @Index(
57              name = "IX_oc_aws_asset_mapping_object_key",
58              columnList = ("object_key")
59          )
60      },
61      uniqueConstraints = @UniqueConstraint(
62          name = "UNQ_aws_archive_mapping_0",
63          columnNames = {"organization", "mediapackage", "mediapackage_element", "version"}
64      )
65  )
66  @NamedQueries({
67      // These exclude deleted mappings!
68      @NamedQuery(
69          name = "AwsAssetMapping.findActiveMapping",
70          query = "SELECT m FROM AwsAssetMapping m WHERE m.organizationId = :organizationId AND "
71              + "m.mediaPackageId = :mediaPackageId AND m.mediaPackageElementId = :mediaPackageElementId AND "
72              + "m.version = :version AND m.deletionDate IS NULL"
73      ),
74      @NamedQuery(
75          name = "AwsAssetMapping.findAllActiveByObjectKey",
76          query = "SELECT m FROM AwsAssetMapping m WHERE m.objectKey = :objectKey AND m.deletionDate IS NULL"
77      ),
78      @NamedQuery(
79          name = "AwsAssetMapping.findAllActiveByMediaPackage",
80          query = "SELECT m FROM AwsAssetMapping m WHERE m.organizationId = :organizationId AND "
81              + "m.mediaPackageId = :mediaPackageId  AND m.deletionDate IS NULL"
82      ),
83      @NamedQuery(
84          name = "AwsAssetMapping.findAllActiveByMediaPackageAndVersion",
85          query = "SELECT m FROM AwsAssetMapping m WHERE m.organizationId = :organizationId AND "
86              + "m.mediaPackageId = :mediaPackageId AND m.version = :version AND m.deletionDate IS NULL"
87      ),
88      // This is to be used when restoring/re-ingesting mps so includes deleted mapings!
89      @NamedQuery(
90          name = "AwsAssetMapping.findAllByMediaPackage",
91          query = "SELECT m FROM AwsAssetMapping m WHERE m.mediaPackageId = :mediaPackageId ORDER BY m.version DESC"
92      )
93  })
94  public class AwsAssetMappingDto {
95  
96    @Id
97    @GeneratedValue(strategy = GenerationType.AUTO)
98    @Column(name = "id", length = 128)
99    @XmlAttribute
100   private long id;
101 
102   @Column(name = "organization", nullable = false, length = 128)
103   private String organizationId;
104 
105   @Column(name = "mediapackage", nullable = false, length = 128)
106   private String mediaPackageId;
107 
108   @Column(name = "mediapackage_element", nullable = false, length = 128)
109   private String mediaPackageElementId;
110 
111   @Column(name = "version", nullable = false)
112   private Long version;
113 
114   // This is the AWS object key
115   @Column(name = "object_key", nullable = false, length = 1024)
116   private String objectKey;
117 
118   // This is the AWS object version
119   @Column(name = "object_version", nullable = false, length = 1024)
120   private String objectVersion;
121 
122   @Column(name = "deletion_date")
123   @Temporal(TemporalType.TIMESTAMP)
124   private Date deletionDate;
125 
126   public AwsAssetMappingDto() {
127   }
128 
129   /** Constructor with all fields. */
130   public AwsAssetMappingDto(String organizationId, String mediaPackageId, String elementId, Long version,
131           String objectKey, String objectVersion) {
132     this.organizationId = organizationId;
133     this.mediaPackageId = mediaPackageId;
134     this.mediaPackageElementId = elementId;
135     this.version = version;
136     this.objectKey = objectKey;
137     this.objectVersion = objectVersion;
138   }
139 
140   /** Convert into business object. */
141   public AwsAssetMapping toAWSArchiveMapping() {
142     return new AwsAssetMapping(organizationId, mediaPackageId, mediaPackageElementId, version, objectKey,
143             objectVersion, deletionDate);
144   }
145 
146   public static Function<EntityManager, AwsAssetMappingDto> storeMappingQuery(StoragePath path, String objectKey,
147       String objectVersion) {
148     return em -> {
149       AwsAssetMappingDto mapDto = new AwsAssetMappingDto(
150           path.getOrganizationId(),
151           path.getMediaPackageId(),
152           path.getMediaPackageElementId(),
153           Long.valueOf(path.getVersion().toString()),
154           objectKey,
155           objectVersion
156       );
157 
158       Optional<AwsAssetMappingDto> existing = findMappingQuery(path).apply(em);
159 
160       // If we've already seen this at some point but deleted it, just undelete it
161       if (existing.isPresent() && objectKey.equals(existing.get().objectKey)
162           && objectVersion.equals(existing.get().objectVersion)) {
163         existing.get().setDeletionDate(null);
164         return existing.get();
165       } else {
166         em.persist(mapDto);
167         return mapDto;
168       }
169     };
170   }
171 
172   /** Find a mapping by its storage path. Returns null if not found. */
173   public static Function<EntityManager, Optional<AwsAssetMappingDto>> findMappingQuery(final StoragePath path) {
174     return namedQuery.findOpt(
175         "AwsAssetMapping.findActiveMapping",
176         AwsAssetMappingDto.class,
177         Pair.of("organizationId", path.getOrganizationId()),
178         Pair.of("mediaPackageId", path.getMediaPackageId()),
179         Pair.of("mediaPackageElementId", path.getMediaPackageElementId()),
180         Pair.of("version", Long.valueOf(path.getVersion().toString()))
181     );
182   }
183 
184   /** Find all assets that link to the AWS S3 object passed. */
185   public static Function<EntityManager, List<AwsAssetMappingDto>> findMappingsByKeyQuery(final String objectKey) {
186     return namedQuery.findAll(
187         "AwsAssetMapping.findAllActiveByObjectKey",
188         AwsAssetMappingDto.class,
189         Pair.of("objectKey", objectKey)
190     );
191   }
192 
193   /** Find all assets that belong to a media package and version (optional). */
194   public static Function<EntityManager, List<AwsAssetMappingDto>> findMappingsByMediaPackageAndVersionQuery(
195       final StoragePath path) {
196     return em -> {
197       // Find a specific versions?
198       TypedQuery<AwsAssetMappingDto> query;
199       if (path.getVersion() != null) {
200         query = em.createNamedQuery("AwsAssetMapping.findAllActiveByMediaPackageAndVersion",
201             AwsAssetMappingDto.class);
202         query.setParameter("version", Long.valueOf(path.getVersion().toString()));
203       } else {
204         query = em.createNamedQuery("AwsAssetMapping.findAllActiveByMediaPackage", AwsAssetMappingDto.class);
205       }
206 
207       query.setParameter("organizationId", path.getOrganizationId());
208       query.setParameter("mediaPackageId", path.getMediaPackageId());
209 
210       return query.getResultList();
211     };
212   }
213 
214   /**
215    * Marks mapping as deleted.
216    */
217   public static Consumer<EntityManager> deleteMapppingQuery(StoragePath path) {
218     return em -> {
219       Optional<AwsAssetMappingDto> mapDto = findMappingQuery(path).apply(em);
220       if (mapDto.isEmpty()) {
221         return;
222       }
223       mapDto.get().setDeletionDate(new Date());
224       em.merge(mapDto.get());
225     };
226   }
227 
228   /** Find all mappings that belong to a media package id. Also returns deleted mappings! */
229   public static Function<EntityManager, List<AwsAssetMappingDto>> findMappingsByMediaPackageQuery(final String mpId) {
230     return namedQuery.findAll(
231         "AwsAssetMapping.findAllByMediaPackage",
232         AwsAssetMappingDto.class,
233         Pair.of("mediaPackageId", mpId)
234     );
235   }
236 
237   public void setDeletionDate(Date deletionDate) {
238     this.deletionDate = deletionDate;
239   }
240 }