/* * JBoss, Home of Professional Open Source * Copyright 2011, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * 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.restcomm.media.control.mgcp.tx.cmd; import java.io.IOException; import org.apache.log4j.Logger; import org.restcomm.media.control.mgcp.MgcpEvent; import org.restcomm.media.control.mgcp.controller.MgcpCall; import org.restcomm.media.control.mgcp.controller.MgcpConnection; import org.restcomm.media.control.mgcp.controller.MgcpEndpoint; import org.restcomm.media.control.mgcp.message.MgcpRequest; import org.restcomm.media.control.mgcp.message.MgcpResponse; import org.restcomm.media.control.mgcp.message.MgcpResponseCode; import org.restcomm.media.control.mgcp.message.Parameter; import org.restcomm.media.control.mgcp.params.LocalConnectionOptions; import org.restcomm.media.control.mgcp.tx.Action; import org.restcomm.media.control.mgcp.tx.Transaction; import org.restcomm.media.scheduler.PriorityQueueScheduler; import org.restcomm.media.scheduler.Scheduler; import org.restcomm.media.scheduler.Task; import org.restcomm.media.scheduler.TaskChain; import org.restcomm.media.spi.ConnectionMode; import org.restcomm.media.spi.ConnectionType; import org.restcomm.media.spi.ModeNotSupportedException; import org.restcomm.media.spi.utils.Text; /** * * @author Oifa Yulian */ public class CreateConnectionCmd extends Action { //response strings private final static Text CALLID_MISSING = new Text("Missing call identifier"); private final static Text MODE_MISSING = new Text("Missing mode value"); private final static Text WILDCARD_ALL_NOT_ALLOWED= new Text("Wildcard <all> not allowed here"); private final static Text SDP_NEGOTIATION_FAILED = new Text("SDP_NEGOTIATION_FAILED"); private final static Text ERROR_ENDPOINT_UNAVAILAVALE = new Text("Endpoint not available"); private final static Text SUCCESS= new Text("Success"); //endpoints for searching private MgcpEndpoint[] endpoints = new MgcpEndpoint[2]; //requested endpoints private MgcpEndpoint endpoint, endpoint2; private MgcpConnection[] connections = new MgcpConnection[2]; //call identifier parameter private Parameter callID; //local and domain name parts of the endpoint identifier private Text localName = new Text(); private Text domainName = new Text(); //layout local and domain names into endpoint identifier private Text[] endpointName = new Text[]{localName, domainName}; //local and domain name parts of the second endpoint identifier private Text localName2 = new Text(); private Text domainName2 = new Text(); //layout local and domain names into second endpoint identifier private Text[] endpointName2 = new Text[]{localName2, domainName2}; //transmission mode parameter private Parameter mode; //session descriptor parameter private Parameter sdp; private TaskChain handler; private Preprocessor preprocessor; private MgcpRequest request; //subtasks private Responder responder; private ErrorHandle errorHandle; private MgcpCall call; //error code and message private int code; private Text message; //local connection options private LocalConnectionOptions lcOptions = new LocalConnectionOptions(); private final static Logger logger = Logger.getLogger(CreateConnectionCmd.class); /** * Creates new instance of this action executor. * * @param scheduler the job scheduler. */ public CreateConnectionCmd(Scheduler scheduler) { handler = new TaskChain(2,scheduler); //intialize action's subtasks responder = new Responder(); preprocessor = new Preprocessor(); errorHandle = new ErrorHandle(); handler.add(preprocessor); handler.add(responder); this.setActionHandler(handler); this.setRollbackHandler(errorHandle); } @Override public void start(Transaction tx) { handler.clean(); handler.add(preprocessor); handler.add(responder); super.start(tx); } /** * Preprocesses MGCP create connection message and dynamically constructs * task chain. */ private class Preprocessor extends Task { public Preprocessor() { super(); } public int getQueueNumber() { return PriorityQueueScheduler.MANAGEMENT_QUEUE; } @Override public long perform() { endpoint = null; endpoint2 = null; request = (MgcpRequest) getEvent().getMessage(); Parameter z2 = request.getParameter(Parameter.SECOND_ENDPOINT); sdp = request.getParameter(Parameter.SDP); //second endpoint and sdp should not be all together in one message if (z2 != null && sdp != null) { throw new MgcpCommandException(MgcpResponseCode.PROTOCOL_ERROR, new Text("Second endpoint and remote SDP present in message")); } //getting call identifier callID = request.getParameter(Parameter.CALL_ID); if (callID == null) { throw new MgcpCommandException(MgcpResponseCode.PROTOCOL_ERROR, CALLID_MISSING); } //getting transmission mode mode = request.getParameter(Parameter.MODE); if (mode == null) { throw new MgcpCommandException(MgcpResponseCode.PROTOCOL_ERROR, MODE_MISSING); } //getting endpoint name request.getEndpoint().divide('@', endpointName); //checking the local name: if (localName.contains('*')) { throw new MgcpCommandException(MgcpResponseCode.WILDCARD_TOO_COMPLICATED, WILDCARD_ALL_NOT_ALLOWED); } if (z2 != null) { z2.getValue().divide('@', endpointName2); //checking the local name 2 if (localName2.contains('*')) { throw new MgcpCommandException(MgcpResponseCode.WILDCARD_TOO_COMPLICATED, new Text("Wildcard all is not allowed here")); } } //modify local connection options Parameter l = request.getParameter(Parameter.LOCAL_CONNECTION_OPTIONS); if (l != null) { lcOptions.setValue(l.getValue()); } else { lcOptions.setValue(null); } //for some reason they are not cleared,and may lead problems connections[0]=null; connections[1]=null; call = transaction().getCall(callID.getValue().hexToInteger(), true); try { //searching endpoint int n = transaction().find(localName, endpoints); if (n == 0) { throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, ERROR_ENDPOINT_UNAVAILAVALE); } //extract found endpoint endpoint = endpoints[0]; } catch (Exception e) { throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, ERROR_ENDPOINT_UNAVAILAVALE); } if (z2 != null) { //create two local connections try { //searching endpoint int n = transaction().find(localName2, endpoints); if (n == 0) { throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, ERROR_ENDPOINT_UNAVAILAVALE); } //extract found endpoint endpoint2 = endpoints[0]; } catch (Exception e) { throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, ERROR_ENDPOINT_UNAVAILAVALE); } try { connections[0] = endpoint.createConnection(call, ConnectionType.LOCAL,false); } catch (Exception e) { throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, new Text("Problem with connection")); } try { connections[1] = endpoint2.createConnection(call, ConnectionType.LOCAL,false); } catch (Exception e) { throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, new Text("Problem with connection")); } try { connections[0].setOtherParty(connections[1]); } catch (Exception e) { logger.error("Could not set remote peer", e); throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, new Text("Problem with joining")); } if (mode == null) { throw new MgcpCommandException(MgcpResponseCode.PROTOCOL_ERROR, new Text("Mode was not specified")); } ConnectionMode m = ConnectionMode.fromDescription(mode.getValue().toString()); try { connections[0].setMode(m); connections[1].setMode(ConnectionMode.SEND_RECV); } catch (Exception e) { throw new MgcpCommandException(MgcpResponseCode.INVALID_OR_UNSUPPORTED_MODE, new Text("Unsupported mode")); } connections[0].setDtmfClamp(lcOptions.getDtmfClamp()); } else { //create one RTP connection try { connections[0] = endpoint.createConnection(call, ConnectionType.RTP,lcOptions.getIsLocal()); connections[0].setCallAgent(getEvent().getAddress()); } catch (Exception e) { throw new MgcpCommandException(MgcpResponseCode.ENDPOINT_NOT_AVAILABLE, new Text("Problem with connection" + e.getMessage())); } if (sdp != null) { try { connections[0].setOtherParty(sdp.getValue()); } catch (IOException e) { logger.error("Could not set remote peer", e); throw new MgcpCommandException(MgcpResponseCode.MISSING_REMOTE_CONNECTION_DESCRIPTOR, SDP_NEGOTIATION_FAILED); } } else { try { connections[0].generateLocalDescriptor(lcOptions.isWebRTC()); } catch (IOException e) { throw new MgcpCommandException(MgcpResponseCode.INTERNAL_INCONSISTENCY_IN_LOCAL_SDP, new Text("Could not generate local connection descriptor.")); } } try { connections[0].setMode(mode.getValue()); } catch (ModeNotSupportedException e) { throw new MgcpCommandException(MgcpResponseCode.INVALID_OR_UNSUPPORTED_MODE, new Text("Not supported mode")); } connections[0].setDtmfClamp(lcOptions.getDtmfClamp()); } return 0; } } private class Responder extends Task { public Responder() { super(); } public int getQueueNumber() { return PriorityQueueScheduler.MANAGEMENT_QUEUE; } @Override public long perform() { MgcpEvent evt = transaction().getProvider().createEvent(MgcpEvent.RESPONSE, getEvent().getAddress()); try { MgcpResponse response = (MgcpResponse) evt.getMessage(); response.setResponseCode(MgcpResponseCode.TRANSACTION_WAS_EXECUTED); response.setResponseString(SUCCESS); //set parameters response.setParameter(Parameter.CONNECTION_ID, connections[0].getTextualID()); response.setParameter(Parameter.ENDPOINT_ID, endpoint.getFullName()); if (endpoint2 == null) { response.setParameter(Parameter.SDP, connections[0].getDescriptor()); } response.setTxID(transaction().getId()); if (endpoint2 != null) { response.setParameter(Parameter.SECOND_ENDPOINT, endpoint2.getFullName()); } if (connections[1] != null) { response.setParameter(Parameter.CONNECTION_ID2, connections[1].getTextualID()); } transaction().getProvider().send(evt); } catch (IOException e) { logger.error(e); } finally { evt.recycle(); } return 0; } } private class ErrorHandle extends Task { public ErrorHandle() { super(); } public int getQueueNumber() { return PriorityQueueScheduler.MANAGEMENT_QUEUE; } @Override public long perform() { if (endpoint != null) { endpoint.share(); if (connections[0] != null) { endpoint.deleteConnection(connections[0].getID()); } } if (endpoint2 != null) { endpoint2.share(); if (connections[1] != null) { endpoint2.deleteConnection(connections[1].getID()); } } code = ((MgcpCommandException)transaction().getLastError()).getCode(); message = ((MgcpCommandException)transaction().getLastError()).getErrorMessage(); MgcpEvent evt = transaction().getProvider().createEvent(MgcpEvent.RESPONSE, getEvent().getAddress()); try { MgcpResponse response = (MgcpResponse) evt.getMessage(); response.setResponseCode(code); response.setResponseString(message); response.setTxID(transaction().getId()); transaction().getProvider().send(evt); } catch (IOException e) { logger.error(e); } finally { evt.recycle(); } return 0; } } }