001    package opendreams.proxy;
002    
003    import java.util.ArrayList;
004    import java.util.Calendar;
005    import java.util.HashSet;
006    import java.util.List;
007    import java.util.Set;
008    
009    import tecgraf.ftc.common.exception.FailureException;
010    import tecgraf.ftc.common.logic.RemoteFileChannel;
011    import tecgraf.ftc.common.logic.RemoteFileChannelImpl;
012    import tecgraf.ftc.utils.Utils;
013    import tecgraf.openbus.data_service.DataDescription;
014    import tecgraf.openbus.data_service.DataKey;
015    import tecgraf.openbus.data_service.IHierarchicalDataService;
016    import tecgraf.openbus.data_service.Metadata;
017    import tecgraf.openbus.data_service.UnstructuredData;
018    import tecgraf.openbus.data_service.UnstructuredDataHelper;
019    import tecgraf.openbus.data_service.project.ProjectItemDescriptionImpl;
020    import tecgraf.openbus.project.ProjectItemDescription;
021    import tecgraf.openbus.project.ProjectItemDescriptionHelper;
022    
023    /**
024     * Um projeto usado para escrever e ler arquivos usados na submissão de comandos
025     * pelo OpenDreams.
026     * 
027     * @author Tecgraf PUC-Rio
028     */
029    public class Project {
030      private IHierarchicalDataService dataService;
031      private DataDescription projectDesc;
032      private String owner;
033      private byte[] currentDir;
034    
035      /**
036       * Constrói uma representação do projeto.
037       * 
038       * @param projectDesc descritor do projeto
039       * @param owner nome do usuário
040       * @param dataService serviço de projeto
041       */
042      Project(DataDescription projectDesc, String owner,
043        IHierarchicalDataService dataService) {
044        this.projectDesc = projectDesc;
045        this.dataService = dataService;
046        this.owner = owner;
047        this.currentDir = projectDesc.fKey;
048      }
049    
050      /**
051       * Altera o diretório corrente para um outro que seja filho do diretório
052       * corrente.
053       * 
054       * @param dirName nome do diretório filho do diretório corrente
055       * @return {@code true}, se o diretório corrente foi alterado ou {@code false}
056       *         , caso contrário.
057       * @throws OpenDreamsException se ocorrer algum erro durante a mudança do
058       *         diretório corrente.
059       * 
060       */
061      public boolean changeDirectory(String dirName) throws OpenDreamsException {
062        return changeDirectory(dirName, false);
063      }
064    
065      /**
066       * Altera o diretório corrente para um outro que seja filho do diretório
067       * corrente. Possibilita que o diretório filho seja criado, se não existir.
068       * 
069       * @param dirName nome do diretório filho
070       * @param create se {@code true}, cria o diretório se não existir
071       * @return {@code true}, se o diretório corrente foi alterado ou {@code false}
072       *         , caso contrário.
073       * @throws OpenDreamsException se ocorrer algum erro durante a mudança do
074       *         diretório corrente.
075       */
076      public boolean changeDirectory(String dirName, boolean create)
077        throws OpenDreamsException {
078        byte[] fkey = this.find(dirName);
079        if (fkey != null) {
080          this.currentDir = fkey;
081          return true;
082        }
083        if (create) {
084          return createDirectory(dirName, true);
085        }
086        return false;
087      }
088    
089      /**
090       * Altera o diretório corrente para o diretório pai.
091       * 
092       * @return {@code true}, se o diretório corrente foi alterado ou {@code false}
093       *         , caso contrário.
094       * @throws OpenDreamsException se o diretório corrente já estiver no próprio
095       *         diretório do projeto ou se ocorrer algum erro na navegação para o
096       *         diretório pai.
097       */
098      public boolean changeDirectoryUp() throws OpenDreamsException {
099        try {
100          if (this.currentDir.equals(projectDesc.fKey)) {
101            throw new OpenDreamsException(
102              "O diretório corrente é o próprio diretório do projeto");
103          }
104          DataDescription dataDescription = dataService.getParent(this.currentDir);
105          if (dataDescription == null) {
106            throw new OpenDreamsException("O diretório pai retornou null");
107          }
108          this.currentDir = dataDescription.fKey;
109        }
110        catch (Exception e) {
111          throw new OpenDreamsException("Erro na navegação para o diretório pai", e);
112        }
113        return true;
114      }
115    
116      /**
117       * Cria um diretório filho do diretório corrente. Possibilita que o novo
118       * sub-diretório passe a ser o diretório corrente.
119       * 
120       * @param dirName nome do diretório
121       * @param change se {@code true}, faz com o que o novo diretório seja o
122       *        corrente
123       * @return {@code true}, se o diretório foi criado e {@code false}, caso
124       *         contrário.
125       * @throws OpenDreamsException se ocorrer algum erro durante a criação do novo
126       *         diretório.
127       */
128      public boolean createDirectory(String dirName, boolean change)
129        throws OpenDreamsException {
130        Set<String> views = new HashSet<String>();
131        views.add(ProjectItemDescriptionHelper.id());
132        long currentDate = Calendar.getInstance().getTimeInMillis();
133        ProjectItemDescription prototype =
134          new ProjectItemDescriptionImpl(dirName, views, new ArrayList<Metadata>(),
135            owner, null, null, null, 0, true, true, true, currentDate, currentDate);
136        try {
137          byte[] key = dataService.createData(currentDir, prototype);
138          if (change) {
139            this.currentDir = key;
140          }
141          return true;
142        }
143        catch (Exception e) {
144          throw new OpenDreamsException("Erro na criação do diretório " + dirName
145            + " no projeto " + projectDesc.fName, e);
146        }
147      }
148    
149      /**
150       * Cria um diretório filho do diretório corrente.
151       * 
152       * @param dirName nome do diretório
153       * @return {@code true}, se o diretório foi criado e {@code false}, caso
154       *         contrário.
155       * @throws OpenDreamsException se ocorrer algum erro durante a criação do novo
156       *         diretório.
157       */
158      public boolean createDirectory(String dirName) throws OpenDreamsException {
159        return createDirectory(dirName, false);
160      }
161    
162      /**
163       * Obtém os dados de um arquivo que está no diretório corrente da área do
164       * projeto.
165       * 
166       * @param fileName nome do arquivo
167       * @return o array com os bytes lidos
168       * @throws OpenDreamsException se ocorrer um erro na recuperação dos dados do
169       *         arquivo.
170       */
171      public byte[] getDataFrom(String fileName) throws OpenDreamsException {
172        byte[] fileKey = this.find(fileName);
173        if (fileKey == null) {
174          throw new OpenDreamsException("Arquivo " + fileName + " não encontrado");
175        }
176        RemoteFileChannel rfc = null;
177        try {
178          UnstructuredData view =
179            (UnstructuredData) dataService.getDataView(fileKey,
180              UnstructuredDataHelper.id());
181          DataKey dataKey = new DataKey(view.fKey);
182          rfc =
183            new RemoteFileChannelImpl(dataKey.getDataId().getBytes(
184              Utils.CHARSET_ENCODING), view.fWritable, view.fHost, view.fPort,
185              view.fAccessKey);
186          rfc.open(true);
187          int fileSize = (int) rfc.getSize();
188          byte[] buffer = new byte[fileSize];
189          if (fileSize != 0) {
190            rfc.read(buffer);
191          }
192          return buffer;
193        }
194        catch (Exception e) {
195          throw new OpenDreamsException("Erro na leitura de um dado no projeto", e);
196        }
197        finally {
198          if (rfc != null) {
199            try {
200              rfc.close();
201            }
202            catch (FailureException e) {
203              throw new OpenDreamsException(
204                "Erro ao fechar o remote file channel na leitura de um dado no projeto",
205                e);
206            }
207          }
208        }
209      }
210    
211      /**
212       * Cria um novo arquivo com o conteúdo passado como parâmetro.
213       * 
214       * @param fileName nome do arquivo
215       * @param data o array com os bytes a serem escritos no arquivo
216       * @throws OpenDreamsException se o arquivo já existir ou se ocorrer algum
217       *         erro durante a criação do arquivo.
218       */
219      public void createFile(String fileName, byte[] data)
220        throws OpenDreamsException {
221        byte[] fileKey = this.find(fileName);
222        if (fileKey != null) {
223          throw new OpenDreamsException("Arquivo " + fileName + " já existe");
224        }
225        RemoteFileChannel rfc = null;
226        try {
227          Set<String> views = new HashSet<String>();
228          views.add(ProjectItemDescriptionHelper.id());
229          long currentDate = Calendar.getInstance().getTimeInMillis();
230          ProjectItemDescription prototype =
231            new ProjectItemDescriptionImpl(fileName, views,
232              new ArrayList<Metadata>(), owner, null, null, null, 0, false, true,
233              true, currentDate, currentDate);
234          fileKey = dataService.createData(currentDir, prototype);
235          UnstructuredData view =
236            (UnstructuredData) dataService.getDataView(fileKey,
237              UnstructuredDataHelper.id());
238          DataKey dataKey = new DataKey(view.fKey);
239          rfc =
240            new RemoteFileChannelImpl(dataKey.getDataId().getBytes(
241              Utils.CHARSET_ENCODING), view.fWritable, view.fHost, view.fPort,
242              view.fAccessKey);
243          rfc.open(false);
244          rfc.write(data);
245        }
246        catch (Exception e) {
247          throw new OpenDreamsException("Erro na criação de um arquivo no projeto",
248            e);
249        }
250        finally {
251          if (rfc != null) {
252            try {
253              rfc.close();
254            }
255            catch (FailureException e) {
256              throw new OpenDreamsException(
257                "Erro ao fechar o remote file channel na criação de um arquivo no projeto",
258                e);
259            }
260          }
261        }
262      }
263    
264      /**
265       * Remove um arquivo do projeto. O arquivo precisa existir.
266       * 
267       * @param fileName nome do arquivo
268       * @throws OpenDreamsException se o arquivo não existir ou se ocorrer algum
269       *         erro durante a remoção do arquivo.
270       */
271      public void removeFile(String fileName) throws OpenDreamsException {
272        byte[] fileKey = this.find(fileName);
273        if (fileKey == null) {
274          throw new OpenDreamsException("Arquivo " + fileName + " não existe");
275        }
276        try {
277          dataService.deleteData(fileKey);
278        }
279        catch (Exception e) {
280          throw new OpenDreamsException("Erro na remoção do arquivo " + fileName, e);
281        }
282      }
283    
284      /**
285       * Verifica se um arquivo ou diretório existe no projeto.
286       * 
287       * @param fileName nome do arquivo ou diretório
288       * @return {@code true}, se existe e {@code false}, caso contrário
289       * @throws OpenDreamsException se ocorrer algum erro no acesso ao arquivo
290       */
291      public boolean hasFile(String fileName) throws OpenDreamsException {
292        try {
293          return this.find(fileName) != null;
294        }
295        catch (Exception e) {
296          throw new OpenDreamsException("Erro na consulta se o arquivo " + fileName
297            + " existe", e);
298        }
299      }
300    
301      /**
302       * Nome do projeto.
303       * 
304       * @return o nome do projeto
305       */
306      public String getName() {
307        return projectDesc.fName;
308      }
309    
310      /**
311       * Retorna a chave para um arquivo ou um diretório filho do diretório
312       * corrente.
313       * 
314       * @param name nome do arquivo ou diretório procurado
315       * @return a chave do arquivo procurado ou null caso ele não exista
316       * @throws OpenDreamsException se ocorrer algum erro no acesso ao arquivo
317       */
318      private byte[] find(String name) throws OpenDreamsException {
319        byte[] fileKey = null;
320        DataDescription[] descriptions;
321        try {
322          descriptions = dataService.getChildren(currentDir);
323          for (DataDescription descr : descriptions) {
324            if (descr.fName.equals(name)) {
325              fileKey = descr.fKey;
326              break;
327            }
328          }
329        }
330        catch (Exception e) {
331          throw new OpenDreamsException(
332            "Erro na procura pelo arquivo ou diretório " + name, e);
333        }
334        return fileKey;
335      }
336    
337      /**
338       * Retorna lista de nomes de arquivos e diretórios do diretório corrente.
339       * 
340       * @return lista com OpenDreamsException de arquivos e diretórios do diretório
341       *         corrente.
342       * @throws OpenDreamsException se ocorrer algum erro no acesso ao diretório
343       *         corrente
344       */
345      public List<String> list() throws OpenDreamsException {
346        ArrayList<String> fileNames = new ArrayList<String>();
347        DataDescription[] descriptions;
348        try {
349          descriptions = dataService.getChildren(currentDir);
350          for (DataDescription descr : descriptions) {
351            fileNames.add(descr.fName);
352          }
353        }
354        catch (Exception e) {
355          throw new OpenDreamsException("Erro na listagem do diretório: "
356            + currentDir, e);
357        }
358        return fileNames;
359      }
360    
361      /**
362       * Verifica se uma dada entrada é um diretório ou não.
363       * 
364       * @param entryName o nome do diretório ou do arquivo
365       * @return {@code true} se é diretório, {@code false} se for arquivo
366       * @throws OpenDreamsException se ocorrer erro durante o acesso ao diretório
367       *         corrente.
368       */
369      public boolean isDirectory(String entryName) throws OpenDreamsException {
370        try {
371          ProjectItemDescription descr =
372            (ProjectItemDescription) dataService.getDataDescription(this
373              .find(entryName));
374          return descr.fIsContainer;
375        }
376        catch (Exception e) {
377          throw new OpenDreamsException("Erro ao checar se " + entryName
378            + " é diretório.", e);
379        }
380      }
381    }