Package: ContentDirectoryClingAdapter
ContentDirectoryClingAdapter
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ContentDirectoryClingAdapter() |
|
|
|
|
|
||||||||||||||||||||
browse(String, BrowseFlag, String, long, long, SortCriterion[]) |
|
|
|
|
|
||||||||||||||||||||
findEntity(ContentDirectoryClingAdapter.BrowseParams) |
|
|
|
|
|
||||||||||||||||||||
lambda$browse$0(ContentDirectoryClingAdapter.BrowseParams) |
|
|
|
|
|
||||||||||||||||||||
lambda$findEntity$1(ContentDirectoryClingAdapter.BrowseParams, Path) |
|
|
|
|
|
||||||||||||||||||||
lambda$log$2(String, String) |
|
|
|
|
|
||||||||||||||||||||
log(String, BrowseResult) |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
Coverage
1: /*
2: * *********************************************************************************************************************
3: *
4: * blueMarine II: Semantic Media Centre
5: * http://tidalwave.it/projects/bluemarine2
6: *
7: * Copyright (C) 2015 - 2021 by Tidalwave s.a.s. (http://tidalwave.it)
8: *
9: * *********************************************************************************************************************
10: *
11: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
12: * the License. You may obtain a copy of the License at
13: *
14: * http://www.apache.org/licenses/LICENSE-2.0
15: *
16: * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
17: * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
18: * specific language governing permissions and limitations under the License.
19: *
20: * *********************************************************************************************************************
21: *
22: * git clone https://bitbucket.org/tidalwave/bluemarine2-src
23: * git clone https://github.com/tidalwave-it/bluemarine2-src
24: *
25: * *********************************************************************************************************************
26: */
27: package it.tidalwave.bluemarine2.upnp.mediaserver.impl;
28:
29: import javax.annotation.Nonnull;
30: import javax.inject.Inject;
31: import java.util.concurrent.atomic.AtomicInteger;
32: import java.util.stream.Stream;
33: import java.nio.file.Path;
34: import org.fourthline.cling.support.model.BrowseFlag;
35: import org.fourthline.cling.support.model.BrowseResult;
36: import org.fourthline.cling.support.model.SortCriterion;
37: import org.fourthline.cling.support.contentdirectory.AbstractContentDirectoryService;
38: import org.fourthline.cling.support.contentdirectory.ContentDirectoryException;
39: import org.fourthline.cling.support.contentdirectory.DIDLParser;
40: import it.tidalwave.bluemarine2.model.spi.CacheManager;
41: import it.tidalwave.bluemarine2.model.spi.CacheManager.Cache;
42: import it.tidalwave.bluemarine2.model.spi.Entity;
43: import it.tidalwave.bluemarine2.mediaserver.ContentDirectory;
44: import it.tidalwave.bluemarine2.upnp.mediaserver.impl.didl.DIDLAdapter.ContentHolder;
45: import lombok.EqualsAndHashCode;
46: import lombok.RequiredArgsConstructor;
47: import lombok.ToString;
48: import lombok.extern.slf4j.Slf4j;
49: import static org.fourthline.cling.support.contentdirectory.ContentDirectoryErrorCode.*;
50: import static it.tidalwave.bluemarine2.util.Formatters.*;
51: import static it.tidalwave.bluemarine2.upnp.mediaserver.impl.UpnpUtilities.*;
52: import static it.tidalwave.bluemarine2.upnp.mediaserver.impl.didl.DIDLAdapter._DIDLAdapter_;
53:
54: /***********************************************************************************************************************
55: *
56: * This class implements a minimal "ContentDirectory" compliant to the UPnP specifications. It acts as an adapter with
57: * respect to a {@link ContentDirectory}, which provides contents.
58: *
59: * @see http://upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf
60: *
61: * @stereotype Adapter
62: *
63: * @author Fabrizio Giudici
64: *
65: **********************************************************************************************************************/
66: @Slf4j
67: public class ContentDirectoryClingAdapter extends AbstractContentDirectoryService
68: {
69: @Inject
70: private ContentDirectory contentDirectory;
71:
72: @Inject
73: private CacheManager cacheManager;
74:
75: private final AtomicInteger updateId = new AtomicInteger(1); // FIXME: increment on database update
76:
77: @RequiredArgsConstructor @EqualsAndHashCode @ToString
78: static class BrowseParams
79: {
80: @Nonnull
81: private final String objectId;
82: @Nonnull
83: private final BrowseFlag browseFlag;
84: private final String filter;
85: private final long firstResult;
86: private final long maxResults;
87: private final SortCriterion[] orderby;
88: }
89:
90: /*******************************************************************************************************************
91: *
92: * Returns information about an object.
93: *
94: * @see http://upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf
95: *
96: * @param objectId the id of the object to browse
97: * @param browseFlag whether metadata or children content are requested
98: * @param filter a filter for returned data
99: * @param firstResult the first index of the items to return
100: * @param maxResults the maximum number of items to return
101: * @param orderby the sort criteria
102: * @return the requested object
103: * @throws ContentDirectoryException in case of error
104: *
105: ******************************************************************************************************************/
106: @Override
107: public BrowseResult browse (@Nonnull final String objectId,
108: @Nonnull final BrowseFlag browseFlag,
109: final String filter,
110: final long firstResult,
111: final long maxResults,
112: final SortCriterion[] orderby)
113: throws ContentDirectoryException
114: {
115: log.info("browse({}, {}, filter: {}, startingIndex: {}, requestedCount: {}, sortCriteria: {})",
116: objectId, browseFlag, filter, firstResult, maxResults, orderby);
117: // this repeated log is for capturing test recordings
118: log.trace("browse @@@ {} @@@ {} @@@ {} @@@ {} @@@ {} @@@ {})",
119: objectId, browseFlag, firstResult, maxResults, filter, orderby);
120:
121: final long baseTime = System.nanoTime();
122: final BrowseParams params = new BrowseParams(objectId, browseFlag, filter, firstResult, maxResults, orderby);
123: final Cache cache = cacheManager.getCache(getClass());
124: final Object result = cache.getCachedObject(params, () -> findEntity(params));
125: log.info(">>>> result computed in {} msec", (System.nanoTime() - baseTime) / 1E6);
126:
127:• if (result instanceof ContentDirectoryException)
128: {
129: throw (ContentDirectoryException)result;
130: }
131:
132: log(">>>> returning", (BrowseResult)result);
133: return (BrowseResult)result;
134: }
135:
136: /*******************************************************************************************************************
137: *
138: * Searches an entity.
139: *
140: * @param params the search parameters
141: * @return a {@link BrowseResult} if the requested entity has been found; a
142: * {@link ContentDirectoryException} in case of error
143: *
144: ******************************************************************************************************************/
145: @Nonnull
146: private Object findEntity (@Nonnull final BrowseParams params)
147: {
148: try
149: {
150: final Path path = didlIdToPath(params.objectId);
151: final Entity entity = contentDirectory.findRoot()
152: .findChildren()
153: .withPath(path)
154: .optionalResult()
155: .orElseThrow(() ->
156: new ContentDirectoryException(NO_SUCH_OBJECT,
157: String.format("%s -> %s", params.objectId, path)));
158: log.debug(">>>> found {}", entity);
159: final ContentHolder holder = entity.as(_DIDLAdapter_).toContent(params.browseFlag,
160: (int)params.firstResult,
161: maxCount(params.maxResults));
162: final DIDLParser parser = new DIDLParser();
163: return new BrowseResult(parser.generate(holder.getContent()),
164: holder.getNumberReturned(),
165: holder.getTotalMatches(),
166: updateId.get());
167: }
168: // this method returns exceptions as a result, so they can be cached
169: catch (ContentDirectoryException e)
170: {
171: log.error("", e);
172: return e;
173: }
174: catch (Exception e)
175: {
176: log.error("", e);
177: return new ContentDirectoryException(CANNOT_PROCESS);
178: }
179: }
180:
181: /*******************************************************************************************************************
182: *
183: ******************************************************************************************************************/
184: private static void log (@Nonnull final String message, @Nonnull final BrowseResult browseResult)
185: {
186: log.info("{} BrowseResult(..., {}, {}, {})",
187: message,
188: browseResult.getCountLong(),
189: browseResult.getTotalMatchesLong(),
190: browseResult.getContainerUpdateIDLong());
191:
192:• if (log.isDebugEnabled())
193: {
194: Stream.of(xmlPrettyPrinted(browseResult.getResult()).split("\n"))
195: .forEach(s -> log.debug("{} {}", message, s));
196: }
197: }
198: }