Package org.cipango.server.session

Source Code of org.cipango.server.session.SessionManager$Scheduler

// ========================================================================
// Copyright 2008-2009 NEXCOM Systems
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================

package org.cipango.server.session;

import static java.lang.Math.round;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

import javax.servlet.sip.SipSession;

import org.cipango.log.event.Events;
import org.cipango.server.ID;
import org.cipango.server.Server;
import org.cipango.server.SipRequest;
import org.cipango.server.SipResponse;
import org.cipango.server.transaction.ClientTransaction;
import org.cipango.server.transaction.ServerTransaction;
import org.cipango.server.transaction.Transaction;
import org.cipango.sipapp.SipAppContext;
import org.cipango.util.TimerList;
import org.cipango.util.TimerQueue;
import org.cipango.util.TimerTask;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.statistic.CounterStatistic;
import org.eclipse.jetty.util.statistic.SampleStatistic;

/**
* Holds and manages all SIP related sessions.
* SIP counterpart of HTTP {@link org.mortbay.jetty.SessionManager}. SIP related sessions consists of three different kinds
* of sessions, managed in a hierarchical structure: the root is the call session which contains SipApplicationSessions
* which contains in turn SipSessions.
*
* Call session is a container structure (not exposed in Sip Servlets API) used to group all data related to a SIP call.
* Call sessions are processed in a pseudo-transactional manner to control concurrency and may be scheduled for execution. 
*/
public class SessionManager extends AbstractLifeCycle
{  
    protected Map<String, CSession> _sessions = new HashMap<String, CSession>(1024);
    protected TimerQueue<CSession> _queue = new TimerQueue<CSession>(1024);
   
    private Thread _scheduler;
    private int _priorityOffset;
   
    private File _storeDir;
    private Server _server;
 
    // statistics
    private CounterStatistic _sessionsStats = new CounterStatistic();
    private SampleStatistic _sessionTimeStats = new SampleStatistic();
   
    private int _callsThreshold = 0;
     
    public SessionManager()
    {
    }
   
    @Override
    protected void doStart() throws Exception
    {
      if (_storeDir != null)
        {
            if (!_storeDir.exists())
                _storeDir.mkdir();
           
            restoreSessions();
        }
     
        new Thread(new Scheduler()).start();
        super.doStart();
    }
   
    @Override
    protected void doStop() throws Exception
    {
      super.doStop();
     
      if (_scheduler != null)
        _scheduler.interrupt();
     
      _sessions.clear();
    }
   
    public void setPriorityOffset(int priorityOffset)
    {
      _priorityOffset = priorityOffset;
    }
   
    public void setStoreDir(File storeDir)
    {
      _storeDir = storeDir;
    }
   
    public SessionScope openScope(String id)
    {
      CSession callSession = null;
     
      synchronized (_sessions)
      {
        callSession =  _sessions.get(id);
        if (callSession == null)
        {
          callSession = newSession(id);
         
          _sessions.put(callSession.getId(), callSession);
         
        _sessionsStats.increment();
         
          if (_callsThreshold > 0 && getCallSessions() == _callsThreshold)
            Events.fire(Events.CALLS_THRESHOLD_READCHED, "Calls threshold reached: " + getCallSessions());
        }
      }
      return new SessionScope(callSession._lock.tryLock() ? callSession : null);
    }
   
    public SessionScope openScope(CallSession callSession)
    {
      CSession csession = (CSession) callSession;
      csession._lock.lock();
      return new SessionScope(csession);
    }
   
    public void close(CSession callSession)
    {
      try
      {
        int holds = callSession._lock.getHoldCount();
       
        if (holds == 1)
        {
          callSession.invalidateSessionsIfReady();
         
          long time = callSession.nextExecutionTime();
 
            if (time > 0)
            {
              while (time < System.currentTimeMillis())
              {
                callSession.runTimers();
                time = callSession.nextExecutionTime();
               
                if (time < 0)
                  break;
              }
             
              if (time > 0)
              {
                synchronized (_queue)
                {
                  //_queue.remove(callSession); // TODO O(n) ?
                  _queue.offer(callSession, time);
                  _queue.notifyAll();
                }
              }
            }
            if (callSession.isDone())
            {
              boolean removed = removeSession(callSession);
              if (removed)
              {
                _sessionsStats.decrement();
                      _sessionTimeStats.set(round((System.currentTimeMillis() - callSession.getCreationTime())/1000.0));
              }
            }
            else
            {
              saveSession(callSession);
            }
        }
      }
      finally
      {
        callSession._lock.unlock();
      }
    }
   
    /**
     * @return <code>true</code> if callSession contains the session.
     */
    protected boolean removeSession(CSession callSession)
    {
      if (Log.isDebugEnabled())
      Log.debug("CallSession " + callSession.getId() + " is done.");
   
    synchronized (_sessions)
      {
        return _sessions.remove(callSession.getId()) != null;
      }
    }
   
    protected CSession newSession(String id)
    {
      return new CSession(id);
    }
   
    public CallSession get(String callId)
    {
      synchronized (_sessions)
      {
      return (CallSession) _sessions.get(callId);
    }
    }
   
    private void runTimers(CSession csession)
  {
      csession._lock.lock();
    try
    {
      csession.runTimers(); // TODO thread pool for app timers at least
    }
    finally
    {
      close(csession);
    }
  }
   
    public void saveSession(CSession session)
    {
      if (_storeDir == null || !_storeDir.exists())
        {
            return;
        }
       
        if (!_storeDir.canWrite())
        {
            Log.warn ("Unable to save session. Session persistence storage directory " + _storeDir.getAbsolutePath() + " is not writeable");
            return;
        }
       
        try
        {
            File file = new File(_storeDir, session.getId());
            if (file.exists())
                file.delete();
            file.createNewFile();
            FileOutputStream fos = new FileOutputStream(file);
            session.save(fos);
            fos.close();
        }
        catch (Exception e)
        {
            Log.warn("Problem persisting session " + session.getId(), e);
        }
    }
   
    public void restoreSessions() throws Exception
    {
      if (_storeDir == null || !_storeDir.exists())
      {
        return;
      }
     
      if (!_storeDir.canRead())
      {
        Log.warn("unable to restore sessions: cannot read from store directory " + _storeDir.getAbsolutePath());
        return;
      }
      File[] files = _storeDir.listFiles();
      for (int i = 0; files != null && i < files.length; i++)
      {
        try
        {
          FileInputStream in = new FileInputStream(files[i]);
          CallSession session = restoreSession(in);
          in.close();
          files[i].delete();
        }
        catch (Exception e)
        {
          Log.warn("problem restoring session " + files[i].getName(), e);
        }
      }
    }
   
    public CallSession restoreSession(FileInputStream fis) throws Exception
    {
      DataInputStream in = new DataInputStream(fis);
      String id = in.readUTF();
      int nbAppSessions = in.readInt();
     
      for (int i = 0; i < nbAppSessions; i++)
      {
        String appId = in.readUTF();
        System.out.println("read call: " + id + " / " + appId);
      }
           
      return null;
    }
   
    public void setServer(Server server)
  {
    _server = server;
  }
 
  public Server getServer()
  {
    return _server;
  }
 
  // ------ statistics --------
 
  public void statsReset()
  {
    _sessionsStats.reset(getCallSessions());
    _sessionTimeStats.reset();
  }
 
  public int getCallSessions()
  {
        return (int) _sessionsStats.getCurrent();
    }
   
    public int getCallSessionsMax()
    {
        return (int) _sessionsStats.getMax();
    }
       
    public long getCallSessionsTotal()
    {
        return _sessionsStats.getTotal();
    }

  public int getCallsThreshold()
  {
    return _callsThreshold;
  }

  public void setCallsThreshold(int callsThreshold)
  {
    _callsThreshold = callsThreshold;
  }
 
  /**
   * Pseudo-transactional scope for session processing.
   */
  public class SessionScope
  {
    private CSession _csession;
   
    public SessionScope(CSession csession)
    {
      _csession = csession;
    }
   
    public CallSession getCallSession()
    {
      return _csession;
    }
   
    public void close()
    {
      if (_csession != null)
        SessionManager.this.close(_csession);
    }
  }
 
    class Scheduler implements Runnable
    {
      public void run()
      {
        _scheduler = Thread.currentThread();
        String name = _scheduler.getName();
        _scheduler.setName("session-scheduler");
        int priority = _scheduler.getPriority();
       
        try
        {
          _scheduler.setPriority(priority + _priorityOffset);
          do
          {
            try
            {
              CSession csession;
              long timeout;
             
            synchronized (_queue)
            {
              csession = (CSession) _queue.peek();
              timeout = (csession != null ? csession.nextExecutionTime() - System.currentTimeMillis() : Long.MAX_VALUE);
             
              if (timeout > 0)
              {
                if (Log.isDebugEnabled())
                  Log.debug("waiting {} ms for call session: {}", timeout, csession);
                _queue.wait(timeout);
              }
              else
              {
                _queue.poll();
              }
            }
            if (timeout <= 0)
            {
              if (Log.isDebugEnabled())
                Log.debug("running timers for call session: {}", csession);
              runTimers(csession);
            }
            }
            catch (InterruptedException e) { continue; }
            catch (Throwable t) { Log.warn(t); }
          }
          while (isRunning());
        }
        finally
        {
          _scheduler.setName(name);
          _scheduler.setPriority(priority);
          _scheduler = null;
         
          String exit = "session-scheduler exited";
          if (isStarted())
            Log.warn(exit);
          else
            Log.debug(exit);
        }
      }
    }
   
    public class CSession extends TimerQueue.Node implements CallSession
    {
      protected String _id;
      protected final long _created;
     
      protected TimerList _timers = new TimerList();
     
      protected List<ServerTransaction> _serverTransactions = new ArrayList<ServerTransaction>(1);
      protected List<ClientTransaction> _clientTransactions = new ArrayList<ClientTransaction>(1);
      protected List<AppSession> _appSessions = new ArrayList<AppSession>(1);
     
      private ReentrantLock _lock = new ReentrantLock();
     
      public CSession(String id)
      {
        _id = id;
        _created = System.currentTimeMillis();
      }
     
      public String getId()
      {
        return _id;
      }
     
      public long getCreationTime()
      {
        return _created;
      }
     
      public Server getServer()
      {
        return SessionManager.this.getServer();
      }
     
      public TimerTask schedule(Runnable runnable, long delay)
      {
        assertLocked();
       
        TimerTask timer = new TimerTask(runnable, System.currentTimeMillis() + delay);
        _timers.addTimer(timer);
       
        if (Log.isDebugEnabled())
          Log.debug("scheduled timer {} for call session: {}", timer, _id);
       
        return timer;
      }
     
      public void cancel(TimerTask timer)
    {
        assertLocked();
       
        if (Log.isDebugEnabled())
          Log.debug("canceled timer {} for call session: {}", timer, _id);
       
        if (timer != null)
        {
          timer.cancel();
          _timers.remove(timer);
        }
    }

      public void addServerTransaction(ServerTransaction transaction)
      {
        _serverTransactions.add(transaction);
      }
     
      public ServerTransaction getServerTransaction(String id)
      {
        for (int i = 0; i < _serverTransactions.size(); i++)
        {
          ServerTransaction transaction = _serverTransactions.get(i);
          if (transaction.getKey().equals(id))
            return transaction;
        }
        return null;
      }
     
      public void removeServerTransaction(ServerTransaction transaction)
      {
        _serverTransactions.remove(transaction);
      }
     
      public void addClientTransaction(ClientTransaction transaction)
      {
        _clientTransactions.add(transaction);
      }
     
      public ClientTransaction getClientTransaction(String id)
      {
        for (int i = 0; i <  _clientTransactions.size(); i++)
        {
          ClientTransaction transaction = _clientTransactions.get(i);
          if (transaction.getKey().equals(id))
            return transaction;
        }
        return null;
      }
     
      public void removeClientTransaction(ClientTransaction transaction)
      {
        _clientTransactions.remove(transaction);
      }
     
      public List<ClientTransaction> getClientTransactions(SipSession session)
      {
      List<ClientTransaction> list = new ArrayList<ClientTransaction>(_clientTransactions.size());
      for (int i = 0; i < _clientTransactions.size(); i++)
      {
        ClientTransaction transaction = _clientTransactions.get(i);
        if (transaction.getRequest().session().equals(session))
          list.add(transaction);
      }
      return list;
    }

    public List<ServerTransaction> getServerTransactions(SipSession session)
    {
      List<ServerTransaction> list = new ArrayList<ServerTransaction>(_serverTransactions.size());
      for (int i = 0; i < _serverTransactions.size(); i++)
      {
        ServerTransaction transaction = _serverTransactions.get(i);
        if (transaction.getRequest().session().equals(session))
          list.add(transaction);
      }
      return list;     
    }

    public boolean hasActiveTransactions(SipSession session)
    {
      for (int i = 0; i < _clientTransactions.size(); i++)
      {
        ClientTransaction transaction = _clientTransactions.get(i);
        if (transaction.getState() < Transaction.STATE_COMPLETED
            && transaction.getRequest().session().equals(session))
          return true;
      }
      for (int i = 0; i < _serverTransactions.size(); i++)
      {
        ServerTransaction transaction = _serverTransactions.get(i);
        if (transaction.getState() < Transaction.STATE_COMPLETED
            && transaction.getRequest().session().equals(session))
          return true;
      }
      return false;
    }
   
    public AppSession createAppSession(SipAppContext context, String id)
    {
      AppSession appSession = newAppSession(this, id);
      appSession.setContext(context);
     
      _appSessions.add(appSession);
      return appSession;
    }
   
    public AppSession getAppSession(String id)
    {
      for (int i = 0; i < _appSessions.size(); i++)
      {
        AppSession appSession = _appSessions.get(i);
        if (appSession.getAppId().equals(id))
          return appSession;
      }
      return null;
    }
     
    public void removeSession(AppSession appSession)
    {
      _appSessions.remove(appSession);
    }
   
    public Session findSession(SipRequest request)
    {
      String appSessionId = request.getParameter(ID.APP_SESSION_ID_PARAMETER);
     
      if (appSessionId != null)
      {
        AppSession appSession = getAppSession(appSessionId);
        return appSession == null ? null : appSession.getSession(request);
      }
      else
      {
        for (int i = 0; i < _appSessions.size(); i++)
        {
          AppSession appSession = _appSessions.get(i);
          Session session = appSession.getSession(request);
          if (session != null)
            return session;
        }
      }
      if (Log.isDebugEnabled())
        Log.debug("could not find session for request {}", request.getRequestLine());
     
      return null;
    }

    public Session findSession(SipResponse response)
    {
      for (int i = 0; i < _appSessions.size(); i++)
      {
        AppSession appSession = _appSessions.get(i);
        Session session = appSession.getSession(response);
        if (session != null)
          return session;
      }
     
      if (Log.isDebugEnabled())
        Log.debug("could not find session for response {}", response.getRequestLine());
     
      return null;
    }
   
    // ==================
   
    protected AppSession newAppSession(CallSession callSession, String id)
    {
      return new AppSession(callSession, id);
    }
   
    protected boolean isDone()
    {
      // No check is done on transaction as
      //  - if tx is completed a timer exists
      //  - else as there is no more timers, no messages are expected to be received and as
      //    there is no more sessions, no message could be sent.
      return (_timers.isEmpty()) && (_appSessions.isEmpty());
    }
   
    protected long nextExecutionTime()
    {
      TimerTask timer = _timers.peek();
      return timer != null ? timer.getExecutionTime() : -1;
    }
   
    protected void runTimers()
    {
      long now = System.currentTimeMillis();
      TimerTask timer = null;
     
      while ((timer = _timers.getExpired(now)) != null)
      {
        if (!timer.isCancelled())
        {
          if (Log.isDebugEnabled())
            Log.debug("running timer {} for call session {}", timer, _id);
          try
          {
            timer.getRunnable().run();
          }
          catch(Throwable t)
          {
            Log.warn(t);
          }
        }
      }
    }
   
    protected void invalidateSessionsIfReady()
    {
      for (int i = _appSessions.size(); i-->0;)
      {
        _appSessions.get(i).invalidateIfReady();
      }
    }
   
    protected void save(FileOutputStream fos) throws IOException
    {
    }
   
      private void assertLocked()
      {
        if (!_lock.isHeldByCurrentThread())
          throw new IllegalStateException("CallSession " + _id + " is not locked by thread " + Thread.currentThread());
      }
     
      protected ReentrantLock getLock()
      {
        return _lock;
      }

      @SuppressWarnings("unchecked")
    public String toString()
        {
          StringBuffer sb = new StringBuffer();
          sb.append(_id
            + "[stxs= " + new ArrayList(_serverTransactions)
            + ", ctxs=" + new ArrayList(_clientTransactions)
            + ", timers=" + new ArrayList(_timers)
            + ", sessions=" + new ArrayList(_appSessions) + "]");
          return sb.toString();
        }
    }
   
    /*
    public void setCallLog(CallLog callLog)
    {
      try
      {
        if (_callLog != null)
          _callLog.stop();
      }
      catch (Exception e)
      {
        Log.warn(e);
      }
      if (getServer() != null)
        getServer().getContainer().update(this, _callLog, callLog, "calllog", true);
     
      _callLog = callLog;
     
      try
      {
        if (isStarted() && (_callLog != null))
          _callLog.start();
      }
      catch (Exception e)
      {
        throw new RuntimeException(e);
      }
    }
      public void save(FileOutputStream fos)  throws IOException
        {
          System.out.println("saving " + getId());

        DataOutputStream out = new DataOutputStream(fos);
        out.writeUTF(_id);
       
        int nbAppSessions = LazyList.size(_appSessions);
        out.writeInt(nbAppSessions);
          System.out.println("appsessions " + nbAppSessions);

        for (int i = 0; i < nbAppSessions; i++)
        {
          ((AppSession) LazyList.get(_appSessions, i)).save(out);
        }
        out.close();
        }
    }*/
}
 
TOP

Related Classes of org.cipango.server.session.SessionManager$Scheduler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.