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.cache.browser.rest.endpoint;
35
36 import info.magnolia.i18nsystem.SimpleTranslator;
37 import info.magnolia.module.cache.Cache;
38 import info.magnolia.module.cache.cachekey.CacheKey;
39 import info.magnolia.module.cache.filter.InMemoryCachedEntry;
40 import info.magnolia.module.cache.inject.CacheFactoryProvider;
41 import info.magnolia.rest.AbstractEndpoint;
42
43 import java.lang.reflect.InvocationTargetException;
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.Comparator;
47 import java.util.List;
48
49 import javax.inject.Inject;
50 import javax.ws.rs.DELETE;
51 import javax.ws.rs.GET;
52 import javax.ws.rs.Path;
53 import javax.ws.rs.PathParam;
54 import javax.ws.rs.Produces;
55 import javax.ws.rs.QueryParam;
56 import javax.ws.rs.core.MediaType;
57 import javax.ws.rs.core.Response;
58
59 import org.apache.commons.beanutils.BeanUtils;
60 import org.apache.commons.lang3.StringUtils;
61 import org.codehaus.jackson.node.JsonNodeFactory;
62 import org.codehaus.jackson.node.ObjectNode;
63
64 import com.cedarsoftware.util.io.JsonIoException;
65 import com.cedarsoftware.util.io.JsonReader;
66 import com.cedarsoftware.util.io.JsonWriter;
67 import com.wordnik.swagger.annotations.Api;
68 import com.wordnik.swagger.annotations.ApiOperation;
69
70
71
72
73 @Api(value = "/cache/v1", description = "The Cache API")
74 @Path("/cache/v1")
75 public class CacheEndpoint extends AbstractEndpoint<CacheEndpointDefinition> {
76
77 public static final String DEFAULT_REST_URL = "/.rest/cache/v1";
78 public static final String PROPERTY_ERROR_MESSAGE = "errorMessage";
79 public static final String PROPERTY_SIZE = "size";
80 public static final String PROPERTY_ORIGINAL_URL = "originalUrl";
81 public static final String PROPERTY_CONTENT_TYPE = "contentType";
82 public static final String PROPERTY_PLAIN_CONTENT = "plainContent";
83 public static final String PROPERTY_UNSUPPORTED_CACHED_ENTRY_TYPE = "unsupportedCachedEntryType";
84 public static final String ASCENDING_ORDER = "ascending";
85 public static final String DESCENDING_ORDER = "descending";
86
87 private final CacheFactoryProvider cacheFactoryProvider;
88 private final SimpleTranslator i18n;
89
90 @Inject
91 public CacheEndpoint(CacheEndpointDefinition endpointDefinition, CacheFactoryProvider cacheFactoryProvider, SimpleTranslator i18n) {
92 super(endpointDefinition);
93 this.cacheFactoryProvider = cacheFactoryProvider;
94 this.i18n = i18n;
95 }
96
97 @GET
98 @Path("/ping")
99 @Produces({MediaType.APPLICATION_JSON})
100 @ApiOperation(value = "Checks if the service is alive")
101 public Response ping() {
102 return Response.ok().build();
103 }
104
105 @GET
106 @Path("/{cacheName}/getAll")
107 @Produces({MediaType.APPLICATION_JSON})
108 @ApiOperation(value = "Get all cache keys", notes = "Returns all cache keys from the specified cache.")
109 public Response getAllKeys(@PathParam("cacheName") String cacheName) {
110 Cache cache = cacheFactoryProvider.get().getCache(cacheName);
111 if (cache == null) {
112 ObjectNode jsonNode = JsonNodeFactory.instance.objectNode();
113 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.getAll.cacheNotFound", cacheName));
114 return Response.status(Response.Status.NOT_FOUND).entity(jsonNode).build();
115 }
116 ArrayList<Object> keys = new ArrayList<>(cache.getKeys());
117 String json;
118 json = JsonWriter.objectToJson(keys);
119 return Response.status(Response.Status.OK).entity(json).build();
120 }
121
122 @GET
123 @Path("/{cacheName}/get")
124 @Produces({MediaType.APPLICATION_JSON})
125 @ApiOperation(value = "Get cache keys based on the offset and page length.", notes = "Returns cache keys from the specified cache.")
126 public Response getKeys(@PathParam("cacheName") String cacheName, @QueryParam("offset") int offset, @QueryParam("pageLength") int pageLength, @QueryParam("sortOrder") String sortOrder, @QueryParam("sortProperty") String sortProperty) {
127 ObjectNode jsonNode = JsonNodeFactory.instance.objectNode();
128 Cache cache = cacheFactoryProvider.get().getCache(cacheName);
129 if (cache == null) {
130 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.get.cacheNotFound", cacheName));
131 return Response.status(Response.Status.NOT_FOUND).entity(jsonNode).build();
132 }
133 if (offset < 0) {
134 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.get.offsetLesserThanZero", cacheName));
135 return Response.status(Response.Status.BAD_REQUEST).entity(jsonNode).build();
136 }
137 if (pageLength < 1) {
138 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.get.pageLengthLesserThanZero", cacheName));
139 return Response.status(Response.Status.BAD_REQUEST).entity(jsonNode).build();
140 }
141 List<Object> keys = new ArrayList<>(cache.getKeys());
142 if (StringUtils.isNotBlank(sortOrder) && StringUtils.isNotBlank(sortProperty)) {
143 sortKeys(keys, sortOrder, sortProperty);
144 }
145 String json;
146 if (keys.size() > offset) {
147 int length = (keys.size() > (offset + pageLength)) ? offset + pageLength : keys.size();
148 keys = new ArrayList<>(keys.subList(offset, length));
149 } else {
150 keys = new ArrayList<>();
151 }
152 json = JsonWriter.objectToJson(keys);
153 return Response.status(Response.Status.OK).entity(json).build();
154 }
155
156 @GET
157 @Path("/{cacheName}/size")
158 @Produces({MediaType.APPLICATION_JSON})
159 @ApiOperation(value = "Get cache size.", notes = "Returns cache size for the specified cache.")
160 public Response getCacheSize(@PathParam("cacheName") String cacheName) {
161 Cache cache = cacheFactoryProvider.get().getCache(cacheName);
162 if (cache == null) {
163 ObjectNode jsonNode = JsonNodeFactory.instance.objectNode();
164 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.size.cacheNotFound", cacheName));
165 return Response.status(Response.Status.NOT_FOUND).entity(jsonNode).build();
166 }
167 int size = cache.getSize();
168 ObjectNode response = JsonNodeFactory.instance.objectNode();
169 response.put(PROPERTY_SIZE, size);
170 return Response.status(Response.Status.OK).entity(response).build();
171 }
172
173 @DELETE
174 @Path("/{cacheName}/delete")
175 @ApiOperation(value = "Delete item from cache.", notes = "Deletes item from the cache based on the passed cache key json.")
176 public Response delete(@PathParam("cacheName") String cacheName, @QueryParam("cacheKey") String json) {
177 ObjectNode jsonNode = JsonNodeFactory.instance.objectNode();
178 if (StringUtils.isBlank(json)) {
179 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.delete.jsonStringIsBlank"));
180 return Response.status(Response.Status.BAD_REQUEST).entity(jsonNode).build();
181 }
182 Cache cache = cacheFactoryProvider.get().getCache(cacheName);
183 CacheKey cacheKey;
184 try {
185 cacheKey = (CacheKey) JsonReader.jsonToJava(json);
186 } catch (JsonIoException e) {
187 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.delete.unableToCreateCacheKeyFromJson", cacheName));
188 return Response.status(Response.Status.NOT_FOUND).entity(jsonNode).build();
189 }
190 if (cache == null) {
191 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.delete.cacheNotFound", cacheName));
192 return Response.status(Response.Status.NOT_FOUND).entity(jsonNode).build();
193 }
194 if (cacheKey == null) {
195 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.delete.cacheKeyNotFound"));
196 return Response.status(Response.Status.NOT_FOUND).entity(jsonNode).build();
197 }
198 cache.remove(cacheKey);
199 return Response.status(Response.Status.OK).build();
200 }
201
202 @GET
203 @Path("/{cacheName}/download")
204 @Produces({MediaType.APPLICATION_JSON})
205 @ApiOperation(value = "Get cache content.", notes = "Returns cache content for the specified cache key.")
206 public Response getCacheContent(@PathParam("cacheName") String cacheName, @QueryParam("cacheKey") String json) {
207 ObjectNode jsonNode = JsonNodeFactory.instance.objectNode();
208 if (StringUtils.isBlank(json)) {
209 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.download.jsonStringIsBlank"));
210 return Response.status(Response.Status.BAD_REQUEST).entity(jsonNode).build();
211 }
212 Cache cache = cacheFactoryProvider.get().getCache(cacheName);
213 CacheKey cacheKey;
214 try {
215 cacheKey = (CacheKey) JsonReader.jsonToJava(json);
216 } catch (JsonIoException e) {
217 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.download.unableToCreateCacheKeyFromJson", cacheName));
218 return Response.status(Response.Status.NOT_FOUND).entity(jsonNode).build();
219 }
220 if (cache == null) {
221 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.download.cacheNotFound", cacheName));
222 return Response.status(Response.Status.NOT_FOUND).entity(jsonNode).build();
223 }
224 if (cacheKey == null) {
225 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.download.cacheKeyNotFound"));
226 return Response.status(Response.Status.NOT_FOUND).entity(jsonNode).build();
227 }
228 Object cachedEntry = cache.getQuiet(cacheKey);
229 if (cachedEntry == null) {
230 jsonNode.put(PROPERTY_ERROR_MESSAGE, i18n.translate("cache.endpoint.download.cachedEntryNotFound"));
231 return Response.status(Response.Status.NOT_FOUND).entity(jsonNode).build();
232 }
233 if (cachedEntry instanceof InMemoryCachedEntry) {
234 InMemoryCachedEntry inMemoryCachedEntry = (InMemoryCachedEntry) cachedEntry;
235 String originalUrl = inMemoryCachedEntry.getOriginalURL();
236 String contentType = inMemoryCachedEntry.getContentType();
237 byte[] plainContent = inMemoryCachedEntry.getPlainContent();
238 jsonNode.put(PROPERTY_ORIGINAL_URL, originalUrl);
239 jsonNode.put(PROPERTY_CONTENT_TYPE, contentType);
240 jsonNode.put(PROPERTY_PLAIN_CONTENT, plainContent);
241 } else {
242 jsonNode.put(PROPERTY_UNSUPPORTED_CACHED_ENTRY_TYPE, i18n.translate("cache.endpoint.download.unsupportedType", cachedEntry.getClass().getCanonicalName(), InMemoryCachedEntry.class.getCanonicalName()));
243 }
244 return Response.status(Response.Status.OK).entity(jsonNode).build();
245 }
246
247 private void sortKeys(List<Object> keys, final String sortOrder, final String sortProperty) {
248 Collections.sort(keys, new Comparator<Object>() {
249 @Override
250 public int compare(Object o1, Object o2) {
251 String prop1;
252 String prop2;
253 try {
254 if (sortProperty.equals("extension")) {
255 prop1 = StringUtils.substringAfterLast(BeanUtils.getProperty(o1, "uri"), ".");
256 prop2 = StringUtils.substringAfterLast(BeanUtils.getProperty(o2, "uri"), ".");
257 } else {
258 prop1 = BeanUtils.getProperty(o1, sortProperty);
259 prop2 = BeanUtils.getProperty(o2, sortProperty);
260 }
261 } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
262 return 0;
263 }
264 if (ASCENDING_ORDER.equals(sortOrder)) {
265 return prop1.compareTo(prop2);
266 } else {
267 return prop2.compareTo(prop1);
268 }
269 }
270 });
271 }
272 }