View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.util.queue;
8   
9   import org.mule.api.MuleContext;
10  import org.mule.api.MuleException;
11  import org.mule.api.context.MuleContextAware;
12  import org.mule.api.transformer.wire.WireFormat;
13  import org.mule.transformer.wire.SerializationWireFormat;
14  import org.mule.util.FileUtils;
15  import org.mule.util.UUID;
16  import org.mule.util.file.DeleteException;
17  
18  import java.io.File;
19  import java.io.FileInputStream;
20  import java.io.FileNotFoundException;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.List;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  
32  public class FilePersistenceStrategy implements QueuePersistenceStrategy, MuleContextAware
33  {
34      private static final Log logger = LogFactory.getLog(FilePersistenceStrategy.class);
35  
36     /** The default queueStore directory for persistence */
37     public static final String DEFAULT_QUEUE_STORE = "queuestore";
38  
39      public static final String EXTENSION = ".msg";
40  
41      private File store;
42  
43      protected MuleContext muleContext;
44  
45      private WireFormat serializer;
46  
47      public FilePersistenceStrategy(WireFormat serializer)
48      {
49          super();
50          this.serializer = serializer;
51      }
52  
53      public FilePersistenceStrategy()
54      {
55          this(new SerializationWireFormat());
56      }
57  
58      public void setMuleContext(MuleContext context)
59      {
60          this.muleContext = context;
61  
62          serializer.setMuleContext(muleContext);
63      }
64  
65      public Object store(String queue, Object obj) throws IOException
66      {
67          String id = UUID.getUUID();
68  
69          String filename = queue + File.separator + id + EXTENSION;
70          File file = FileUtils.newFile(store, filename);
71  
72          if (!file.getParentFile().exists())
73          {
74              createStoreDirectory(file.getParentFile());
75          }
76  
77          OutputStream out = new FileOutputStream(file);
78          try
79          {
80              serializer.write(out, obj, muleContext.getConfiguration().getDefaultEncoding());
81          }
82          catch (MuleException e)
83          {
84              IOException iox = new IOException();
85              iox.initCause(e);
86              throw iox;
87          }
88          finally
89          {
90              out.close();
91          }
92  
93          return id;
94      }
95  
96      protected synchronized void createStoreDirectory(File direcetory) throws IOException
97      {
98          // To support concurrency we need to check if directory exists again inside
99          // synchronized method
100         if (!direcetory.exists() && !direcetory.mkdirs())
101         {
102             throw new IOException("Failed to create directory: " + direcetory.getAbsolutePath());
103         }
104     }
105 
106     public void remove(String queue, Object id) throws IOException
107     {
108         String fileName = queue + File.separator + id + EXTENSION;
109         File file = FileUtils.newFile(store, fileName);
110         if (file.exists())
111         {
112             if (!file.delete())
113             {
114                 throw new DeleteException(file);
115             }
116         }
117         else
118         {
119             throw new FileNotFoundException(file.getAbsolutePath());
120         }
121     }
122 
123     public Object load(String queue, Object id) throws IOException
124     {
125         String fileName = queue + File.separator + id + EXTENSION;
126         File file = FileUtils.newFile(store, fileName);
127 
128         InputStream in = new FileInputStream(file);
129         try
130         {
131             return serializer.read(in);
132         }
133         catch (MuleException e)
134         {
135             IOException iox = new IOException(e.getDetailedMessage());
136             iox.initCause(e);
137             throw iox;
138         }
139         finally
140         {
141             in.close();
142         }
143     }
144 
145     public List<Holder> restore() throws IOException
146     {
147         List<Holder> msgs = new ArrayList<Holder>();
148         if (store == null)
149         {
150             logger.warn("No store has be set on the File Persistence Strategy. Not restoring at this time");
151             return msgs;
152         }
153 
154         try
155         {
156             restoreFiles(store, msgs);
157 
158             if (logger.isDebugEnabled())
159             {
160                 logger.debug("Restore retrieved " + msgs.size() + " objects");
161             }
162 
163             return msgs;
164         }
165         catch (ClassNotFoundException e)
166         {
167             throw (IOException) new IOException("Could not restore").initCause(e);
168         }
169     }
170 
171     protected void restoreFiles(File dir, List<Holder> msgs) throws IOException, ClassNotFoundException
172     {
173         File[] files = dir.listFiles();
174         if (files == null)
175         {
176             return;
177         }
178         // sort the files so they are in the order in which
179         // their ids were generated in method store()
180         Arrays.sort(files);
181         for (int i = 0; i < files.length; i++)
182         {
183             if (files[i].isDirectory())
184             {
185                 restoreFiles(files[i], msgs);
186             }
187             else if (files[i].getName().endsWith(EXTENSION))
188             {
189                 if (files[i].length() == 0)
190                 {
191                     FileUtils.deleteQuietly(files[i]);
192                     logger.debug("Removing zero size file: " + files[i].getAbsolutePath());
193                     continue;
194                 }
195 
196                 String id = files[i].getCanonicalPath();
197                 id = id.substring(store.getCanonicalPath().length() + 1, id.length() - EXTENSION.length());
198                 String queue = id.substring(0, id.indexOf(File.separator));
199                 id = id.substring(queue.length() + 1);
200                 msgs.add(new HolderImpl(queue, id));
201             }
202         }
203     }
204 
205     public void open() throws IOException
206     {
207         String path = muleContext.getConfiguration().getWorkingDirectory() + File.separator + DEFAULT_QUEUE_STORE;
208         store = FileUtils.newFile(path).getCanonicalFile();
209         if (!store.exists())
210         {
211             createStoreDirectory(store);
212         }
213     }
214 
215     public void close() throws IOException
216     {
217         // Nothing to do
218     }
219 
220     protected static class HolderImpl implements Holder
221     {
222         private String queue;
223         private Object id;
224 
225         public HolderImpl(String queue, Object id)
226         {
227             this.queue = queue;
228             this.id = id;
229         }
230 
231         public Object getId()
232         {
233             return id;
234         }
235 
236         public String getQueue()
237         {
238             return queue;
239         }
240     }
241 
242     public boolean isTransient()
243     {
244         return false;
245     }
246 }