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

com.sun.enterprise.transaction.jts.recovery.RecoveryLockFile Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2010-2012 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

//----------------------------------------------------------------------------
//
// Description: Recovery lock file handling
// Author:      Marina Vatkina
// Date:        Sep 2010
//
//----------------------------------------------------------------------------

package com.sun.enterprise.transaction.jts.recovery;

import java.util.*;
import java.io.*;
import java.nio.channels.FileLock;

import java.util.logging.Logger;
import java.util.logging.Level;
import com.sun.logging.LogDomains;

import com.sun.enterprise.transaction.jts.api.TransactionRecoveryFence;
import com.sun.enterprise.transaction.jts.api.DelegatedTransactionRecoveryFence;

import com.sun.jts.CosTransactions.Configuration;
import com.sun.jts.CosTransactions.LogControl;
import com.sun.jts.CosTransactions.RecoveryManager;

/**
 * This class manages lock file required for delegated recovery.
 * @author mvatkina
 *
 * @see
 * Records in the recovery lock file have the following format:
 * PREFIX INSTANCE_NAME TIMESTAMP
 * Where PREFIX can be one of:
 * - "O" means OWNED by this instance, i.e. non-delegated recovery 
 * - "B" means recovered BY the specified instance 
 * - "F" means recovered FOR the specified instance
 * TIMESTAMP is the time of the recovery operation
 * 
*/

public class RecoveryLockFile implements TransactionRecoveryFence, DelegatedTransactionRecoveryFence {

    // Logger to log transaction messages = use class from com.sun.jts sub-package to find the bundle
    static Logger _logger = LogDomains.getLogger(Configuration.class, LogDomains.TRANSACTION_LOGGER);

    private final static String SEPARATOR = " ";
    private final static String OWN = "O";
    private final static String FOR = "F";
    private final static String BY = "B";
    private final static String END_LINE = "\n";
    
    // Single instance
    private static final RecoveryLockFile instance = new RecoveryLockFile();

    private volatile boolean started = false;
    private String instance_name;
    private String log_path;
    private GMSCallBack gmsCallBack;

    private RecoveryLockFile() {
    }

    public static DelegatedTransactionRecoveryFence getDelegatedTransactionRecoveryFence(GMSCallBack gmsCallBack) {
        instance.init(gmsCallBack);

        return instance;
    }

    public void start() {
        if (!started) {
            gmsCallBack.finishDelegatedRecovery(log_path);
            started = true;
        }
    }

    private void init(GMSCallBack gmsCallBack) {
        this.gmsCallBack = gmsCallBack;
        instance_name = Configuration.getPropertyValue(Configuration.INSTANCE_NAME);
        log_path = LogControl.getLogPath();
        // Create (if it doesn't exist) recoveryLockFile to hold info about instance and delegated recovery
        File recoveryLockFile = LogControl.recoveryLockFile(null, log_path);
        try {
            recoveryLockFile.createNewFile();
        } catch (Exception ex) {
            _logger.log(Level.WARNING, "jts.exception_creating_recovery_file", recoveryLockFile);
            _logger.log(Level.WARNING, "", ex);
        }
        RecoveryManager.registerTransactionRecoveryFence(this);
    }

    /**
     * {@inheritDoc}
     */
    public void raiseFence() {
        while (isRecovering()) {
            //wait
            try {
                Thread.sleep(60000);
            } catch (Exception e) {
            }
        }
        registerRecovery();
    }

    /**
     * {@inheritDoc}
     */
    public void lowerFence() {
        _logger.log(Level.INFO, "Lower Fence request for instance " + instance_name);
        doneRecovering();
        _logger.log(Level.INFO, "Fence lowered for instance " + instance_name);
    }

    /**
     * {@inheritDoc}
     */
    public boolean isFenceRaised(String logDir, String instance, long timestamp) {
        return isRecovering(logDir, instance, timestamp, BY);
    }

    /**
     * {@inheritDoc}
     */
    public void raiseFence(String logPath, String instance) {
        raiseFence(logPath, instance, 0L);
    }

    /**
     * {@inheritDoc}
     */
    public void raiseFence(String logPath, String instance, long timestamp) {
        _logger.log(Level.INFO, "Raise Fence request for instance " + instance);
        while (isRecovering(logPath, instance, timestamp, BY)) {
            //wait
            try {
                Thread.sleep(60000);
            } catch (Exception e) {
            }
        }
        registerRecovery(logPath, instance);
        _logger.log(Level.INFO, "Fence raised for instance " + instance);
    }

    /**
     * {@inheritDoc}
     */
    public void lowerFence(String logPath, String instance) {
        _logger.log(Level.INFO, "Lower Fence request for instance " + instance);
        doneRecovering(logPath, instance);
        _logger.log(Level.INFO, "Fence lowered for instance " + instance);
    }

    /**
     * {@inheritDoc}
     */
    public String getInstanceRecoveredFor(String path, long timestamp) {
        if (!isRecovering(path, null, timestamp, FOR)) {
            return doneRecovering(path, null, FOR);
        }

        return null;
    }

    /**
     * {@inheritDoc}
     */
    public void transferRecoveryTo(String logDir, String instance) {
        doneRecovering(logDir, null, BY);
        registerRecovery(logDir, instance);
    }

    /**
     * Returns true if running instance is doing its own recovery
     */
    private boolean isRecovering() {
        return isRecovering(log_path, instance_name, 0L, BY);
    }

    /**
     * Returns true if recovery file on the specified path contains information 
     * that the specified instance started recovery after specified timestamp
     * either for itself or by another instance.
     */
    private boolean isRecovering(String logDir, String instance, long timestamp, String prefix) {
        BufferedReader reader = null;
        File recoveryLockFile = LogControl.recoveryLockFile(".", logDir);
        if (!recoveryLockFile.exists()) {
            _logger.log(Level.INFO, "Lock File not found " + recoveryLockFile);
            return false;
        }

        boolean result = false;
        try {
            _logger.log(Level.INFO, "Checking Lock File " + recoveryLockFile);
            RandomAccessFile raf = new RandomAccessFile(recoveryLockFile, "rw");
            FileLock lock = raf.getChannel().lock();
            try {
                reader = new BufferedReader(new FileReader(recoveryLockFile));
                String line = null;
                while( (line = reader.readLine()) != null) {
                    _logger.log(Level.INFO, "Testing line: " + line);
                    String[] parts = line.split(SEPARATOR);
                    if (parts.length != 3) {
                        throw new IllegalStateException();
                    } else if ((parts[0].equals(OWN) && parts[1].equals(instance)) ||
                             (instance == null && parts[0].equals(prefix))) {
                        result = (Long.parseLong(parts[2]) > timestamp);
                        break;
                    } else {
                        // skip all other lines
                        continue;
                    }
                } 
            } finally {
                lock.release();
            }
        } catch (Exception ex) {
            _logger.log(Level.WARNING, "jts.exception_in_recovery_file_handling", ex);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception ex) {
                    _logger.log(Level.WARNING, "jts.exception_in_recovery_file_handling", ex);
                }
            }
        }

        _logger.log(Level.INFO, "Recovering? " + result);
        return result;
    }

    /**
     * Removes recovery data from the recovery lock file for running instance
     */
    private void doneRecovering() {
        doneRecovering(log_path, instance_name, OWN);
    }

    /**
     * Removes recovery data from the recovery lock files for both, the instance that the
     * recovery is done for (i.e. for specified instance), and the current instance which the 
     * recovery is done by (in the lock file on the specified path)
     */
    private void doneRecovering(String logPath, String instance) {
        doneRecovering(log_path, instance, FOR);
        doneRecovering(logPath, instance_name, BY);
    }

    /**
     * Removes recovery data from the recovery lock file.
     * @return instance name if instance was unknown (null).
     */
    private String doneRecovering(String logPath, String instance, String prefix) {
        BufferedReader reader = null;
        FileWriter writer = null;
        String result = null;
        File recoveryLockFile = LogControl.recoveryLockFile(".", logPath);
        if (!recoveryLockFile.exists()) {
            _logger.log(Level.INFO, "Lock Fine not found: " + recoveryLockFile);
            return null;
        }

        try {
            RandomAccessFile raf = new RandomAccessFile(recoveryLockFile, "rw");
            FileLock lock = raf.getChannel().lock();
            try {
                reader = new BufferedReader(new FileReader(recoveryLockFile));
                _logger.log(Level.INFO, "Updating File " + recoveryLockFile);
                String line = null;
                List list_out = new ArrayList();
                while( (line = reader.readLine()) != null) {
                    _logger.log(Level.INFO, "Processing line: " + line);
                    String[] parts = line.split(SEPARATOR);
                    if (parts.length != 3) {
                        // Remove such line
                        _logger.log(Level.INFO, "...skipping bad line ...");
                        continue;
                    } else if (parts[0].equals(prefix) && (instance == null || parts[1].equals(instance))) {
                        // Remove such line
                        _logger.log(Level.INFO, "...skipping found line ...");
                        result = parts[1];
                        continue;
                    }
    
                    list_out.add(line);
                } 

                reader.close();
                reader = null;

                writer = new FileWriter(recoveryLockFile);
                for (String out : list_out) {
                    _logger.log(Level.INFO, "Re-adding line: " + out);
                    writer.write(out);
                    writer.write(END_LINE);
                }
            } finally {
                lock.release();
            }
        } catch (Exception ex) {
            _logger.log(Level.WARNING, "jts.exception_in_recovery_file_handling", ex);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception ex) {
                    _logger.log(Level.WARNING, "jts.exception_in_recovery_file_handling", ex);
                }
            }
            if (writer != null) {
                try {
                    writer.close();
                } catch (Exception ex) {
                    _logger.log(Level.WARNING, "jts.exception_in_recovery_file_handling", ex);
                }
            }
        }

        return result;
    }

    /**
     * Writes into recovery lock file data about recovery for the running instance.
     */
    private void registerRecovery() {
        // Remove any stale data 
        doneRecovering(log_path, null, BY);

        // And mark that it's self-recovery
        registerRecovery(log_path, instance_name, OWN);
    }

    /**
     * Writes into recovery lock file data about recovery for the specified instance by
     * the current instance.
     */
    private void registerRecovery(String logPath, String instance) {
        // Remove stale data if there is any
        doneRecovering(log_path, null, FOR);

        registerRecovery(logPath, instance_name, BY);
        registerRecovery(log_path, instance, FOR);
    }
    
    /**
     * Writes data into recovery lock file on the specified path
     */
    private void registerRecovery(String logPath, String instance, String prefix) {
        FileWriter writer = null;
        File recoveryLockFile = LogControl.recoveryLockFile(".", logPath);
        if (!recoveryLockFile.exists()) {
            _logger.log(Level.INFO, "Lock File not found " + recoveryLockFile);
            return;
        }

        try {
            RandomAccessFile raf = new RandomAccessFile(recoveryLockFile, "rw");
            FileLock lock = raf.getChannel().lock();
            try {
                writer = new FileWriter(recoveryLockFile, true);
                    _logger.log(Level.INFO, "Writing into file " + recoveryLockFile);
                StringBuffer b = (new StringBuffer()).append(prefix).append(SEPARATOR).append(instance).
                        append(SEPARATOR).append(System.currentTimeMillis()).append(END_LINE);
                _logger.log(Level.INFO, "Storing " + b);
                writer.write(b.toString());
            } finally {
                lock.release();
            }
        } catch (Exception ex) {
            _logger.log(Level.WARNING, "jts.exception_in_recovery_file_handling", ex);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (Exception ex) {
                    _logger.log(Level.WARNING, "jts.exception_in_recovery_file_handling", ex);
                }
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy