package com.destinationradiodenver.mobileStreaming.singleton; /* * RED5 Open Source Flash Server - http://www.osflash.org/red5 * * Copyright (c) 2006-2008 by respective authors (see below). All rights reserved. * * This library 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 2.1 of the License, or (at your option) any later * version. * * This library 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 library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ import java.util.ArrayList; import java.util.HashMap; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import javax.ejb.AccessTimeout; import javax.ejb.ConcurrencyManagement; import javax.ejb.ConcurrencyManagementType; import javax.ejb.Lock; import javax.ejb.LockType; import javax.ejb.Singleton; import javax.ejb.Startup; import org.jboss.logging.Logger; import org.red5.service.httpstream.model.MobileProfile; /** * Creates, updates, locates, and manages media segments. * * Original Concept * @author Paul Gregoire * * Named Pipe Adaptation and EJB Migration * @author Connor Penhale * */ @Startup @Singleton @ConcurrencyManagement(ConcurrencyManagementType.BEAN) @AccessTimeout(value=10, unit=TimeUnit.SECONDS) @Lock(LockType.READ) public class AvailabilityService { private static final Logger log = Logger.getLogger(AvailabilityService.class.getName()); // map of currently available (in-memory) segments, keyed by stream name private static ConcurrentHashMap<String, SegmentFacade> segmentMap = new ConcurrentHashMap<String, SegmentFacade>(); public static ConcurrentHashMap<String, ArrayList<MobileProfile>> profileMap = new ConcurrentHashMap<String, ArrayList<MobileProfile>>(); //TODO: Put this in a .props private static final int numMaxSegments = 10; private static final int segmentTimeLimit = 6000; private static final int numExpiredSegmentsOnCDN = 2; public String createUniqueName(String streamName, String mobileProfileName){ String uniqueName = streamName; uniqueName += "_"; uniqueName += mobileProfileName; return uniqueName; } public int getSegmentIndex(String streamName, String mobileProfileName) { return segmentMap.get(createUniqueName(streamName, mobileProfileName)).getIndex(); } public boolean isAvailable(String streamName, String mobileProfileName){ return segmentMap.containsKey(createUniqueName(streamName, mobileProfileName)); } @Lock(LockType.WRITE) public void update(String uniqueName, Object passed) { log.errorf("Availability Service recieved an update from an SegmentConsumer"); log.tracef("The SegmentConsumer passing the update is: %s", uniqueName); SegmentFacade segmentFacade = segmentMap.get(uniqueName); if(segmentFacade!=null){ log.tracef("Adding to an existing segment"); segmentFacade.addSegment((byte[]) passed); segmentMap.replace(uniqueName, segmentFacade); }else{ log.errorf("Creating a new Segment for SegmentConsumer: %s", uniqueName); SegmentFacade newFacade = new SegmentFacade(uniqueName, numMaxSegments); newFacade.addSegment((byte[]) passed); segmentMap.put(uniqueName, newFacade); } } //TEST2 public byte[] getSegment(String streamName, String mobileProfileName, int sequenceNumber) { return segmentMap.get(createUniqueName(streamName, mobileProfileName)).getSegment(sequenceNumber); } public int getSegmentCount(String streamName, String mobileProfileName) { //break it down String uniqueName = createUniqueName(streamName, mobileProfileName); log.errorf("UniqueName: %s", uniqueName); SegmentFacade sF = segmentMap.get(uniqueName); if(sF!=null) return sF.getSegmentCount(); log.errorf("Segment Facade retreived was null"); return 0; } public int getNumExpiredSegmentsOnCDN() { return numExpiredSegmentsOnCDN; } public int getSegmentTimeLimit() { return segmentTimeLimit; } public int getNumMaxSegments() { return numMaxSegments; } public boolean isADPAvailable(String streamName) { Set<String> set = segmentMap.keySet(); log.tracef("There are %s segments in the segment map", set.size()); for(String s : set){ log.tracef("Examining key %s", s); if(s.startsWith(streamName)) return true; } return false; } public ArrayList<MobileProfile> getMobileProfiles(String streamName) { return profileMap.get(streamName); } private class SegmentFacade { // map of currently available segments HashMap<Integer, byte[]> segments = new HashMap<Integer, byte[]>(); private int index; private String uniqueName; private int numMaxSegments; public int getIndex() { //for saftey, return one behind current int temp = index - 1; return temp; } public void setNumMaxSegments(int numMaxSegments) { this.numMaxSegments = numMaxSegments; } public SegmentFacade(String name, int numMaxSegments){ log.tracef("Creating SegmentFacade for %s with a maximum of %s segments", name, numMaxSegments); setUniqueName(name); setNumMaxSegments(numMaxSegments); index = 0; } public void setUniqueName(String uniqueName) { this.uniqueName = uniqueName; } public int getSegmentCount() { return segments.size(); } public void addSegment(byte[] segment){ log.tracef("Adding segment %s in facade for: %s", index, uniqueName); if(segments.size() >= numMaxSegments){ segments.remove(index-numMaxSegments); } segments.put(index, segment); log.tracef("Added segment %s in facade for: %s", index, uniqueName); index++; //TODO: Determine if this level of logging is useful // It may be the lack of sleep but I think this is more logging than // most people put in their applications /*if(log.isTraceEnabled()){ int streamSizeInMemory = 0; for(int i = index; i < getSegmentCount(); i--){ streamSizeInMemory += segments.get(i).length; } streamSizeInMemory = streamSizeInMemory/1024; log.tracef("SegmentFacade for %s is %skb:", uniqueName, streamSizeInMemory); long totalMemory = Runtime.getRuntime().totalMemory()/1024; long percent = (streamSizeInMemory/totalMemory)*100; log.tracef("SegmentFacade for %s is using %s% of JVM", uniqueName, percent); } */ } /** * Returns a segment matching the requested index. * * @return segment matching the index or null */ public byte[] getSegment(int index) { return segments.get(index); } } public void addProfile(String publishedName, MobileProfile mP) { ArrayList<MobileProfile> list = getMobileProfiles(publishedName); if(list!=null){ if(list.size()>0) list.add(mP); }else{ list = new ArrayList<MobileProfile>(); list.add(mP); } log.infof("Adding %s to profileMap", publishedName); profileMap.put(publishedName, list); } public void removeProfile(String publishedName, MobileProfile mP) { ArrayList<MobileProfile> list = getMobileProfiles(publishedName); if(list!=null){ if(list.size()>0){ list.remove(mP); profileMap.put(publishedName, list); } } } }