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 }