View Javadoc

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  package org.apache.jetspeed.deployment.impl;
18  
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileNotFoundException;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.util.Enumeration;
26  import java.util.Iterator;
27  import java.util.jar.JarFile;
28  import java.util.zip.ZipEntry;
29  
30  import org.apache.jetspeed.components.portletregistry.PortletRegistry;
31  import org.apache.jetspeed.deployment.DeploymentEvent;
32  import org.apache.jetspeed.deployment.DeploymentException;
33  import org.apache.jetspeed.tools.pamanager.PortletApplicationManagement;
34  import org.jdom.Attribute;
35  import org.jdom.Document;
36  import org.jdom.Element;
37  import org.jdom.JDOMException;
38  import org.jdom.input.SAXBuilder;
39  import org.jdom.output.Format;
40  import org.jdom.output.XMLOutputter;
41  import org.xml.sax.EntityResolver;
42  import org.xml.sax.InputSource;
43  import org.xml.sax.SAXException;
44  
45  /***
46   * @author <a href="mailto:ate@douma.nu">Ate Douma</a>
47   * @version $Id: JettyDeployPortletAppEventListener.java 549661 2007-06-22 01:24:32Z ate $
48   */
49  public class JettyDeployPortletAppEventListener extends DeployPortletAppEventListener
50  {
51      private String jettyContextsDir;
52      
53      public JettyDeployPortletAppEventListener(PortletApplicationManagement pam, PortletRegistry registry, String webAppDir, String localAppDir, boolean stripLoggers, String jettyContextsDir) throws FileNotFoundException
54      {
55          super(pam, registry, webAppDir, localAppDir, stripLoggers);
56          initJettyContextsDir(jettyContextsDir);
57      }
58  
59      public JettyDeployPortletAppEventListener(PortletApplicationManagement pam, PortletRegistry registry, String webAppDir, String localAppDir, String localAppStagingDir, boolean stripLoggers, String jettyContextsDir) throws FileNotFoundException
60      {
61          super(pam, registry, webAppDir, localAppDir, localAppStagingDir, stripLoggers);
62          initJettyContextsDir(jettyContextsDir);
63      }
64      
65      private void initJettyContextsDir(String jettyContextsDir) throws FileNotFoundException
66      {
67          File jettyContextsDirFile = new File(jettyContextsDir);
68  
69          if (jettyContextsDirFile.exists())
70          {
71              try
72              {
73                  this.jettyContextsDir = jettyContextsDirFile.getCanonicalPath();
74              }
75              catch (IOException e) {}
76          }
77          else
78          {
79              throw new FileNotFoundException("The jetty contexts directory \""
80                                              + jettyContextsDirFile.getAbsolutePath() + "\" does not exist.");
81          }
82      }
83  
84      protected void deployPortletApplication(DeploymentEvent event) throws DeploymentException
85      {        
86          try
87          {
88              String fileName = event.getName();
89              String filePath = event.getPath();
90              String appName = fileName.substring(0, fileName.length() - 4);
91              Document context = getJettyContext(filePath);
92              File contextFile = getCurrentJettyContextFile(appName);
93              if (contextFile != null)
94              {
95                  if (context == null)
96                  {
97                      context = getCurrentJettyContext(contextFile);
98                  }
99                  contextFile.delete();
100             }
101             if (context == null)
102             {
103                 context = getJettyContextTemplate();
104             }
105             updateJettyContext(appName, new File(getWebAppDir(), fileName).getAbsolutePath(), context);            
106             removeCurrentPA(appName);
107             super.deployPortletApplication(event);
108             writeJettyContext(appName,context);
109         }
110         catch (Exception e)
111         {
112             throw new DeploymentException(e);
113         }
114     }
115     
116     protected void removeCurrentPA(String contextName) throws IOException
117     {
118         File warFile = new File(getWebAppDir(), contextName + ".war");
119         if (warFile.exists())
120         {
121             warFile.delete();
122         }
123         File warDir = new File(getWebAppDir(), contextName);
124         if (warDir.exists() && warDir.isDirectory())
125         {
126             removeDir(warDir);
127         }
128     }
129     
130     protected boolean removeDir(File file)
131     {
132         if (file.isDirectory())
133         {
134             String[] children = file.list();
135             for (int i = 0; i < children.length; i++)
136             {
137                 boolean success = removeDir(new File(file, children[i]));
138                 if (!success)
139                 {
140                     return false;
141                 }
142             }
143         }
144 
145         // The directory is now empty so delete it OR it is a plain file
146         return file.delete();        
147     }
148     
149     protected File getCurrentJettyContextFile(String contextName) throws IOException
150     {
151         File contextFile = new File(jettyContextsDir, contextName+".xml");
152         if (contextFile.exists())
153         {
154             if (contextFile.isDirectory())
155             {
156                 throw new IOException("Cannot deploy application"+contextName+" as there already exists a directory in "+jettyContextsDir+" with the same name");
157             }
158             return contextFile;
159         }
160         return null;
161     }
162     
163     protected Document getCurrentJettyContext(File contextFile) throws IOException
164     {
165         InputStream source = null;
166         try
167         {
168             source = new FileInputStream(contextFile);
169             return parseJettyContext(source);
170         }
171         finally
172         {
173             if (source != null)
174             {
175                 try
176                 {
177                     source.close();
178                 }
179                 catch (IOException e1)
180                 {
181                     // ignore
182                 }
183             }
184         }
185     }
186     
187     protected Document getJettyContextTemplate() throws IOException
188     {
189         InputStream source = null;
190         try
191         {
192             source = getClass().getResourceAsStream("jetty/context-template.xml");
193             return parseJettyContext(source);
194         }
195         finally
196         {
197             if (source != null)
198             {
199                 try
200                 {
201                     source.close();
202                 }
203                 catch (IOException e1)
204                 {
205                     // ignore
206                 }
207             }
208         }
209     }
210     
211     protected Document getJettyContext(String fileName) throws IOException
212     {
213         JarFile jin = null;
214         InputStream source = null;
215         try
216         {
217             jin = new JarFile(fileName);
218             
219             ZipEntry src;
220             Enumeration zipEntries = jin.entries();
221             while (zipEntries.hasMoreElements())
222             {
223                 src = (ZipEntry) zipEntries.nextElement();
224                 String target = src.getName();
225                 if ("META-INF/jetspeed-jetty-context.xml".equals(target))
226                 {
227                     System.out.println("Found jetspeed-jetty-context.xml");
228                     source = jin.getInputStream(src);
229                     return parseJettyContext(source);
230                 }                
231             }
232             return null;
233         }        
234         finally
235         {
236             if (source != null)
237             {
238                 try
239                 {
240                     source.close();
241                 }
242                 catch (IOException e1)
243                 {
244                     // ignore
245                 }
246             }
247             if (jin != null)
248             {
249                 try
250                 {
251                     jin.close();
252                     jin = null;
253                 }
254                 catch (IOException e1)
255                 {
256                     // ignore
257                 }
258             }
259         }
260     }
261     
262     protected void updateJettyContext(String contextName, String warPath, Document context)
263     {
264         Element root = context.getRootElement();
265         Iterator iter = root.getChildren("Set").iterator();
266         boolean foundSetWar = false;
267         boolean foundSetContextPath = false;
268         boolean foundSetConfigurationClasses = false;
269         
270         while (iter.hasNext())
271         {
272             Element set = (Element)iter.next();
273             String name = set.getAttribute("name").getName();
274             if (name.equals("contextPath"))
275             {
276                 set.setText("/"+contextName);
277                 foundSetContextPath = true;
278             }
279             else if (name.equals("resourceBase"))
280             {
281                 iter.remove();
282             }
283             else if (name.equals("war"))
284             {
285                 set.setText(warPath);
286                 foundSetWar = true;
287             }
288             else if (name.equals("configurationClasses"))
289             {
290                 foundSetConfigurationClasses = true;
291             }
292         }        
293         if (!foundSetContextPath)
294         {
295             root.addContent(new Element("Set").setAttribute(new Attribute("name", "contextPath")).setText("/"+contextName));
296         }
297         if (!foundSetWar)
298         {
299             root.addContent(new Element("Set").setAttribute(new Attribute("name", "war")).setText(warPath));
300         }
301         if (!foundSetConfigurationClasses)
302         {
303             Element array = new Element("Array").setAttribute(new Attribute("type","java.lang.String"));
304             array.addContent(new Element("Item").setText("org.mortbay.jetty.webapp.WebInfConfiguration"));
305             array.addContent(new Element("Item").setText("org.mortbay.jetty.plus.webapp.EnvConfiguration"));
306             array.addContent(new Element("Item").setText("org.mortbay.jetty.plus.webapp.Configuration"));
307             array.addContent(new Element("Item").setText("org.mortbay.jetty.webapp.JettyWebXmlConfiguration"));
308             array.addContent(new Element("Item").setText("org.mortbay.jetty.webapp.TagLibConfiguration"));
309             root.addContent(new Element("Set").setAttribute(new Attribute("name", "configurationClasses")).setContent(array));
310         }
311     }
312     
313     protected void writeJettyContext(String contextName, Document context) throws IOException
314     {
315         File contextFile = new File(jettyContextsDir, contextName+".xml");
316         if (contextFile.exists())
317         {
318             throw new IOException("Jetty context file "+contextFile.getAbsolutePath()+" found.");
319         }
320         FileOutputStream output = null;
321         try
322         {
323             output = new FileOutputStream(contextFile);
324             XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
325             xmlOutputter.output(context, output);
326         }
327         finally
328         {
329             if (output != null)
330             {
331                 try
332                 {
333                     output.close();
334                 }
335                 catch (IOException e1)
336                 {
337                     // ignore
338                 }
339             }
340         }
341     }
342 
343     protected Document parseJettyContext(InputStream source) throws IOException 
344     {
345         // Parse using the local dtds instead of remote dtds. This
346         // allows to deploy the application offline
347         SAXBuilder saxBuilder = new SAXBuilder();
348         saxBuilder.setEntityResolver(new EntityResolver()
349         {
350             public InputSource resolveEntity(java.lang.String publicId, java.lang.String systemId) throws SAXException,
351                             java.io.IOException
352             {
353                 if (systemId.equals("http://jetty.mortbay.org/configure.dtd"))
354                 {
355                     return new InputSource(getClass().getResourceAsStream("jetty/configure_6_0.dtd"));
356                 }
357                 return null;
358             }
359         });
360         try
361         {
362             Document document = saxBuilder.build(source);
363             return document;
364         }
365         catch (JDOMException e)
366         {
367             IOException ioException = new IOException("Parse failure: "+e.getMessage());
368             ioException.fillInStackTrace();
369             throw ioException;
370         }
371     }
372 }