svn-gvsig-desktop / trunk / build / distribution / IzPack / src / lib / com / izforge / izpack / compiler / Packager.java @ 21757
History | View | Annotate | Download (18.8 KB)
1 | 21757 | jcampos | /*
|
---|---|---|---|
2 | * $Id: Packager.java 5819 2006-06-14 07:29:09Z cesar $
|
||
3 | * IzPack
|
||
4 | * Copyright (C) 2001-2004 Julien Ponge
|
||
5 | *
|
||
6 | * File : Packager.java
|
||
7 | * Description : Packs files and data into an installer
|
||
8 | * Author's email : julien@izforge.com
|
||
9 | *
|
||
10 | * This program is free software; you can redistribute it and/or
|
||
11 | * modify it under the terms of the GNU General Public License
|
||
12 | * as published by the Free Software Foundation; either version 2
|
||
13 | * of the License, or any later version.
|
||
14 | *
|
||
15 | * This program is distributed in the hope that it will be useful,
|
||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
18 | * GNU General Public License for more details.
|
||
19 | *
|
||
20 | * You should have received a copy of the GNU General Public License
|
||
21 | * along with this program; if not, write to the Free Software
|
||
22 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
23 | */
|
||
24 | package com.izforge.izpack.compiler; |
||
25 | |||
26 | import java.io.File; |
||
27 | import java.io.FileInputStream; |
||
28 | import java.io.FileOutputStream; |
||
29 | import java.io.IOException; |
||
30 | import java.io.InputStream; |
||
31 | import java.io.ObjectOutputStream; |
||
32 | import java.io.OutputStream; |
||
33 | import java.net.URL; |
||
34 | import java.util.ArrayList; |
||
35 | import java.util.HashMap; |
||
36 | import java.util.HashSet; |
||
37 | import java.util.Iterator; |
||
38 | import java.util.List; |
||
39 | import java.util.Map; |
||
40 | import java.util.Properties; |
||
41 | import java.util.Set; |
||
42 | import java.util.jar.JarOutputStream; |
||
43 | import java.util.zip.Deflater; |
||
44 | import java.util.zip.ZipEntry; |
||
45 | import java.util.zip.ZipException; |
||
46 | import java.util.zip.ZipInputStream; |
||
47 | import java.util.zip.ZipOutputStream; |
||
48 | |||
49 | import com.izforge.izpack.CustomData; |
||
50 | import com.izforge.izpack.GUIPrefs; |
||
51 | import com.izforge.izpack.Info; |
||
52 | import com.izforge.izpack.Pack; |
||
53 | import com.izforge.izpack.PackFile; |
||
54 | import com.izforge.izpack.Panel; |
||
55 | |||
56 | /**
|
||
57 | * The packager class. The packager is used by the compiler to put files into an
|
||
58 | * installer, and create the actual installer files.
|
||
59 | *
|
||
60 | * @author Julien Ponge
|
||
61 | * @author Chadwick McHenry
|
||
62 | */
|
||
63 | public class Packager |
||
64 | { |
||
65 | /** Path to the skeleton installer. */
|
||
66 | public static final String SKELETON_SUBPATH = "lib/installer.jar"; |
||
67 | |||
68 | /** Base file name of all jar files. This has no ".jar" suffix. */
|
||
69 | private File baseFile = null; |
||
70 | |||
71 | /** Executable zipped output stream. First to open, last to close. */
|
||
72 | private JarOutputStream primaryJarStream; |
||
73 | |||
74 | /** Basic installer info. */
|
||
75 | private Info info = null; |
||
76 | |||
77 | /** Gui preferences of instatller. */
|
||
78 | private GUIPrefs guiPrefs = null; |
||
79 | |||
80 | /** The variables used in the project */
|
||
81 | private Properties variables = new Properties(); |
||
82 | |||
83 | /** The ordered panels informations. */
|
||
84 | private List panelList = new ArrayList(); |
||
85 | |||
86 | /** The ordered packs informations (as PackInfo objects). */
|
||
87 | private List packsList = new ArrayList(); |
||
88 | |||
89 | /** The ordered langpack ISO3 names. */
|
||
90 | private List langpackNameList = new ArrayList(); |
||
91 | |||
92 | /** The ordered custom actions informations. */
|
||
93 | private List customDataList = new ArrayList(); |
||
94 | |||
95 | /** The langpack URLs keyed by ISO3 name. */
|
||
96 | private Map installerResourceURLMap = new HashMap(); |
||
97 | |||
98 | /** Jar file URLs who's contents will be copied into the installer. */
|
||
99 | private Set includedJarURLs = new HashSet(); |
||
100 | |||
101 | /** Each pack is created in a separte jar if webDirURL is non-null. */
|
||
102 | private boolean packJarsSeparate = false; |
||
103 | |||
104 | /** The listeners. */
|
||
105 | private PackagerListener listener;
|
||
106 | |||
107 | /** The constructor. */
|
||
108 | public Packager() {}
|
||
109 | |||
110 | /**
|
||
111 | * Create the installer, beginning with the specified jar. If the name
|
||
112 | * specified does not end in ".jar", it is appended. If secondary jars are
|
||
113 | * created for packs (if the Info object added has a webDirURL set), they are
|
||
114 | * created in the same directory, named sequentially by inserting ".pack#"
|
||
115 | * (where '#' is the pack number) ".jar" suffix: e.g. "foo.pack1.jar". If any
|
||
116 | * file exists, it is overwritten.
|
||
117 | */
|
||
118 | public void createInstaller(File primaryFile) throws IOException |
||
119 | { |
||
120 | // preliminary work
|
||
121 | String baseName = primaryFile.getName();
|
||
122 | if (baseName.endsWith(".jar")) |
||
123 | { |
||
124 | baseName = baseName.substring(0, baseName.length()-4); |
||
125 | baseFile = new File(primaryFile.getParentFile(), baseName); |
||
126 | } |
||
127 | else
|
||
128 | baseFile = primaryFile; |
||
129 | |||
130 | info.setInstallerBase(baseFile.getName()); |
||
131 | packJarsSeparate = (info.getWebDirURL() != null);
|
||
132 | |||
133 | // primary (possibly only) jar. -1 indicates primary
|
||
134 | primaryJarStream = getJarOutputStream(baseFile.getName() + ".jar");
|
||
135 | |||
136 | sendStart(); |
||
137 | |||
138 | // write the primary jar. MUST be first so manifest is not overwritten by
|
||
139 | // an included jar
|
||
140 | writeSkeletonInstaller(); |
||
141 | |||
142 | writeInstallerObject("info", info);
|
||
143 | writeInstallerObject("vars", variables);
|
||
144 | writeInstallerObject("GUIPrefs", guiPrefs);
|
||
145 | writeInstallerObject("panelsOrder", panelList);
|
||
146 | writeInstallerObject("customData", customDataList);
|
||
147 | writeInstallerObject("langpacks.info", langpackNameList);
|
||
148 | writeInstallerResources(); |
||
149 | writeIncludedJars(); |
||
150 | |||
151 | // Pack File Data may be written to separate jars
|
||
152 | writePacks(); |
||
153 | |||
154 | // finish up
|
||
155 | primaryJarStream.close(); |
||
156 | |||
157 | sendStop(); |
||
158 | } |
||
159 | |||
160 | /* **********************************************************************
|
||
161 | * Listener assistance
|
||
162 | * **********************************************************************/
|
||
163 | |||
164 | /**
|
||
165 | * Adds a listener.
|
||
166 | *
|
||
167 | * @param listener The listener.
|
||
168 | */
|
||
169 | public void setPackagerListener(PackagerListener listener) |
||
170 | { |
||
171 | this.listener = listener;
|
||
172 | } |
||
173 | |||
174 | /**
|
||
175 | * Dispatches a message to the listeners.
|
||
176 | *
|
||
177 | * @param job The job description.
|
||
178 | */
|
||
179 | private void sendMsg(String job) |
||
180 | { |
||
181 | if (listener != null) |
||
182 | listener.packagerMsg(job); |
||
183 | } |
||
184 | |||
185 | /** Dispatches a start event to the listeners. */
|
||
186 | private void sendStart() |
||
187 | { |
||
188 | if (listener != null) |
||
189 | listener.packagerStart(); |
||
190 | } |
||
191 | |||
192 | /** Dispatches a stop event to the listeners. */
|
||
193 | private void sendStop() |
||
194 | { |
||
195 | if (listener != null) |
||
196 | listener.packagerStop(); |
||
197 | } |
||
198 | |||
199 | |||
200 | /* **********************************************************************
|
||
201 | * Public methods to add data to the Installer being packed
|
||
202 | * **********************************************************************/
|
||
203 | |||
204 | /**
|
||
205 | * Sets the informations related to this installation.
|
||
206 | *
|
||
207 | * @param info The info section.
|
||
208 | * @exception Exception Description of the Exception
|
||
209 | */
|
||
210 | public void setInfo(Info info) throws Exception |
||
211 | { |
||
212 | sendMsg("Setting the installer informations ...");
|
||
213 | this.info = info;
|
||
214 | } |
||
215 | |||
216 | /**
|
||
217 | * Sets the GUI preferences.
|
||
218 | *
|
||
219 | * @param prefs The new gUIPrefs value
|
||
220 | * @exception Exception Description of the Exception
|
||
221 | */
|
||
222 | public void setGUIPrefs(GUIPrefs prefs) |
||
223 | { |
||
224 | sendMsg("Setting the GUI preferences ...");
|
||
225 | guiPrefs = prefs; |
||
226 | } |
||
227 | |||
228 | /**
|
||
229 | * Allows access to add, remove and update the variables for the project,
|
||
230 | * which are maintained in the packager.
|
||
231 | *
|
||
232 | * @return map of variable names to values
|
||
233 | */
|
||
234 | public Properties getVariables() |
||
235 | { |
||
236 | return variables;
|
||
237 | } |
||
238 | |||
239 | /**
|
||
240 | * Add a panel, where order is important. Only one copy of the class files
|
||
241 | * neeed are inserted in the installer.
|
||
242 | */
|
||
243 | public void addPanelJar(Panel panel, URL jarURL ) |
||
244 | { |
||
245 | panelList.add(panel); // serialized to keep order/variables correct
|
||
246 | addJarContent(jarURL); // each included once, no matter how many times added
|
||
247 | } |
||
248 | |||
249 | |||
250 | /**
|
||
251 | * Add a custom data like custom actions,
|
||
252 | * where order is important. Only one copy of the class files
|
||
253 | * neeed are inserted in the installer.
|
||
254 | * @param ca custom action object
|
||
255 | * @param url the URL to include once
|
||
256 | */
|
||
257 | public void addCustomJar(CustomData ca, URL url) |
||
258 | { |
||
259 | customDataList.add(ca); // serialized to keep order/variables correct
|
||
260 | addJarContent(url); // each included once, no matter how many times added
|
||
261 | } |
||
262 | /**
|
||
263 | * Adds a pack, order is mostly irrelevant.
|
||
264 | *
|
||
265 | * @param pack contains all the files and items that go with a pack
|
||
266 | */
|
||
267 | public void addPack(PackInfo pack) |
||
268 | { |
||
269 | packsList.add(pack); |
||
270 | } |
||
271 | /**
|
||
272 | * Gets the packages list
|
||
273 | */
|
||
274 | public List getPacksList() |
||
275 | { |
||
276 | return packsList;
|
||
277 | } |
||
278 | |||
279 | /**
|
||
280 | * Adds a language pack.
|
||
281 | *
|
||
282 | * @param iso3 The ISO3 code.
|
||
283 | * @param xmlURL The location of the xml local info
|
||
284 | * @param flagURL The location of the flag image resource
|
||
285 | * @exception Exception Description of the Exception
|
||
286 | */
|
||
287 | public void addLangPack(String iso3, URL xmlURL, URL flagURL) |
||
288 | { |
||
289 | sendMsg("Adding langpack : " + iso3 + " ..."); |
||
290 | // put data & flag as entries in installer, and keep array of iso3's names
|
||
291 | langpackNameList.add(iso3); |
||
292 | addResource("flag." + iso3, flagURL);
|
||
293 | installerResourceURLMap.put("langpacks/" + iso3 + ".xml", xmlURL); |
||
294 | } |
||
295 | |||
296 | /**
|
||
297 | * Adds a resource.
|
||
298 | *
|
||
299 | * @param resId The resource Id.
|
||
300 | * @param url The location of the data
|
||
301 | * @exception Exception Description of the Exception
|
||
302 | */
|
||
303 | public void addResource(String resId, URL url) |
||
304 | { |
||
305 | sendMsg("Adding resource : " + resId + " ..."); |
||
306 | installerResourceURLMap.put("res/" + resId, url);
|
||
307 | } |
||
308 | |||
309 | /**
|
||
310 | * Adds a native library.
|
||
311 | *
|
||
312 | * @param name The native library name.
|
||
313 | * @param url The url to get the data from.
|
||
314 | * @exception Exception Description of the Exception
|
||
315 | */
|
||
316 | public void addNativeLibrary(String name, URL url) throws Exception |
||
317 | { |
||
318 | sendMsg("Adding native library : " + name + " ..."); |
||
319 | installerResourceURLMap.put("native/" + name, url);
|
||
320 | } |
||
321 | |||
322 | /**
|
||
323 | * Adds a jar file content to the installer. Package structure is
|
||
324 | * maintained. Need mechanism to copy over signed entry information.
|
||
325 | *
|
||
326 | * @param jarURL The url of the jar to add to the installer. We use
|
||
327 | * a URL so the jar may be nested within another.
|
||
328 | */
|
||
329 | public void addJarContent(URL jarURL) |
||
330 | { |
||
331 | sendMsg("Adding content of jar : " + jarURL.getFile() + " ..."); |
||
332 | includedJarURLs.add(jarURL); |
||
333 | } |
||
334 | |||
335 | /**
|
||
336 | * Marks a native library to be added to the uninstaller.
|
||
337 | * @param data the describing custom action data object
|
||
338 | */
|
||
339 | public void addNativeUninstallerLibrary(CustomData data) |
||
340 | { |
||
341 | customDataList.add(data); // serialized to keep order/variables correct
|
||
342 | |||
343 | } |
||
344 | |||
345 | |||
346 | /* **********************************************************************
|
||
347 | * Private methods used when writing out the installer to jar files.
|
||
348 | * **********************************************************************/
|
||
349 | |||
350 | /**
|
||
351 | * Write skeleton installer to primary jar. It is just an included jar,
|
||
352 | * except that we copy the META-INF as well.
|
||
353 | */
|
||
354 | private void writeSkeletonInstaller() throws IOException |
||
355 | { |
||
356 | sendMsg("Copying the skeleton installer ...");
|
||
357 | |||
358 | InputStream is = Packager.class.getResourceAsStream("/" + SKELETON_SUBPATH); |
||
359 | if (is == null) |
||
360 | { |
||
361 | File skeleton = new File(Compiler.IZPACK_HOME, SKELETON_SUBPATH); |
||
362 | is = new FileInputStream(skeleton); |
||
363 | } |
||
364 | ZipInputStream inJarStream = new ZipInputStream(is); |
||
365 | copyZip(inJarStream, primaryJarStream); |
||
366 | } |
||
367 | |||
368 | /**
|
||
369 | * Write an arbitrary object to primary jar.
|
||
370 | */
|
||
371 | private void writeInstallerObject(String entryName, Object object) |
||
372 | throws IOException |
||
373 | { |
||
374 | primaryJarStream.putNextEntry(new ZipEntry(entryName)); |
||
375 | ObjectOutputStream out = new ObjectOutputStream(primaryJarStream); |
||
376 | out.writeObject(object); |
||
377 | out.flush(); |
||
378 | primaryJarStream.closeEntry(); |
||
379 | } |
||
380 | |||
381 | /** Write the data referenced by URL to primary jar. */
|
||
382 | private void writeInstallerResources() throws IOException |
||
383 | { |
||
384 | sendMsg("Copying " + installerResourceURLMap.size() +
|
||
385 | " files into installer ...");
|
||
386 | |||
387 | Iterator i = installerResourceURLMap.keySet().iterator();
|
||
388 | while (i.hasNext())
|
||
389 | { |
||
390 | String name = (String) i.next(); |
||
391 | InputStream in = ((URL)installerResourceURLMap.get(name)).openStream(); |
||
392 | primaryJarStream.putNextEntry(new ZipEntry(name)); |
||
393 | copyStream(in, primaryJarStream); |
||
394 | primaryJarStream.closeEntry(); |
||
395 | in.close(); |
||
396 | } |
||
397 | } |
||
398 | |||
399 | /** Copy included jars to primary jar. */
|
||
400 | private void writeIncludedJars() throws IOException |
||
401 | { |
||
402 | sendMsg("Copying contents of " + includedJarURLs.size() +
|
||
403 | " jars into installer ...");
|
||
404 | |||
405 | Iterator i = includedJarURLs.iterator();
|
||
406 | while (i.hasNext())
|
||
407 | { |
||
408 | InputStream is = ((URL)i.next()).openStream(); |
||
409 | ZipInputStream inJarStream = new ZipInputStream(is); |
||
410 | copyZip(inJarStream, primaryJarStream); |
||
411 | } |
||
412 | } |
||
413 | |||
414 | /**
|
||
415 | * Write Packs to primary jar or each to a separate jar.
|
||
416 | */
|
||
417 | private void writePacks() throws IOException |
||
418 | { |
||
419 | sendMsg("Writing Packs ...");
|
||
420 | |||
421 | // Map to remember pack number and bytes offsets of back references
|
||
422 | Map storedFiles = new HashMap(); |
||
423 | |||
424 | // First write the serialized files and file metadata data for each pack
|
||
425 | // while counting bytes.
|
||
426 | |||
427 | int packNumber = 0; |
||
428 | Iterator packIter = packsList.iterator();
|
||
429 | while (packIter.hasNext())
|
||
430 | { |
||
431 | PackInfo packInfo = (PackInfo) packIter.next(); |
||
432 | Pack pack = packInfo.getPack(); |
||
433 | pack.nbytes = 0;
|
||
434 | |||
435 | // create a pack specific jar if required
|
||
436 | JarOutputStream packStream = primaryJarStream;
|
||
437 | if (packJarsSeparate)
|
||
438 | { |
||
439 | // See installer.Unpacker#getPackAsStream for the counterpart
|
||
440 | String name = baseFile.getName() + ".pack" + packNumber + ".jar"; |
||
441 | packStream = getJarOutputStream(name); |
||
442 | } |
||
443 | |||
444 | sendMsg("Writing Pack #" + packNumber + " : " + pack.name); |
||
445 | |||
446 | // Retrieve the correct output stream
|
||
447 | ZipEntry entry = new ZipEntry("packs/pack" + packNumber); |
||
448 | packStream.putNextEntry(entry); |
||
449 | packStream.flush(); // flush before we start counting
|
||
450 | |||
451 | ByteCountingOutputStream dos = new ByteCountingOutputStream(packStream);
|
||
452 | ObjectOutputStream objOut = new ObjectOutputStream(dos); |
||
453 | |||
454 | // We write the actual pack files
|
||
455 | long packageBytes = 0; |
||
456 | objOut.writeInt(packInfo.getPackFiles().size()); |
||
457 | |||
458 | Iterator iter = packInfo.getPackFiles().iterator();
|
||
459 | while (iter.hasNext())
|
||
460 | { |
||
461 | boolean addFile = !pack.loose;
|
||
462 | PackFile pf = (PackFile) iter.next(); |
||
463 | File file = packInfo.getFile(pf);
|
||
464 | |||
465 | // use a back reference if file was in previous pack, and in same jar
|
||
466 | long[] info = (long[]) storedFiles.get(file); |
||
467 | if (info != null && ! packJarsSeparate) |
||
468 | { |
||
469 | pf.setPreviousPackFileRef((int) info[0], info[1]); |
||
470 | addFile = false;
|
||
471 | } |
||
472 | |||
473 | objOut.writeObject(pf); // base info
|
||
474 | objOut.flush(); //make sure it is written
|
||
475 | |||
476 | if (addFile && ! pf.isDirectory())
|
||
477 | { |
||
478 | long pos = dos.getByteCount(); //get the position |
||
479 | |||
480 | FileInputStream inStream = new FileInputStream(file); |
||
481 | long bytesWritten = copyStream(inStream, objOut);
|
||
482 | |||
483 | if (bytesWritten != pf.length())
|
||
484 | throw new IOException("File size mismatch when reading " + file); |
||
485 | |||
486 | inStream.close(); |
||
487 | storedFiles.put(file, new long[] { packNumber, pos }); |
||
488 | } |
||
489 | |||
490 | // even if not written, it counts towards pack size
|
||
491 | pack.nbytes += pf.length(); |
||
492 | } |
||
493 | |||
494 | // Write out information about parsable files
|
||
495 | objOut.writeInt(packInfo.getParsables().size()); |
||
496 | iter = packInfo.getParsables().iterator(); |
||
497 | while (iter.hasNext())
|
||
498 | objOut.writeObject(iter.next()); |
||
499 | |||
500 | // Write out information about executable files
|
||
501 | objOut.writeInt(packInfo.getExecutables().size()); |
||
502 | iter = packInfo.getExecutables().iterator(); |
||
503 | while (iter.hasNext())
|
||
504 | objOut.writeObject(iter.next()); |
||
505 | |||
506 | // Write out information about updatecheck files
|
||
507 | objOut.writeInt(packInfo.getUpdateChecks().size()); |
||
508 | iter = packInfo.getUpdateChecks().iterator(); |
||
509 | while (iter.hasNext())
|
||
510 | objOut.writeObject(iter.next()); |
||
511 | |||
512 | // Cleanup
|
||
513 | objOut.flush(); |
||
514 | packStream.closeEntry(); |
||
515 | |||
516 | // close pack specific jar if required
|
||
517 | if (packJarsSeparate)
|
||
518 | packStream.close(); |
||
519 | |||
520 | packNumber++; |
||
521 | } |
||
522 | |||
523 | // Now that we know sizes, write pack metadata to primary jar.
|
||
524 | primaryJarStream.putNextEntry(new ZipEntry("packs.info")); |
||
525 | ObjectOutputStream out = new ObjectOutputStream(primaryJarStream); |
||
526 | out.writeInt(packsList.size()); |
||
527 | |||
528 | Iterator i = packsList.iterator();
|
||
529 | while (i.hasNext())
|
||
530 | { |
||
531 | PackInfo pack = (PackInfo) i.next(); |
||
532 | out.writeObject(pack.getPack()); |
||
533 | } |
||
534 | out.flush(); |
||
535 | primaryJarStream.closeEntry(); |
||
536 | } |
||
537 | |||
538 | |||
539 | /* **********************************************************************
|
||
540 | * Stream utilites for creation of the installer.
|
||
541 | * **********************************************************************/
|
||
542 | |||
543 | /** Return a stream for the next jar. */
|
||
544 | private JarOutputStream getJarOutputStream(String name) throws IOException |
||
545 | { |
||
546 | File file = new File(baseFile.getParentFile(), name); |
||
547 | sendMsg("Building installer jar: " + file.getAbsolutePath());
|
||
548 | |||
549 | JarOutputStream jar = new JarOutputStream(new FileOutputStream(file)); |
||
550 | jar.setLevel(Deflater.BEST_COMPRESSION);
|
||
551 | |||
552 | return jar;
|
||
553 | } |
||
554 | |||
555 | /**
|
||
556 | * Copies contents of one jar to another.
|
||
557 | *
|
||
558 | * <p>TODO: it would be useful to be able to keep signature information from
|
||
559 | * signed jar files, can we combine manifests and still have their content
|
||
560 | * signed?
|
||
561 | *
|
||
562 | * @see #copyStream(InputStream, OutputStream)
|
||
563 | */
|
||
564 | private void copyZip(ZipInputStream zin, ZipOutputStream out) |
||
565 | throws IOException |
||
566 | { |
||
567 | ZipEntry zentry;
|
||
568 | while ( (zentry = zin.getNextEntry()) != null) |
||
569 | { |
||
570 | try
|
||
571 | { |
||
572 | out.putNextEntry(new ZipEntry(zentry.getName())); |
||
573 | copyStream(zin, out); |
||
574 | out.closeEntry(); |
||
575 | zin.closeEntry(); |
||
576 | } catch (ZipException x) |
||
577 | { |
||
578 | // This avoids any problem that can occur with duplicate
|
||
579 | // directories. for instance all META-INF data in jars
|
||
580 | } |
||
581 | } |
||
582 | } |
||
583 | |||
584 | /**
|
||
585 | * Copies all the data from the specified input stream to the specified
|
||
586 | * output stream.
|
||
587 | *
|
||
588 | * @param in the input stream to read
|
||
589 | * @param out the output stream to write
|
||
590 | * @return the total number of bytes copied
|
||
591 | * @exception IOException if an I/O error occurs
|
||
592 | */
|
||
593 | private long copyStream(InputStream in, OutputStream out) |
||
594 | throws IOException |
||
595 | { |
||
596 | byte[] buffer = new byte[5120]; |
||
597 | long bytesCopied = 0; |
||
598 | int bytesInBuffer;
|
||
599 | while ((bytesInBuffer = in.read(buffer)) != -1) |
||
600 | { |
||
601 | out.write(buffer, 0, bytesInBuffer);
|
||
602 | bytesCopied += bytesInBuffer; |
||
603 | } |
||
604 | return bytesCopied;
|
||
605 | } |
||
606 | } |