/** * 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.process.local; import com.liferay.portal.kernel.io.unsync.UnsyncBufferedOutputStream; import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream; import com.liferay.portal.kernel.io.unsync.UnsyncPrintWriter; import com.liferay.portal.kernel.process.ClassPathUtil; import com.liferay.portal.kernel.process.ProcessCallable; import com.liferay.portal.kernel.process.ProcessException; import com.liferay.portal.kernel.process.log.ProcessOutputStream; import com.liferay.portal.kernel.util.ClassLoaderObjectInputStream; import com.liferay.portal.kernel.util.StringPool; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PrintStream; import java.io.Serializable; import java.net.URLClassLoader; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; /** * @author Shuyang Zhou */ public class LocalProcessLauncher { public static void main(String[] arguments) throws IOException { PrintStream oldOutPrintStream = System.out; ObjectOutputStream objectOutputStream = null; ProcessOutputStream outProcessOutputStream = null; synchronized (oldOutPrintStream) { oldOutPrintStream.flush(); FileOutputStream fileOutputStream = new FileOutputStream( FileDescriptor.out); objectOutputStream = new ObjectOutputStream( new UnsyncBufferedOutputStream(fileOutputStream)); outProcessOutputStream = new ProcessOutputStream( objectOutputStream, false); ProcessContext._setProcessOutputStream(outProcessOutputStream); PrintStream newOutPrintStream = new PrintStream( outProcessOutputStream, true); System.setOut(newOutPrintStream); } ProcessOutputStream errProcessOutputStream = new ProcessOutputStream( objectOutputStream, true); PrintStream errPrintStream = new PrintStream( errProcessOutputStream, true); System.setErr(errPrintStream); Thread currentThread = Thread.currentThread(); ClassLoader contextClassLoader = currentThread.getContextClassLoader(); try { ObjectInputStream bootstrapObjectInputStream = new ObjectInputStream(System.in); String processCallableName = (String)bootstrapObjectInputStream.readObject(); String logPrefixString = StringPool.OPEN_BRACKET.concat(processCallableName).concat( StringPool.CLOSE_BRACKET); byte[] logPrefix = logPrefixString.getBytes(StringPool.UTF8); outProcessOutputStream.setLogPrefix(logPrefix); errProcessOutputStream.setLogPrefix(logPrefix); String classPath = (String)bootstrapObjectInputStream.readObject(); ClassLoader classLoader = new URLClassLoader( ClassPathUtil.getClassPathURLs(classPath)); currentThread.setContextClassLoader(classLoader); ObjectInputStream objectInputStream = new ClassLoaderObjectInputStream( bootstrapObjectInputStream, classLoader); ProcessCallable<?> processCallable = (ProcessCallable<?>)objectInputStream.readObject(); Thread thread = new Thread( new ProcessCallableRunner(objectInputStream), "ProcessCallable-Runner"); thread.setDaemon(true); thread.start(); Serializable result = processCallable.call(); System.out.flush(); outProcessOutputStream.writeProcessCallable( new ReturnProcessCallable<Serializable>(result)); outProcessOutputStream.flush(); } catch (Throwable t) { errPrintStream.flush(); ProcessException processException = null; if (t instanceof ProcessException) { processException = (ProcessException)t; } else { processException = new ProcessException(t); } errProcessOutputStream.writeProcessCallable( new ExceptionProcessCallable(processException)); errProcessOutputStream.flush(); } finally { currentThread.setContextClassLoader(contextClassLoader); } } public static class ProcessContext { public static boolean attach( String message, long interval, ShutdownHook shutdownHook) { HeartbeatThread heartbeatThread = new HeartbeatThread( message, interval, shutdownHook); boolean value = _heartbeatThreadReference.compareAndSet( null, heartbeatThread); if (value) { heartbeatThread.start(); } return value; } public static void detach() throws InterruptedException { HeartbeatThread heartbeatThread = _heartbeatThreadReference.getAndSet(null); if (heartbeatThread != null) { heartbeatThread.detach(); heartbeatThread.join(); } } public static ConcurrentMap<String, Object> getAttributes() { return _attributes; } public static ProcessOutputStream getProcessOutputStream() { return _processOutputStream; } public static boolean isAttached() { HeartbeatThread attachThread = _heartbeatThreadReference.get(); if (attachThread != null) { return true; } else { return false; } } private static void _setProcessOutputStream( ProcessOutputStream processOutputStream) { _processOutputStream = processOutputStream; } private ProcessContext() { } private static final ConcurrentMap<String, Object> _attributes = new ConcurrentHashMap<>(); private static final AtomicReference<HeartbeatThread> _heartbeatThreadReference = new AtomicReference<>(); private static ProcessOutputStream _processOutputStream; } public interface ShutdownHook { public static final int BROKEN_PIPE_CODE = 1; public static final int INTERRUPTION_CODE = 2; public static final int UNKNOWN_CODE = 3; public boolean shutdown(int shutdownCode, Throwable shutdownThrowable); } private static class HeartbeatThread extends Thread { public HeartbeatThread( String message, long interval, ShutdownHook shutdownHook) { if (shutdownHook == null) { throw new IllegalArgumentException("Shutdown hook is null"); } _interval = interval; _shutdownHook = shutdownHook; _pringBackProcessCallable = new PingbackProcessCallable(message); setDaemon(true); setName(HeartbeatThread.class.getSimpleName()); } public void detach() { _detach = true; interrupt(); } @Override public void run() { ProcessOutputStream processOutputStream = ProcessContext.getProcessOutputStream(); int shutdownCode = 0; Throwable shutdownThrowable = null; while (!_detach) { try { sleep(_interval); processOutputStream.writeProcessCallable( _pringBackProcessCallable); } catch (InterruptedException ie) { if (_detach) { return; } else { shutdownThrowable = ie; shutdownCode = ShutdownHook.INTERRUPTION_CODE; } } catch (IOException ioe) { shutdownThrowable = ioe; shutdownCode = ShutdownHook.BROKEN_PIPE_CODE; } catch (Throwable throwable) { shutdownThrowable = throwable; shutdownCode = ShutdownHook.UNKNOWN_CODE; } if (shutdownCode != 0) { _detach = _shutdownHook.shutdown( shutdownCode, shutdownThrowable); } } } private volatile boolean _detach; private final long _interval; private final ProcessCallable<String> _pringBackProcessCallable; private final ShutdownHook _shutdownHook; } private static class PingbackProcessCallable implements ProcessCallable<String> { public PingbackProcessCallable(String message) { _message = message; } @Override public String call() { return _message; } private static final long serialVersionUID = 1L; private final String _message; } private static class ProcessCallableRunner implements Runnable { public ProcessCallableRunner(ObjectInputStream objectInputStream) { _objectInputStream = objectInputStream; } @Override public void run() { while (true) { try { ProcessCallable<?> processCallable = (ProcessCallable<?>)_objectInputStream.readObject(); processCallable.call(); } catch (Exception e) { UnsyncByteArrayOutputStream unsyncByteArrayOutputStream = new UnsyncByteArrayOutputStream(); UnsyncPrintWriter unsyncPrintWriter = new UnsyncPrintWriter( unsyncByteArrayOutputStream); unsyncPrintWriter.println(e); e.printStackTrace(unsyncPrintWriter); unsyncPrintWriter.println(); unsyncPrintWriter.flush(); System.err.write( unsyncByteArrayOutputStream.unsafeGetByteArray(), 0, unsyncByteArrayOutputStream.size()); System.err.flush(); } } } private final ObjectInputStream _objectInputStream; } }