001    package org.shiftone.jrat.inject.process;
002    
003    
004    import org.shiftone.jrat.core.JRatException;
005    import org.shiftone.jrat.inject.Injector;
006    import org.shiftone.jrat.inject.InjectorOptions;
007    import org.shiftone.jrat.inject.bytecode.Transformer;
008    import org.shiftone.jrat.util.Assert;
009    import org.shiftone.jrat.util.io.IOUtil;
010    import org.shiftone.jrat.util.log.Logger;
011    
012    import java.io.File;
013    import java.io.InputStream;
014    import java.io.OutputStream;
015    
016    
017    /**
018     * @author jeff@shiftone.org (Jeff Drost)
019     *         <p/>
020     *         todo - this logic should all go in the Injector.
021     */
022    public abstract class AbstractFileProcessor implements FileProcessor {
023    
024        private static final Logger LOG = Logger.getLogger(AbstractFileProcessor.class);
025        private static final long DEFAULT_BUFFER_SIZE = 1024 * 6;
026        private boolean forceOverwrite = true;    // false;
027        private boolean overwriteNewer = false;
028        private boolean preserveLastModified = false;
029    
030        public void process(Transformer transformer, InjectorOptions options, File source, File target) {
031    
032            LOG.debug("process " + source.getAbsolutePath() + " " + target.getAbsolutePath());
033            Assert.assertNotNull("transformer", transformer);
034    
035            long lastModified;
036    
037            if (!source.exists()) {
038                throw new JRatException("source file does not exist : " + source);
039            }
040    
041            LOG.debug("source exists");
042    
043            if (source.isDirectory()) {
044                throw new JRatException("source file is a directory : " + source);
045            }
046    
047            LOG.debug("source is real file (not dir)");
048    
049            if (source.canRead() == false) {
050                throw new JRatException("source file can not be read (check permissions): " + source);
051            }
052    
053            LOG.debug("source can be read");
054    
055            lastModified = source.lastModified();
056    
057            if (target.exists()) {
058                LOG.debug("target exists " + target.getAbsolutePath());
059    
060                if (forceOverwrite == false) {
061                    throw new JRatException("target exists and forceOverwrite is disabled : " + source);
062                }
063    
064                if (target.isDirectory()) {
065                    throw new JRatException("target is directory : " + target);
066                }
067    
068                if (target.canWrite() == false) {
069                    throw new JRatException("unable to write to target (check permissions) : " + target);
070                }
071    
072                // newer is bigger
073                if (target.lastModified() > source.lastModified()) {
074    
075                    // target is newer than source
076                    if (!overwriteNewer) {
077                        throw new JRatException("target is newer than source and overwriteNewer is disabled : "
078                                + source);
079                    }
080                }
081    
082                processUsingSwapFile(transformer, options, source, target);
083            } else {
084                LOG.debug("target does not exist " + target.getAbsolutePath());
085                processFile(transformer, options, source, target);
086            }
087    
088            if (preserveLastModified) {
089                target.setLastModified(lastModified);
090            }
091        }
092    
093    
094        protected void processUsingSwapFile(Transformer transformer, InjectorOptions options, File source, File target) {
095    
096            LOG.debug("processUsingSwapFile " + source.getAbsolutePath() + " " + target.getAbsolutePath());
097    
098            File workFile = new File(target.getAbsolutePath() + Injector.WORK_FILE_END);
099    
100            if (workFile.exists()) {
101                LOG.info("workfile found, deleting");
102                IOUtil.delete(workFile);
103            }
104    
105            try {
106                processFile(transformer, options, source, workFile);
107    
108                if (!workFile.exists()) {
109                    throw new JRatException("processFile seems to have worked, but target file doesn't exist : "
110                            + source);
111                }
112    
113                IOUtil.rename(workFile, target, true);
114            }
115            catch (Throwable e) {
116                String msg = "Failed to instrument " + source + " : " + e;
117    
118                if ((workFile.exists()) && (!workFile.delete())) {
119                    msg += " and couldn't delete the corrupt file " + workFile.getAbsolutePath();
120                }
121    
122                throw new JRatException(msg, e);
123            }
124            finally {
125                IOUtil.deleteIfExists(workFile);
126            }
127        }
128    
129    
130        protected void processFile(Transformer transformer, InjectorOptions options, File source, File target) {
131    
132            int bufferSize = (int) Math.min(DEFAULT_BUFFER_SIZE, source.length());
133            InputStream inputStream = null;
134            OutputStream outputStream = null;
135    
136            try {
137                inputStream = IOUtil.openInputStream(source, bufferSize);
138                outputStream = IOUtil.openOutputStream(target, bufferSize);
139    
140                LOG.debug("calling processStream");
141                processStream(transformer, options, inputStream, outputStream, source.getName());
142            }
143            finally {
144                IOUtil.close(inputStream);
145                IOUtil.close(outputStream);
146            }
147        }
148    
149    
150        protected void processStream(
151                Transformer transformer, InjectorOptions options, InputStream inputStream, OutputStream outputStream, String fileName) {
152            throw new UnsupportedOperationException("processStream should be implemented by derived class");
153        }
154    }