001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.console.databasemanager.wizard;
018    
019    import java.io.BufferedOutputStream;
020    import java.io.File;
021    import java.io.FileOutputStream;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.io.OutputStream;
025    import java.io.Serializable;
026    import java.net.MalformedURLException;
027    import java.net.URL;
028    import java.net.URLConnection;
029    import java.util.ArrayList;
030    import java.util.Collections;
031    import java.util.HashSet;
032    import java.util.Iterator;
033    import java.util.List;
034    import java.util.Properties;
035    import java.util.Random;
036    import java.util.Set;
037    import java.util.jar.JarEntry;
038    import java.util.jar.JarFile;
039    import org.apache.commons.logging.Log;
040    import org.apache.commons.logging.LogFactory;
041    import org.apache.geronimo.kernel.repository.Artifact;
042    import org.apache.geronimo.kernel.repository.FileWriteMonitor;
043    import org.apache.geronimo.kernel.repository.WriteableRepository;
044    
045    /**
046     * A utility that handles listing and downloading available JDBC driver JARs.
047     * It can handle straight JARs and also JARs in ZIP files.
048     *
049     * @version $Rev: 476061 $ $Date: 2006-11-17 01:36:50 -0500 (Fri, 17 Nov 2006) $
050     */
051    public class DriverDownloader {
052        private final static Log log = LogFactory.getLog(DriverDownloader.class);
053        Random random;
054    
055        public Properties readDriverFile(URL url) {
056            try {
057                InputStream in = url.openStream();
058                Properties props = new Properties();
059                props.load(in);
060                in.close();
061                return props;
062            } catch (IOException e) {
063                log.error("Unable to download driver properties", e);
064                return null;
065            }
066        }
067    
068        public DriverInfo[] loadDriverInfo(URL driverInfoFile) {
069            List list = new ArrayList();
070            Properties props = readDriverFile(driverInfoFile);
071            if(props == null) {
072                return new DriverInfo[0];
073            }
074            Set drivers = new HashSet();
075            for (Iterator it = props.keySet().iterator(); it.hasNext();) {
076                String key = (String) it.next();
077                if(!key.startsWith("driver.")) {
078                    continue;
079                }
080                int pos = key.indexOf('.', 7);
081                if(pos > -1) {
082                    drivers.add(key.substring(7, pos));
083                }
084            }
085            List urls = new ArrayList();
086            for (Iterator it = drivers.iterator(); it.hasNext();) {
087                String driver = (String) it.next();
088                String name = props.getProperty("driver."+driver+".name");
089                String repository = props.getProperty("driver."+driver+".repository");
090                String unzip = props.getProperty("driver."+driver+".unzip");
091                urls.clear();
092                int index = 1;
093                while(true) {
094                    String url = props.getProperty("driver."+driver+".url."+index);
095                    if(url != null) {
096                        ++index;
097                        try {
098                            urls.add(new URL(url));
099                        } catch (MalformedURLException e) {
100                            log.error("Unable to process URL from driver list", e);
101                        }
102                    } else {
103                        break;
104                    }
105                }
106                if(name != null && repository != null && urls.size() > 0) {
107                    DriverInfo info = new DriverInfo(name, repository);
108                    info.setUnzipPath(unzip);
109                    info.setUrls((URL[]) urls.toArray(new URL[urls.size()]));
110                    list.add(info);
111                }
112            }
113            Collections.sort(list);
114            return (DriverInfo[]) list.toArray(new DriverInfo[list.size()]);
115        }
116    
117        /**
118         * Downloads a driver and loads it into the local repository.
119         */
120        public void loadDriver(WriteableRepository repo, DriverInfo driver, FileWriteMonitor monitor) throws IOException {
121            int urlIndex = 0;
122            if (driver.urls.length > 1) {
123                if (random == null) {
124                    random = new Random();
125                }
126                urlIndex = random.nextInt(driver.urls.length);
127            }
128            URL url = driver.urls[urlIndex];
129            InputStream in;
130            String uri = driver.getRepositoryURI();
131            if (driver.unzipPath != null) {
132                byte[] buf = new byte[1024];
133                int size;
134                int total = 0;
135                int threshold = 10240;
136                URLConnection uc = url.openConnection();
137                int filesize = uc.getContentLength();
138                InputStream net = uc.getInputStream();
139                JarFile jar = null;
140                File download = null;
141                try {
142                    download = File.createTempFile("geronimo-driver-download", ".zip");
143                    OutputStream out = new BufferedOutputStream(new FileOutputStream(download));
144                    if (monitor != null) {
145                        monitor.writeStarted("Download driver archive to " + download, filesize);
146                    }
147                    try {
148                        while ((size = net.read(buf)) > -1) {
149                            out.write(buf, 0, size);
150                            if (monitor != null) {
151                                total += size;
152                                if (total > threshold) {
153                                    monitor.writeProgress(total);
154                                    threshold += 10240;
155                                }
156                            }
157                        }
158                        out.flush();
159                        out.close();
160                    } finally {
161                        if (monitor != null) {
162                            monitor.writeComplete(total);
163                        }
164                    }
165                    jar = new JarFile(download);
166                    JarEntry entry = jar.getJarEntry(driver.unzipPath);
167                    if (entry == null) {
168                        log.error("Cannot extract driver JAR " + driver.unzipPath + " from download file " + url);
169                    } else {
170                        in = jar.getInputStream(entry);
171                        repo.copyToRepository(in, (int)entry.getSize(), Artifact.create(uri), monitor);
172                    }
173                } finally {
174                    if (jar != null) try {
175                        jar.close();
176                    } catch (IOException e) {
177                        log.error("Unable to close JAR file", e);
178                    }
179                    if (download != null) {
180                        download.delete();
181                    }
182                }
183            } else {
184                URLConnection con = url.openConnection();
185                in = con.getInputStream();
186                repo.copyToRepository(in, con.getContentLength(), Artifact.create(uri), monitor);
187            }
188        }
189    
190        public static class DriverInfo implements Comparable, Serializable {
191            private final static long serialVersionUID = -1202452382992975449L;
192            
193            private String name;
194            private String repositoryURI;
195            private String unzipPath;
196            private URL[] urls;
197    
198            public DriverInfo(String name, String repositoryURI) {
199                this.name = name;
200                this.repositoryURI = repositoryURI;
201            }
202    
203            public String getName() {
204                return name;
205            }
206    
207            public void setName(String name) {
208                this.name = name;
209            }
210    
211            public String getRepositoryURI() {
212                return repositoryURI;
213            }
214    
215            public void setRepositoryURI(String repositoryURI) {
216                this.repositoryURI = repositoryURI;
217            }
218    
219            public String getUnzipPath() {
220                return unzipPath;
221            }
222    
223            public void setUnzipPath(String unzipPath) {
224                this.unzipPath = unzipPath;
225            }
226    
227            public URL[] getUrls() {
228                return urls;
229            }
230    
231            public void setUrls(URL[] urls) {
232                this.urls = urls;
233            }
234    
235            public int compareTo(Object o) {
236                return name.compareTo(((DriverInfo)o).name);
237            }
238        }
239    }