1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.geronimo.console.configmanager;
19
20 import java.io.ByteArrayInputStream;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.io.StringWriter;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.ArrayList;
28 import javax.enterprise.deploy.shared.factories.DeploymentFactoryManager;
29 import javax.enterprise.deploy.spi.DeploymentManager;
30 import javax.enterprise.deploy.spi.Target;
31 import javax.enterprise.deploy.spi.TargetModuleID;
32 import javax.enterprise.deploy.spi.status.ProgressObject;
33 import javax.portlet.ActionRequest;
34 import javax.portlet.ActionResponse;
35 import javax.portlet.PortletConfig;
36 import javax.portlet.PortletException;
37 import javax.portlet.PortletRequestDispatcher;
38 import javax.portlet.RenderRequest;
39 import javax.portlet.RenderResponse;
40 import javax.xml.parsers.DocumentBuilder;
41
42 import org.apache.commons.fileupload.FileItem;
43 import org.apache.commons.fileupload.FileUploadException;
44 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
45 import org.apache.commons.fileupload.portlet.PortletFileUpload;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.apache.geronimo.console.BasePortlet;
49 import org.apache.geronimo.deployment.plugin.jmx.JMXDeploymentManager;
50 import org.apache.geronimo.deployment.plugin.ConfigIDExtractor;
51 import org.apache.geronimo.common.DeploymentException;
52 import org.apache.geronimo.kernel.repository.Artifact;
53 import org.apache.geronimo.kernel.util.XmlUtil;
54 import org.apache.geronimo.upgrade.Upgrade1_0To1_1;
55 import org.w3c.dom.Document;
56
57 /**
58 * $Rev: 783189 $ $Date: 2009-06-09 19:59:31 -0700 (Tue, 09 Jun 2009) $
59 */
60 public class DeploymentPortlet extends BasePortlet {
61 private static final Logger log = LoggerFactory.getLogger(DeploymentPortlet.class);
62
63 private static final String DEPLOY_VIEW = "/WEB-INF/view/configmanager/deploy.jsp";
64 private static final String HELP_VIEW = "/WEB-INF/view/configmanager/deployHelp.jsp";
65 private static final String MIGRATED_PLAN_PARM = "migratedPlan";
66 private static final String ORIGINAL_PLAN_PARM = "originalPlan";
67
68 private PortletRequestDispatcher deployView;
69 private PortletRequestDispatcher helpView;
70
71 public void processAction(ActionRequest actionRequest,
72 ActionResponse actionResponse) throws PortletException, IOException {
73 if (!PortletFileUpload.isMultipartContent(actionRequest)) {
74 throw new PortletException("Expected file upload");
75 }
76
77 File rootDir = new File(System.getProperty("java.io.tmpdir"));
78 PortletFileUpload uploader = new PortletFileUpload(new DiskFileItemFactory(10240, rootDir));
79 File moduleFile = null;
80 File planFile = null;
81 String startApp = null;
82 String redeploy = null;
83 try {
84 List items = uploader.parseRequest(actionRequest);
85 for (Iterator i = items.iterator(); i.hasNext();) {
86 FileItem item = (FileItem) i.next();
87 if (!item.isFormField()) {
88 String fieldName = item.getFieldName();
89 String name = item.getName().trim();
90 File file;
91 if (name.length() == 0) {
92 file = null;
93 } else {
94 // Firefox sends basename, IE sends full path
95 int index = name.lastIndexOf('\\');
96 if (index != -1) {
97 name = name.substring(index + 1);
98 }
99 file = new File(rootDir, name);
100 }
101 if ("module".equals(fieldName)) {
102 moduleFile = file;
103 } else if ("plan".equals(fieldName)) {
104 planFile = file;
105 }
106 if (file != null) {
107 try {
108 item.write(file);
109 } catch (Exception e) {
110 throw new PortletException(e);
111 }
112 }
113 } else {
114 // retrieve 'startApp' form field value
115 if ("startApp".equalsIgnoreCase(item.getFieldName())) {
116 startApp = item.getString();
117 } else if ("redeploy".equalsIgnoreCase(item.getFieldName())) {
118 redeploy = item.getString();
119 }
120 }
121 }
122 } catch (FileUploadException e) {
123 throw new PortletException(e);
124 }
125 DeploymentFactoryManager dfm = DeploymentFactoryManager.getInstance();
126 FileInputStream fis = null;
127 try {
128 DeploymentManager mgr = dfm.getDeploymentManager("deployer:geronimo:inVM", null, null);
129 try {
130 boolean isRedeploy = redeploy != null && !redeploy.equals("");
131 if(mgr instanceof JMXDeploymentManager) {
132 ((JMXDeploymentManager)mgr).setLogConfiguration(false, true);
133 }
134 Target[] all = mgr.getTargets();
135 if (null == all) {
136 throw new IllegalStateException("No target to distribute to");
137 }
138
139 ProgressObject progress;
140 if(isRedeploy) {
141 TargetModuleID[] targets = identifyTargets(moduleFile, planFile, mgr.getAvailableModules(null, all));
142 if(targets.length == 0) {
143 addErrorMessage(actionRequest, getLocalizedString(actionRequest, "plugin.errorMsg04"), null);
144 log.error(getLocalizedString(actionRequest, "plugin.errorMsg04"));
145 return;
146 }
147 progress = mgr.redeploy(targets, moduleFile, planFile);
148 } else {
149 progress = mgr.distribute(new Target[] {all[0]}, moduleFile, planFile);
150 }
151 while(progress.getDeploymentStatus().isRunning()) {
152 Thread.sleep(100);
153 }
154
155 String abbrStatusMessage;
156 String fullStatusMessage = null;
157 if(progress.getDeploymentStatus().isCompleted()) {
158 abbrStatusMessage = getLocalizedString(actionRequest, !isRedeploy ? "plugin.infoMsg01" : "plugin.infoMsg02");
159 addInfoMessage(actionRequest, abbrStatusMessage);
160 // start installed app/s
161 if (!isRedeploy && startApp != null && !startApp.equals("")) {
162 progress = mgr.start(progress.getResultTargetModuleIDs());
163 while(progress.getDeploymentStatus().isRunning()) {
164 Thread.sleep(100);
165 }
166 if (progress.getDeploymentStatus().isCompleted()) {
167 abbrStatusMessage = getLocalizedString(actionRequest, "plugin.infoMsg03");
168 addInfoMessage(actionRequest, abbrStatusMessage);
169 } else {
170 abbrStatusMessage = getLocalizedString(actionRequest, "plugin.errorMsg02");
171 fullStatusMessage = progress.getDeploymentStatus().getMessage();
172 addErrorMessage(actionRequest, abbrStatusMessage, fullStatusMessage);
173 log.error(abbrStatusMessage + "\n" + fullStatusMessage);
174 }
175 }
176 } else {
177 fullStatusMessage = progress.getDeploymentStatus().getMessage();
178 // for the abbreviated status message clip off everything
179 // after the first line, which in most cases means the gnarly stacktrace
180 abbrStatusMessage = getLocalizedString(actionRequest, "plugin.errorMsg01");
181 addErrorMessage(actionRequest, abbrStatusMessage, fullStatusMessage);
182 log.error(abbrStatusMessage + "\n" + fullStatusMessage);
183 // try to provide an upgraded version of the plan
184 try {
185 if (planFile != null && planFile.exists()) {
186 byte[] plan = new byte[(int) planFile.length()];
187 fis = new FileInputStream(planFile);
188 fis.read(plan);
189 DocumentBuilder documentBuilder = XmlUtil.newDocumentBuilderFactory().newDocumentBuilder();
190 Document doc = documentBuilder.parse(new ByteArrayInputStream(plan));
191 // v1.1 switched from configId to moduleId
192 String configId = doc.getDocumentElement().getAttribute("configId");
193 if (configId != null && !("".equals(configId))) {
194 StringWriter sw = new StringWriter();
195 new Upgrade1_0To1_1().upgrade(new ByteArrayInputStream(plan), sw);
196 // have to store the original and upgraded plans in the session
197 // because the buffer size for render parameters is sometimes not
198 // big enough
199 actionRequest.getPortletSession().setAttribute(MIGRATED_PLAN_PARM, sw.getBuffer());
200 actionRequest.getPortletSession().setAttribute(ORIGINAL_PLAN_PARM, new String(plan));
201 }
202 }
203 } catch (Exception e) {
204 // cannot provide a migrated plan in this case, most likely
205 // because the deployment plan would not parse. a valid
206 // status message has already been provided in this case
207 }
208 }
209 } finally {
210 mgr.release();
211 if (fis!=null) fis.close();
212 if(moduleFile != null && moduleFile.exists()) {
213 if(!moduleFile.delete()) {
214 log.debug("Unable to delete temporary file "+moduleFile);
215 moduleFile.deleteOnExit();
216 }
217 }
218 if(planFile != null && planFile.exists()) {
219 if(!planFile.delete()) {
220 log.debug("Unable to delete temporary file "+planFile);
221 planFile.deleteOnExit();
222 }
223 }
224 }
225 } catch (Exception e) {
226 throw new PortletException(e);
227 }
228 }
229
230 private TargetModuleID[] identifyTargets(File module, File plan, TargetModuleID[] allModules) throws PortletException {
231 String moduleId = null;
232 List modules = new ArrayList();
233 try {
234 if(plan != null) {
235 moduleId = ConfigIDExtractor.extractModuleIdFromPlan(plan);
236 } else if(module != null) {
237 moduleId = ConfigIDExtractor.extractModuleIdFromArchive(module);
238 if(moduleId == null) {
239 int pos = module.getName().lastIndexOf('.');
240 moduleId = pos > -1 ? module.getName().substring(0, pos) : module.getName();
241 }
242 }
243 if(moduleId != null) {
244 modules.addAll(ConfigIDExtractor.identifyTargetModuleIDs(allModules, moduleId, true));
245 } else {
246 String name = module != null ? module.getName() : plan.getName();
247 int pos = name.lastIndexOf('.');
248 if(pos > -1) {
249 name = name.substring(0, pos);
250 }
251 modules.addAll(ConfigIDExtractor.identifyTargetModuleIDs(allModules, Artifact.DEFAULT_GROUP_ID+"/"+name+"//", true));
252 }
253 } catch (IOException e) {
254 throw new PortletException("Unable to read input files: "+e.getMessage());
255 } catch (DeploymentException e) {
256 return new TargetModuleID[0];
257 }
258 return (TargetModuleID[]) modules.toArray(new TargetModuleID[modules.size()]);
259 }
260
261 protected void doView(RenderRequest renderRequest,
262 RenderResponse renderResponse) throws PortletException, IOException {
263 // The deployment plans and messages from the deployers sometime exceeds
264 // the buffer size for render attributes. To avoid the buffer
265 // overrun the render attributes are temporarily stored in the portlet
266 // session during the processAction phase and then copied into render
267 // attributes here so the JSP has easier access to them. This seems
268 // to only be an issue on tomcat.
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 }