OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 19 | Rev 73 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
18 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.erp.modules;
15
 
16
import org.openconcerto.utils.StreamUtils;
17
 
18
import java.io.BufferedInputStream;
19
import java.io.BufferedOutputStream;
20
import java.io.File;
19 ilm 21
import java.io.FileFilter;
18 ilm 22
import java.io.FileInputStream;
23
import java.io.FileOutputStream;
24
import java.io.IOException;
25
import java.io.InputStream;
26
import java.util.ArrayList;
19 ilm 27
import java.util.Enumeration;
20 ilm 28
import java.util.HashSet;
18 ilm 29
import java.util.List;
20 ilm 30
import java.util.Set;
18 ilm 31
import java.util.jar.JarEntry;
19 ilm 32
import java.util.jar.JarFile;
18 ilm 33
import java.util.jar.JarOutputStream;
19 ilm 34
import java.util.zip.ZipFile;
18 ilm 35
 
36
/**
37
 * Package a module from its properties and classes.
38
 *
39
 * @author Sylvain CUAZ
40
 */
41
public class ModulePackager {
42
    public static final String MODULE_PROPERTIES_PATH = "META-INF/openConcertoModule.properties";
43
 
44
    private final List<File> classesDirs;
19 ilm 45
    private final List<File> jars = new ArrayList<File>();
18 ilm 46
    private final File propsFile;
20 ilm 47
    private final Set<String> dirEntries, fileEntries;
48
    private boolean skipDuplicateFiles;
18 ilm 49
 
50
    public ModulePackager(final File propsFile, final File classes) {
51
        this.propsFile = propsFile;
52
        this.classesDirs = new ArrayList<File>(8);
53
        this.classesDirs.add(classes);
20 ilm 54
        this.dirEntries = new HashSet<String>();
55
        this.fileEntries = new HashSet<String>();
56
        this.setSkipDuplicateFiles(false);
18 ilm 57
    }
58
 
20 ilm 59
    public final void setSkipDuplicateFiles(boolean skipDuplicateFiles) {
60
        this.skipDuplicateFiles = skipDuplicateFiles;
61
    }
62
 
63
    public final boolean getSkipDuplicateFiles() {
64
        return this.skipDuplicateFiles;
65
    }
66
 
18 ilm 67
    public final void addDir(final File classesDir) {
68
        this.classesDirs.add(classesDir);
69
    }
70
 
19 ilm 71
    public final void addJarsFromDir(final File dir) {
72
        if (!dir.exists())
73
            throw new IllegalArgumentException("Directory " + dir.getAbsolutePath() + " not found");
74
        final File[] jarFiles = dir.listFiles(new FileFilter() {
75
            @Override
76
            public boolean accept(File f) {
77
                return f.isFile() && f.getName().endsWith(".jar");
78
            }
79
        });
80
        for (final File jarFile : jarFiles) {
81
            addJarUnsafe(jarFile);
82
        }
83
    }
84
 
85
    public final void addJar(final File jarFile) {
86
        if (!jarFile.isFile())
87
            throw new IllegalArgumentException("File " + jarFile.getAbsolutePath() + " not found");
88
        if (!jarFile.getName().endsWith(".jar"))
89
            throw new IllegalArgumentException("File " + jarFile.getAbsolutePath() + " is not a jar file");
90
        addJarUnsafe(jarFile);
91
    }
92
 
93
    private final void addJarUnsafe(File jarFile) {
94
        this.jars.add(jarFile);
95
    }
96
 
18 ilm 97
    /**
98
     * Write the package to the passed directory.
99
     *
100
     * @param dir where to create the package (its name will contain its id and version).
19 ilm 101
     * @return the created jar.
18 ilm 102
     * @throws IOException if the package cannot be created.
103
     */
19 ilm 104
    public final File writeToDir(final File dir) throws IOException {
18 ilm 105
        final RuntimeModuleFactory f = new RuntimeModuleFactory(this.propsFile);
106
        final File jarFile = new File(dir, f.getID() + "-" + f.getMajorVersion() + "." + f.getMinorVersion() + ".jar");
107
        final JarOutputStream jarStream = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(jarFile)));
20 ilm 108
        this.dirEntries.clear();
109
        this.fileEntries.clear();
18 ilm 110
        try {
111
            if (!this.zipExistingFile(jarStream, this.propsFile, MODULE_PROPERTIES_PATH))
112
                throw new IllegalStateException("Missing properties file : " + this.propsFile);
19 ilm 113
            for (File jarFileToAdd : this.jars) {
114
                this.zipJarFile(jarStream, jarFileToAdd);
115
            }
116
            for (final File classesDir : this.classesDirs) {
18 ilm 117
                this.zipBelow(jarStream, classesDir);
19 ilm 118
            }
18 ilm 119
        } finally {
120
            jarStream.close();
20 ilm 121
            this.dirEntries.clear();
122
            this.fileEntries.clear();
18 ilm 123
        }
19 ilm 124
        return jarFile;
18 ilm 125
    }
126
 
127
    // doesn't zip f
128
    protected void zipBelow(JarOutputStream jarStream, File f) throws IOException {
129
        this.zipRec(jarStream, f, "");
130
    }
131
 
132
    private String getPath(File f) throws IOException {
133
        String res = f.getAbsolutePath();
134
        if (f.isDirectory() && !res.endsWith("/"))
135
            res += '/';
136
        return res;
137
    }
138
 
139
    private String getEntryName(File f, File base) throws IOException {
140
        // needed otherwise we could pass ('abc', 'a')
141
        // but if base is a directory then its path is 'a/'
142
        if (!base.isDirectory())
143
            throw new IllegalArgumentException("Base not a directory : " + base);
144
        final String fPath = getPath(f);
145
        final String basePath = getPath(base);
146
        if (!fPath.startsWith(basePath))
147
            throw new IllegalArgumentException("Base is not a parent :\n" + base + "\n" + f);
148
        return fPath.substring(basePath.length()).replace('\\', '/');
149
    }
150
 
151
    protected void zipRec(JarOutputStream jarStream, File f, File base) throws IOException {
152
        zipRec(jarStream, f, getEntryName(f, base));
153
    }
154
 
155
    protected void zipRec(JarOutputStream jarStream, File f, final String entryName) throws IOException {
156
        if (entryName.length() > 0)
157
            this.zipExistingFile(jarStream, f, entryName);
158
        if (f.isDirectory())
159
            for (final File child : f.listFiles()) {
160
                this.zipRec(jarStream, child, entryName + '/' + child.getName());
161
            }
162
    }
163
 
164
    protected boolean zipExistingFile(JarOutputStream jarStream, File f, File base) throws IOException {
165
        return this.zipExistingFile(jarStream, f, getEntryName(f, base));
166
    }
167
 
168
    protected boolean zipExistingFile(JarOutputStream jarStream, File f, final String entryName) throws IOException {
169
        if (!f.exists())
170
            return false;
171
        final boolean isDir = f.isDirectory();
172
 
173
        String name = entryName;
174
        if (name.startsWith("/"))
175
            name = name.substring(1);
176
        if (isDir && !name.endsWith("/"))
177
            name += "/";
178
        final JarEntry entry = new JarEntry(name);
179
        entry.setTime(f.lastModified());
180
        if (!isDir)
181
            entry.setSize(f.length());
182
 
183
        final InputStream in = isDir ? null : new BufferedInputStream(new FileInputStream(f));
184
        try {
185
            zip(jarStream, entry, in);
186
        } finally {
187
            if (in != null)
188
                in.close();
189
        }
190
        return true;
191
    }
192
 
193
    private void zip(JarOutputStream jarStream, final JarEntry entry, InputStream in) throws IOException {
20 ilm 194
        // ignore duplicate directories
195
        final boolean isDir = entry.isDirectory();
196
        if (isDir && !this.dirEntries.add(entry.getName()))
197
            return;
198
        if (!isDir && this.getSkipDuplicateFiles() && !this.fileEntries.add(entry.getName()))
199
            return;
200
 
18 ilm 201
        jarStream.putNextEntry(entry);
202
        if (in != null) {
203
            StreamUtils.copy(in, jarStream);
204
        }
205
        jarStream.closeEntry();
206
    }
19 ilm 207
 
208
    private void zipJarFile(JarOutputStream out, File file) throws IOException {
209
        final JarFile f = new JarFile(file, false, ZipFile.OPEN_READ);
210
        try {
211
            final Enumeration<JarEntry> entries = f.entries();
212
            while (entries.hasMoreElements()) {
213
                final JarEntry e = entries.nextElement();
214
                if (!e.getName().startsWith("META-INF")) {
215
                    // use copy-constructor to keep all fields
216
                    zip(out, new JarEntry(e), f.getInputStream(e));
217
                }
218
            }
219
        } finally {
220
            f.close();
221
        }
222
    }
18 ilm 223
}