/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 com.sun.jini.mahalo; import net.jini.core.transaction.*; import net.jini.core.transaction.server.*; import com.sun.jini.mahalo.*; import com.sun.jini.mahalo.log.*; import com.sun.jini.thread.*; import java.rmi.activation.ActivateFailedException; import java.rmi.RemoteException; import java.rmi.NoSuchObjectException; import java.rmi.MarshalException; import java.rmi.StubNotFoundException; import java.rmi.UnexpectedException; import java.rmi.UnknownHostException; import java.rmi.UnmarshalException; import java.rmi.ConnectIOException; import java.rmi.AccessException; import java.rmi.ConnectException; import java.util.logging.Level; import java.util.logging.Logger; /** * An implementation of a <code>Job</code> which interacts with * a set of <code>TransactionParticipant</code>s to inform them * to roll forward changes associated with a given <code>Transaction</code>. * * @author Sun Microsystems, Inc. * * @see com.sun.jini.mahalo.Job * @see com.sun.jini.mahalo.ParticipantTask * @see net.jini.core.transaction.Transaction * @see net.jini.core.transaction.server.TransactionParticipant */ public class CommitJob extends Job implements TransactionConstants { ServerTransaction tr; ClientLog log; ParticipantHandle[] handles; int maxtries = Integer.MAX_VALUE; static final Logger logger = TxnManagerImpl.participantLogger; /** * Constructs an <code>CommitJob</code> * * * @param tr The <code>Transaction</code> whose participants * will be instructed to roll-forward. * * @param pool The <code>TaskManager</code> which provides the * threads used for interacting with participants. * * @param log The <code>ClientLog</code> used for recording * recovery data. * * @param handles The array of participants which will be contacted * and informed to roll-forward. * * @see com.sun.jini.thread.TaskManager * @see com.sun.jini.mahalo.log.ClientLog * @see net.jini.core.transaction.server.TransactionParticipant */ public CommitJob(Transaction tr, TaskManager pool, WakeupManager wm, ClientLog log, ParticipantHandle[] handles) { super(pool, wm); if (log == null) throw new IllegalArgumentException("CommitJob: CommitJob: " + "log is null"); this.log = log; if (!(tr instanceof ServerTransaction)) throw new IllegalArgumentException("PrepareJob: PrepareJob: " + "must be a ServerTransaction"); this.tr = (ServerTransaction) tr; if (handles == null) throw new IllegalArgumentException("PrepareJob: PrepareJob: " + "must have participants"); if (handles.length == 0) throw new IllegalArgumentException("PrepareJob: PrepareJob: " + "must have participants"); this.handles = handles; } /** * The work to be performed by each <code>TaskManager.Task</code> * is provided by the <code>Job</code> that creates it. * The work performed by a task belonging to the CommitJob * contacts a participant, instructs it to roll-forward and * log appropriately. * * @param who The task performing the work * * @param param A parameter, of the task's choosing, useful * in performing work. * * @see com.sun.jini.mahalo.Job * @see com.sun.jini.thread.TaskManager.Task */ Object doWork(TaskManager.Task who, Object param) { ParticipantHandle handle = (ParticipantHandle)param; TransactionParticipant par = null; //Check to see if participant has state associated //with it on account of being recovered from a log if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "CommitJob:doWork committing handle: {0}", handle); } int vote = handle.getPrepState(); switch (vote) { case COMMITTED: case NOTCHANGED: case ABORTED: return new Integer(vote); } //Instruct the TransactionParticipant to roll forward //after unpacking it and checking against the //max retry threshold. if (par == null) par = handle.getPreParedParticipant(); //If you have exhausted the max retry threshold //stop, so that no further attempts are made. try { if (attempt(who) > maxtries) { return new Integer(COMMITTED); } } catch (JobException je) { return null; } //At this point, if participant is null, there //must be an error unpacking, so retry later if (par == null) return null; //Here we actually need to instruct the participant to //roll forward. Note the RemoteException causes a //retry. Here we only log info for the cases //where a final outcome is available. Object response = null; try { par.commit(tr.mgr, tr.id); response = new Integer(COMMITTED); } catch (TransactionException bte) { //The participant doesn't have record of the //transaction, so it must have already rolled //forward. response = new Integer(COMMITTED); } catch (NoSuchObjectException nsoe) { //No definition for object in VM, so stop //and consider committed. response = new Integer(COMMITTED); } catch (ConnectException ce) { //failure setting up connection, so give //participant more time by retrying } catch (UnknownHostException uhe) { //could not resolve host for participant, so //stop and consider committed response = new Integer(COMMITTED); } catch (ConnectIOException cioe) { //multiplexed connection or cached //connection problem, give participant more time } catch (MarshalException me) { //cannot send parameters, so stop and consider done response = new Integer(COMMITTED); } catch (AccessException ae) { //Access error on registry or rmid consider done response = new Integer(COMMITTED); } catch (ActivateFailedException afe) { //Activatable Ref Stub couldn't activate //participant, so stop and consider done response = new Integer(COMMITTED); } catch (RemoteException re) { //Something happened with the network, so //return null to retry at a later time. } catch (RuntimeException rte) { //Something happened with the participant, so //stop and consider done response = new Integer(COMMITTED); } if (response != null) { handle.setPrepState(COMMITTED); try { log.write( new ParticipantCommitRecord(handle)); } catch (com.sun.jini.mahalo.log.LogException le) { //the full package name used to disambiguate //the LogException } return response; } return null; } /** * Creates the <code>TaskManager.Task</code>s necessary to * inform participants to roll-back. */ TaskManager.Task[] createTasks() { TaskManager.Task[] tmp = new TaskManager.Task[handles.length]; for (int i = 0; i < handles.length; i++) { tmp[i] = new ParticipantTask(getPool(), getMgr(), this, handles[i]); } return tmp; } /** * Gathers partial results submitted by tasks and produces * a single outcome. * * @see com.sun.jini.mahalo.Job */ Object computeResult() throws JobException { try { if (!isCompleted(0)) throw new ResultNotReadyException("Cannot compute result " + "since there are jobs pending"); } catch (JobNotStartedException jnse) { throw new ResultNotReadyException("Cannot compute result since" + " jobs were not created"); } int tmp = 0; int count = 0; checkresults: for (int i = 0; i < results.length; i++) { tmp = ((Integer)results[i]).intValue(); if (tmp == COMMITTED) count++; } if (logger.isLoggable(Level.FINEST)) { logger.log(Level.FINEST, "CommitJob:computeResult {0} participants COMMITTED", new Integer(count)); } return new Integer(COMMITTED); } }