/* * This 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 software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.mobicents.slee.service.interop; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.slee.ActivityContextInterface; import javax.slee.CreateException; import javax.slee.Sbb; import javax.slee.SbbContext; import javax.slee.UnrecognizedActivityException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.mobicents.seam.CallManager; import org.mobicents.mscontrol.MsConnection; import org.mobicents.mscontrol.MsConnectionEvent; import org.mobicents.mscontrol.MsEndpoint; import org.mobicents.mscontrol.MsLink; import org.mobicents.mscontrol.MsLinkEvent; import org.mobicents.mscontrol.MsLinkMode; import org.mobicents.mscontrol.MsNotifyEvent; import org.mobicents.mscontrol.MsProvider; import org.mobicents.mscontrol.MsSession; import org.mobicents.mscontrol.events.MsEventAction; import org.mobicents.mscontrol.events.MsEventFactory; import org.mobicents.mscontrol.events.MsEventIdentifier; import org.mobicents.mscontrol.events.MsRequestedEvent; import org.mobicents.mscontrol.events.MsRequestedSignal; import org.mobicents.mscontrol.events.ann.MsPlayRequestedSignal; import org.mobicents.mscontrol.events.dtmf.MsDtmfNotifyEvent; import org.mobicents.mscontrol.events.dtmf.MsDtmfRequestedEvent; import org.mobicents.mscontrol.events.pkg.DTMF; import org.mobicents.mscontrol.events.pkg.MsAnnouncement; import org.mobicents.slee.resource.media.ratype.MediaRaActivityContextInterfaceFactory; import org.mobicents.slee.resource.tts.ratype.TTSActivityContextInterfaceFactory; import org.mobicents.slee.resource.tts.ratype.TTSProvider; import org.mobicents.slee.resource.tts.ratype.TTSSession; import org.mobicents.slee.service.events.InteropCustomEvent; /** * * @author <A HREF="mailto:jean.deruelle@gmail.com">Jean Deruelle</A> * */ public abstract class InteropSbb implements Sbb { private static final String IVR_ENDPOINT_NAME = "media/trunk/IVR/$"; private static final String PR_ENDPOINT_NAME = "media/trunk/PacketRelay/$"; private static final String OPENING_ANNOUNCEMENT = "Welcome to JavaOne 2008. Please enter your booth number followed by the pound sign to get some free beers."; // the sbb's sbbContext private SbbContext sbbContext; private Log logger = LogFactory.getLog(InteropSbb.class); String audioFilePath = null; String callerSip = null; String adminSip = null; long waitingTime = 0; private MsProvider msProvider; private MediaRaActivityContextInterfaceFactory mediaAcif; private TTSActivityContextInterfaceFactory ttsActivityContextInterfaceFactory; private TTSProvider ttsProvider; /** Creates a new instance of SecondBounceSbb */ public InteropSbb() { super(); } protected SbbContext getSbbContext() { return this.sbbContext; } /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 54 for further information. <br> * The SLEE invokes this method after a new instance of the SBB abstract * class is created. During this method, an SBB entity has not been assigned * to the SBB object. The SBB object can take advantage of this method to * allocate and initialize state or connect to resources that are to be held * by the SBB object during its lifetime. Such state and resources cannot be * specific to an SBB entity because the SBB object might be reused during * its lifetime to serve multiple SBB entities. <br> * This method indicates a transition from state "DOES NOT EXIST" to * "POOLED" (see page 52) */ public void setSbbContext(SbbContext sbbContext) { this.sbbContext = sbbContext; try { Context ctx = (Context) new InitialContext() .lookup("java:comp/env"); audioFilePath = System.getProperty("jboss.server.data.dir") + "/JavaOne2008.wav"; msProvider = (MsProvider) ctx .lookup("slee/resources/media/1.0/provider"); mediaAcif = (MediaRaActivityContextInterfaceFactory) ctx .lookup("slee/resources/media/1.0/acifactory"); ttsActivityContextInterfaceFactory = (TTSActivityContextInterfaceFactory) ctx .lookup("slee/resources/ttsRA/0.1/acifactory"); ttsProvider = (TTSProvider) ctx .lookup("slee/resources/ttsRA/0.1/provider"); } catch (NamingException ne) { logger.error("Could not set SBB context: " + ne.toString(), ne); } } public void onInitMedia(InteropCustomEvent event, ActivityContextInterface ac) { logger.info("****** org.mobicents.slee.service.interopdemo.INIT_MEDIA ******* "); logger.info("InteropSbb: " + this + ": received an INIT_MEDIA event. username = " + event.getBoothNumber() + ". sdpContent = " + new String(event.getSdpContent())); this.setInteropCustomEvent(event); MsConnection msConnection = msProvider.createSession().createNetworkConnection(PR_ENDPOINT_NAME); try { ActivityContextInterface msAci = mediaAcif.getActivityContextInterface(msConnection); msAci.attach(this.getSbbContext().getSbbLocalObject()); } catch (Exception ex) { logger.error("Internal server error", ex); return; } msConnection.modify("$", new String(event.getSdpContent())); } public void onPlayOpeningAnnouncement(InteropCustomEvent event, ActivityContextInterface ac) { logger.info("****** org.mobicents.slee.service.interopdemo.PLAY_ANN ******* "); logger.info("InteropSbb: " + this + ": received an PLAY_ANN_OPENING event. username = " + event.getBoothNumber() + ". sdpContent = " + event.getSdpContent()); this.setInteropCustomEvent(event); playAnnouncement(OPENING_ANNOUNCEMENT, false, true, false); } public void onPlayConfirmationAnnouncement(InteropCustomEvent event, ActivityContextInterface ac) { logger.info("****** org.mobicents.slee.service.interopdemo.PLAY_ANN ******* "); logger.info("InteropSbb: " + this + ": received an PLAY_ANN_CONFIRM event. username = " + event.getBoothNumber() + ". sdpContent = " + event.getSdpContent()); this.setInteropCustomEvent(event); String announcement = generateConfirmationAnnouncement(event.getBoothNumber()); playAnnouncement(announcement, true, false, true); } /** * @param event * @return */ private static String generateConfirmationAnnouncement( String boothNumber) { StringBuffer stringBuffer = new StringBuffer(); if(boothNumber !=null && boothNumber.length() > 0) { stringBuffer.append("Free beers are on their way to your booth number "); stringBuffer.append(boothNumber); stringBuffer.append(". Feel free to call again. Bye."); } else { stringBuffer.append("We didn't understand your booth number, sorry. Please call again !"); } return stringBuffer.toString(); } public void onLinkConnected(MsLinkEvent evt, ActivityContextInterface aci) { System.out.println("LINK CONNECTED"); } private void playAnnouncement(String announcement, boolean attachToGeneratorActivity, boolean listenForDTMF, boolean listenForCompletion) { try { Thread.sleep(1000); } catch (InterruptedException e1) {} TTSSession ttsSession = ttsProvider.getNewTTSSession( audioFilePath, "kevin"); ttsSession.textToAudioFile(announcement); MsEventFactory eventFactory = msProvider .getEventFactory(); MsLink link = getLink(); if(link == null) { logger.error("Connection could not be created, closing the call "); CallManager callManagerRef = (CallManager)this.getInteropCustomEvent().getCallManagerRef(); try { callManagerRef.endCall(null, false); } catch (IOException e) { logger.error("Impossible to call back the EJB", e); } return; } if(attachToGeneratorActivity) { try { ActivityContextInterface generatorActivity = mediaAcif .getActivityContextInterface(link.getSession()); generatorActivity.attach(getSbbContext().getSbbLocalObject()); } catch (javax.slee.UnrecognizedActivityException e) { logger.error("Impossible to attach to Media Signal Generator activity", e); } } String announcementFile = "file:" + audioFilePath; // Let us request for Announcement Complete event or Failure // in case if it happens MsRequestedEvent onCompleted = eventFactory.createRequestedEvent(MsAnnouncement.COMPLETED); onCompleted.setEventAction(MsEventAction.NOTIFY); MsRequestedEvent onFailed = eventFactory.createRequestedEvent(MsAnnouncement.FAILED); onFailed.setEventAction(MsEventAction.NOTIFY); MsPlayRequestedSignal play = (MsPlayRequestedSignal) eventFactory.createRequestedSignal(MsAnnouncement.PLAY); play.setURL(announcementFile); MsDtmfRequestedEvent dtmf = (MsDtmfRequestedEvent) eventFactory.createRequestedEvent(DTMF.TONE); MsRequestedSignal[] requestedSignals = new MsRequestedSignal[] { play }; List<MsRequestedEvent> eventList = new ArrayList<MsRequestedEvent>(); eventList.add(onFailed); if(listenForCompletion) { eventList.add(onCompleted); } if(listenForDTMF) { try { ActivityContextInterface dtmfAci = mediaAcif.getActivityContextInterface(link.getSession()); dtmfAci.attach(getSbbContext().getSbbLocalObject()); eventList.add(dtmf); } catch (UnrecognizedActivityException e) { logger.error("Internal Server Erro", e); } } MsRequestedEvent[] requestedEvents = eventList.toArray(new MsRequestedEvent[eventList.size()]); link.getEndpoints()[1].execute(requestedSignals, requestedEvents, link); } private MsConnection getConnection() { ActivityContextInterface[] activities = getSbbContext().getActivities(); for (int i = 0; i < activities.length; i++) { if (activities[i].getActivity() instanceof MsConnection) { return (MsConnection) activities[i].getActivity(); } } logger.info("Connection is null..."); return null; } private MsLink getLink() { ActivityContextInterface[] activities = getSbbContext().getActivities(); for (int i = 0; i < activities.length; i++) { if (activities[i].getActivity() instanceof MsLink) { return (MsLink) activities[i].getActivity(); } } logger.info("Link is null..."); return null; } public void onConnectionHalfOpened(MsConnectionEvent evt, ActivityContextInterface aci) { logger.info("Connection Created"); logger.info("user name : " + this.getInteropCustomEvent().getBoothNumber()); logger.info("initial SDP content : " + new String(this.getInteropCustomEvent().getSdpContent())); MsConnection connection = evt.getConnection(); String sdp = connection.getLocalDescriptor(); logger.info("connection SDP content : " + sdp); CallManager callManagerRef = (CallManager)this.getInteropCustomEvent().getCallManagerRef(); try { callManagerRef.mediaConnectionCreated(sdp); } catch (IOException e) { logger.error("Impossible to call back the EJB", e); } } public void onConnectionOpened(MsConnectionEvent evt, ActivityContextInterface aci) { logger.info("Connection Modified"); logger.info("user name : " + this.getInteropCustomEvent().getBoothNumber()); logger.info("initial SDP content : " + new String(this.getInteropCustomEvent().getSdpContent())); MsConnection connection = evt.getConnection(); String sdp = connection.getLocalDescriptor(); logger.info("connection SDP content : " + sdp); if(connection == null) { logger.error("Connection could not be created, closing the call "); CallManager callManagerRef = (CallManager)this.getInteropCustomEvent().getCallManagerRef(); try { callManagerRef.endCall(null, false); } catch (IOException e) { logger.error("Impossible to call back the EJB", e); } return; } MsSession session = connection.getSession(); MsLink link = session.createLink(MsLinkMode.FULL_DUPLEX); ActivityContextInterface linkActivity = null; try { linkActivity = mediaAcif.getActivityContextInterface(link); } catch (UnrecognizedActivityException ex) { } linkActivity.attach(sbbContext.getSbbLocalObject()); link.join(evt.getConnection().getEndpoint().getLocalName(), IVR_ENDPOINT_NAME); } public void onAnnouncementComplete(MsNotifyEvent evt, ActivityContextInterface aci) { logger.info("Confirmation Announcement complete"); CallManager callManagerRef = (CallManager)this.getInteropCustomEvent().getCallManagerRef(); try { callManagerRef.endCall(null, false); } catch (IOException e) { logger.error("Impossible to call back the EJB", e); } getConnection().release(); } private void initDtmfDetector(MsLink link, String endpointName) { MsEventFactory eventFactory = msProvider .getEventFactory(); try { ActivityContextInterface dtmfAci = mediaAcif .getActivityContextInterface(link.getSession()); dtmfAci.attach(getSbbContext().getSbbLocalObject()); MsDtmfRequestedEvent dtmf = (MsDtmfRequestedEvent) eventFactory.createRequestedEvent(DTMF.TONE); MsRequestedSignal[] signals = new MsRequestedSignal[] {}; MsRequestedEvent[] events = new MsRequestedEvent[] { dtmf }; link.getEndpoints()[1].execute(signals, events, link); } catch (UnrecognizedActivityException e) { logger.error("Internal Server Erro", e); } } public void onDtmf(MsNotifyEvent evt, ActivityContextInterface aci) { MsEventIdentifier identifier = evt.getEventID(); if (identifier.equals(DTMF.TONE)) { MsDtmfNotifyEvent event = (MsDtmfNotifyEvent) evt; String signal = event.getSequence(); logger.info("org.mobicents.slee.media.dtmf.DTMF " + signal); int cause = -1; try { cause = Integer.parseInt(signal); } catch (java.lang.NumberFormatException e) { //user entered a # sign String announcement = generateConfirmationAnnouncement(getBoothNumber()); playAnnouncement(announcement, true, false, true); return ; } String boothNumber = getBoothNumber(); if(boothNumber == null) { boothNumber = ""; } switch (cause) { case 0: boothNumber = boothNumber + "0"; break; case 1: boothNumber = boothNumber + "1"; break; case 2: boothNumber = boothNumber + "2"; break; case 3: boothNumber = boothNumber + "3"; break; case 4: boothNumber = boothNumber + "4"; break; case 5: boothNumber = boothNumber + "5"; break; case 6: boothNumber = boothNumber + "6"; break; case 7: boothNumber = boothNumber + "7"; break; case 8: boothNumber = boothNumber + "8"; break; case 9: boothNumber = boothNumber + "9"; break; default: break; } setBoothNumber(boothNumber); this.initDtmfDetector(getLink(), IVR_ENDPOINT_NAME); } } public abstract void setInteropCustomEvent(InteropCustomEvent customEvent); public abstract InteropCustomEvent getInteropCustomEvent(); public abstract void setBoothNumber(String boothNumber); public abstract String getBoothNumber(); /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 54 for further information. <br> * The SLEE invokes this method before terminating the life of the SBB * object. The SBB object can take advantage of this method to free state or * resources that are held by the SBB object. These state and resources * typically had been allocated by the setSbbContext method. <br> * This method indicates a transition from state "POOLED" to "DOES NOT * EXIST" (see page 52) */ public void unsetSbbContext() { logger.info("CommonSbb: " + this + ": unsetSbbContext() called."); } /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 55 for further information. <br> * The SLEE invokes this method on an SBB object before the SLEE creates a * new SBB entity in response to an initial event or an invocation of the * create method on a ChildRelation object. This method should initialize * the SBB object using the CMP field get and set accessor methods, such * that when this method returns, the persistent representation of the SBB * entity can be created. <br> * This method is the first part of a transition from state "POOLED" to * "READY" (see page 52) */ public void sbbCreate() throws javax.slee.CreateException { logger.info("CommonSbb: " + this + ": sbbCreate() called."); } /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 55 for further information. <br> * The SLEE invokes this method on an SBB object after the SLEE creates a * new SBB entity. The SLEE invokes this method after the persistent * representation of the SBB entity has been created and the SBB object is * assigned to the created SBB entity. This method gives the SBB object a * chance to initialize additional transient state and acquire additional * resources that it needs while it is in the Ready state. <br> * This method is the second part of a transition from state "POOLED" to * "READY" (see page 52) */ public void sbbPostCreate() throws CreateException { logger.info("CommonSbb: " + this + ": sbbPostCreate() called."); } /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 55 for further information. <br> * The SLEE invokes this method on an SBB object when the SLEE picks the SBB * object in the pooled state and assigns it to a specific SBB entity. This * method gives the SBB object a chance to initialize additional transient * state and acquire additional resources that it needs while it is in the * Ready state. <br> * This method indicates a transition from state "POOLED" to "READY" (see * page 52) */ public void sbbActivate() { logger.info("CommonSbb: " + this + ": sbbActivate() called."); } /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 56 for further information. <br> * The SLEE invokes this method on an SBB object when the SLEE decides to * disassociate the SBB object from the SBB entity, and to put the SBB * object back into the pool of available SBB objects. This method gives the * SBB object the chance to release any state or resources that should not * be held while the SBB object is in the pool. These state and resources * typically had been allocated during the sbbActivate method. <br> * This method indicates a transition from state "READY" to "POOLED" (see * page 52) */ public void sbbPassivate() { logger.info("CommonSbb: " + this + ": sbbPassivate() called."); } /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 56 for further information. <br> * The SLEE invokes the sbbRemove method on an SBB object before the SLEE * removes the SBB entity assigned to the SBB object. <br> * This method indicates a transition from state "READY" to "POOLED" (see * page 52) */ public void sbbRemove() { logger.info("CommonSbb: " + this + ": sbbRemove() called."); } /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 56 for further information. <br> * The SLEE calls this method to synchronize the state of an SBB object with * its assigned SBB entity�s persistent state. The SBB Developer can assume * that the SBB object�s persistent state has been loaded just before this * method is invoked. <br> * This method indicates a transition from state "READY" to "READY" (see * page 52) */ public void sbbLoad() { logger.info("CommonSbb: " + this + ": sbbLoad() called."); } /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 57 for further information. <br> * The SLEE calls this method to synchronize the state of the SBB entity�s * persistent state with the state of the SBB object. The SBB Developer * should use this method to update the SBB object using the CMP field * accessor methods before its persistent state is synchronized. <br> * This method indicates a transition from state "READY" to "READY" (see * page 52) */ public void sbbStore() { logger.info("CommonSbb: " + this + ": sbbStore() called."); } /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 67 for further information. <br> * The SLEE invokes the sbbRolledBack callback method after a transaction * used in a SLEE originated invocation has rolled back. */ public void sbbRolledBack(javax.slee.RolledBackContext rolledBackContext) { logger.info("CommonSbb: " + this + ": sbbRolledBack() called."); } /** * implements javax.slee.Sbb Please refer to JSLEE v1.1 Specification, Early * Draft Review Page 65 for further information. <br> * The SLEE invokes this method after a SLEE originated invocation of a * transactional method of the SBB object returns by throwing a * RuntimeException. */ public void sbbExceptionThrown(Exception exception, Object obj, javax.slee.ActivityContextInterface activityContextInterface) { logger.info("CommonSbb: " + this + ": sbbExceptionThrown() called."); } }