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 }