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 }