/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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.
*/
package com.liferay.portal.kernel.resiliency.spi.remote;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.nio.intraband.RegistrationReference;
import com.liferay.portal.kernel.nio.intraband.rpc.IntrabandRPCUtil;
import com.liferay.portal.kernel.nio.intraband.welder.Welder;
import com.liferay.portal.kernel.nio.intraband.welder.WelderFactoryUtil;
import com.liferay.portal.kernel.process.ProcessCallable;
import com.liferay.portal.kernel.process.ProcessException;
import com.liferay.portal.kernel.process.local.LocalProcessLauncher;
import com.liferay.portal.kernel.process.log.ProcessOutputStream;
import com.liferay.portal.kernel.resiliency.mpi.MPI;
import com.liferay.portal.kernel.resiliency.mpi.MPIHelperUtil;
import com.liferay.portal.kernel.resiliency.spi.SPI;
import com.liferay.portal.kernel.resiliency.spi.SPIConfiguration;
import com.liferay.portal.kernel.resiliency.spi.agent.SPIAgent;
import com.liferay.portal.kernel.resiliency.spi.agent.SPIAgentFactoryUtil;
import com.liferay.portal.kernel.resiliency.spi.provider.SPISynchronousQueueUtil;
import com.liferay.portal.kernel.util.PropsKeys;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
/**
* @author Shuyang Zhou
*/
public abstract class RemoteSPI implements ProcessCallable<SPI>, Remote, SPI {
public RemoteSPI(SPIConfiguration spiConfiguration) {
this.spiConfiguration = spiConfiguration;
mpi = MPIHelperUtil.getMPI();
UUID uuidObject = UUID.randomUUID();
uuid = uuidObject.toString();
welder = WelderFactoryUtil.createWelder();
}
@Override
public SPI call() throws ProcessException {
try {
SPIShutdownHook spiShutdownHook = new SPIShutdownHook();
LocalProcessLauncher.ProcessContext.attach(
spiConfiguration.getSPIId(), spiConfiguration.getPingInterval(),
spiShutdownHook);
Runtime runtime = Runtime.getRuntime();
runtime.addShutdownHook(spiShutdownHook);
SPI spi = (SPI)UnicastRemoteObject.exportObject(this, 0);
RegisterCallback registerCallback = new RegisterCallback(uuid, spi);
ProcessOutputStream processOutputStream =
LocalProcessLauncher.ProcessContext.getProcessOutputStream();
processOutputStream.writeProcessCallable(registerCallback);
registrationReference = welder.weld(MPIHelperUtil.getIntraband());
ConcurrentMap<String, Object> attributes =
LocalProcessLauncher.ProcessContext.getAttributes();
attributes.put(SPI.SPI_INSTANCE_PUBLICATION_KEY, this);
return spi;
}
catch (RemoteException re) {
throw new ProcessException("Failed to export SPI as RMI stub.", re);
}
catch (IOException ioe) {
throw new ProcessException(ioe);
}
}
@Override
public void destroy() throws RemoteException {
try {
doDestroy();
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
finally {
UnicastRemoteObject.unexportObject(RemoteSPI.this, true);
}
}
@Override
public MPI getMPI() {
return mpi;
}
@Override
public RegistrationReference getRegistrationReference() {
return registrationReference;
}
@Override
public SPIAgent getSPIAgent() {
if (spiAgent == null) {
spiAgent = SPIAgentFactoryUtil.createSPIAgent(
spiConfiguration, registrationReference);
}
return spiAgent;
}
@Override
public SPIConfiguration getSPIConfiguration() {
return spiConfiguration;
}
public String getUUID() {
return uuid;
}
public Welder getWelder() {
return welder;
}
@Override
public boolean isAlive() {
return true;
}
protected abstract void doDestroy() throws RemoteException;
protected transient CountDownLatch countDownLatch;
protected final MPI mpi;
protected RegistrationReference registrationReference;
protected transient volatile SPIAgent spiAgent;
protected final SPIConfiguration spiConfiguration;
protected final String uuid;
protected final Welder welder;
protected static class RegisterCallback implements ProcessCallable<SPI> {
public RegisterCallback(String spiUUID, SPI spi) {
_spiUUID = spiUUID;
_spi = spi;
}
@Override
public SPI call() throws ProcessException {
try {
SPISynchronousQueueUtil.notifySynchronousQueue(_spiUUID, _spi);
}
catch (InterruptedException ie) {
throw new ProcessException(ie);
}
return _spi;
}
private static final long serialVersionUID = 1L;
private final SPI _spi;
private final String _spiUUID;
}
protected static class UnregisterSPIProcessCallable
implements ProcessCallable<Boolean> {
public UnregisterSPIProcessCallable(
String spiProviderName, String spiId) {
_spiProviderName = spiProviderName;
_spiId = spiId;
}
@Override
public Boolean call() {
SPI spi = MPIHelperUtil.getSPI(_spiProviderName, _spiId);
if (spi != null) {
return MPIHelperUtil.unregisterSPI(spi);
}
return false;
}
private static final long serialVersionUID = 1L;
private final String _spiId;
private final String _spiProviderName;
}
protected class SPIShutdownHook
extends Thread implements LocalProcessLauncher.ShutdownHook {
public SPIShutdownHook() {
setDaemon(true);
setName(SPIShutdownHook.class.getSimpleName());
}
@Override
public void run() {
if (countDownLatch.getCount() == 0) {
return;
}
boolean unregistered = false;
try {
Future<Boolean> future = IntrabandRPCUtil.execute(
registrationReference,
new UnregisterSPIProcessCallable(
getSPIProviderName(), spiConfiguration.getSPIId()));
unregistered = future.get();
}
catch (Exception e) {
if (_log.isWarnEnabled()) {
_log.warn("Unable to unregister SPI from MPI", e);
}
}
if (unregistered || !_waitForMPI()) {
_shutdown();
}
}
@Override
public boolean shutdown(int shutdownCode, Throwable shutdownThrowable) {
Runtime runtime = Runtime.getRuntime();
runtime.removeShutdownHook(this);
_shutdown();
return true;
}
private void _shutdown() {
try {
RemoteSPI.this.stop();
}
catch (RemoteException re) {
_log.error("Unable to stop SPI", re);
}
try {
RemoteSPI.this.destroy();
}
catch (RemoteException re) {
_log.error("Unable to destroy SPI", re);
}
}
private boolean _waitForMPI() {
if (_log.isInfoEnabled()) {
_log.info(
"Wait up to " + spiConfiguration.getShutdownTimeout() +
" ms for MPI shutdown request");
}
try {
if (countDownLatch.await(
spiConfiguration.getShutdownTimeout(),
TimeUnit.MILLISECONDS)) {
if (_log.isInfoEnabled()) {
_log.info("MPI shutdown request received");
}
return true;
}
}
catch (InterruptedException ie) {
}
if (_log.isInfoEnabled()) {
_log.info("Proceed with SPI shutdown");
}
return false;
}
}
private void readObject(ObjectInputStream objectInputStream)
throws ClassNotFoundException, IOException {
objectInputStream.defaultReadObject();
System.setProperty(
PropsKeys.INTRABAND_IMPL, objectInputStream.readUTF());
System.setProperty(
PropsKeys.INTRABAND_TIMEOUT_DEFAULT, objectInputStream.readUTF());
System.setProperty(
PropsKeys.INTRABAND_WELDER_IMPL, objectInputStream.readUTF());
System.setProperty(
"portal:" + PropsKeys.LIFERAY_HOME, objectInputStream.readUTF());
// Disable auto deploy
System.setProperty("portal:" + PropsKeys.AUTO_DEPLOY_ENABLED, "false");
// Disable cluster link
System.setProperty("portal:" + PropsKeys.CLUSTER_LINK_ENABLED, "false");
// Disable dependency management
System.setProperty(
"portal:" + PropsKeys.HOT_DEPLOY_DEPENDENCY_MANAGEMENT_ENABLED,
"false");
// Log4j log file postfix
System.setProperty("spi.id", "-" + spiConfiguration.getSPIId());
countDownLatch = new CountDownLatch(1);
}
private void writeObject(ObjectOutputStream objectOutputStream)
throws IOException {
objectOutputStream.defaultWriteObject();
objectOutputStream.writeUTF(
System.getProperty(PropsKeys.INTRABAND_IMPL));
objectOutputStream.writeUTF(
System.getProperty(PropsKeys.INTRABAND_TIMEOUT_DEFAULT));
objectOutputStream.writeUTF(
System.getProperty(PropsKeys.INTRABAND_WELDER_IMPL));
objectOutputStream.writeUTF(System.getProperty(PropsKeys.LIFERAY_HOME));
}
private static final Log _log = LogFactoryUtil.getLog(RemoteSPI.class);
}