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.List;
028 import java.util.Set;
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.parsers.DocumentBuilder;
034 import javax.xml.parsers.DocumentBuilderFactory;
035 import javax.xml.parsers.ParserConfigurationException;
036 import javax.xml.transform.OutputKeys;
037 import javax.xml.transform.Transformer;
038 import javax.xml.transform.TransformerException;
039 import javax.xml.transform.TransformerFactory;
040 import javax.xml.transform.dom.DOMSource;
041 import javax.xml.transform.stream.StreamResult;
042 import org.apache.commons.logging.Log;
043 import org.apache.commons.logging.LogFactory;
044 import org.apache.geronimo.gbean.AbstractName;
045 import org.apache.geronimo.gbean.AbstractNameQuery;
046 import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
047 import org.apache.geronimo.kernel.Kernel;
048 import org.apache.geronimo.kernel.KernelRegistry;
049 import org.apache.geronimo.kernel.util.XmlUtil;
050 import org.apache.geronimo.kernel.config.ConfigurationInfo;
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.NoSuchStoreException;
055 import org.apache.geronimo.kernel.config.NoSuchConfigException;
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.system.plugin.PluginInstaller;
060 import org.apache.geronimo.system.plugin.PluginMetadata;
061 import org.w3c.dom.Document;
062 import org.w3c.dom.Element;
063 import org.w3c.dom.Text;
064
065 /**
066 * Servlet that lets you download a CAR from the repository
067 *
068 * @version $Rev: 549707 $ $Date: 2007-06-22 00:54:05 -0400 (Fri, 22 Jun 2007) $
069 */
070 public class GeronimoAsMavenServlet extends HttpServlet {
071 private final static Log log = LogFactory.getLog(GeronimoAsMavenServlet.class);
072
073 protected void doHead(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
074 handleRequest(httpServletRequest, httpServletResponse, false);
075 }
076
077 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
078 handleRequest(request, response, true);
079 }
080 protected void handleRequest(HttpServletRequest request, HttpServletResponse response, boolean reply) throws ServletException, IOException {
081 String path = request.getPathInfo();
082 if(path == null) {
083 throw new ServletException("No configId specified for CAR download");
084 }
085 Kernel kernel = KernelRegistry.getSingleKernel();
086 if(path.equals("/geronimo-plugins.xml")) {
087 response.setContentType("text/xml");
088 if(reply) {
089 try {
090 generateConfigFile(request, kernel, response.getWriter());
091 } catch (Exception e) {
092 throw new ServletException("Unable to generate Geronimo configuration list", e);
093 }
094 }
095 } else if(path.endsWith("/maven-metadata.xml")) {
096 response.setContentType("text/xml");
097 try {
098 String start = path.substring(0, path.lastIndexOf('/'));
099 if(start.charAt(0) == '/') {
100 start = start.substring(1);
101 }
102 String[] parts = start.split("/");
103 if(parts.length > 2) {
104 StringBuffer buf = new StringBuffer();
105 for (int i = 0; i < parts.length-1; i++) {
106 String part = parts[i];
107 if(i > 0) buf.append('.');
108 buf.append(part);
109 }
110 generateMavenFile(kernel, response.getWriter(), buf.toString(), parts[parts.length-1], reply);
111 } else {
112 generateMavenFile(kernel, response.getWriter(), parts[0], parts[1], reply);
113 }
114 } catch (Exception e) {
115 throw new ServletException("Unable to generate Geronimo configuration list", e);
116 }
117 } else {
118 if(path.startsWith("/")) {
119 path = path.substring(1);
120 }
121 String configId = parsePath(path, response);
122 if(configId == null) { // we already sent the 404
123 return;
124 }
125 if(!produceDownloadFile(kernel, Artifact.create(configId), response, reply)) {
126 response.sendError(404, "Cannot locate download file "+path);
127 }
128 }
129 }
130
131 private static String parsePath(String path, HttpServletResponse response) throws IOException {
132 String[] parts = path.split("/");
133 String groupId, artifactId, version, type;
134 if(parts.length < 4) {
135 response.sendError(404, "Unrecognized path form "+path);
136 return null;
137 } else { // e.g. console/MyDatabase/1.0-SNAPSHOT/MyDatabase-1.0-SNAPSHOT.rar
138 groupId = parts[0];
139 for(int i=4; i<parts.length; i++) {
140 groupId = groupId+"."+parts[i-3];
141 }
142 artifactId = parts[parts.length-3];
143 version = parts[parts.length-2];
144 if(!parts[parts.length-1].startsWith(artifactId+"-"+version)) {
145 response.sendError(404, "Unrecognized path structure "+path);
146 return null;
147 }
148 type = parts[parts.length-1].substring(artifactId.length()+version.length()+2);
149 }
150 return groupId+"/"+artifactId+"/"+version+"/"+type;
151 }
152
153 private boolean produceDownloadFile(Kernel kernel, Artifact configId, HttpServletResponse response, boolean reply) throws IOException {
154 //todo: replace kernel mumbo jumbo with JSR-77 navigation
155 // Step 1: check if it's in a configuration store
156 ConfigurationManager mgr = ConfigurationUtil.getConfigurationManager(kernel);
157 if(mgr.isConfiguration(configId)) {
158 ConfigurationStore store = mgr.getStoreForConfiguration(configId);
159 response.setContentType("application/zip");
160 if(!reply) {
161 return true;
162 }
163 try {
164 store.exportConfiguration(configId, response.getOutputStream());
165 return true;
166 } catch (NoSuchConfigException e) {
167 log.error("Inconsistent ConfigurationStore data; ConfigManager claims it has configuration "+configId+" but store claims it doesn't",e);
168 throw new IOException("Unable to write ZIP file; see server log for details");
169 }
170 }
171 // Step 2: check if it's in a repository
172 Set repos = kernel.listGBeans(new AbstractNameQuery(Repository.class.getName()));
173 for (Iterator it = repos.iterator(); it.hasNext();) {
174 AbstractName name = (AbstractName) it.next();
175 Repository repo = (Repository) kernel.getProxyManager().createProxy(name, Repository.class);
176 if(repo.contains(configId)) {
177 File path = repo.getLocation(configId);
178 if(!path.exists()) throw new IllegalStateException("Can't find file '"+path.getAbsolutePath()+"' though repository said there's an artifact there!");
179 response.setContentType("application/zip");
180 if(!reply) {
181 return true;
182 }
183 InputStream in = new BufferedInputStream(new FileInputStream(path));
184 response.setContentLength((int)path.length());
185 OutputStream out = response.getOutputStream();
186 byte[] buf = new byte[1024];
187 int count;
188 while((count = in.read(buf)) > -1) {
189 out.write(buf, 0, count);
190 }
191 in.close();
192 return true;
193 }
194 }
195 // Step 3: wasn't found
196 return false;
197 }
198
199 private void generateConfigFile(HttpServletRequest request, Kernel kernel, PrintWriter out) throws ParserConfigurationException, NoSuchStoreException, TransformerException {
200 ConfigurationManager mgr = ConfigurationUtil.getConfigurationManager(kernel);
201 PluginInstaller installer = getInstaller(kernel);
202 DocumentBuilderFactory factory = XmlUtil.newDocumentBuilderFactory();
203 factory.setNamespaceAware(true);
204 DocumentBuilder builder = factory.newDocumentBuilder();
205 Document doc = builder.newDocument();
206 Element root = doc.createElementNS("http://geronimo.apache.org/xml/ns/plugins-1.1", "geronimo-plugin-list");
207 root.setAttribute("xmlns", "http://geronimo.apache.org/xml/ns/plugins-1.1");
208 doc.appendChild(root);
209 List stores = mgr.listStores();
210 for (int i = 0; i < stores.size(); i++) {
211 AbstractName name = (AbstractName) stores.get(i);
212 List configs = mgr.listConfigurations(name);
213 for (int j = 0; j < configs.size(); j++) {
214 ConfigurationInfo info = (ConfigurationInfo) configs.get(j);
215 PluginMetadata data = installer.getPluginMetadata(info.getConfigID());
216 Element config = doc.createElement("plugin");
217 root.appendChild(config);
218 createText(doc, config, "name", data.getName());
219 createText(doc, config, "module-id", data.getModuleId().toString());
220 createText(doc, config, "category", "Geronimo Deployments");
221 createText(doc, config, "description", data.getCategory().equals("Unknown") ? "Automatically generated plugin metadata" : data.getDescription());
222 if(data.getPluginURL() != null) {
223 createText(doc, config, "url", data.getPluginURL());
224 }
225 if(data.getAuthor() != null) {
226 createText(doc, config, "author", data.getAuthor());
227 }
228 for (int k = 0; k < data.getLicenses().length; k++) {
229 PluginMetadata.License license = data.getLicenses()[k];
230 Element lic = doc.createElement("license");
231 lic.setAttribute("osi-approved", Boolean.toString(license.isOsiApproved()));
232 createText(doc, lic, license.getName());
233 config.appendChild(lic);
234 }
235 // Skip hash since the CAR will be re-exported anyway and the file will be different
236 PluginMetadata.geronimoVersions[] versions = data.getGeronimoVersions();
237 for (int k = 0; k < versions.length; k++) {
238 PluginMetadata.geronimoVersions ver = versions[k];
239 writeGeronimoVersion(doc, config, ver);
240 }
241 String[] jvmVersions = data.getJvmVersions();
242 for (int k = 0; k < versions.length; k++) {
243 String ver = jvmVersions[k];
244 createText(doc, config, "jvm-version", ver);
245 }
246 for (int k = 0; k < data.getPrerequisites().length; k++) {
247 PluginMetadata.Prerequisite prereq = data.getPrerequisites()[k];
248 writePrerequisite(doc, config, prereq);
249 }
250 for (int k = 0; k < data.getDependencies().length; k++) {
251 String dep = data.getDependencies()[k];
252 createText(doc, config, "dependency", dep);
253 }
254 for (int k = 0; k < data.getObsoletes().length; k++) {
255 String obs = data.getObsoletes()[k];
256 createText(doc, config, "obsoletes", obs);
257 }
258 // Skip repositories since we want the download to come from here
259 }
260 }
261 String repo = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+request.getContextPath()+request.getServletPath();
262 if(!repo.endsWith("/")) repo += "/";
263 createText(doc, root, "default-repository", repo);
264 TransformerFactory xfactory = XmlUtil.newTransformerFactory();
265 Transformer xform = xfactory.newTransformer();
266 xform.setOutputProperty(OutputKeys.INDENT, "yes");
267 xform.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
268 xform.transform(new DOMSource(doc), new StreamResult(out));
269 }
270
271 private PluginInstaller getInstaller(Kernel kernel) {
272 Set names = kernel.listGBeans(new AbstractNameQuery(PluginInstaller.class.getName()));
273 if(names.size() == 0) {
274 return null;
275 }
276 return (PluginInstaller) kernel.getProxyManager().createProxy((AbstractName) names.iterator().next(), PluginInstaller.class);
277 }
278
279 private void generateMavenFile(Kernel kernel, PrintWriter writer, String groupId, String artifactId, boolean reply) throws ParserConfigurationException, TransformerException {
280 ConfigurationManager mgr = ConfigurationUtil.getConfigurationManager(kernel);
281 Artifact[] artifacts = mgr.getArtifactResolver().queryArtifacts(new Artifact(groupId, artifactId, (Version)null, null));
282 if(!reply) {
283 return;
284 }
285
286 DocumentBuilderFactory factory = XmlUtil.newDocumentBuilderFactory();
287 DocumentBuilder builder = factory.newDocumentBuilder();
288 Document doc = builder.newDocument();
289 Element root = doc.createElement("metadata");
290 doc.appendChild(root);
291 createText(doc, root, "groupId", groupId);
292 createText(doc, root, "artifactId", artifactId);
293 if(artifacts.length > 0) {
294 createText(doc, root, "version", artifacts[0].getVersion().toString());
295 }
296 Element versioning = doc.createElement("versioning");
297 root.appendChild(versioning);
298 Element versions = doc.createElement("versions");
299 versioning.appendChild(versions);
300 for (int i = 0; i < artifacts.length; i++) {
301 Artifact artifact = artifacts[i];
302 createText(doc, versions, "version", artifact.getVersion().toString());
303 }
304 TransformerFactory xfactory = XmlUtil.newTransformerFactory();
305 Transformer xform = xfactory.newTransformer();
306 xform.setOutputProperty(OutputKeys.INDENT, "yes");
307 xform.transform(new DOMSource(doc), new StreamResult(writer));
308 }
309
310 private void writePrerequisite(Document doc, Element config, PluginMetadata.Prerequisite req) {
311 Element prereq = doc.createElement("prerequisite");
312 config.appendChild(prereq);
313 createText(doc, prereq, "id", req.getModuleId().toString());
314 createText(doc, prereq, "resource-type", req.getResourceType());
315 createText(doc, prereq, "description", req.getDescription());
316 }
317
318 private void writeGeronimoVersion(Document doc, Element config, PluginMetadata.geronimoVersions ver){
319 Element ger = doc.createElement("geronimo-versions");
320 createText(doc, ger, "version", ver.getVersion());
321 if (ver.getModuleId() != null){
322 createText(doc, ger, "module-id", ver.getModuleId());
323 }
324 if (ver.getRepository() != null) {
325 String[] repos = ver.getRepository();
326 for ( int i=0; i < repos.length; i++ ) {
327 createText(doc, ger, "source-repository", repos[i]);
328 }
329 }
330 if (ver.getPreReqs() != null){
331 for (int j = 0; j < ver.getPreReqs().length; j++) {
332 PluginMetadata.Prerequisite prereq = ver.getPreReqs()[j];
333 Element pre = doc.createElement("prerequisite");
334 createText(doc, pre, "id", prereq.getModuleId().toString());
335 if(prereq.getResourceType() != null) {
336 createText(doc, pre, "resource-type", prereq.getResourceType());
337 }
338 if(prereq.getDescription() != null) {
339 createText(doc, pre, "description", prereq.getDescription());
340 }
341 ger.appendChild(pre);
342 }
343 }
344 }
345
346 private void createText(Document doc, Element parent, String name, String text) {
347 Element child = doc.createElement(name);
348 parent.appendChild(child);
349 Text node = doc.createTextNode(text);
350 child.appendChild(node);
351 }
352
353 private void createText(Document doc, Element parent, String text) {
354 Text node = doc.createTextNode(text);
355 parent.appendChild(node);
356 }
357 }