All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.caucho.v5.amp.journal.JournalImpl Maven / Gradle / Ivy

There is a newer version: 1.0.1
Show newest version
/*
 * Copyright (c) 1998-2015 Caucho Technology -- all rights reserved
 *
 * This file is part of Baratine(TM)
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Baratine is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Baratine 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, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Baratine; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.v5.amp.journal;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.caucho.v5.amp.deliver.QueueDeliver;
import com.caucho.v5.amp.message.OnSaveMessage;
import com.caucho.v5.amp.message.OnSaveRequestMessage;
import com.caucho.v5.amp.message.ReplayQueryMessage;
import com.caucho.v5.amp.message.ReplaySendMessage;
import com.caucho.v5.amp.spi.InboxAmp;
import com.caucho.v5.amp.spi.MessageAmp;
import com.caucho.v5.amp.stub.StubAmp;
import com.caucho.v5.db.journal.JournalStream;
import com.caucho.v5.db.journal.JournalStream.ReplayCallback;
import com.caucho.v5.h3.H3;
import com.caucho.v5.h3.InH3;
import com.caucho.v5.h3.OutFactoryH3;
import com.caucho.v5.h3.OutH3;
import com.caucho.v5.util.Alarm;
import com.caucho.v5.util.AlarmListener;
import com.caucho.v5.util.WeakAlarm;
import com.caucho.v5.vfs.ReadStream;

import io.baratine.service.Result;

/**
 * Interface to the journal itself. Journal writers open a stream and write to
 * it. Journal readers receive events.
 */
public class JournalImpl implements JournalAmp
{
  private static final Logger log
    = Logger.getLogger(JournalImpl.class.getName());
  
  private static final int CODE_SEND = 1;
  private static final int CODE_QUERY = 2;
  /*
  private static final int CODE_QUERY_REPLY = 3;
  private static final int CODE_QUERY_ERROR = 4;
  */
  
  //private static final int CODE_CHECKPOINT_START = 5;
  //private static final int CODE_CHECKPOINT_END = 6;
  
  private InH3 _hIn;
  private OutH3 _hOut;
  private final JournalStream _jOut;
  private final OutputStreamJournal _jOs;
  
  private final int _maxCount;
  
  private long _count;
  private long _nextMaxCount;
  private long _lastFlushCount;
  
  private long _delay;
  private long _nextTimeout;
  
  private Alarm _timeoutAlarm;
  private TimeoutListener _timeoutListener;

  private OutFactoryH3 _serializer;
  
  protected JournalImpl()
  {
    this(null, -1, -1);
  }
  
  public JournalImpl(JournalStream jOut,
                     int maxCount,
                     long timeout)
  {
    if (jOut == null) {
      jOut = createJournalStream();
    }

    _jOut = jOut;
    
    _serializer = H3.newOutFactory().get();
    /*
    _hOut = new Hessian2Output();
    //_hOut.setUnshared(true);
    
    _hIn = new Hessian2Input();
    // _hIn.setUnshared(true);
     * 
     */
    
    if (maxCount < 0) {
      maxCount = Integer.MAX_VALUE;
    }
    
    _maxCount = Math.max(1, maxCount);
    _nextMaxCount = _maxCount;
    
    if (timeout < 0 || Integer.MAX_VALUE / 2 < timeout) {
      timeout = -1;
    }

    _delay = timeout;

    _jOs = new OutputStreamJournal(_jOut);
  }
  
  protected JournalStream createJournalStream()
  {
    throw new UnsupportedOperationException(getClass().getName());
  }
  
  @Override
  public long getDelay()
  {
    return _delay;
  }

  @Override
  public void setInbox(InboxAmp inbox)
  {
    if (_delay > 0 && inbox != null) {
      _timeoutListener = new TimeoutListener(inbox);
      _timeoutAlarm = new WeakAlarm(_timeoutListener);
    }
  }
  
  @Override
  public boolean isSaveRequest()
  {
    /*
    boolean result = false;
    
    if (_nextMaxCount <= _count) {
      _nextMaxCount = _count + _maxCount;
      
      result = true;
    }
    else if (_lastFlushCount < _count && _timeout >= 0) {
      long now = CurrentTime.getCurrentTime();
      
      if (_nextTimeout < now) {
        result = true;
        _nextTimeout = now + _timeout;
      }
      else  {
        if (_nextTimeout <= 0) {
          _nextTimeout = now + _timeout;
        }
        
        if (_timeoutAlarm != null) {
          _timeoutAlarm.queue(_timeout);
        }
      }
    }
    
    _lastFlushCount = _count;
    
    return result;
    */
    
    return false;
  }
  
  /**
   * Writes the send to the journal. The queryRef values are not
   * saved, because restoring them does not make sense.
   */
  @Override
  public void writeSend(StubAmp actor,
                        String methodName,
                        Object[] args,
                        InboxAmp inbox)
  {
    try (OutputStream os = openItem(inbox)) {
      // XXX: should keep open
      try (OutH3 out = _serializer.out(os)) {
        String key = actor.journalKey();
      
        out.writeLong(CODE_SEND);
        out.writeString(key);
        out.writeString(methodName);
        out.writeLong(args.length);
      
        for (Object arg : args) {
          out.writeObject(arg);
        }
      }
      
      _count++;
    } catch (IOException e) {
      log.log(Level.FINER, e.toString(), e);
    }
  }
  
  /**
   * Writes the query to the journal. The queryRef values are not
   * saved, because restoring them does not make sense.
   */
  @Override
  public void writeQuery(StubAmp actor,
                         String methodName,
                         Object[] args,
                         InboxAmp inbox)
  {
    try (OutputStream os = openItem(inbox)) {
      try (OutH3 out = _serializer.out(os)) {
      
        //hOut.initPacket(os);
      
        String key = actor.journalKey();
      
        out.writeLong(CODE_QUERY);
        out.writeString(key);
        out.writeString(methodName);
        out.writeLong(args.length);
      
        for (Object arg : args) {
          out.writeObject(arg);
        }
      
        //out.flush();
      
        _count++;
      }
    } catch (IOException e) {
      log.log(Level.FINER, e.toString(), e);
    }
  }
  
  @Override
  public boolean saveStart()
  {
    return _jOut.saveStart();
  }
  
  @Override
  public void saveEnd(boolean isComplete)
  {
    _jOut.saveEnd();
  }
  
  private OutputStream openItem(InboxAmp inbox)
  {
    Objects.requireNonNull(inbox);
    
    _jOs.init(inbox);
    
    return _jOs;
  }

  @Override
  public void flush()
  {
    _jOut.flush();
  }
  
  //
  // replay
  //
  
  private void readItem(InputStream is,
                        QueueDeliver queue)
    throws IOException
  {
    //_hIn.initPacket(is);

    try (InH3 in = _serializer.in(is)) {
      int ch = in.readInt();
    
      switch (ch) {
      case CODE_SEND:
        readSend(in, queue);
        break;
      
      case CODE_QUERY:
        readQuery(in, queue);
        break;
      }
    }
  }
  
  private void readSend(InH3 hIn,
                        QueueDeliver queue)
    throws IOException
  {
    String keyPath = hIn.readString();
    
    String methodName = hIn.readString();
    
    int count = hIn.readInt();
    
    Object []args = new Object[count];
    
    for (int i = 0; i < args.length; i++ ){
      args[i] = hIn.readObject();
    }
    
    ReplaySendMessage msg = new ReplaySendMessage(keyPath, methodName, args);

    queue.offer(msg, 60, TimeUnit.SECONDS);
  }
  
  private void readQuery(InH3 hIn,
                         QueueDeliver queue)
    throws IOException
  {
    String keyPath = hIn.readString();
    
    String methodName = hIn.readString();
    
    int count = hIn.readInt();
    
    Object []args = new Object[count];

    for (int i = 0; i < args.length; i++ ){
      args[i] = hIn.readObject();
    }
    
    ReplayQueryMessage msg = new ReplayQueryMessage(keyPath, methodName, args);
    queue.offer(msg, 60, TimeUnit.SECONDS);
  }
  
  @Override
  public long getReplaySequence()
  {
    return _jOs.getReplaySequence();
  }

  @Override
  public void replayStart(Result result,
                          InboxAmp inbox,
                          QueueDeliver queue)
  {
    Objects.requireNonNull(inbox);
    
    ReplayCallbackImpl replayTask = new ReplayCallbackImpl(inbox, queue, result);
    
    _jOs.replay(replayTask);
  }
  
  @Override
  public String toString()
  {
    return getClass().getSimpleName() + "[" + _jOut + "]";
  }
  
  private class OutputStreamJournal extends OutputStream
  {
    private final JournalStream _jOut;
    private final byte []_data = new byte[1];
    private InboxAmp _inbox;
    
    OutputStreamJournal(JournalStream jOut)
    {
      _jOut = jOut;
    }
    
    public long getReplaySequence()
    {
      return _jOut.getReplaySequence();
    }

    public void replay(ReplayCallback replayCallback)
    {
      _jOut.replay(replayCallback);
    }

    void init(InboxAmp inbox)
    {
      Objects.requireNonNull(inbox);
      _inbox = inbox;
      
      _jOut.start();
    }
    
    @Override
    public void write(int value)
    {
      _data[0] = (byte) value;
      
      _jOut.write(_data, 0, 1);
    }
    
    @Override
    public void write(byte []buffer, int offset, int length)
    {
      _jOut.write(buffer, offset, length);
    }
    
    @Override
    public void close()
    {
      InboxAmp inbox = _inbox;
      _inbox = null;
      
      _jOut.complete();
      
      
      if (inbox != null && _jOut.isSaveRequired()) {
        JournalAmp journal = JournalImpl.this;
        
        long timeout = InboxAmp.TIMEOUT_INFINITY;

        inbox.offer(new OnSaveMessage(journal, inbox), timeout);
        // queue.getQueueWorker().wake();
      }
    }
  }
  
  private class ReplayCallbackImpl implements ReplayCallback
  {
    private InboxAmp _inbox;
    private QueueDeliver _queue;
    private Result _result;
    
    ReplayCallbackImpl(InboxAmp inbox,
                       QueueDeliver queue,
                       Result result)
    {
      Objects.requireNonNull(inbox);
      Objects.requireNonNull(queue);
      
      _inbox = inbox;
      _queue = queue;
      _result = result;
    }

    @Override
    public void onItem(ReadStream is) throws IOException
    {
      readItem(is, _queue);
    }

    @Override
    public void completed()
    {
      _queue.wake();
      _inbox.worker().wake();
      
      if (_result != null) {
        _result.ok(true);
      }
      
      // _inbox.offerAndWake(new OnSaveRequestMessage(_inbox, Result.ignore()), 0);
    }
  }
  
  private class TimeoutListener implements AlarmListener {
    private InboxAmp _inbox;
    
    TimeoutListener(InboxAmp inbox)
    {
      _inbox = inbox;
    }
    
    @Override
    public void handleAlarm(Alarm alarm)
    {
      _inbox.offerAndWake(new OnSaveRequestMessage(_inbox, Result.ignore()), 0);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy