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    }