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

com.sun.enterprise.v3.admin.CheckpointHelper 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) 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";

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy