com.sun.enterprise.v3.admin.CheckpointHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of payara-micro Show documentation
Show all versions of payara-micro Show documentation
Micro Distribution of the Payara Project
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2013 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.
*/
package com.sun.enterprise.v3.admin;
import com.sun.enterprise.util.LocalStringManagerImpl;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import jakarta.inject.Inject;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginException;
import org.glassfish.admin.payload.PayloadImpl;
import org.glassfish.api.admin.Payload;
import org.glassfish.api.admin.Payload.Inbound;
import org.glassfish.api.admin.Payload.Outbound;
import org.glassfish.api.admin.Payload.Part;
import com.sun.enterprise.util.io.FileUtils;
import java.io.FilenameFilter;
import java.io.Serializable;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
import org.glassfish.api.admin.Job;
import org.glassfish.api.admin.JobManager;
import org.glassfish.common.util.ObjectInputOutputStreamFactory;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.security.services.api.authentication.AuthenticationService;
import org.jvnet.hk2.annotations.Service;
/**
* This class is starting point for persistent CheckpointHelper, and currently only
* persists and restores AdminCommandContext with payloads in separate files.
*
* @author Andriy Zhdanov
*
*/
@Service
public class CheckpointHelper {
public static class CheckpointFilename {
enum ExtensionType {
BASIC, ATTACHMENT, PAYLOAD_INBOUD, PAYLOAD_OUTBOUND;
}
private static final Map EXTENSIONS;
static {
Map extMap = new EnumMap(ExtensionType.class);
extMap.put(ExtensionType.BASIC, ".checkpoint");
extMap.put(ExtensionType.ATTACHMENT, ".checkpoint_attach");
extMap.put(ExtensionType.PAYLOAD_INBOUD, ".checkpoint_inb");
extMap.put(ExtensionType.PAYLOAD_OUTBOUND, ".checkpoint_outb");
EXTENSIONS = Collections.unmodifiableMap(extMap);
}
private final ExtensionType ext;
private final String jobId;
private String attachmentId;
private final File parentDir;
private String cachedFileName;
private CheckpointFilename(String jobId, File parentDir, ExtensionType ext) {
this.ext = ext;
this.jobId = jobId;
this.parentDir = parentDir;
}
private CheckpointFilename(CheckpointFilename basic, ExtensionType ext) {
this.ext = ext;
this.jobId = basic.jobId;
this.attachmentId = basic.attachmentId;
this.parentDir = basic.parentDir;
}
private CheckpointFilename(Job job, String attachmentId) {
this(job.getId(), job.getJobsFile().getParentFile(), ExtensionType.ATTACHMENT);
this.attachmentId = attachmentId;
}
public CheckpointFilename(File file) throws IOException {
this.parentDir = file.getParentFile();
String name = file.getName();
this.cachedFileName = name;
//Find extension
int ind = name.lastIndexOf('.');
if (ind <= 0) {
throw new IOException(strings.getLocalString("checkpointhelper.wrongfileextension", "Wrong checkpoint file extension {0}", file.getName()));
}
String extensionStr = name.substring(ind);
ExtensionType extension = null;
for (Map.Entry entry : EXTENSIONS.entrySet()) {
if (extensionStr.equals(entry.getValue())) {
extension = entry.getKey();
break;
}
}
if (extension == null) {
throw new IOException(strings.getLocalString("checkpointhelper.wrongfileextension", "Wrong checkpoint file extension {0}", file.getName()));
}
this.ext = extension;
//Parse id
name = name.substring(0, ind);
if (this.ext == ExtensionType.ATTACHMENT) {
ind = name.indexOf('-');
if (ind < 0) {
throw new IOException(strings.getLocalString("checkpointhelepr.wrongfilename", "Wrong checkpoint filename format: {0}.", file.getName()));
}
this.jobId = name.substring(0, ind);
this.attachmentId = name.substring(ind + 1);
} else {
this.jobId = name;
}
}
public ExtensionType getExt() {
return ext;
}
public String getJobId() {
return jobId;
}
public String getAttachmentId() {
return attachmentId;
}
public File getParentDir() {
return parentDir;
}
@Override
public String toString() {
if (cachedFileName == null) {
StringBuilder result = new StringBuilder();
result.append(jobId);
if (ext == ExtensionType.ATTACHMENT) {
result.append("-");
if (attachmentId == null) {
result.append("null");
} else if (!attachmentId.isEmpty()) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(attachmentId.getBytes("UTF-8"));
for (int i = 0; i < thedigest.length; i++) {
result.append(Integer.toString((thedigest[i] & 0xff) + 0x100, 16).substring(1));
}
} catch (Exception ex) {
result.append(attachmentId);
}
}
}
result.append(EXTENSIONS.get(ext));
cachedFileName = result.toString();
}
return cachedFileName;
}
public File getFile() {
return new File(parentDir, toString());
}
public CheckpointFilename getForPayload(boolean inbound) {
return new CheckpointFilename(this, inbound ? ExtensionType.PAYLOAD_INBOUD : ExtensionType.PAYLOAD_OUTBOUND);
}
public static CheckpointFilename createBasic(Job job) {
return createBasic(job.getId(), job.getJobsFile().getParentFile());
}
public static CheckpointFilename createBasic(String jobId, File dir) {
return new CheckpointFilename(jobId, dir, ExtensionType.BASIC);
}
public static CheckpointFilename createAttachment(Job job, String attachmentId) {
return new CheckpointFilename(job, attachmentId);
}
}
private final static LocalStringManagerImpl strings = new LocalStringManagerImpl(CheckpointHelper.class);
@Inject
AuthenticationService authenticationService;
@Inject
ServiceLocator serviceLocator;
@Inject
ObjectInputOutputStreamFactory factory;
public void save(JobManager.Checkpoint checkpoint) throws IOException {
CheckpointFilename cf = CheckpointFilename.createBasic(checkpoint.getJob());
ObjectOutputStream oos = null;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(cf.getFile());
oos = factory.createObjectOutputStream(fos);
oos.writeObject(checkpoint);
oos.close();
Outbound outboundPayload = checkpoint.getContext().getOutboundPayload();
if (outboundPayload != null && outboundPayload.isDirty()) {
saveOutbound(outboundPayload, cf.getForPayload(false).getFile());
}
Inbound inboundPayload = checkpoint.getContext().getInboundPayload();
if (inboundPayload != null) {
saveInbound(inboundPayload, cf.getForPayload(true).getFile());
}
} catch (IOException e) {
try {oos.close();} catch (Exception ex) {
}
try {fos.close();} catch (Exception ex) {
}
File file = cf.getFile();
if (file.exists()) {
file.delete();;
}
file = cf.getForPayload(true).getFile();
if (file.exists()) {
file.delete();
}
file = cf.getForPayload(false).getFile();
if (file.exists()) {
file.delete();
}
throw e;
}
}
public void saveAttachment(Serializable data, Job job, String attachmentId) throws IOException {
ObjectOutputStream oos = null;
FileOutputStream fos = null;
CheckpointFilename cf = CheckpointFilename.createAttachment(job, attachmentId);
try {
fos = new FileOutputStream(cf.getFile());
oos = factory.createObjectOutputStream(fos);
oos.writeObject(data);
} finally {
try {oos.close();} catch (Exception ex) {
}
try {fos.close();} catch (Exception ex) {
}
}
}
public JobManager.Checkpoint load(CheckpointFilename cf, Outbound outbound) throws IOException, ClassNotFoundException {
FileInputStream fis = null;
ObjectInputStream ois = null;
JobManager.Checkpoint checkpoint;
try {
fis = new FileInputStream(cf.getFile());
ois = factory.createObjectInputStream(fis);
checkpoint = (JobManager.Checkpoint) ois.readObject();
} finally {
try {ois.close();} catch (Exception ex) {
}
try {fis.close();} catch (Exception ex) {
}
}
if (outbound != null) {
loadOutbound(outbound, cf.getForPayload(false).getFile());
checkpoint.getContext().setOutboundPayload(outbound);
}
Inbound inbound = loadInbound(cf.getForPayload(true).getFile());
checkpoint.getContext().setInboundPayload(inbound);
try {
String username = checkpoint.getJob().getSubjectUsernames().get(0);
Subject subject = authenticationService.impersonate(username, /* groups */ null, /* subject */ null, /* virtual */ false);
checkpoint.getContext().setSubject(subject);
} catch (LoginException e) {
throw new RuntimeException(e);
}
return checkpoint;
}
public T loadAttachment(Job job, String attachmentId) throws IOException, ClassNotFoundException {
CheckpointFilename cf = CheckpointFilename.createAttachment(job, attachmentId);
File file = cf.getFile();
if (!file.exists()) {
return null;
}
ObjectInputStream ois = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(cf.getFile());
ois = factory.createObjectInputStream(fis);
return (T) ois.readObject();
} finally {
try {ois.close();} catch (Exception ex) {
}
try {fis.close();} catch (Exception ex) {
}
}
}
public Collection listCheckpoints(File dir) {
if (dir == null || !dir.exists()) {
return Collections.emptyList();
}
final String extension = CheckpointFilename.EXTENSIONS.get(CheckpointFilename.ExtensionType.BASIC);
File[] checkpointFiles = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(extension);
}
});
if (checkpointFiles != null) {
Collection result = new ArrayList(checkpointFiles.length);
for (File checkpointFile : checkpointFiles) {
try {
result.add(new CheckpointFilename(checkpointFile));
} catch (IOException ex) {
}
}
return result;
} else {
return Collections.emptyList();
}
}
/*
private void writeHeader(OutputStream os, Job job) throws IOException {
writeHeader(os, StringUtils.nvl(job.getScope()) + job.getName());
}
private void writeHeader(OutputStream os, String headerStr) throws IOException {
byte[] headerB = headerStr.getBytes("UTF-8");
int len = headerB.length;
while (len >= 255) {
os.write(255);
len -= 255;
}
os.write(len);
os.write(headerB);
}
private String readHeader(InputStream is) throws IOException {
int length = 0;
int r;
while ((r = is.read()) == 255) {
length += 255;
}
if (r < 0) {
throw new IOException(strings.getLocalString("checkpointhelper.wrongheader", "Can not load checkpoint. Wrong header."));
}
length += r;
byte[] headerB = new byte[length];
int readLen = is.read(headerB);
if (readLen < length) {
throw new IOException(strings.getLocalString("checkpointhelper.wrongheader", "Can not load checkpoint. Wrong header."));
}
return new String(headerB, "UTF-8");
}
private ObjectInputStream getObjectInputStream(InputStream is, String header) throws IOException {
if (!StringUtils.ok(header)) {
return new ObjectInputStream(is);
}
AdminCommand command = serviceLocator.getService(AdminCommand.class, header);
if (command == null) {
throw new IOException(strings.getLocalString("checkpointhelper.cannotlocatecommand", "Can not load checkpoint. Can not locate command {0}.", header));
}
List cls = new ArrayList(10);
cls.add(command.getClass().getClassLoader());
cls.add(this.getClass().getClassLoader());
cls.add(ClassLoader.getSystemClassLoader());
cls.addAll(getJobCreators());
return new ObjectInputStreamForClassloader(is, cls);
}
private List getJobCreators() {
List jcs = serviceLocator.getAllServices(JobCreator.class);
if (jcs == null) {
return Collections.EMPTY_LIST;
}
List result = new ArrayList(jcs.size());
for (JobCreator jc : jcs) {
result.add(jc.getClass().getClassLoader());
}
return result;
}
*/
private void saveOutbound(Payload.Outbound outbound, File outboundFile) throws IOException {
FileOutputStream os = new FileOutputStream(outboundFile);
// Outbound saves text/plain with one part as text with no any details, force zip
writePartsTo(outbound.parts(), os);
outbound.resetDirty();
}
private void loadOutbound(Outbound outbound, File outboundFile) throws IOException {
if (outbound == null || !outboundFile.exists()) {
return;
}
Inbound outboundSource = loadInbound(outboundFile);
Iterator parts = outboundSource.parts();
File topDir = createTempDir("checkpoint", "");
FileUtils.deleteOnExit(topDir);
while (parts.hasNext()) {
Part part = parts.next();
File sourceFile = File.createTempFile("source", "", topDir);
FileUtils.copy(part.getInputStream(), new FileOutputStream(sourceFile), Long.MAX_VALUE);
outbound.addPart(part.getContentType(), part.getName(), part.getProperties(), new FileInputStream(sourceFile));
}
outbound.resetDirty();
}
private void saveInbound(Payload.Inbound inbound, File inboundFile) throws IOException {
if (!inboundFile.exists()) { // not saved yet
FileOutputStream os = new FileOutputStream(inboundFile);
writePartsTo(inbound.parts(), os);
}
}
private Inbound loadInbound(File inboundFile) throws IOException {
if (inboundFile == null || !inboundFile.exists()) {
return null;
}
FileInputStream is = new FileInputStream(inboundFile);
Inbound inboundSource = PayloadImpl.Inbound.newInstance("application/zip", is);
return inboundSource;
}
// ZipPayloadImpl
private void writePartsTo(Iterator parts, OutputStream os) throws IOException {
ZipOutputStream zos = new ZipOutputStream(os);
while (parts.hasNext()) {
Part part = parts.next();
prepareEntry(part, zos);
part.copy(zos);
zos.closeEntry();
}
zos.close();
}
private void prepareEntry(final Payload.Part part, final ZipOutputStream zos) throws IOException {
ZipEntry entry = new ZipEntry(part.getName());
entry.setExtra(getExtraBytes(part));
zos.putNextEntry(entry);
}
private byte[] getExtraBytes(Part part) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Properties props = part.getProperties();
Properties fullProps = new Properties();
if (props != null) {
fullProps.putAll(props);
}
fullProps.setProperty(CONTENT_TYPE_NAME, part.getContentType());
try {
fullProps.store(baos, null);
return baos.toByteArray();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private File createTempDir(final String prefix, final String suffix) throws IOException {
File temp = File.createTempFile(prefix, suffix);
if ( ! temp.delete()) {
throw new IOException("Cannot delete temp file " + temp.getAbsolutePath());
}
if ( ! temp.mkdirs()) {
throw new IOException("Cannot create temp directory" + temp.getAbsolutePath());
}
return temp;
}
private static final String CONTENT_TYPE_NAME = "Content-Type";
}