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.car;
018    
019    import java.io.BufferedInputStream;
020    import java.io.File;
021    import java.io.FileInputStream;
022    import java.io.IOException;
023    import java.io.InputStream;
024    import java.io.OutputStream;
025    import java.io.PrintWriter;
026    import java.util.Iterator;
027    import java.util.Set;
028    
029    import javax.servlet.ServletException;
030    import javax.servlet.http.HttpServlet;
031    import javax.servlet.http.HttpServletRequest;
032    import javax.servlet.http.HttpServletResponse;
033    import javax.xml.bind.JAXBException;
034    import javax.xml.parsers.DocumentBuilder;
035    import javax.xml.parsers.DocumentBuilderFactory;
036    import javax.xml.parsers.ParserConfigurationException;
037    import javax.xml.stream.XMLStreamException;
038    import javax.xml.transform.OutputKeys;
039    import javax.xml.transform.Transformer;
040    import javax.xml.transform.TransformerException;
041    import javax.xml.transform.TransformerFactory;
042    import javax.xml.transform.dom.DOMSource;
043    import javax.xml.transform.stream.StreamResult;
044    
045    import org.apache.commons.logging.Log;
046    import org.apache.commons.logging.LogFactory;
047    import org.apache.geronimo.gbean.AbstractName;
048    import org.apache.geronimo.gbean.AbstractNameQuery;
049    import org.apache.geronimo.kernel.Kernel;
050    import org.apache.geronimo.kernel.KernelRegistry;
051    import org.apache.geronimo.kernel.config.ConfigurationManager;
052    import org.apache.geronimo.kernel.config.ConfigurationStore;
053    import org.apache.geronimo.kernel.config.ConfigurationUtil;
054    import org.apache.geronimo.kernel.config.NoSuchConfigException;
055    import org.apache.geronimo.kernel.config.NoSuchStoreException;
056    import org.apache.geronimo.kernel.repository.Artifact;
057    import org.apache.geronimo.kernel.repository.Repository;
058    import org.apache.geronimo.kernel.repository.Version;
059    import org.apache.geronimo.kernel.util.XmlUtil;
060    import org.apache.geronimo.system.plugin.PluginInstaller;
061    import org.apache.geronimo.system.plugin.PluginXmlUtil;
062    import org.apache.geronimo.system.plugin.model.PluginListType;
063    import org.w3c.dom.Document;
064    import org.w3c.dom.Element;
065    import org.w3c.dom.Text;
066    
067    /**
068     * Servlet that lets you download a CAR from the repository
069     *
070     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
071     */
072    public class GeronimoAsMavenServlet extends HttpServlet {
073        private final static Log log = LogFactory.getLog(GeronimoAsMavenServlet.class);
074    
075        protected void doHead(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
076            handleRequest(httpServletRequest, httpServletResponse, false);
077        }
078    
079        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
080            handleRequest(request, response, true);
081        }
082        protected void handleRequest(HttpServletRequest request, HttpServletResponse response, boolean reply) throws ServletException, IOException {
083            String path = request.getPathInfo();
084            if(path == null) {
085                throw new ServletException("No configId specified for CAR download");
086            }
087            Kernel kernel = KernelRegistry.getSingleKernel();
088            if(path.equals("/geronimo-plugins.xml")) {
089                response.setContentType("text/xml");
090                if(reply) {
091                    try {
092                        generateConfigFile(request, kernel, response.getWriter());
093                    } catch (Exception e) {
094                        throw new ServletException("Unable to generate Geronimo configuration list", e);
095                    }
096                }
097            } else if(path.endsWith("/maven-metadata.xml")) {
098                response.setContentType("text/xml");
099                try {
100                    String start = path.substring(0, path.lastIndexOf('/'));
101                    if(start.charAt(0) == '/') {
102                        start = start.substring(1);
103                    }
104                    String[] parts = start.split("/");
105                    if(parts.length > 2) {
106                        StringBuffer buf = new StringBuffer();
107                        for (int i = 0; i < parts.length-1; i++) {
108                            String part = parts[i];
109                            if(i > 0) buf.append('.');
110                            buf.append(part);
111                        }
112                        generateMavenFile(kernel, response.getWriter(), buf.toString(), parts[parts.length-1], reply);
113                    } else {
114                        generateMavenFile(kernel, response.getWriter(), parts[0], parts[1], reply);
115                    }
116                } catch (Exception e) {
117                    throw new ServletException("Unable to generate Geronimo configuration list", e);
118                }
119            } else {
120                if(path.startsWith("/")) {
121                    path = path.substring(1);
122                }
123                String configId = parsePath(path, response);
124                if(configId == null) { // we already sent the 404
125                    return;
126                }
127                if(!produceDownloadFile(kernel, Artifact.create(configId), response, reply)) {
128                    response.sendError(404, "Cannot locate download file "+path);
129                }
130            }
131        }
132    
133        private static String parsePath(String path, HttpServletResponse response) throws IOException {
134            String[] parts = path.split("/");
135            String groupId, artifactId, version, type;
136            if(parts.length < 4) {
137                response.sendError(404, "Unrecognized path form "+path);
138                return null;
139            } else {  // e.g.   console/MyDatabase/1.0-SNAPSHOT/MyDatabase-1.0-SNAPSHOT.rar
140                groupId = parts[0];
141                for(int i=4; i<parts.length; i++) {
142                    groupId = groupId+"."+parts[i-3];
143                }
144                artifactId = parts[parts.length-3];
145                version = parts[parts.length-2];
146                if(!parts[parts.length-1].startsWith(artifactId+"-"+version)) {
147                    response.sendError(404, "Unrecognized path structure "+path);
148                    return null;
149                }
150                type = parts[parts.length-1].substring(artifactId.length()+version.length()+2);
151            }
152            return groupId+"/"+artifactId+"/"+version+"/"+type;
153        }
154    
155        private boolean produceDownloadFile(Kernel kernel, Artifact configId, HttpServletResponse response, boolean reply) throws IOException {
156            //todo: replace kernel mumbo jumbo with JSR-77 navigation
157            // Step 1: check if it's in a configuration store
158            ConfigurationManager mgr = ConfigurationUtil.getConfigurationManager(kernel);
159            if(mgr.isConfiguration(configId)) {
160                ConfigurationStore store = mgr.getStoreForConfiguration(configId);
161                response.setContentType("application/zip");
162                if(!reply) {
163                    return true;
164                }
165                try {
166                    store.exportConfiguration(configId, response.getOutputStream());
167                    return true;
168                } catch (NoSuchConfigException e) {
169                    log.error("Inconsistent ConfigurationStore data; ConfigManager claims it has configuration "+configId+" but store claims it doesn't",e);
170                    throw new IOException("Unable to write ZIP file; see server log for details");
171                }
172            }
173            // Step 2: check if it's in a repository
174            Set repos = kernel.listGBeans(new AbstractNameQuery(Repository.class.getName()));
175            for (Iterator it = repos.iterator(); it.hasNext();) {
176                AbstractName name = (AbstractName) it.next();
177                Repository repo = (Repository) kernel.getProxyManager().createProxy(name, Repository.class);
178                if(repo.contains(configId)) {
179                    File path = repo.getLocation(configId);
180                    if(!path.exists()) throw new IllegalStateException("Can't find file '"+path.getAbsolutePath()+"' though repository said there's an artifact there!");
181                    response.setContentType("application/zip");
182                    if(!reply) {
183                        return true;
184                    }
185                    InputStream in = new BufferedInputStream(new FileInputStream(path));
186                    response.setContentLength((int)path.length());
187                    OutputStream out = response.getOutputStream();
188                    byte[] buf = new byte[1024];
189                    int count;
190                    while((count = in.read(buf)) > -1) {
191                        out.write(buf, 0, count);
192                    }
193                    in.close();
194                    return true;
195                }
196            }
197            // Step 3: wasn't found
198            return false;
199        }
200    
201        private void generateConfigFile(HttpServletRequest request, Kernel kernel, PrintWriter out) throws NoSuchStoreException, JAXBException, XMLStreamException {
202            String repo = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+request.getServletPath();
203            if(!repo.endsWith("/")) repo += "/";
204            ConfigurationManager mgr = ConfigurationUtil.getConfigurationManager(kernel);
205            PluginInstaller installer = getInstaller(kernel);
206            PluginListType pluginList = installer.createPluginListForRepositories(repo);
207            PluginXmlUtil.writePluginList(pluginList, out);
208        }
209    
210    
211        private PluginInstaller getInstaller(Kernel kernel) {
212            Set names = kernel.listGBeans(new AbstractNameQuery(PluginInstaller.class.getName()));
213            if(names.size() == 0) {
214                return null;
215            }
216            return (PluginInstaller) kernel.getProxyManager().createProxy((AbstractName) names.iterator().next(), PluginInstaller.class);
217        }
218    
219        private void generateMavenFile(Kernel kernel, PrintWriter writer, String groupId, String artifactId, boolean reply) throws ParserConfigurationException, TransformerException {
220            ConfigurationManager mgr = ConfigurationUtil.getConfigurationManager(kernel);
221            Artifact[] artifacts = mgr.getArtifactResolver().queryArtifacts(new Artifact(groupId, artifactId, (Version)null, null));
222            if(!reply) {
223                return;
224            }
225    
226            DocumentBuilderFactory factory = XmlUtil.newDocumentBuilderFactory();
227            DocumentBuilder builder = factory.newDocumentBuilder();
228            Document doc = builder.newDocument();
229            Element root = doc.createElement("metadata");
230            doc.appendChild(root);
231            createText(doc, root, "groupId", groupId);
232            createText(doc, root, "artifactId", artifactId);
233            if(artifacts.length > 0) {
234                createText(doc, root, "version", artifacts[0].getVersion().toString());
235            }
236            Element versioning = doc.createElement("versioning");
237            root.appendChild(versioning);
238            Element versions = doc.createElement("versions");
239            versioning.appendChild(versions);
240            for (int i = 0; i < artifacts.length; i++) {
241                Artifact artifact = artifacts[i];
242                createText(doc, versions, "version", artifact.getVersion().toString());
243            }
244            TransformerFactory xfactory = XmlUtil.newTransformerFactory();
245            Transformer xform = xfactory.newTransformer();
246            xform.setOutputProperty(OutputKeys.INDENT, "yes");
247            xform.transform(new DOMSource(doc), new StreamResult(writer));
248        }
249    
250    
251        private void createText(Document doc, Element parent, String name, String text) {
252            Element child = doc.createElement(name);
253            parent.appendChild(child);
254            Text node = doc.createTextNode(text);
255            child.appendChild(node);
256        }
257    
258    }