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 018 package org.apache.geronimo.console.configmanager; 019 020 import java.io.ByteArrayInputStream; 021 import java.io.File; 022 import java.io.FileInputStream; 023 import java.io.IOException; 024 import java.io.StringWriter; 025 import java.util.Iterator; 026 import java.util.List; 027 import java.util.ArrayList; 028 import javax.enterprise.deploy.shared.factories.DeploymentFactoryManager; 029 import javax.enterprise.deploy.spi.DeploymentManager; 030 import javax.enterprise.deploy.spi.Target; 031 import javax.enterprise.deploy.spi.TargetModuleID; 032 import javax.enterprise.deploy.spi.status.ProgressObject; 033 import javax.portlet.ActionRequest; 034 import javax.portlet.ActionResponse; 035 import javax.portlet.PortletConfig; 036 import javax.portlet.PortletException; 037 import javax.portlet.PortletRequestDispatcher; 038 import javax.portlet.RenderRequest; 039 import javax.portlet.RenderResponse; 040 import javax.xml.parsers.DocumentBuilder; 041 042 import org.apache.commons.fileupload.FileItem; 043 import org.apache.commons.fileupload.FileUploadException; 044 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 045 import org.apache.commons.fileupload.portlet.PortletFileUpload; 046 import org.apache.commons.logging.Log; 047 import org.apache.commons.logging.LogFactory; 048 import org.apache.geronimo.console.BasePortlet; 049 import org.apache.geronimo.deployment.plugin.jmx.JMXDeploymentManager; 050 import org.apache.geronimo.deployment.plugin.ConfigIDExtractor; 051 import org.apache.geronimo.common.DeploymentException; 052 import org.apache.geronimo.kernel.repository.Artifact; 053 import org.apache.geronimo.kernel.util.XmlUtil; 054 import org.apache.geronimo.upgrade.Upgrade1_0To1_1; 055 import org.w3c.dom.Document; 056 057 /** 058 * $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 059 */ 060 public class DeploymentPortlet extends BasePortlet { 061 private final static Log log = LogFactory.getLog(DeploymentPortlet.class); 062 063 private static final String DEPLOY_VIEW = "/WEB-INF/view/configmanager/deploy.jsp"; 064 private static final String HELP_VIEW = "/WEB-INF/view/configmanager/deployHelp.jsp"; 065 private static final String MIGRATED_PLAN_PARM = "migratedPlan"; 066 private static final String ORIGINAL_PLAN_PARM = "originalPlan"; 067 private static final String FULL_STATUS_PARM = "fullStatusMessage"; 068 private static final String ABBR_STATUS_PARM = "abbrStatusMessage"; 069 private PortletRequestDispatcher deployView; 070 private PortletRequestDispatcher helpView; 071 072 public void processAction(ActionRequest actionRequest, 073 ActionResponse actionResponse) throws PortletException, IOException { 074 if (!PortletFileUpload.isMultipartContent(actionRequest)) { 075 throw new PortletException("Expected file upload"); 076 } 077 078 File rootDir = new File(System.getProperty("java.io.tmpdir")); 079 PortletFileUpload uploader = new PortletFileUpload(new DiskFileItemFactory(10240, rootDir)); 080 File moduleFile = null; 081 File planFile = null; 082 String startApp = null; 083 String redeploy = null; 084 try { 085 List items = uploader.parseRequest(actionRequest); 086 for (Iterator i = items.iterator(); i.hasNext();) { 087 FileItem item = (FileItem) i.next(); 088 if (!item.isFormField()) { 089 String fieldName = item.getFieldName(); 090 String name = item.getName().trim(); 091 File file; 092 if (name.length() == 0) { 093 file = null; 094 } else { 095 // Firefox sends basename, IE sends full path 096 int index = name.lastIndexOf('\\'); 097 if (index != -1) { 098 name = name.substring(index + 1); 099 } 100 file = new File(rootDir, name); 101 } 102 if ("module".equals(fieldName)) { 103 moduleFile = file; 104 } else if ("plan".equals(fieldName)) { 105 planFile = file; 106 } 107 if (file != null) { 108 try { 109 item.write(file); 110 } catch (Exception e) { 111 throw new PortletException(e); 112 } 113 } 114 } else { 115 // retrieve 'startApp' form field value 116 if ("startApp".equalsIgnoreCase(item.getFieldName())) { 117 startApp = item.getString(); 118 } else if ("redeploy".equalsIgnoreCase(item.getFieldName())) { 119 redeploy = item.getString(); 120 } 121 } 122 } 123 } catch (FileUploadException e) { 124 throw new PortletException(e); 125 } 126 DeploymentFactoryManager dfm = DeploymentFactoryManager.getInstance(); 127 FileInputStream fis = null; 128 try { 129 DeploymentManager mgr = dfm.getDeploymentManager("deployer:geronimo:inVM", null, null); 130 try { 131 boolean isRedeploy = redeploy != null && !redeploy.equals(""); 132 if(mgr instanceof JMXDeploymentManager) { 133 ((JMXDeploymentManager)mgr).setLogConfiguration(false, true); 134 } 135 Target[] all = mgr.getTargets(); 136 if (null == all) { 137 throw new IllegalStateException("No target to distribute to"); 138 } 139 140 ProgressObject progress; 141 if(isRedeploy) { 142 TargetModuleID[] targets = identifyTargets(moduleFile, planFile, mgr.getAvailableModules(null, all)); 143 if(targets.length == 0) { 144 throw new PortletException("Unable to identify modules to replace. Please include a Geronimo deployment plan or use the command-line deployment tool."); 145 } 146 progress = mgr.redeploy(targets, moduleFile, planFile); 147 } else { 148 progress = mgr.distribute(new Target[] {all[0]}, moduleFile, planFile); 149 } 150 while(progress.getDeploymentStatus().isRunning()) { 151 Thread.sleep(100); 152 } 153 154 String abbrStatusMessage; 155 String fullStatusMessage = null; 156 if(progress.getDeploymentStatus().isCompleted()) { 157 abbrStatusMessage = "The application was successfully "+(isRedeploy ? "re" : "")+"deployed.<br/>"; 158 // start installed app/s 159 if (!isRedeploy && startApp != null && !startApp.equals("")) { 160 progress = mgr.start(progress.getResultTargetModuleIDs()); 161 while(progress.getDeploymentStatus().isRunning()) { 162 Thread.sleep(100); 163 } 164 if (progress.getDeploymentStatus().isCompleted()) { 165 abbrStatusMessage += "The application was successfully started"; 166 } else { 167 abbrStatusMessage += "The application was not successfully started"; 168 fullStatusMessage = progress.getDeploymentStatus().getMessage(); 169 } 170 } 171 } else { 172 fullStatusMessage = progress.getDeploymentStatus().getMessage(); 173 // for the abbreviated status message clip off everything 174 // after the first line, which in most cases means the gnarly stacktrace 175 abbrStatusMessage = "Deployment failed:<br/>" 176 + fullStatusMessage.substring(0, fullStatusMessage.indexOf('\n')); 177 // try to provide an upgraded version of the plan 178 try { 179 if (planFile != null && planFile.exists()) { 180 byte[] plan = new byte[(int) planFile.length()]; 181 fis = new FileInputStream(planFile); 182 fis.read(plan); 183 DocumentBuilder documentBuilder = XmlUtil.newDocumentBuilderFactory().newDocumentBuilder(); 184 Document doc = documentBuilder.parse(new ByteArrayInputStream(plan)); 185 // v1.1 switched from configId to moduleId 186 String configId = doc.getDocumentElement().getAttribute("configId"); 187 if (configId != null && !("".equals(configId))) { 188 StringWriter sw = new StringWriter(); 189 new Upgrade1_0To1_1().upgrade(new ByteArrayInputStream(plan), sw); 190 // have to store the original and upgraded plans in the session 191 // because the buffer size for render parameters is sometimes not 192 // big enough 193 actionRequest.getPortletSession().setAttribute(MIGRATED_PLAN_PARM, sw.getBuffer()); 194 actionRequest.getPortletSession().setAttribute(ORIGINAL_PLAN_PARM, new String(plan)); 195 } 196 } 197 } catch (Exception e) { 198 // cannot provide a migrated plan in this case, most likely 199 // because the deployment plan would not parse. a valid 200 // status message has already been provided in this case 201 } 202 } 203 // have to store the status messages in the portlet session 204 // because the buffer size for render parameters is sometimes not big enough 205 actionRequest.getPortletSession().setAttribute(FULL_STATUS_PARM, fullStatusMessage); 206 actionRequest.getPortletSession().setAttribute(ABBR_STATUS_PARM, abbrStatusMessage); 207 } finally { 208 mgr.release(); 209 if (fis!=null) fis.close(); 210 if(moduleFile != null && moduleFile.exists()) { 211 if(!moduleFile.delete()) { 212 log.debug("Unable to delete temporary file "+moduleFile); 213 moduleFile.deleteOnExit(); 214 } 215 } 216 if(planFile != null && planFile.exists()) { 217 if(!planFile.delete()) { 218 log.debug("Unable to delete temporary file "+planFile); 219 planFile.deleteOnExit(); 220 } 221 } 222 } 223 } catch (Exception e) { 224 throw new PortletException(e); 225 } 226 } 227 228 private TargetModuleID[] identifyTargets(File module, File plan, TargetModuleID[] allModules) throws PortletException { 229 String moduleId = null; 230 List modules = new ArrayList(); 231 try { 232 if(plan != null) { 233 moduleId = ConfigIDExtractor.extractModuleIdFromPlan(plan); 234 } else if(module != null) { 235 moduleId = ConfigIDExtractor.extractModuleIdFromArchive(module); 236 if(moduleId == null) { 237 int pos = module.getName().lastIndexOf('.'); 238 moduleId = pos > -1 ? module.getName().substring(0, pos) : module.getName(); 239 } 240 } 241 if(moduleId != null) { 242 modules.addAll(ConfigIDExtractor.identifyTargetModuleIDs(allModules, moduleId, true)); 243 } else { 244 String name = module != null ? module.getName() : plan.getName(); 245 int pos = name.lastIndexOf('.'); 246 if(pos > -1) { 247 name = name.substring(0, pos); 248 } 249 modules.addAll(ConfigIDExtractor.identifyTargetModuleIDs(allModules, Artifact.DEFAULT_GROUP_ID+"/"+name+"//", true)); 250 } 251 } catch (IOException e) { 252 throw new PortletException("Unable to read input files: "+e.getMessage()); 253 } catch (DeploymentException e) { 254 throw new PortletException(e.getMessage(), e); 255 } 256 return (TargetModuleID[]) modules.toArray(new TargetModuleID[modules.size()]); 257 } 258 259 protected void doView(RenderRequest renderRequest, 260 RenderResponse renderResponse) throws PortletException, IOException { 261 // The deployment plans and messages from the deployers sometime exceeds 262 // the buffer size for render attributes. To avoid the buffer 263 // overrun the render attributes are temporarily stored in the portlet 264 // session during the processAction phase and then copied into render 265 // attributes here so the JSP has easier access to them. This seems 266 // to only be an issue on tomcat. 267 copyRenderAttribute(renderRequest, FULL_STATUS_PARM); 268 copyRenderAttribute(renderRequest, ABBR_STATUS_PARM); 269 copyRenderAttribute(renderRequest, MIGRATED_PLAN_PARM); 270 copyRenderAttribute(renderRequest, ORIGINAL_PLAN_PARM); 271 deployView.include(renderRequest, renderResponse); 272 } 273 274 private void copyRenderAttribute(RenderRequest renderRequest, String attr) { 275 Object value = renderRequest.getPortletSession().getAttribute(attr); 276 renderRequest.getPortletSession().removeAttribute(attr); 277 renderRequest.setAttribute(attr, value); 278 } 279 280 protected void doHelp(RenderRequest renderRequest, 281 RenderResponse renderResponse) throws PortletException, IOException { 282 helpView.include(renderRequest, renderResponse); 283 } 284 285 public void init(PortletConfig portletConfig) throws PortletException { 286 super.init(portletConfig); 287 deployView = portletConfig.getPortletContext().getRequestDispatcher(DEPLOY_VIEW); 288 helpView = portletConfig.getPortletContext().getRequestDispatcher(HELP_VIEW); 289 } 290 291 public void destroy() { 292 deployView = null; 293 helpView = null; 294 super.destroy(); 295 } 296 }