View Javadoc

1   /**
2    * Copyright (C) 2005-2009 Alfresco Software Limited.
3    *
4    * This file is part of the Spring Surf Extension project.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.springframework.extensions.surf.task;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.FileOutputStream;
25  import java.io.IOException;
26  import java.io.InputStream;
27  import java.util.Enumeration;
28  import java.util.zip.ZipEntry;
29  import java.util.zip.ZipFile;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.springframework.extensions.surf.exception.PlatformRuntimeException;
34  import org.springframework.extensions.surf.util.DataUtil;
35  import org.springframework.extensions.surf.util.ObjectGUID;
36  import org.springframework.extensions.webscripts.connector.Connector;
37  import org.springframework.extensions.webscripts.connector.RemoteClient;
38  import org.springframework.extensions.webscripts.connector.Response;
39  
40  /**
41   * Task which can be used to import an archive from a remote location
42   * 
43   * This task was a quick migration from the ScriptImporter bean which was
44   * previously used and, as such, does some fairly ridiculous things (like
45   * loading the entire byte array into memory).
46   * 
47   * At present, the only archives being distributed are those directly from
48   * Alfresco and we know these to be pretty small.  Thus, leaving this in for
49   * the time being.
50   * 
51   * In short order, however, we should refactor to stream the byte array
52   * directly to a temp file on disk.
53   * 
54   * @author muzquiano
55   */
56  public class ImportTask extends AbstractTask
57  {
58      protected static Log logger = LogFactory.getLog(ImportTask.class);
59      
60      protected static String DEFAULT_WEBAPP_ID = "ROOT";
61      
62      protected Connector alfrescoConnector;
63      
64      protected String storeId;
65      protected String webappId = DEFAULT_WEBAPP_ID;
66      protected String url;
67      
68  	public ImportTask(String name)
69  	{
70  	    super(name);
71  	}
72  	
73  	/**
74  	 * Sets the "alfresco" endpoint connector
75  	 * 
76  	 * @param context the new request context
77  	 */
78  	public void setAlfrescoConnector(Connector alfrescoConnector)
79  	{
80  	    this.alfrescoConnector = alfrescoConnector;
81  	}
82  	
83  	/**
84  	 * Gets the "alfresco" endpoint connector.
85  	 * 
86  	 * @return the alfresco connector
87  	 */
88  	public Connector getAlfrescoConnector()
89  	{
90  	    return this.alfrescoConnector;
91  	}
92  	
93  	/**
94  	 * Sets the store id.
95  	 * 
96  	 * @param storeId the new store id
97  	 */
98  	public void setStoreId(String storeId)
99  	{
100 	    this.storeId = storeId;
101 	}
102 	
103 	public void setWebappId(String webappId)
104 	{
105 	    this.webappId = webappId;
106 	}
107 	
108 	public void setUrl(String url)
109 	{
110 	    this.url = url;
111 	}
112 
113     /* (non-Javadoc)
114      * @see org.alfresco.web.framework.AbstractTask#cancel()
115      */
116     public void cancel()
117     {
118         // TODO: introduce a way to interrupt the import?
119         
120         this.isCancelled = true;
121     }   
122 	
123 	/* (non-Javadoc)
124 	 * @see org.alfresco.web.framework.AbstractTask#execute()
125 	 */
126 	public void execute() throws Throwable
127 	{
128 	    // update
129 	    this.setStatus("Starting Import");
130 	    
131         // build a remote client to the destination
132         RemoteClient r = new RemoteClient("");
133 
134         // update
135         this.setStatus("Downloading from Alfresco Network...");
136         
137         // pull down the bytes
138         ByteArrayOutputStream baos = new ByteArrayOutputStream();
139         r.call(url, baos);
140 
141         byte[] array = baos.toByteArray();
142         
143         importArchive(array);
144     }
145 
146     /**
147      * Imports a Surf archive
148      * 
149      * @param array         the byte array
150      */
151     private void importArchive(byte[] array)
152     {
153         // assume empty
154         String alfrescoPath = "/";
155 
156         if (webappId != null && !"".equals(webappId))
157         {
158             alfrescoPath = "/WEB-INF/classes";
159         }
160 
161         // update
162         this.setStatus("Writing archive...");
163         
164         // write to temporary file
165         String tempFilePath = writeToTempFile(array);
166 
167         // update
168         this.setStatus("Importing archive...");
169         
170         // read zip file entries
171         int count = 0;
172         ZipFile zf = null;
173         try
174         {
175             zf = new ZipFile(tempFilePath);
176             
177             int totalEntries = zf.size();
178             
179             Enumeration en = zf.entries();
180             while (en.hasMoreElements())
181             {
182                 // update
183                 this.setStatus("Importing " + (count+1) + " of " + totalEntries);
184                 
185                 // get the entry
186                 ZipEntry zipEntry = (ZipEntry) en.nextElement();
187 
188                 // get the entry name
189                 String zipEntryName = zipEntry.getName();
190 
191                 if (!zipEntry.isDirectory())
192                 {
193                     if (zipEntryName.startsWith("alfresco/"))
194                     {
195                         String path = zipEntryName;
196 
197                         // post to "alfresco" directory location
198                         String uri = "/avmstore/create" + alfrescoPath + "/"
199                                 + path + "?s=" + storeId + "&w=" + webappId;
200 
201                         post(zf, zipEntry, uri);
202                     }
203                     if (zipEntryName.startsWith("web-root/"))
204                     {
205                         String path = zipEntryName.substring(9);
206 
207                         // post to "alfresco" directory location
208                         String uri = "/avmstore/create/" + path + "?s="
209                                 + storeId + "&w=" + webappId;
210 
211                         post(zf, zipEntry, uri);
212                     }
213                 }
214                 
215                 // increment the count
216                 count++;
217             }
218 
219         }
220         catch (IOException ioe)
221         {
222             logger.warn("Unable to import archive from zip: " + tempFilePath, ioe);
223         }
224         finally
225         {
226             try
227             {
228                 if (zf != null)
229                 {
230                     zf.close();
231                 }
232 
233                 File f = new File(tempFilePath);
234                 if (f.exists())
235                 {
236                     f.delete();
237                 }
238             }
239             catch (Exception e)
240             {
241                 // oh well, we gave it our best shot
242             }
243         }
244     }
245 
246     protected void post(ZipFile zf, ZipEntry zipEntry, String uri)
247     {
248         String zipEntryName = zipEntry.getName();
249 
250         // Open a Connector to Alfresco
251         InputStream in = null;
252         try
253         {
254             in = zf.getInputStream(zipEntry);
255 
256             Connector c = getAlfrescoConnector();
257 
258             Response response = c.call(uri, null, in);
259             if (response.getStatus().getCode() != 200)
260             {
261                 // throw out
262                 logger.warn(
263                         "Received a " + response.getStatus().getCode()
264                                 + " on call to: " + uri);
265             }
266         }
267         catch (IOException ioe)
268         {
269             // we failed to write one of the files...
270             logger.warn(
271                     "The file '" + zipEntryName
272                             + "' could not be written to the store");
273         }
274         finally
275         {
276             if (in != null)
277             {
278                 try
279                 {
280                     in.close();
281                 }
282                 catch (IOException i)
283                 {
284                 }
285             }
286         }
287     }
288 
289     /**
290      * Writes the contents of an array to a temporary file
291      * 
292      * @param array
293      * @return
294      */
295     protected String writeToTempFile(byte[] array)
296     {
297         ByteArrayInputStream in = new ByteArrayInputStream(array);
298 
299         String tempFileName = new ObjectGUID().toString() + ".zip";
300         File tempDir = getTempDir();
301 
302         // write the temp file
303         String tempFilePath = tempDir.getPath() + File.separatorChar + tempFileName;
304         File tempFile = new File(tempFilePath);
305         try
306         {
307             FileOutputStream out = new FileOutputStream(tempFile);
308             DataUtil.copyStream(in, out);
309 
310             out.close();
311             in.close();
312         }
313         catch (IOException ioe)
314         {
315             ioe.printStackTrace();
316             tempFilePath = null;
317         }
318 
319         return tempFilePath;
320     }
321     
322     /**
323      * @return Returns the system temporary directory i.e. <code>isDir == true</code>
324      */
325     private static File getSystemTempDir()
326     {
327         String systemTempDirPath = System.getProperty("java.io.tmpdir");
328         if (systemTempDirPath == null)
329         {
330             throw new PlatformRuntimeException("System property not available: java.io.tmpdir");
331         }
332         File systemTempDir = new File(systemTempDirPath);
333         return systemTempDir;
334     }
335     
336     /**
337      * @return Returns a temporary directory, i.e. <code>isDir == true</code>
338      */
339     private static File getTempDir()
340     {
341         File systemTempDir = getSystemTempDir();
342         // append the temporary directory
343         File tempDir = new File(systemTempDir, "SpringSurf");
344         // ensure that the temp directory exists
345         if (!tempDir.exists())
346         {
347             if (!tempDir.mkdirs())
348             {
349                 throw new PlatformRuntimeException("Failed to create temp directory: " + tempDir);
350             }
351             if (logger.isDebugEnabled())
352             {
353                 logger.debug("Created temp directory: " + tempDir);
354             }
355         }
356         return tempDir;
357     }
358 }