/* * Copyright (C) 2010 Teleal GmbH, Switzerland * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.teleal.cling.support.contentdirectory; import org.teleal.cling.binding.annotations.UpnpAction; import org.teleal.cling.binding.annotations.UpnpInputArgument; import org.teleal.cling.binding.annotations.UpnpOutputArgument; import org.teleal.cling.binding.annotations.UpnpService; import org.teleal.cling.binding.annotations.UpnpServiceId; import org.teleal.cling.binding.annotations.UpnpServiceType; import org.teleal.cling.binding.annotations.UpnpStateVariable; import org.teleal.cling.binding.annotations.UpnpStateVariables; import org.teleal.cling.model.types.ErrorCode; import org.teleal.cling.model.types.UnsignedIntegerFourBytes; import org.teleal.cling.model.types.csv.CSV; import org.teleal.cling.model.types.csv.CSVString; import org.teleal.cling.support.model.BrowseFlag; import org.teleal.cling.support.model.BrowseResult; import org.teleal.cling.support.model.DIDLContent; import org.teleal.cling.support.model.SortCriterion; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.List; /** * Simple ContentDirectory service skeleton. * <p> * Only state variables and actions required by <em>ContentDirectory:1</em> * (not the optional ones) are implemented. * </p> * * @author Alessio Gaeta * @author Christian Bauer */ @UpnpService( serviceId = @UpnpServiceId("ContentDirectory"), serviceType = @UpnpServiceType(value = "ContentDirectory", version = 1) ) @UpnpStateVariables({ @UpnpStateVariable( name = "A_ARG_TYPE_ObjectID", sendEvents = false, datatype = "string"), @UpnpStateVariable( name = "A_ARG_TYPE_Result", sendEvents = false, datatype = "string"), @UpnpStateVariable( name = "A_ARG_TYPE_BrowseFlag", sendEvents = false, datatype = "string", allowedValuesEnum = BrowseFlag.class), @UpnpStateVariable( name = "A_ARG_TYPE_Filter", sendEvents = false, datatype = "string"), @UpnpStateVariable( name = "A_ARG_TYPE_SortCriteria", sendEvents = false, datatype = "string"), @UpnpStateVariable( name = "A_ARG_TYPE_Index", sendEvents = false, datatype = "ui4"), @UpnpStateVariable( name = "A_ARG_TYPE_Count", sendEvents = false, datatype = "ui4"), @UpnpStateVariable( name = "A_ARG_TYPE_UpdateID", sendEvents = false, datatype = "ui4"), @UpnpStateVariable( name = "A_ARG_TYPE_URI", sendEvents = false, datatype = "uri"), @UpnpStateVariable( name = "A_ARG_TYPE_SearchCriteria", sendEvents = false, datatype = "string") }) public abstract class AbstractContentDirectoryService { public static final String CAPS_WILDCARD = "*"; @UpnpStateVariable(sendEvents = false) final private CSV<String> searchCapabilities; @UpnpStateVariable(sendEvents = false) final private CSV<String> sortCapabilities; @UpnpStateVariable( sendEvents = true, defaultValue = "0", eventMaximumRateMilliseconds = 200 ) private UnsignedIntegerFourBytes systemUpdateID = new UnsignedIntegerFourBytes(0); final protected PropertyChangeSupport propertyChangeSupport; protected AbstractContentDirectoryService() { this(new ArrayList(), new ArrayList(), null); } protected AbstractContentDirectoryService(List<String> searchCapabilities, List<String> sortCapabilities) { this(searchCapabilities, sortCapabilities, null); } protected AbstractContentDirectoryService(List<String> searchCapabilities, List<String> sortCapabilities, PropertyChangeSupport propertyChangeSupport) { this.propertyChangeSupport = propertyChangeSupport != null ? propertyChangeSupport : new PropertyChangeSupport(this); this.searchCapabilities = new CSVString(); this.searchCapabilities.addAll(searchCapabilities); this.sortCapabilities = new CSVString(); this.sortCapabilities.addAll(sortCapabilities); } @UpnpAction(out = @UpnpOutputArgument(name = "SearchCaps")) public CSV<String> getSearchCapabilities() { return searchCapabilities; } @UpnpAction(out = @UpnpOutputArgument(name = "SortCaps")) public CSV<String> getSortCapabilities() { return sortCapabilities; } @UpnpAction(out = @UpnpOutputArgument(name = "Id")) synchronized public UnsignedIntegerFourBytes getSystemUpdateID() { return systemUpdateID; } public PropertyChangeSupport getPropertyChangeSupport() { return propertyChangeSupport; } /** * Call this method after making changes to your content directory. * <p> * This will notify clients that their view of the content directory is potentially * outdated and has to be refreshed. * </p> */ synchronized protected void changeSystemUpdateID() { Long oldUpdateID = getSystemUpdateID().getValue(); systemUpdateID.increment(true); getPropertyChangeSupport().firePropertyChange( "SystemUpdateID", oldUpdateID, getSystemUpdateID().getValue() ); } @UpnpAction(out = { @UpnpOutputArgument(name = "Result", stateVariable = "A_ARG_TYPE_Result", getterName = "getResult"), @UpnpOutputArgument(name = "NumberReturned", stateVariable = "A_ARG_TYPE_Count", getterName = "getCount"), @UpnpOutputArgument(name = "TotalMatches", stateVariable = "A_ARG_TYPE_Count", getterName = "getTotalMatches"), @UpnpOutputArgument(name = "UpdateID", stateVariable = "A_ARG_TYPE_UpdateID", getterName = "getContainerUpdateID") }) public BrowseResult browse( @UpnpInputArgument(name = "ObjectID", aliases = "ContainerID") String objectId, @UpnpInputArgument(name = "BrowseFlag") String browseFlag, @UpnpInputArgument(name = "Filter") String filter, @UpnpInputArgument(name = "StartingIndex", stateVariable = "A_ARG_TYPE_Index") UnsignedIntegerFourBytes firstResult, @UpnpInputArgument(name = "RequestedCount", stateVariable = "A_ARG_TYPE_Count") UnsignedIntegerFourBytes maxResults, @UpnpInputArgument(name = "SortCriteria") String orderBy) throws ContentDirectoryException { SortCriterion[] orderByCriteria; try { orderByCriteria = SortCriterion.valueOf(orderBy); } catch (Exception ex) { throw new ContentDirectoryException(ContentDirectoryErrorCode.UNSUPPORTED_SORT_CRITERIA, ex.toString()); } try { return browse( objectId, BrowseFlag.valueOrNullOf(browseFlag), filter, firstResult.getValue(), maxResults.getValue(), orderByCriteria ); } catch (ContentDirectoryException ex) { throw ex; } catch (Exception ex) { throw new ContentDirectoryException(ErrorCode.ACTION_FAILED, ex.toString()); } } /** * Implement this method to implement browsing of your content. * <p> * This is a required action defined by <em>ContentDirectory:1</em>. * </p> * <p> * You should wrap any exception into a {@link ContentDirectoryException}, so a propery * error message can be returned to control points. * </p> */ public abstract BrowseResult browse(String objectID, BrowseFlag browseFlag, String filter, long firstResult, long maxResults, SortCriterion[] orderby) throws ContentDirectoryException; @UpnpAction(out = { @UpnpOutputArgument(name = "Result", stateVariable = "A_ARG_TYPE_Result", getterName = "getResult"), @UpnpOutputArgument(name = "NumberReturned", stateVariable = "A_ARG_TYPE_Count", getterName = "getCount"), @UpnpOutputArgument(name = "TotalMatches", stateVariable = "A_ARG_TYPE_Count", getterName = "getTotalMatches"), @UpnpOutputArgument(name = "UpdateID", stateVariable = "A_ARG_TYPE_UpdateID", getterName = "getContainerUpdateID") }) public BrowseResult search( @UpnpInputArgument(name = "ContainerID", stateVariable = "A_ARG_TYPE_ObjectID") String containerId, @UpnpInputArgument(name = "SearchCriteria") String searchCriteria, @UpnpInputArgument(name = "Filter") String filter, @UpnpInputArgument(name = "StartingIndex", stateVariable = "A_ARG_TYPE_Index") UnsignedIntegerFourBytes firstResult, @UpnpInputArgument(name = "RequestedCount", stateVariable = "A_ARG_TYPE_Count") UnsignedIntegerFourBytes maxResults, @UpnpInputArgument(name = "SortCriteria") String orderBy) throws ContentDirectoryException { SortCriterion[] orderByCriteria; try { orderByCriteria = SortCriterion.valueOf(orderBy); } catch (Exception ex) { throw new ContentDirectoryException(ContentDirectoryErrorCode.UNSUPPORTED_SORT_CRITERIA, ex.toString()); } try { return search( containerId, searchCriteria, filter, firstResult.getValue(), maxResults.getValue(), orderByCriteria ); } catch (ContentDirectoryException ex) { throw ex; } catch (Exception ex) { throw new ContentDirectoryException(ErrorCode.ACTION_FAILED, ex.toString()); } } /** * Override this method to implement searching of your content. * <p> * The default implementation returns an empty result. * </p> */ public BrowseResult search(String containerId, String searchCriteria, String filter, long firstResult, long maxResults, SortCriterion[] orderBy) throws ContentDirectoryException { try { return new BrowseResult(new DIDLParser().generate(new DIDLContent()), 0, 0); } catch (Exception ex) { throw new ContentDirectoryException(ErrorCode.ACTION_FAILED, ex.toString()); } } }