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