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 }