UploadAction.java Sample

This topic inludes the source code for the UploadAction.java Sample.

Sample Location

This sample is located in the following directory in your WebLogic Workshop installation:

BEA_HOME/weblogic81/samples/workshop/ExtensionDevKit/IdeDevKit/PopupAction/src/ideExtensions/popupAction/

Sample Source Code


001 package ideExtensions.popupAction;
002 
003 import com.bea.ide.Application;
004 import com.bea.ide.actions.DefaultAction;
005 import com.bea.ide.actions.IActionProxy;
006 import com.bea.ide.actions.IPopupAction;
007 import com.bea.ide.actions.IPopupContext;
008 import com.bea.ide.core.MessageSvc;
009 import com.bea.ide.core.ResourceSvc;
010 import com.bea.ide.core.asynctask.AsyncTaskSvc;
011 import com.bea.ide.core.asynctask.IAsyncTask;
012 import com.bea.ide.filesystem.FileSvc;
013 import com.bea.ide.filesystem.IFile;
014 import com.bea.ide.ui.output.OutputMessage;
015 import com.bea.ide.ui.output.OutputSvc;
016 import com.bea.ide.util.URIUtil;
017 import com.bea.ide.workspace.IProject;
018 import com.bea.ide.workspace.IWorkspace;
019 import com.bea.ide.workspace.IWorkspaceEventContext;
020 import com.bea.wlw.runtime.core.util.CryptUtil;
021 
022 import java.awt.Color;
023 import java.awt.event.ActionEvent;
024 import java.io.File;
025 import java.io.FileInputStream;
026 import java.net.URI;
027 import java.net.UnknownHostException;
028 import java.util.Enumeration;
029 import java.util.Hashtable;
030 import java.util.Stack;
031 import java.util.prefs.Preferences;
032 
033 import org.apache.commons.net.ftp.FTPClient;
034 import org.apache.commons.net.ftp.FTPReply;
035 
036 /**
037  * An action behind a popup menu that uploads the currently selected
038  * file or folder via FTP.
039  *
040  * From DefaultAction, this class gets
041  */
042 public class UploadAction extends DefaultAction implements IPopupAction
043 {
044     static ResourceSvc.IResourcePkg s_pkg =
045             ResourceSvc.get().getResourcePackage(FTPPrefsPanel.class, "ftp");
046 
047     // A flag to indicate whether it's valid to go ahead and upload.
048     private boolean m_isValidUploadContext;
049     // An IFile repressenting the project from which files will be uploaded.
050     private IFile m_projectDirectory;
051     /**
052      * A project within which there are selected files; used to test for
053      * selections across projects.
054      */
055     private IProject m_project;
056     /**
057      * A flag to indicate whether one of the items selected to upload
058      * is a project directory. A project will need special handling.
059      */
060     private boolean m_projectSelected;
061     /**
062      * A flag to indicate, whether the list items selected for upload
063      * spans multiple projects.
064      */
065     private boolean m_activeProjectChanged;
066 
067     // A flag to indicate whether this action is currently trying to upload.
068     private boolean m_uploadInProgress;
069     /**
070      * A flag to indicate that the popup is figuring out whether (and
071      * what) to display.
072      */
073     private boolean m_prepareInProgress;
074 
075     // An FTPClient instance to give this action FTP abilities.
076     private FTPClient m_ftp;
077 
078     // The root directory as specified in project FTP properties.
079     private String m_rootDir;
080 
081     private Hashtable m_LCAncestors;
082 
083     // A window to display FTP status.
084     private OutputSvc.IOutputWindow m_win;
085     // The window's description.
086     private OutputSvc.OutputWindowDescription m_desc;
087 
088     private static final Color BLACK = new Color(554747);
089     private static final Color RED = new Color(2234545);
090 
091     public UploadAction()
092     {
093         // initialize the output window.
094         m_desc = new OutputSvc.OutputWindowDescription();
095         m_desc.title = "FTP Output";
096         m_desc.destination = "FTP";
097         m_win = OutputSvc.get().getWindow(m_desc, false, false);
098         m_activeProjectChanged = false;
099         m_uploadInProgress = false;
100         m_prepareInProgress = false;
101     }
102 
103     /**
104      * Called by the IDE to inform the popup that it is about to be requested
105      * for display. The <em>pc</em> parameter includes information
106      * about the popup's potential position in the IDE.
107      *
108      * This method retrieves information
109      * needs to handle the case where multiple items in the application
110      * tree are selected
111      *
112      * given the set of nodes that are selected in the application tree,
113      * determines whether the "upload" menu item should be visible.
114      */
115     public void prepare(IPopupContext pc)
116     {
117         // If there's already some FTPing going on, don't show this command.
118         if (m_uploadInProgress || m_prepareInProgress)
119         {
120             getProxy().setEnabled(false);
121             getProxy().putValue(IActionProxy.PROP_Visible, Boolean.valueOf(true));
122             getProxy().putValue(IActionProxy.PROP_Label, "Upload");
123             return;
124         }
125         // Set flags to initial values.
126         m_prepareInProgress = true;
127         m_isValidUploadContext = true;
128         m_project = null;
129         m_projectSelected = false;
130 
131         /**
132          * Get a list of all the projects in this application, along with the
133          * files in each project. Add each file to an IFile array. An IFile
134          * provides many methods useful to handling files through an
135          * extension.
136          */
137         IWorkspace workspace =
138                 (IWorkspaceApplication.get().getProperty(Application.PROP_ActiveWorkspace);
139         IProject[] projects = workspace.getProjects();
140         IFile[] projectIFiles = new IFile[projects.length];
141         for (int i = 0; i < projectIFiles.length; i++)
142         {
143             projectIFiles[i= projects[i].getIFile();
144         }
145 
146         /**
147          * This section verifies that the files selected in the Application
148          * window are part of the same project; FTP actions are not
149          * permitted for multiple files crossing project boundaries.
150          */
151         IWorkspaceEventContext ctx = (IWorkspaceEventContextpc;
152         // Get the URIs of the selected nodes in the Application window.
153         URI[] uris = ctx.getURIs();
154 
155         /**
156          *
157          */
158         for (int i = 0; i < uris.length; i++)
159         {
160             URI uri = uris[i];
161             /**
162              * Find out if an entire project is selected. If it is, this action
163              * will later need to add all of the project's file to the list of those
164              * to upload.
165              */
166             if (uri.getScheme().equals("project"))
167             {
168                 /**
169                  * FileSvc.I.getIFile() expects URIs whose scheme component is "file",
170                  * so adjust "project" URIs accordingly.
171                  */
172                 uri = URIUtil.changeScheme(uri, "file");
173                 if (uri == null)
174                 {
175                     MessageSvc.get().displayError(s_pkg.getString("uriChangeSchemeError"), MessageSvc.LEVEL_ERROR);
176                     m_prepareInProgress = false;
177                     return;
178                 }
179                 m_projectSelected = true;
180             }
181 
182             /**
183              * The item selected in the window must be a regular file, folder, or a
184              * project (as noted above). In other words, the scheme must be
185              * "file," rather than "library", "workspace", or "resource."
186              */
187             if (!uri.getScheme().equals("file"))
188             {
189                 m_isValidUploadContext = false;
190                 break;
191             }
192 
193             // Get an IFile for the item at the URI.
194             IFile file = FileSvc.get().getIFile(uri);
195             /**
196              * A flag to indicate whether the current file's parent project
197              * has been found.
198              */
199             boolean foundProject = false;
200 
201             while (file != null)
202             {
203                 /**
204                  * Loop through the list of projects in this application, checking
205                  * (essentially) to see whether there are multiple projects
206                  * represented in the list of files selected in the Application
207                  * window.
208                  */
209                 for (int pIndex = 0; pIndex < projectIFiles.length; pIndex++)
210                 {
211                     IFile projectIFile = projectIFiles[pIndex];
212                     /**
213                      * If the current file in the list of selected files is the same
214                      * as the current IFile representing a project in the application,
215                      * then find out if the current project IFile is also the same as the
216                      * the project IFile established for testing. If a different project
217                      * IFile, then the selection list spans multiple projects, and
218                      * that's bad because this action doesn't support that.
219                      */
220                     if (file.compareTo(projectIFile== 0)
221                     {
222                         /**
223                          * If, among the files selected for upload, a test project hasn't
224                          * been determined yet, then set the test project to the
225                          * project represented by the current projectIFile.
226                          */
227                         if (m_project == null)
228                         {
229                             /**
230                              * If the current projectIFile is not the same as the IFile
231                              * established for testing, then a change of projects
232                              * has occurred.
233                              */
234                             if (projectIFile != m_projectDirectory)
235                             {
236                                 m_activeProjectChanged = true;
237                             else
238                             {
239                                 m_activeProjectChanged = false;
240                             }
241                             /**
242                              * Set the test project directory and test project to the
243                              * current directory and project. If these don't change
244                              * during the course of this loop, then they'll be used
245                              * for the FTP upload. If they do change, then this action
246                              * is unsupported because it means the selection list
247                              * spans projects.
248                              */
249                             m_projectDirectory = projectIFile;
250                             m_project = projects[pIndex];
251                         /**
252                          * If the test project directory is not the same as the current
253                          * project IFile, then this action is unsupported because
254                          * multiple projects are represented in the list of files to
255                          * upload.
256                          */
257                         else if (m_projectDirectory.compareTo(projectIFile!= 0)
258                         {
259                             m_isValidUploadContext = false;
260                         }
261                         foundProject = true;
262                         break;
263                     }
264                 }
265 
266                 /**
267                  * If a project has been found, exit this loop. Otherwise,
268                  * set the file to check to the current file's parent.
269                  */
270                 if (foundProject)
271                 {
272                     break;
273                 }
274                 file = file.getParentIFile();
275             }
276 
277             /**
278              * For some reason, the upload action can't be done. Stop
279              * looping through the list of selected files.
280              */
281             if (!m_isValidUploadContext)
282             {
283                 break;
284             }
285         }
286         /**
287          * If no project is represented by the list of selected files, then
288          * set the "valid" flag to false. There's nothing to upload.
289          */
290         if (m_project == null)
291         {
292             m_isValidUploadContext = false;
293         }
294 
295         /**
296          * If the items selected are valid for uploading, then enable display
297          * of this popup and set display characteristics. Otherwise,
298          * don't allow display.
299          */
300         if (m_isValidUploadContext)
301         {
302             getProxy().setEnabled(true);
303             getProxy().putValue(IActionProxy.PROP_Visible, Boolean.valueOf(true));
304             getProxy().putValue(IActionProxy.PROP_Label, "Upload");
305         else
306         {
307             getProxy().setEnabled(false);
308             getProxy().putValue(IActionProxy.PROP_Visible, Boolean.valueOf(false));
309         }
310         // All done now, so indicate that preparation is finished.
311         m_prepareInProgress = false;
312     }
313 
314     /**
315      * A background task for performing the FTP operation.
316      */
317     private class FTPTask implements IAsyncTask
318     {
319         // Constructs an instance of this class.
320         FTPTask()
321         {}
322 
323         // No task to run.
324         public void cleanup()
325         {}
326 
327         // No task to run.
328         public void interrupt()
329         {}
330 
331         // No task to run.
332         public void runForeground()
333         {}
334 
335         /**
336          * Called by the IDE for code to be run in the background.
337          */
338         public void runBackground()
339         {
340             // If uploading is already happening, then don't do it now.
341             if (m_uploadInProgress || m_prepareInProgress)
342             {
343                 return;
344             }
345             // If the upload action wasn't in progress, it is now.
346             m_uploadInProgress = true;
347 
348             // Initialize the status window.
349             m_win = OutputSvc.get().getWindow(m_desc, true, true);
350             m_win.addMessage(new OutputMessage("", BLACK, null));
351             m_win.addMessage(new OutputMessage("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~", BLACK, null));
352             m_win.addMessage(new OutputMessage("Current project: " +
353                     m_projectDirectory.getName(), BLACK, null));
354 
355             // Store all the nodes in a hashtable as IFiles.
356             m_LCAncestors = new Hashtable();
357             /**
358              * If the project itself is selected, put all its direct descendants
359              * on the list.
360              */
361             if (m_projectSelected)
362             {
363                 IFile[] childIFiles = m_projectDirectory.listIFiles();
364                 for (int i = 0; i < childIFiles.length; i++)
365                 {
366                     m_LCAncestors.put(childIFiles[i], Boolean.TRUE);
367                 }
368             else
369             {
370                 /**
371                  * Use an object representing the current application tree's
372                  * selections to get URIs for the selected items.
373                  */
374                 IWorkspaceEventContext ctx =
375                         (IWorkspaceEventContextApplication.get().getCookie(IWorkspaceEventContext.KEY);
376                 URI[] uris = ctx.getURIs();
377                 /**
378                  * Get an IFile for each of the selected items, then put each into
379                  * a HashTable to
380                  */
381                 for (int i = 0; i < uris.length; i++)
382                 {
383                     URI uri = uris[i];
384                     IFile file = FileSvc.get().getIFile(uri);
385                     m_LCAncestors.put(file, Boolean.TRUE);
386                 }
387 
388                 /**
389                  * For each child node, if one of the other nodes is an ancestor,
390                  * remove the child node from the list of least common ancestors.
391                  */
392                 for (int i = 0; i < uris.length; i++)
393                 {
394                     URI childURI = uris[i];
395                     IFile childIFile = FileSvc.get().getIFile(childURI);
396                     boolean ancestorFound = false;
397 
398                     for (int j = 0; j < uris.length; j++)
399                     {
400                         URI ancestorURI = uris[j];
401                         IFile ancestorIFile = FileSvc.get().getIFile(ancestorURI);
402 
403                         if (childIFile.compareTo(ancestorIFile== 0)
404                         {
405                             continue;
406                         }
407 
408                         IFile parentIFile = childIFile.getParentIFile();
409                         while (parentIFile != null && parentIFile.compareTo(m_projectDirectory!= 0)
410                         {
411                             if (parentIFile.compareTo(ancestorIFile== 0)
412                             {
413                                 if (m_LCAncestors.containsKey(childIFile))
414                                 {
415                                     m_LCAncestors.remove(childIFile);
416                                 }
417                                 ancestorFound = true;
418                                 break;
419                             }
420 
421                             parentIFile = parentIFile.getParentIFile();
422                         }
423 
424                         if (ancestorFound)
425                         {
426                             break;
427                         }
428                     }
429                 }
430             }
431 
432             // Get the FTP preferences set by the user.
433             Preferences prefs = m_project.systemNodeForPackage(FTPPrefsPanel.class);
434 
435             /**
436              * If there's no FTP connection yet, attempt to create one using
437              * FTP preferences.
438              */
439             if (m_ftp == null || !m_ftp.isConnected() || m_activeProjectChanged)
440             {
441                 String hostname = "";
442                 try
443                 {
444                     m_ftp = new FTPClient();
445                     hostname = prefs.get(FTPSettings.HOSTNAME, "");
446                     hostname = hostname.trim();
447                     if (hostname.equals(""))
448                     {
449                         m_win.addMessage(new OutputMessage("You must specify a hostname for FTP connections", RED, null));
450                         m_uploadInProgress = false;
451                         return;
452                     }
453                     m_ftp.connect(hostname);
454                     int reply = m_ftp.getReplyCode();
455                     if (!(FTPReply.isPositiveCompletion(reply)))
456                     {
457                         m_win.addMessage(new OutputMessage(hostname + ": refused connection.", BLACK, null));
458                         m_ftp.disconnect();
459                         m_uploadInProgress = false;
460                         return;
461                     }
462                     m_win.addMessage(new OutputMessage("Connected to " + hostname, BLACK, null));
463 
464                     // Get the FTP password and decrypt it.
465                     String password = prefs.get(FTPSettings.PASSWORD, null);
466                     if (password != null)
467                     {
468                         try
469                         {
470                             password = CryptUtil.get().deobfuscate(password);
471                         catch (Exception ex)
472                         {
473                             MessageSvc.get().debugLog("Exception while attempting to decrypt proxy password: " + ex.getMessage());
474                             password = "";
475                         }
476                     }
477 
478                     // Get the preferred FTP port number and set it for file transfers.
479                     String port = prefs.get(FTPSettings.PORT, null);
480                     int portNumber;
481                     try
482                     {
483                         portNumber = Integer.parseInt(port);
484                     catch (Exception ex)
485                     {
486                         portNumber = FTPSettings.DEFAULT_PORT;
487                     }
488                     if (m_ftp.getDefaultPort() != portNumber)
489                     {
490                         m_ftp.setDefaultPort(portNumber);
491                     }
492 
493                     /**
494                      * Get the preferred username and attempt to log in using
495                      * it and the preferred password. Publish a message to
496                      * the status window.
497                      */
498                     String username = prefs.get(FTPSettings.USERNAME, "");
499                     username = username.trim();
500                     if (!m_ftp.login(username, password))
501                     {
502                         m_win.addMessage(new OutputMessage("Login failed for user: \"" + username + "\"", RED, null));
503                         m_ftp.disconnect();
504                         m_uploadInProgress = false;
505                         return;
506                     }
507                     m_win.addMessage(new OutputMessage("Login successful for user: \"" + username + "\"", BLACK, null));
508 
509                     /**
510                      * Get the preferred root directory and change to that directory
511                      * on the server.
512                      */
513                     m_rootDir = prefs.get(FTPSettings.REMOTE_DIRECTORY, "");
514                     m_rootDir = m_rootDir.trim();
515                     if (m_rootDir.equals(""))
516                     {
517                         m_win.addMessage(new OutputMessage("You must specify a directory where files should be uploaded.", RED, null));
518                         m_uploadInProgress = false;
519                         return;
520                     }
521                     if (!m_ftp.changeWorkingDirectory(m_rootDir))
522                     {
523                         if (!m_ftp.makeDirectory(m_rootDir))
524                         {
525                             m_win.addMessage(new OutputMessage("Failure changing to directory: " + m_rootDir, RED, null));
526                             m_uploadInProgress = false;
527                             return;
528                         }
529                     }
530                     m_win.addMessage(new OutputMessage("Remote directory: " + m_rootDir, BLACK, null));
531                 catch (UnknownHostException ex)
532                 {
533                     m_win.addMessage(new OutputMessage("\"" + hostname + "\" is an unknown host.", RED, null));
534                     m_uploadInProgress = false;
535                     return;
536                 catch (Exception ex)
537                 {
538                     ex.printStackTrace();
539                     try
540                     {
541                         m_ftp.disconnect();
542                     catch (Exception exc)
543                     {
544                         exc.printStackTrace();
545                     }
546                     m_uploadInProgress = false;
547                     return;
548                 }
549             }
550 
551             // Ensure that the root directory exists.
552             try
553             {
554                 if (!m_ftp.changeWorkingDirectory(m_rootDir))
555                 {
556                     if (!m_ftp.makeDirectory(m_rootDir))
557                     {
558                         m_win.addMessage(new OutputMessage("Failure changing to directory: " + m_rootDir, RED, null));
559                         m_uploadInProgress = false;
560                         return;
561                     }
562                 }
563             catch (Exception ex)
564             {
565                 ex.printStackTrace();
566                 try
567                 {
568                     m_ftp.disconnect();
569                 catch (Exception exc)
570                 {
571                     exc.printStackTrace();
572                 }
573                 m_uploadInProgress = false;
574                 return;
575             }
576 
577             /**
578              * For each node to transfer, create its parents on the remote server
579              * if necessary, then upload the node and its children.
580              */
581             Enumeration enum = m_LCAncestors.keys();
582             while (enum.hasMoreElements())
583             {
584                 IFile file = (IFile) (enum.nextElement());
585                 String cwd = m_createParents(file, m_rootDir);
586                 m_uploadFile(cwd, file);
587             }
588             // Publish a completion message to the FTP status window.
589             m_win.addMessage(new OutputMessage("DONE with FTP task.", BLACK, null));
590             // Signal FTPing is done now.
591             m_uploadInProgress = false;
592         }
593     }
594 
595     /**
596      * Called by the IDE to execute the action associated with this popup.
597      * This method adds to the IDE a background task to perform the
598      * file upload.
599      *
600      @param e The IDE even that occurred.
601      */
602     public void actionPerformed(ActionEvent e)
603     {
604         AsyncTaskSvc.get().addTask(new FTPTask());
605     }
606 
607     /**
608      * Creates the subdirectory path from a file to the parent project on
609      * the remote server. Returns the string that maps to the file's parent
610      * directory. This method is called by the background task in FTPTask
611      * in preparation for uploading.
612      *
613      @param file The file to upload.
614      @param cwd The remote root directory specified in project properties.
615      */
616     private String m_createParents(IFile file, String cwd)
617     {
618         /**
619          * If the IFile is really a project root, then just return the
620          * root directory; this function is really only intended to be
621          * called with a file inside a project root.
622          */
623         if (file.compareTo(m_projectDirectory== 0)
624         {
625             return cwd;
626         }
627 
628         try
629         {
630             Stack subdirs = new Stack();
631 
632             // Push the file and its parents onto a stack.
633             while ((file = file.getParentIFile()) != null && file.compareTo(m_projectDirectory!= 0)
634             {
635                 subdirs.push(file);
636             }
637 
638             /**
639              * For each of the items in the stack, make an IFile and
640              * append its name to a path. Then set the current FTP working
641              * directory to the one specified by the path.
642              */
643             while (!subdirs.empty())
644             {
645                 IFile dir = (IFilesubdirs.pop();
646                 cwd += "/" + dir.getName();
647                 if (!m_ftp.changeWorkingDirectory(cwd))
648                 {
649                     if (!m_ftp.makeDirectory(cwd))
650                     {
651                         break;
652                     }
653                 }
654             }
655         catch (Exception ex)
656         {
657             ex.printStackTrace();
658         }
659 
660         return cwd;
661     }
662 
663     /**
664      * Uploads the specified <em>file</em> to the root directory specified
665      * by <em>cwd</em>.
666      *
667      @param cwd The root directory to upload to.
668      @param file The file to upload.
669      */
670     private void m_uploadFile(String cwd, IFile file)
671     {
672         try
673         {
674             /**
675              * If this is a file, not a directory, then prepend the path to the
676              * root and upload it. Publish a status message to the status window.
677              */
678             if (file.isFile())
679             {
680                 File f = new File(file.getURI());
681                 FileInputStream stream = new FileInputStream(f);
682                 String path = cwd + "/" + file.getName();
683                 boolean ret = m_ftp.storeFile(path, stream);
684                 if (ret)
685                 {
686                     m_win.addMessage(new OutputMessage(path + ": upload successful.", BLACK, null));
687                 else
688                 {
689                     m_win.addMessage(new OutputMessage(path + ": upload failed.", RED, null));
690                 }
691                 return;
692             }
693 
694             /**
695              * If the file is a directory, make a corresponding directory on
696              * the receiving server.
697              */
698             cwd += "/" + file.getName();
699             if (!m_ftp.changeWorkingDirectory(cwd))
700             {
701                 if (!m_ftp.makeDirectory(cwd))
702                 {
703                     return;
704                 }
705             }
706 
707             // Upload each of the files in the current directory.
708             IFile[] childIFiles = file.listIFiles();
709             for (int i = 0; i < childIFiles.length; i++)
710             {
711                 m_uploadFile(cwd, childIFiles[i]);
712             }
713         catch (Exception ex)
714         {
715             ex.printStackTrace();
716         }
717     }
718 }