/*
* Mobicents, Communications Middleware
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* 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 distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
*
* Boston, MA 02110-1301 USA
*/
package org.mobicents.slee.resource.diameter.sh.client;
import static org.jdiameter.client.impl.helpers.Parameters.MessageTimeOut;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import javax.management.ObjectName;
import javax.naming.NamingException;
import javax.naming.OperationNotSupportedException;
import javax.slee.Address;
import javax.slee.facilities.EventLookupFacility;
import javax.slee.management.UnrecognizedResourceAdaptorEntityException;
import javax.slee.resource.ActivityHandle;
import javax.slee.resource.BootstrapContext;
import javax.slee.resource.FailureReason;
import javax.slee.resource.Marshaler;
import javax.slee.resource.ResourceAdaptor;
import javax.slee.resource.ResourceAdaptorTypeID;
import javax.slee.resource.ResourceException;
import javax.slee.resource.SleeEndpoint;
import net.java.slee.resource.diameter.base.CreateActivityException;
import net.java.slee.resource.diameter.base.DiameterActivity;
import net.java.slee.resource.diameter.base.events.DiameterMessage;
import net.java.slee.resource.diameter.base.events.ErrorAnswer;
import net.java.slee.resource.diameter.base.events.avp.AvpNotAllowedException;
import net.java.slee.resource.diameter.base.events.avp.DiameterIdentity;
import net.java.slee.resource.diameter.sh.client.DiameterShAvpFactory;
import net.java.slee.resource.diameter.sh.client.ShClientActivity;
import net.java.slee.resource.diameter.sh.client.ShClientActivityContextInterfaceFactory;
import net.java.slee.resource.diameter.sh.client.ShClientMessageFactory;
import net.java.slee.resource.diameter.sh.client.ShClientProvider;
import net.java.slee.resource.diameter.sh.client.ShClientSubscriptionActivity;
import net.java.slee.resource.diameter.sh.client.events.SubscribeNotificationsAnswer;
import net.java.slee.resource.diameter.sh.client.events.UserDataAnswer;
import net.java.slee.resource.diameter.sh.server.events.ProfileUpdateRequest;
import net.java.slee.resource.diameter.sh.server.events.PushNotificationAnswer;
import org.apache.log4j.Logger;
import org.jdiameter.api.Answer;
import org.jdiameter.api.ApplicationId;
import org.jdiameter.api.AvpDataException;
import org.jdiameter.api.IllegalDiameterStateException;
import org.jdiameter.api.InternalException;
import org.jdiameter.api.Message;
import org.jdiameter.api.OverloadException;
import org.jdiameter.api.Peer;
import org.jdiameter.api.PeerTable;
import org.jdiameter.api.Request;
import org.jdiameter.api.RouteException;
import org.jdiameter.api.Session;
import org.jdiameter.api.SessionFactory;
import org.jdiameter.api.Stack;
import org.jdiameter.api.app.AppAnswerEvent;
import org.jdiameter.api.app.AppRequestEvent;
import org.jdiameter.api.app.AppSession;
import org.jdiameter.api.app.StateChangeListener;
import org.jdiameter.api.sh.ClientShSession;
import org.jdiameter.api.sh.ClientShSessionListener;
import org.jdiameter.api.sh.events.ProfileUpdateAnswer;
import org.jdiameter.api.sh.events.PushNotificationRequest;
import org.jdiameter.api.sh.events.SubscribeNotificationsRequest;
import org.jdiameter.api.sh.events.UserDataRequest;
import org.jdiameter.client.api.ISessionFactory;
import org.jdiameter.client.impl.app.sh.ShClientSessionImpl;
import org.jdiameter.common.api.app.IAppSessionFactory;
import org.jdiameter.common.api.app.sh.IShMessageFactory;
import org.jdiameter.common.impl.app.AppAnswerEventImpl;
import org.jdiameter.common.impl.app.AppRequestEventImpl;
import org.mobicents.diameter.stack.DiameterListener;
import org.mobicents.diameter.stack.DiameterStackMultiplexerMBean;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.resource.ResourceAdaptorActivityContextInterfaceFactory;
import org.mobicents.slee.resource.ResourceAdaptorEntity;
import org.mobicents.slee.resource.ResourceAdaptorState;
import org.mobicents.slee.resource.diameter.base.DiameterActivityHandle;
import org.mobicents.slee.resource.diameter.base.DiameterActivityImpl;
import org.mobicents.slee.resource.diameter.base.DiameterAvpFactoryImpl;
import org.mobicents.slee.resource.diameter.base.DiameterMessageFactoryImpl;
import org.mobicents.slee.resource.diameter.base.events.DiameterMessageImpl;
import org.mobicents.slee.resource.diameter.base.events.ErrorAnswerImpl;
import org.mobicents.slee.resource.diameter.base.events.ExtensionDiameterMessageImpl;
import org.mobicents.slee.resource.diameter.sh.client.events.ProfileUpdateAnswerImpl;
import org.mobicents.slee.resource.diameter.sh.client.events.PushNotificationRequestImpl;
import org.mobicents.slee.resource.diameter.sh.client.events.SubscribeNotificationsAnswerImpl;
import org.mobicents.slee.resource.diameter.sh.client.events.UserDataAnswerImpl;
import org.mobicents.slee.resource.diameter.sh.client.handlers.ShClientSessionListener;
import org.mobicents.slee.resource.diameter.sh.server.events.ProfileUpdateRequestImpl;
import org.mobicents.slee.resource.diameter.sh.server.events.PushNotificationAnswerImpl;
import org.mobicents.slee.resource.diameter.sh.server.events.SubscribeNotificationsRequestImpl;
import org.mobicents.slee.resource.diameter.sh.server.events.UserDataRequestImpl;
/**
*
* <br>Project: mobicents-diameter-server
* <br>11:08:09 AM May 26, 2009
* <br>
*
* Mobicents Diameter Sh (Client-side) Resource Adaptor
*
* @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
* @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
*/
public class DiameterShClientResourceAdaptor implements ResourceAdaptor, DiameterListener , ShClientSessionListener{
private static final long serialVersionUID = 1L;
private static transient Logger logger = Logger.getLogger(DiameterShClientResourceAdaptor.class);
private Stack stack;
private SessionFactory sessionFactory = null;
private long messageTimeout = 5000;
//private DiameterStackMultiplexerProxyMBeanImpl proxy = null;
private DiameterStackMultiplexerMBean diameterMux = null;
private ResourceAdaptorState state;
/**
* The BootstrapContext provides the resource adaptor with the required
* capabilities in the SLEE to execute its work. The bootstrap context is
* implemented by the SLEE. The BootstrapContext object holds references to
* a number of objects that are of interest to many resource adaptors. For
* further information see JSLEE v1.1 Specification Page 305. The
* bootstrapContext will be set in entityCreated() method.
*/
private transient BootstrapContext bootstrapContext = null;
/**
* The SLEE endpoint defines the contract between the SLEE and the resource
* adaptor that enables the resource adaptor to deliver events
* asynchronously to SLEE endpoints residing in the SLEE. This contract
* serves as a generic contract that allows a wide range of resources to be
* plugged into a SLEE environment via the resource adaptor architecture.
* For further information see JSLEE v1.1 Specification Page 307 The
* sleeEndpoint will be initialized in entityCreated() method.
*/
private transient SleeEndpoint sleeEndpoint = null;
/**
* the EventLookupFacility is used to look up the event id of incoming
* events
*/
private transient EventLookupFacility eventLookup = null;
private static final Map<Integer, String> events;
static
{
Map<Integer, String> eventsTemp = new HashMap<Integer, String>();
eventsTemp.put(ProfileUpdateRequest.commandCode, "ProfileUpdate");
eventsTemp.put(PushNotificationAnswer.commandCode, "PushNotification");
eventsTemp.put(UserDataAnswer.commandCode, "UserData");
eventsTemp.put(SubscribeNotificationsAnswer.commandCode, "SubscribeNotifications");
events = Collections.unmodifiableMap(eventsTemp);
}
/**
* A link to the DiameterProvider which then will be exposed to Sbbs
*/
//private transient DiameterProvider raProvider = null;
private transient ShClientProviderImpl clientProvider = null;
/**
* The list of activites stored in this resource adaptor. If this resource
* adaptor were a distributed and highly available solution, this storage
* were one of the candidates for distribution.
*/
private transient ConcurrentHashMap<ActivityHandle, DiameterActivity> activities = null;
/**
* The activity context interface factory defined in
* DiameterRAActivityContextInterfaceFactoryImpl
*/
private transient ShClientActivityContextInterfaceFactory acif = null;
private DiameterAvpFactoryImpl diameterAvpFactory = new DiameterAvpFactoryImpl();
/**
* implements javax.slee.resource.ResourceAdaptor Please refer to JSLEE v1.1
* Specification Page 301 for further information. <br>
* The SLEE calls this method to inform the resource adaptor that the SLEE
* has completed activity end processing for the activity represented by the
* activity handle. The resource adaptor should release any resource related
* to this activity as the SLEE will not ask for it again.
*/
public void activityEnded(ActivityHandle handle)
{
logger.info("Diameter ShClient RA :: activityEnded :: handle[" + handle + ".");
if (this.activities != null)
{
synchronized (this.activities)
{
this.activities.remove(handle);
}
}
}
/**
* implements javax.slee.resource.ResourceAdaptor Please refer to JSLEE v1.1
* Specification Page 301 for further information. <br>
* The SLEE calls this method to inform the resource adaptor that the
* activity�s Activity Context object is no longer attached to any SBB
* entities and is no longer referenced by any SLEE Facilities. This enables
* the resource adaptor to implicitly end the Activity object.
*/
public void activityUnreferenced(ActivityHandle handle)
{
logger.info("Diameter ShClient RA :: activityUnreferenced :: handle[" + handle + "].");
this.activityEnded(handle);
}
/**
* implements javax.slee.resource.ResourceAdaptor The JSLEE v1.1
* Specification does not include entityActivated(). However, the API
* description of JSLEE v1.1 does include this method already. So, the
* documentation follows the code. <br>
* This method is called in context of project Mobicents in context of
* resource adaptor activation. More precisely,
* org.mobicents.slee.resource.ResourceAdaptorEntity.activate() calls this
* method entityActivated(). This method signals the resource adaptor the
* transition from state "INACTIVE" to state "ACTIVE".
*/
public void entityActivated() throws ResourceException
{
logger.info("Diameter ShClient RA :: entityActivated.");
try {
logger.info("Activating Diameter ShClient RA Entity");
initializeNamingContext();
this.activities = new ConcurrentHashMap();
this.state = ResourceAdaptorState.CONFIGURED;
}
catch (Exception e) {
logger.error("Error Configuring Diameter ShClient RA Entity", e);
}
try
{
// Initialize the protocol stack
ObjectName diameterMultiplexerObjectName = new ObjectName("diameter.mobicents:service=DiameterStackMultiplexer");
Object[] params = new Object[]{};
String[] signature = new String[]{};
String operation = "getMultiplexerMBean";
Object object = SleeContainer.lookupFromJndi().getMBeanServer().invoke( diameterMultiplexerObjectName, operation, params, signature );
if(object instanceof DiameterStackMultiplexerMBean)
{
this.diameterMux = (DiameterStackMultiplexerMBean) object;
}
initStack();
// Resource Adaptor ready to rumble!
this.state = ResourceAdaptorState.ACTIVE;
this.sessionFactory = this.stack.getSessionFactory();
((ISessionFactory) sessionFactory).registerAppFacory(ClientShSession.class, new ShClientSessionFactory(this));
}
catch (Exception e) {
logger.error("Error Activating Diameter ShClient RA Entity", e);
}
}
/**
* implements javax.slee.resource.ResourceAdaptor Please refer to JSLEE v1.1
* Specification Page 298 for further information. <br>
* This method is called by the SLEE when a resource adaptor object instance
* is bootstrapped, either when a resource adaptor entity is created or
* during SLEE startup. The SLEE implementation will construct the resource
* adaptor object and then invoke the entityCreated method before any other
* operations can be invoked on the resource adaptor object.
*/
public void entityCreated(BootstrapContext bootstrapContext) throws ResourceException
{
logger.info("Diameter ShClient RA :: entityCreated :: bootstrapContext[" + bootstrapContext + "].");
this.bootstrapContext = bootstrapContext;
this.sleeEndpoint = bootstrapContext.getSleeEndpoint();
this.eventLookup = bootstrapContext.getEventLookupFacility();
this.state = ResourceAdaptorState.UNCONFIGURED;
}
/**
* implements javax.slee.resource.ResourceAdaptor The JSLEE v1.1
* Specification does not include entityDeactivated(). However, the API
* description of JSLEE v1.1 does include this method already. So, the
* documentation follows the code. <br>
* This method is called in context of project Mobicents in context of
* resource adaptor deactivation. More precisely,
* org.mobicents.slee.resource.ResourceAdaptorEntity.deactivate() calls this
* method entityDeactivated(). The method call is done AFTER the call to
* entityDeactivating(). This method signals the resource adaptor the
* transition from state "STOPPING" to state "INACTIVE".
*/
public void entityDeactivated()
{
logger.info("Diameter ShClient RA :: entityDeactivated.");
logger.info("Diameter ShClient RA :: Cleaning RA Activities.");
synchronized (this.activities)
{
activities.clear();
}
activities = null;
logger.info("Diameter ShClient RA :: Cleaning naming context.");
try
{
cleanNamingContext();
}
catch (NamingException e) {
logger.error("Diameter ShClient RA :: Cannot unbind naming context.", e);
}
logger.info("Diameter ShClient RA :: RA Stopped.");
}
/**
* This method is called in context of project Mobicents in context of
* resource adaptor deactivation. More precisely,
* org.mobicents.slee.resource.ResourceAdaptorEntity.deactivate() calls this
* method entityDeactivating() PRIOR to invoking entityDeactivated(). This
* method signals the resource adaptor the transition from state "ACTIVE" to
* state "STOPPING".
*/
public void entityDeactivating()
{
logger.info("Diameter ShClient RA :: entityDeactivating.");
this.state = ResourceAdaptorState.STOPPING;
try
{
diameterMux.unregisterListener(this);
}
catch (Exception e) {
logger.error("Failed to unregister ShClient RA from Diameter Mux.", e);
}
synchronized (this.activities)
{
for (ActivityHandle activityHandle : activities.keySet())
{
try
{
logger.info("Ending activity [" + activityHandle + "]");
activities.get(activityHandle).endActivity();
}
catch (Exception e) {
logger.error("Error Deactivating Activity", e);
}
}
}
logger.info("Diameter ShClient RA :: entityDeactivating completed.");
}
/**
* implements javax.slee.resource.ResourceAdaptor Please refer to JSLEE v1.1
* Specification Page 299 for further information. <br>
* This method is called by the SLEE when a resource adaptor object instance
* is being removed, either when a resource adaptor entity is deleted or
* during SLEE shutdown. When receiving this invocation the resource adaptor
* object is expected to close any system resources it has allocated.
*/
public void entityRemoved()
{
// Clean up!
this.acif = null;
this.activities = null;
this.bootstrapContext = null;
this.eventLookup = null;
//this.raProvider = null;
this.sleeEndpoint = null;
this.stack = null;
logger.info("Diameter ShClient RA :: entityRemoved.");
}
private void initializeNamingContext() throws NamingException
{
// get the reference to the SLEE container from JNDI
SleeContainer container = SleeContainer.lookupFromJndi();
// get the entities name
String entityName = bootstrapContext.getEntityName();
ResourceAdaptorEntity resourceAdaptorEntity;
try
{
resourceAdaptorEntity = ((ResourceAdaptorEntity) container.getResourceAdaptorEntity(entityName));
}
catch (UnrecognizedResourceAdaptorEntityException uraee) {
throw new NamingException("Failure setting up Naming Context. RA Entity not found.");
}
ResourceAdaptorTypeID raTypeId = resourceAdaptorEntity.getInstalledResourceAdaptor().getRaType().getResourceAdaptorTypeID();
// create the ActivityContextInterfaceFactory
acif = new ShClientActivityContextInterfaceFactoryImpl(resourceAdaptorEntity.getServiceContainer(), entityName);
// set the ActivityContextInterfaceFactory
resourceAdaptorEntity.getServiceContainer().getActivityContextInterfaceFactories().put(raTypeId, acif);
try
{
if (this.acif != null)
{
// parse the string = java:slee/resources/RAFrameRA/raframeacif
String jndiName = ((ResourceAdaptorActivityContextInterfaceFactory) acif).getJndiName();
int begind = jndiName.indexOf(':');
int toind = jndiName.lastIndexOf('/');
String prefix = jndiName.substring(begind + 1, toind);
String name = jndiName.substring(toind + 1);
logger.info("Diameter ShClient RA :: Registering in JNDI :: Prefix[" + prefix + "], Name[" + name + "].");
SleeContainer.registerWithJndi(prefix, name, this.acif);
logger.info("Diameter ShClient RA :: Registered in JNDI successfully.");
}
}
catch (IndexOutOfBoundsException iobe) {
logger.info("Failure initializing name context.", iobe);
}
}
/**
* Clean the JNDI naming context
*/
private void cleanNamingContext() throws NamingException
{
try
{
if (this.acif != null)
{
// parse the string = java:slee/resources/RAFrameRA/raframeacif
String jndiName = ((ResourceAdaptorActivityContextInterfaceFactory) this.acif).getJndiName();
// remove "java:" prefix
int begind = jndiName.indexOf(':');
String javaJNDIName = jndiName.substring(begind + 1);
logger.info("Diameter ShClient RA :: Unregistering from JNDI :: Name[" + javaJNDIName + "].");
SleeContainer.unregisterWithJndi(javaJNDIName);
logger.info("Diameter ShClient RA :: Unregistered from JNDI successfully.");
}
}
catch (IndexOutOfBoundsException iobe) {
logger.error("Failure cleaning name context.", iobe);
}
}
/**
* Initializes the RA Diameter Stack.
*
* @throws Exception
*/
private synchronized void initStack() throws Exception
{
// Regsiter in the Mux as a Sh app listener.
this.diameterMux.registerListener(this, new ApplicationId[]{ApplicationId.createByAuthAppId(10415L, 16777217L)});
// Get the stack (should not mess with)
this.stack = this.diameterMux.getStack();
this.messageTimeout = stack.getMetaData().getConfiguration().getLongValue(MessageTimeOut.ordinal(), (Long) MessageTimeOut.defValue());
this.clientProvider=new ShClientProviderImpl(this);
logger.info("Diameter ShClient RA :: Successfully initialized stack.");
}
/**
* implements javax.slee.resource.ResourceAdaptor Please refer to JSLEE v1.1
* Specification Page 300 for further information. <br>
* The SLEE calls this method to inform the resource adaptor object that the
* specified event was processed unsuccessfully by the SLEE. Event
* processing can fail if, for example, the SLEE doesn�t have enough
* resource to process the event, a SLEE node fails during event processing
* or a system level failure prevents the SLEE from committing transactions.
*/
public void eventProcessingFailed(ActivityHandle handle, Object event, int eventID, Address address, int flags, FailureReason reason)
{
logger.info("Diameter ShClient RA :: eventProcessingFailed :: handle[" + handle + "], event[" + event + "], eventID[" + eventID + "], address[" + address + "], flags["
+ flags + "], reason[" + reason + "].");
}
/**
* implements javax.slee.resource.ResourceAdaptor Please refer to JSLEE v1.1
* Specification Page 300 for further information. <br>
* The SLEE calls this method to inform the resource adaptor object that the
* specified event was processed successfully by the SLEE. An event is
* considered to be processed successfully if the SLEE has attempted to
* deliver the event to all interested SBBs.
*/
public void eventProcessingSuccessful(ActivityHandle handle, Object event, int eventID, Address address, int flags)
{
logger.info("Diameter ShClient RA :: eventProcessingSuccessful :: handle[" + handle + "], event[" + event + "], eventID[" + eventID + "], address[" + address + "], flags["
+ flags + "].");
}
public Object getActivity(ActivityHandle activityHandle)
{
return this.activities.get(activityHandle);
}
public ActivityHandle getActivityHandle(Object activity)
{
logger.info("Diameter ShClient RA :: getActivityHandle :: activity[" + activity + "].");
if (!(activity instanceof DiameterActivity))
{
logger.warn( "Diameter ShClient RA :: getActivityHandle :: Invalid Activity passed (not instanceof DiameterActivity)." );
return null;
}
DiameterActivity inActivity = (DiameterActivity) activity;
for (Entry<ActivityHandle, DiameterActivity> activityInfo : this.activities.entrySet())
{
Object curActivity = activityInfo.getValue();
if (curActivity.equals(inActivity))
return activityInfo.getKey();
}
return null;
}
public Marshaler getMarshaler()
{
// TODO Auto-generated method stub
return null;
}
public Object getSBBResourceAdaptorInterface(String className)
{
return this.clientProvider;
}
public Answer processRequest(Request request)
{
try
{
clientProvider.createActivity(request);
//baranowb: do nothing here, if its valid it should be processed, f not we will get exception
}
catch (CreateActivityException e) {
logger.error("Failed creating activity to process request.", e);
}
// returning null so we can answer later
return null;
}
public void receivedSuccessMessage(Request req, Answer ans)
{
logger.info("Diameter ShClient RA :: receivedSuccessMessage :: " + "Request[" + req + "], Answer[" + ans + "].");
try
{
logger.info("Received Message Result-Code: " + ans.getResultCode().getUnsigned32());
}
catch (AvpDataException ignore) {
// ignore, this was just for informational purposes...
}
}
public void timeoutExpired(Request req)
{
logger.info("Diameter Base RA :: timeoutExpired :: " + "Request[" + req + "].");
// Message delivery timed out - we have to remove activity
DiameterActivityHandle ah = new DiameterActivityHandle(req.getSessionId());
try
{
activities.get(ah).endActivity();
}
catch (Exception e) {
logger.error("Failure processing timeout message.", e);
}
}
/**
* Method for obtaining the Peers the RA is currently conneceted to.
*
* @return an array of DiameterIdentity AVPs representing the peers.
*/
public DiameterIdentity[] getConnectedPeers()
{
if (this.stack != null)
{
try
{
// Get the list of peers from the stack
List<Peer> peers = stack.unwrap(PeerTable.class).getPeerTable();
DiameterIdentity[] result = new DiameterIdentity[peers.size()];
int i = 0;
// Get each peer from the list and make a DiameterIdentity
for (Peer peer : peers)
{
DiameterIdentity identity = new DiameterIdentity(peer.getUri().toString());
result[i++] = identity;
}
return result;
}
catch (Exception e)
{
logger.error("Failure getting peer list.", e);
}
}
return null;
}
/**
* Method for firing event to SLEE
*
* @param handle
* the handle for the activity where event will be fired on
* @param name
* the unqualified Event name
* @param request
* the request that will be wrapped in the event, if any
* @param answer
* the answer that will be wrapped in the event, if any
*/
public void fireEvent(ActivityHandle handle, String name, Request request, Answer answer)
{
try
{
int eventID = eventLookup.getEventID(name, "java.net", "0.8");
DiameterMessage event = (DiameterMessage) createEvent(request, answer);
sleeEndpoint.fireEvent(handle, event, eventID, null);
}
catch (Exception e) {
logger.error("Can not send event", e);
}
}
/**
* Create Event object from request/answer
*
* @param request
* the request to create the event from, if any.
* @param answer
* the answer to create the event from, if any.
* @return a DiameterMessage object wrapping the request/answer
* @throws OperationNotSupportedException
*/
public DiameterMessage createEvent(Request request, Answer answer) throws OperationNotSupportedException {
if (request == null && answer == null) {
return null;
}
int commandCode = (request != null ? request.getCommandCode() : answer.getCommandCode());
if (answer != null && answer.isError()) {
return new ErrorAnswerImpl(answer);
}
switch (commandCode) {
case PushNotificationRequestImpl.commandCode: // PNR/PNA
return request != null ? new PushNotificationRequestImpl(request) : new PushNotificationAnswerImpl(answer);
case ProfileUpdateRequestImpl.commandCode: // PUR/PUA
return request != null ? new ProfileUpdateRequestImpl(request) : new ProfileUpdateAnswerImpl(answer);
case SubscribeNotificationsRequestImpl.commandCode: // SNR/SNA
return request != null ? new SubscribeNotificationsRequestImpl(request) : new SubscribeNotificationsAnswerImpl(answer);
case net.java.slee.resource.diameter.sh.server.events.UserDataRequest.commandCode: // UDR/UDA
return request != null ? new UserDataRequestImpl(request) : new UserDataAnswerImpl(answer);
default:
return new ExtensionDiameterMessageImpl(request != null ? request : answer);
}
}
/**
* Method for performing tasks when activity is created, such as informing
* SLEE about it and storing into internal map.
*
* @param ac
* the activity that has been created
*/
private void activityCreated(DiameterActivity ac)
{
try
{
// Inform SLEE that Activity Started
DiameterActivityImpl activity = (DiameterActivityImpl) ac;
sleeEndpoint.activityStarted(activity.getActivityHandle());
// Put it into our activites map
activities.put(activity.getActivityHandle(), activity);
logger.info("Activity started [" + activity.getActivityHandle() + "]");
}
catch (Exception e) {
logger.error("Error creating/starting activity.", e);
}
}
private class ShClientSessionFactory implements IAppSessionFactory, ClientShSessionListener, StateChangeListener, IShMessageFactory
{
DiameterShClientResourceAdaptor ra = null;
public ShClientSessionFactory(DiameterShClientResourceAdaptor ra)
{
super();
this.ra = ra;
}
public AppSession getNewSession(String sessionId, Class<? extends AppSession> aClass, ApplicationId applicationId, Object[] args)
{
try
{
if (aClass == ClientShSession.class)
{
ShClientSessionImpl clientSession=null;
if(args!=null && args.length>1 && args[0] instanceof Request)
{
Request request = (Request) args[0];
clientSession=new ShClientSessionImpl(request.getSessionId(),this,sessionFactory,this);
}
else
{
clientSession=new ShClientSessionImpl(null,this,sessionFactory,this);
}
clientSession.addStateChangeNotification(this);
return clientSession;
}
else
{
throw new IllegalArgumentException("Wrong session class!!["+aClass+"]. Supported["+ClientShSession.class+"]");
}
}
catch (Exception e) {
logger.error("Failure to obtain new Accounting Session.", e);
}
return null;
}
public void doOtherEvent(AppSession appSession, AppRequestEvent request, AppAnswerEvent answer) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException
{
logger.info("Diameter ShClient RA :: doOtherEvent :: appSession[" + appSession + "], Request[" + request + "], Answer[" + answer + "]");
DiameterActivityHandle handle = new DiameterActivityHandle(appSession.getSessions().get(0).getSessionId());
if (answer != null)
{
if(answer.getMessage().isError())
this.ra.fireEvent(handle, _ErrorAnswer, null, (Answer) answer.getMessage());
else
this.ra.fireEvent(handle, _ExtensionDiameterMessage, null, (Answer) answer.getMessage());
}
else
{
this.ra.fireEvent(handle, _ExtensionDiameterMessage, (Request) request.getMessage(), null);
}
}
public void doProfileUpdateAnswerEvent(ClientShSession appSession, org.jdiameter.api.sh.events.ProfileUpdateRequest request, ProfileUpdateAnswer answer) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException
{
logger.info("doProfileUpdateAnswerEvent :: appSession[" + appSession + "], request[" + request + "], answer[" + answer + "]");
DiameterActivityHandle handle = new DiameterActivityHandle(appSession.getSessions().get(0).getSessionId());
if(answer.getMessage().isError())
this.ra.fireEvent(handle, _ErrorAnswer, null, (Answer) answer.getMessage());
else
this.ra.fireEvent(handle, _ProfileUpdateAnswer, null, (Answer) answer.getMessage());
}
public void doPushNotificationRequestEvent(ClientShSession appSession, PushNotificationRequest request) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException
{
logger.info("doPushNotificationRequestEvent :: appSession[" + appSession + "], request[" + request + "], answer[" + null + "]");
DiameterActivityHandle handle = new DiameterActivityHandle(appSession.getSessions().get(0).getSessionId());
this.ra.fireEvent(handle, _PushNotificationRequest, (Request) request.getMessage(), null);
}
public void doSubscribeNotificationsAnswerEvent(ClientShSession appSession, SubscribeNotificationsRequest request, org.jdiameter.api.sh.events.SubscribeNotificationsAnswer answer) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException
{
logger.info("doSubscribeNotificationsAnswerEvent :: appSession[" + appSession + "], request[" + request + "], answer[" + answer + "]");
DiameterActivityHandle handle = new DiameterActivityHandle(appSession.getSessions().get(0).getSessionId());
if(answer.getMessage().isError())
this.ra.fireEvent(handle, _ErrorAnswer, null, (Answer) answer.getMessage());
else
this.ra.fireEvent(handle, _SubscribeNotificationsAnswer, null, (Answer) answer.getMessage());
}
public void doUserDataAnswerEvent(ClientShSession appSession, UserDataRequest request, org.jdiameter.api.sh.events.UserDataAnswer answer) throws InternalException, IllegalDiameterStateException, RouteException, OverloadException
{
logger.info("doUserDataAnswerEvent :: appSession[" + appSession + "], request[" + request + "], answer[" + answer + "]");
DiameterActivityHandle handle = new DiameterActivityHandle(appSession.getSessions().get(0).getSessionId());
if(answer.getMessage().isError())
this.ra.fireEvent(handle, _ErrorAnswer, null, (Answer) answer.getMessage());
else
this.ra.fireEvent(handle, _UserDataAnswer, null, (Answer) answer.getMessage());
}
public void stateChanged(Enum oldState, Enum newState)
{
logger.info("Diameter Sh ClientSessionFactory :: stateChanged :: oldState[" + oldState + "], newState[" + newState + "]");
}
public AppAnswerEvent createProfileUpdateAnswer(Answer answer)
{
return new AppAnswerEventImpl(answer);
}
public AppRequestEvent createProfileUpdateRequest(Request request)
{
return new AppRequestEventImpl(request);
}
public AppAnswerEvent createPushNotificationAnswer(Answer answer)
{
return new AppAnswerEventImpl(answer);
}
public AppRequestEvent createPushNotificationRequest(Request request)
{
return new AppRequestEventImpl(request);
}
public AppAnswerEvent createSubscribeNotificationsAnswer(Answer answer)
{
return new AppAnswerEventImpl(answer);
}
public AppRequestEvent createSubscribeNotificationsRequest(Request request)
{
return new AppRequestEventImpl(request);
}
public AppAnswerEvent createUserDataAnswer(Answer answer)
{
return new AppAnswerEventImpl(answer);
}
public AppRequestEvent createUserDataRequest(Request request)
{
return new AppRequestEventImpl(request);
}
public long getApplicationId()
{
return ShClientMessageFactory._SH_APP_ID;
}
public long getMessageTimeout()
{
return this.ra.messageTimeout;
}
}
private class ShClientProviderImpl implements ShClientProvider
{
DiameterShClientResourceAdaptor ra = null;
public ShClientProviderImpl(DiameterShClientResourceAdaptor ra)
{
super();
this.ra = ra;
}
/**
* This method is for internal use only, it creates activities for
* requests that do not fall in certain app range or no activitis were
* found <br>
* It should distinguish between initial requests, requests with
* diferent domains etc. - respo for createing XXXServerSession or basic
* diameter activity lies in this method
*
* @param message
* @return
*/
DiameterActivity createActivity(Message message) throws CreateActivityException
{
String sessionId = message.getSessionId();
DiameterActivityHandle handle = new DiameterActivityHandle(sessionId);
if (activities.keySet().contains(handle))
{
return activities.get(handle);
}
else
{
//FIXME: baranowb: here we can receive only (valid) PNR, other message are errors?
if(message.getCommandCode() != PushNotificationRequestImpl.commandCode)
{
throw new CreateActivityException("Cant create activity for unexpected message:\r\n" + message);
}
ShClientSubscriptionActivityImpl activity = (ShClientSubscriptionActivityImpl) this.createShClientSubscriptionActivity(new PushNotificationRequestImpl( message));
return activity;
}
}
private ShClientSubscriptionActivity createShClientSubscriptionActivity(net.java.slee.resource.diameter.sh.client.events.PushNotificationRequest pushNotificationRequest)
{
ClientShSession session = null;
try
{
session = ((ISessionFactory) stack.getSessionFactory()).getNewAppSession(null, null, ClientShSession.class, null);
if (session == null)
{
logger.error("Failure creating Sh-Client Subscription Session (null).");
return null;
}
}
catch (Exception e)
{
logger.error("Failure creating Sh-Client Subscription Session.", e);
return null;
}
DiameterMessageFactoryImpl msgFactory = new DiameterMessageFactoryImpl(session.getSessions().get(0), stack, null, null);
ShClientSubscriptionActivityImpl activity = new ShClientSubscriptionActivityImpl(msgFactory, new ShClientMessageFactoryImpl(session.getSessions().get(0), stack), diameterAvpFactory, new DiameterShAvpFactoryImpl(diameterAvpFactory), session, messageTimeout, null, null, sleeEndpoint);
activity.fetchSessionData(pushNotificationRequest,true);
activity.setSessionListener(ra);
activityCreated(activity);
//FIXME: baranowb: this is akward, jdiam has weird api
//This is trick to trigger fire and state machine
((ShClientSessionImpl)session).processRequest((Request) ((DiameterMessageImpl)pushNotificationRequest).getGenericData());
return activity;
}
public ShClientActivity createShClientActivity() throws CreateActivityException
{
ClientShSession session = null;
try
{
session = ((ISessionFactory) stack.getSessionFactory()).getNewAppSession(null, null, ClientShSession.class, null);
if (session == null)
{
logger.error("Failure creating Sh-Client Session (null).");
return null;
}
}
catch (Exception e)
{
logger.error("Failure creating Sh-Client Session.", e);
return null;
}
DiameterMessageFactoryImpl msgFactory = new DiameterMessageFactoryImpl(session.getSessions().get(0), stack, null, null);
ShClientActivityImpl activity = new ShClientActivityImpl(msgFactory, new ShClientMessageFactoryImpl(session.getSessions().get(0), stack), diameterAvpFactory, new DiameterShAvpFactoryImpl(diameterAvpFactory), session, messageTimeout, null, null, sleeEndpoint);;
activity.setSessionListener(ra);
activityCreated(activity);
return activity;
}
public ShClientMessageFactory getClientMessageFactory()
{
return new ShClientMessageFactoryImpl(stack);
}
public net.java.slee.resource.diameter.sh.client.events.ProfileUpdateAnswer profileUpdateRequest(ProfileUpdateRequest message) throws IOException {
// This is sync, we dont care about activities or FSM, someone else
// should care...
if (message == null)
throw new IOException("Cant send null message");
try {
String sessionID = message.getSessionId();
if (sessionID == null) {
throw new IllegalArgumentException("Session Id must not be null.");
}
Session session = stack.getSessionFactory().getNewSession(sessionID);
Future<Message> f = session.send(((DiameterMessageImpl) message).getGenericData());
return new ProfileUpdateAnswerImpl(f.get());
} catch (AvpNotAllowedException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new IOException("Failed to send due to: " + e);
}
}
public SubscribeNotificationsAnswer subscribeNotificationsRequest(net.java.slee.resource.diameter.sh.server.events.SubscribeNotificationsRequest message) throws IOException {
// This is sync, we dont care about activities or FSM, someone else
// should care...
if (message == null)
throw new IOException("Cant send null message");
try {
String sessionID = message.getSessionId();
if (sessionID == null) {
throw new IllegalArgumentException("Session Id must not be null.");
}
Session session = stack.getSessionFactory().getNewSession(sessionID);
Future<Message> f = session.send(((DiameterMessageImpl) message).getGenericData());
return new SubscribeNotificationsAnswerImpl(f.get());
} catch (AvpNotAllowedException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new IOException("Failed to send due to: " + e);
}
}
public UserDataAnswer userDataRequest(net.java.slee.resource.diameter.sh.server.events.UserDataRequest message) throws IOException {
// This is sync, we dont care about activities or FSM, someone else
// should care...
if (message == null)
throw new IOException("Cant send null message");
try {
String sessionID = message.getSessionId();
if (sessionID == null) {
throw new IllegalArgumentException("Session Id must not be null.");
}
Session session = stack.getSessionFactory().getNewSession(sessionID);
Future<Message> f = session.send(((DiameterMessageImpl) message).getGenericData());
return new UserDataAnswerImpl(f.get());
} catch (AvpNotAllowedException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
throw new IOException("Failed to send due to: " + e);
}
}
public ShClientSubscriptionActivity createShClientSubscriptionActivity() throws CreateActivityException
{
ClientShSession session = null;
try
{
session = ((ISessionFactory) stack.getSessionFactory()).getNewAppSession(null, null, ClientShSession.class, null);
if (session == null)
{
logger.error("Failure creating Sh-Client Subscription Session (null).");
return null;
}
}
catch (Exception e)
{
logger.error("Failure creating Sh-Client Subscription Session.", e);
return null;
}
DiameterMessageFactoryImpl msgFactory = new DiameterMessageFactoryImpl(session.getSessions().get(0), stack, null, null);
ShClientSubscriptionActivityImpl activity = new ShClientSubscriptionActivityImpl(msgFactory, new ShClientMessageFactoryImpl(session.getSessions().get(0), stack), diameterAvpFactory, new DiameterShAvpFactoryImpl(diameterAvpFactory), session, messageTimeout, null, null, sleeEndpoint);
activity.setSessionListener(ra);
activityCreated(activity);
return activity;
}
public DiameterShAvpFactory getClientAvpFactory()
{
return new DiameterShAvpFactoryImpl(diameterAvpFactory);
}
public DiameterIdentity[] getConnectedPeers()
{
return this.ra.getConnectedPeers();
}
public int getPeerCount()
{
return getConnectedPeers().length;
}
}
// ################################
// # SERVICE FILTERING #
// ################################
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#queryLiveness(javax.slee.resource.ActivityHandle)
*/
public void queryLiveness(ActivityHandle handle)
{
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#serviceActivated(java.lang.String)
*/
public void serviceActivated(String serviceKey)
{
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#serviceDeactivated(java.lang.String)
*/
public void serviceDeactivated(String serviceKey)
{
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#serviceInstalled(java.lang.String, int[], java.lang.String[])
*/
public void serviceInstalled(String serviceKey, int[] eventIDs, String[] resourceOptions)
{
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#serviceUninstalled(java.lang.String)
*/
public void serviceUninstalled(String serviceKey)
{
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
* @see org.mobicents.slee.resource.diameter.sh.client.handlers.ShClientSessionListener#sessionDestroyed(java.lang.String, org.jdiameter.api.sh.ClientShSession)
*/
public void sessionDestroyed(String sessionId,ClientShSession session)
{
try
{
this.sleeEndpoint.activityEnding(getActivityHandle(sessionId));
}
catch (Exception e) {
logger.error( "Failed to end activity with handle[" + getActivityHandle(sessionId) );
}
}
protected DiameterActivityHandle getActivityHandle(String sessionId)
{
return new DiameterActivityHandle(sessionId);
}
}