View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2024 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.handler.mediasource.ngdm.impl;
21  
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  import org.apache.commons.lang3.StringUtils;
26  import org.apache.commons.lang3.Strings;
27  import org.apache.sling.api.resource.Resource;
28  import org.apache.sling.api.resource.ResourceResolver;
29  import org.jetbrains.annotations.NotNull;
30  import org.jetbrains.annotations.Nullable;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  import com.day.cq.dam.api.Asset;
35  
36  /**
37   * Parses and validates Dynamic Media with OpenAPI asset references.
38   *
39   * <p>
40   * Example: <code>/urn:aaid:aem:12345678-abcd-abcd-abcd-abcd12345678/my-image.jpg</code>
41   * </p>
42   */
43  public final class NextGenDynamicMediaReference {
44  
45    private static final Pattern REFERENCE_PATTERN = Pattern.compile("^/(urn:[^/]+)/([^/]+)$");
46    private static final String ASSET_ID_PREFIX = "urn:";
47  
48    private final String assetId;
49    private final String fileName;
50    private final Asset asset;
51  
52    private static final Logger log = LoggerFactory.getLogger(NextGenDynamicMediaReference.class);
53  
54    /**
55     * @param assetId Asset ID (has to start with "urn:")
56     * @param fileName File name
57     */
58    public NextGenDynamicMediaReference(@NotNull String assetId, @NotNull String fileName) {
59      this(assetId, fileName, null);
60    }
61  
62    /**
63     * @param assetId Asset ID (has to start with "urn:")
64     * @param fileName File name
65     */
66    public NextGenDynamicMediaReference(@NotNull String assetId, @NotNull String fileName, @Nullable Asset asset) {
67      if (!Strings.CS.startsWith(assetId, ASSET_ID_PREFIX)) {
68        throw new IllegalArgumentException("Asset ID must start with '" + ASSET_ID_PREFIX + "'");
69      }
70      this.assetId = assetId;
71      this.fileName = fileName;
72      this.asset = asset;
73    }
74  
75    /**
76     * @return Asset ID
77     */
78    public @NotNull String getAssetId() {
79      return assetId;
80    }
81  
82    /**
83     * @return File name
84     */
85    public @NotNull String getFileName() {
86      return fileName;
87    }
88  
89    /**
90     * @return Asset (if reference points to local asset)
91     */
92    public @Nullable Asset getAsset() {
93      return asset;
94    }
95  
96    /**
97     * @return True if reference points to local asset.
98     */
99    public boolean isLocal() {
100     return asset != null;
101   }
102 
103   /**
104    * @return Reference
105    */
106   public @NotNull String toReference() {
107     return "/" + assetId + "/" + fileName;
108   }
109 
110   /**
111    * Parses a next generation dynamic media reference.
112    * @param reference Reference
113    * @return Parsed reference or null if reference is invalid
114    */
115   public static @Nullable NextGenDynamicMediaReference fromReference(@Nullable String reference) {
116     if (reference == null) {
117       return null;
118     }
119     Matcher matcher = REFERENCE_PATTERN.matcher(reference);
120     if (!matcher.matches()) {
121       return null;
122     }
123     String assetId = matcher.group(1);
124     String fileName = matcher.group(2);
125     return new NextGenDynamicMediaReference(assetId, fileName);
126   }
127 
128   /**
129    * Parses a next generation dynamic media reference.
130    * @param reference Reference
131    * @return Parsed reference or null if reference is invalid
132    */
133   public static @Nullable NextGenDynamicMediaReference fromDamAssetReference(@Nullable String reference, @NotNull ResourceResolver resourceResolver) {
134     if (reference == null) {
135       return null;
136     }
137     Resource resource = resourceResolver.getResource(reference);
138     if (resource == null) {
139       return null;
140     }
141     Asset asset = resource.adaptTo(Asset.class);
142     if (asset == null) {
143       return null;
144     }
145     String uuid = asset.getID();
146     if (StringUtils.isBlank(uuid)) {
147       log.trace("Ignoring DAM asset without UUID: {}", asset.getPath());
148       return null;
149     }
150     String assetId = "urn:aaid:aem:" + uuid;
151     String fileName = asset.getName();
152     return new NextGenDynamicMediaReference(assetId, fileName, asset);
153   }
154 
155   /**
156    * Checks if given string is a valid next generation dynamic media reference.
157    * @param reference Reference
158    * @return true if reference is valid
159    */
160   public static boolean isReference(@Nullable String reference) {
161     return reference != null && REFERENCE_PATTERN.matcher(reference).matches();
162   }
163 
164   @Override
165   public String toString() {
166     return toReference();
167   }
168 
169 }