001/* 002 * Copyright (c) 2009-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2014-08-13 22:56:50 +0000 (Wed, 13 Aug 2014) $' 007 * '$Revision: 32878 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.objectmanager.cache; 031 032import java.io.File; 033import java.io.FileInputStream; 034import java.io.FileNotFoundException; 035import java.io.FileOutputStream; 036import java.io.IOException; 037import java.io.InputStream; 038import java.io.ObjectInput; 039import java.io.ObjectInputStream; 040import java.io.ObjectOutputStream; 041import java.io.OutputStream; 042import java.sql.Connection; 043import java.sql.PreparedStatement; 044import java.sql.ResultSet; 045import java.sql.SQLException; 046import java.sql.Statement; 047import java.util.Arrays; 048import java.util.Enumeration; 049import java.util.Iterator; 050import java.util.LinkedHashMap; 051import java.util.LinkedList; 052import java.util.List; 053import java.util.Map; 054import java.util.StringTokenizer; 055import java.util.Vector; 056 057import javax.swing.tree.DefaultMutableTreeNode; 058import javax.swing.tree.DefaultTreeModel; 059import javax.swing.tree.TreeModel; 060 061import org.apache.commons.logging.Log; 062import org.apache.commons.logging.LogFactory; 063import org.kepler.build.modules.Module; 064import org.kepler.build.modules.ModuleTree; 065import org.kepler.configuration.ConfigurationManager; 066import org.kepler.configuration.ConfigurationProperty; 067import org.kepler.sms.NamedOntModel; 068import org.kepler.sms.OntologyCatalog; 069import org.kepler.util.DotKeplerManager; 070import org.kepler.util.FileUtil; 071import org.kepler.util.StatusNotifier; 072import org.kepler.util.sql.DatabaseFactory; 073 074import ptolemy.util.MessageHandler; 075 076public class LocalRepositoryManager { 077 private static final Log log = LogFactory 078 .getLog(LocalRepositoryManager.class.getName()); 079 private static final boolean isDebugging = log.isDebugEnabled(); 080 081 public static final String KAR_LOCAL_REPOS_TABLE_NAME = "KAR_LOCAL_REPOS"; 082 083 /** The name in the configuration file containing the display name 084 * for modules. 085 */ 086 private static final String MODULE_DISPLAY_NAME = "moduleDisplayName"; 087 088 /** 089 * The list of KAR files that make up the library. 090 */ 091 private Vector<File> _karFiles; 092 093 /** List of MoML files that make up the library. */ 094 private Vector<File> _xmlFiles; 095 096 private Connection _conn; 097 private Statement _stmt; 098 private PreparedStatement _insertPrepStmt; 099 private PreparedStatement _deletePrepStmt; 100 private PreparedStatement _updateNamePrepStmt; 101 private PreparedStatement _updatePathPrepStmt; 102 103 private LinkedHashMap<String, TreeModel> _folderModel; 104 105 public TreeModel getFolderModel(String name) { 106 return _folderModel.get(name); 107 } 108 109 public void setFolderModel(LinkedHashMap<String, TreeModel> folderModel) { 110 _folderModel = folderModel; 111 } 112 113 /** 114 * The local repositories that the cache is built from. Keys are the 115 * repository names, values are the directory files. 116 */ 117 private LinkedHashMap<LocalRepository, String> _localRepositories; 118 119 /** 120 * This is the folder that we save KARs to by default. 121 */ 122 private LocalRepository _localSaveRepo; 123 124 /** 125 * The file in the module readwrite area where we'll save the name of the 126 * user selected local repository to save KAR files in by default. 127 */ 128 private String _localSaveRepoFileName; 129 130 /** 131 * We keep a copy of the initial local repositories so we can see if they 132 * have changed. 133 */ 134 private LinkedHashMap<LocalRepository, String> _checkpointRepos; 135 136 private File _defaultUserWorkflowDirectory; 137 138 /** 139 * Empty Constructor. 140 */ 141 public LocalRepositoryManager() { 142 143 DotKeplerManager dkm = DotKeplerManager.getInstance(); 144 145 // Set up file name for storing default local save directory 146 File modDir = dkm.getTransientModuleDirectory("core"); 147 if (modDir != null) { 148 _localSaveRepoFileName = modDir.toString(); 149 } else { 150 _localSaveRepoFileName = System.getProperty("KEPLER"); 151 } 152 if (!_localSaveRepoFileName.endsWith(File.separator)) { 153 _localSaveRepoFileName += File.separator; 154 } 155 _localSaveRepoFileName += "LocalSaveRepository"; 156 157 // Set up the location of the default workflows directory 158 _defaultUserWorkflowDirectory = dkm.getPersistentUserWorkflowsDir();//new File(persistentDir, "workflows"); 159 if (!_defaultUserWorkflowDirectory.exists()) { 160 _defaultUserWorkflowDirectory.mkdirs(); 161 } 162 163 // Set up prepared statements for select,insert,update,delete 164 // local repository information 165 try { 166 _conn = DatabaseFactory.getDBConnection(); 167 } catch (Exception e) { 168 MessageHandler.error("Error opening cache database.", e); 169 return; 170 } 171 172 try { 173 _stmt = _conn.createStatement(); 174 _insertPrepStmt = _conn.prepareStatement("insert into " 175 + KAR_LOCAL_REPOS_TABLE_NAME 176 + " (name,path) values ( ?, ? ) "); 177 _deletePrepStmt = _conn.prepareStatement("DELETE FROM " 178 + KAR_LOCAL_REPOS_TABLE_NAME + " WHERE PATH = ? "); 179 _updateNamePrepStmt = _conn.prepareStatement("UPDATE " 180 + KAR_LOCAL_REPOS_TABLE_NAME 181 + " SET NAME = ? WHERE PATH = ? "); 182 _updatePathPrepStmt = _conn.prepareStatement("UPDATE " 183 + KAR_LOCAL_REPOS_TABLE_NAME 184 + " SET PATH = ? WHERE NAME = ? "); 185 186 } catch (SQLException e) { 187 e.printStackTrace(); 188 } 189 190 initLocalRepos(); 191 initLocalSaveRepo(); 192 } 193 194 /** 195 * Initialize local repositories that contain KAR files. 196 */ 197 private void initLocalRepos() { 198 199 // Check to see if there are any local repositories 200 try { 201 String query = "SELECT count(*) FROM " + KAR_LOCAL_REPOS_TABLE_NAME; 202 if (isDebugging) 203 log.debug(query); 204 ResultSet rs = null; 205 try { 206 rs = _stmt.executeQuery(query); 207 if (rs != null && rs.next()) { 208 int cnt = rs.getInt(1); 209 if (cnt <= 0) { 210 // Set the defaults if there are no local repositories in 211 // the database 212 this.setDefaultLocalRepos(); 213 } 214 } 215 } finally { 216 if(rs != null) { 217 rs.close(); 218 } 219 } 220 } catch (SQLException sqle) { 221 log.error(sqle.getMessage()); 222 } 223 224 refreshReposFromDB(); 225 226 } 227 228 public LinkedHashMap<LocalRepository, String> selectReposFromDB() { 229 LinkedHashMap<LocalRepository, String> localRepos = new LinkedHashMap<LocalRepository, String>(); 230 try { 231 String query = "SELECT name,path FROM " 232 + KAR_LOCAL_REPOS_TABLE_NAME + " order by name"; 233 if (isDebugging) 234 log.debug(query); 235 ResultSet rs = null; 236 try { 237 rs = _stmt.executeQuery(query); 238 if (rs != null) { 239 while (rs.next()) { 240 String theName = rs.getString(1); 241 String paths = rs.getString(2); 242 LocalRepository repo = new LocalRepository(paths); 243 localRepos.put(repo, theName); 244 } 245 } 246 } finally { 247 if(rs != null) { 248 rs.close(); 249 } 250 } 251 } catch (SQLException sqle) { 252 log.error(sqle.getMessage()); 253 sqle.printStackTrace(); 254 } 255 return localRepos; 256 } 257 258 /** 259 * Repopulate our local hashtable from the database. 260 */ 261 private void refreshReposFromDB() { 262 _localRepositories = selectReposFromDB(); 263 } 264 265 /** 266 * Return a list of all the KAR files that were found after calling 267 * scanReposForKarFiles() 268 * 269 * @return Vector of File objects pointing to KAR files 270 */ 271 public Vector<File> getKarFiles() { 272 return _karFiles; 273 } 274 275 /** Return a list of all the XML files that were found after calling 276 * scanReposForXMLFiles() 277 */ 278 public Vector<File> getXMLFiles() { 279 //return new Vector<File>(); 280 return _xmlFiles; 281 } 282 283 /** 284 * Search for Kar files in local Kar Repositories and build a list of all 285 * the KAR files that are found. This list can be retrieved using 286 * getKarFiles() 287 */ 288 public void scanReposForKarFiles() { 289 StatusNotifier.log("Scanning Local Repositories for KAR files."); 290 _karFiles = new Vector<File>(); 291 _xmlFiles = new Vector<File>(); 292 293 LinkedHashMap<String, TreeModel> folderModel = new LinkedHashMap<String, TreeModel>(); 294 setFolderModel(folderModel); 295 296 for (LocalRepository repoRoot : getLocalRepositories().keySet()) { 297 if (isDebugging) { 298 log.debug("Recursing for Kar files in local repository: " 299 + repoRoot.toString()); 300 } 301 refreshFolderModelForRepo(repoRoot); 302 } 303 } 304 305 /** 306 * Refresh the folder model for the specified local repository. 307 * 308 * @param repo 309 */ 310 public void refreshFolderModelForRepo(LocalRepository repo) { 311 if (isDebugging) log.debug("refreshFolderModelForRepo("+repo.toString()+")"); 312 313 String repoName = getLocalRepositories().get(repo); 314 if (repoName == null) { 315 log.warn("Error: not a local repository: " + repo); 316 return; 317 } 318 319 TreeModel tm = getFolderModel(repoName); 320 if (tm == null) { 321 tm = new DefaultTreeModel(new DefaultMutableTreeNode(repo)); 322 _folderModel.put(repoName, tm); 323 } 324 325 DefaultMutableTreeNode root = (DefaultMutableTreeNode) tm.getRoot(); 326 root.removeAllChildren(); 327 328 for(File dir : repo.getDirectories()) { 329 findKarsRecursive(dir, 20, root); 330 } 331 } 332 333 /** 334 * Given the File object for a folder in a local repository, return the 335 * corresponding DefaultMutableTreeNode object from the Folder model. 336 * 337 * @param folder 338 * @return 339 */ 340 public DefaultMutableTreeNode getFolderModelNode(File folder) { 341 String folderStr = folder.toString(); 342 for (LocalRepository repo : getLocalRepositories().keySet()) { 343 for(File repoRootDir : repo.getDirectories()) { 344 if (folderStr.equals(repoRootDir.toString())) { 345 return (DefaultMutableTreeNode) getFolderModel( 346 getLocalRepositories().get(repo)).getRoot(); 347 } else if (folderStr.startsWith(repoRootDir.toString())) { 348 String remainder = folderStr.substring(repoRootDir.toString().length()); 349 if (remainder.startsWith(File.separator)) { 350 remainder = remainder.substring(1); 351 } 352 StringTokenizer st = new StringTokenizer(remainder, 353 File.separator); 354 355 TreeModel tm = getFolderModel(getLocalRepositories().get(repo)); 356 DefaultMutableTreeNode root = (DefaultMutableTreeNode) tm 357 .getRoot(); 358 String dir = null; 359 File current = repoRootDir; 360 int count = st.countTokens(); 361 while (st.hasMoreTokens()) { 362 dir = st.nextToken(); 363 current = new File(current.toString(), dir); 364 DefaultMutableTreeNode dmtn = checkChildren(root, current); 365 if (dmtn == null) { 366 return null; 367 } 368 if (count == 1) { 369 return dmtn; 370 } else { 371 root = dmtn; 372 } 373 count--; 374 } 375 } 376 } 377 } 378 379 return null; 380 } 381 382 /** 383 * 384 * @param dmtn 385 * @param dir 386 * @return the TreeNode that corresponds to the given directory name 387 */ 388 private DefaultMutableTreeNode checkChildren(DefaultMutableTreeNode dmtn, 389 File folder) { 390 391 Enumeration<?> children = dmtn.children(); 392 while (children.hasMoreElements()) { 393 DefaultMutableTreeNode child = (DefaultMutableTreeNode) children 394 .nextElement(); 395 if (child.getUserObject().equals(folder)) { 396 return child; 397 } 398 399 } 400 return null; 401 } 402 403 public void refreshFolderModelForFolder(File folder) { 404 405 // find the corresponding TreeNode 406 DefaultMutableTreeNode dmtn = getFolderModelNode(folder); 407 if (dmtn == null) { 408 return; 409 } 410 411 // remove the children of that TreeNode 412 dmtn.removeAllChildren(); 413 414 // rebuild the children of the TreeNode 415 findKarsRecursive(folder, 20, dmtn); 416 417 } 418 419 /** 420 * Recursive function for finding files that end in ".kar" (case 421 * insensitive). 422 * 423 * @param dir 424 * The root of the local repository that contains KAR files 425 * @param depth 426 * The maximum recursion depth 427 */ 428 private void findKarsRecursive(File dir, int depth, 429 DefaultMutableTreeNode tn) { 430 if (isDebugging) 431 log.debug(depth + ": " + dir.toString()); 432 if (!dir.exists()) { 433 log.warn(dir.toString() + " does not exist"); 434 return; 435 } 436 if (!dir.isDirectory()) { 437 log.warn(dir.toString() + " is not a directory"); 438 return; 439 } 440 if (depth < 0) { 441 log.warn(dir.toString() + " is too deep"); 442 return; 443 } 444 445 File[] listing = dir.listFiles(); 446 for (int i = 0; i < listing.length; i++) { 447 File currentListing = listing[i]; 448 if (currentListing.isDirectory()) { 449 if (currentListing.getName().equals(".svn")) { 450 // skip .svn folders 451 } else if (currentListing.getName().contains(".")) { 452 // skip any folders that contain periods 453 // ptolemy cannot handle periods in NamedObj Names. 454 System.out.println("WARNING: skipping due to periods: " + currentListing); 455 } else { 456 DefaultMutableTreeNode dmtn = new DefaultMutableTreeNode( 457 currentListing); 458 tn.add(dmtn); 459 findKarsRecursive(currentListing, (depth - 1), dmtn); 460 } 461 } else { 462 if (currentListing.getName().toLowerCase().endsWith(".kar")) { 463 _karFiles.addElement(currentListing); 464 } else if(currentListing.getName().toLowerCase().endsWith(".xml")) { 465 _xmlFiles.addElement(currentListing); 466 } 467 } 468 } 469 } 470 471 /** 472 * 473 */ 474 private void initLocalSaveRepo() { 475 File localSaveRepoFile = new File(_localSaveRepoFileName); 476 477 if (localSaveRepoFile.exists()) { 478 if (isDebugging) { 479 log.debug("localSaveRepo exists: " 480 + localSaveRepoFile.toString()); 481 } 482 483 try { 484 InputStream is = null; 485 ObjectInput oi = null; 486 try { 487 is = new FileInputStream(localSaveRepoFile); 488 oi = new ObjectInputStream(is); 489 Object newObj = oi.readObject(); 490 setLocalSaveRepo((File) newObj); 491 return; 492 } finally { 493 if(oi != null) { 494 oi.close(); 495 } 496 if(is != null) { 497 is.close(); 498 } 499 } 500 } catch (Exception e1) { 501 // problem reading file, try to delete it 502 log.warn("Exception while reading localSaveRepoFile: " 503 + e1.getMessage()); 504 try { 505 localSaveRepoFile.delete(); 506 } catch (Exception e2) { 507 log.warn("Unable to delete localSaveRepoFile: " 508 + e2.getMessage()); 509 } 510 } 511 } 512 try { 513 setDefaultSaveRepo(); 514 } catch (Exception e) { 515 e.printStackTrace(); 516 } 517 } 518 519 /** 520 * @param directory 521 */ 522 public void setLocalSaveRepo(File dir) { 523 if (isDebugging) 524 log.debug("setLocalSaveRepo(" + dir + ")"); 525 if (getRepositoryForFile(dir) != null) { 526 _localSaveRepo = new LocalRepository(dir); 527 serializeLocalSaveRepo(); 528 } 529 } 530 531 /** 532 * Set the default Save repository. 533 * 534 * @throws Exception 535 */ 536 public void setDefaultSaveRepo() { 537 if (isDebugging) 538 log.debug("setDefaultSaveRepo()"); 539 540 // Use the default workflows directory 541 542 // see if it's the root of a local repository 543 LocalRepository repo = getRepositoryForFile(_defaultUserWorkflowDirectory); 544 if(repo == null) { 545 // see if it's a subdirectory in a local repository 546 repo = getContainingLocalRepository(_defaultUserWorkflowDirectory); 547 } 548 549 if(repo != null) { 550 setLocalSaveRepo(repo.getDefaultDirectory()); 551 return; 552 } 553 554 // If there is no default workflows directory 555 // Set the save repo to the first local repo in the list 556 for (LocalRepository localRepo : _localRepositories.keySet()) { 557 setLocalSaveRepo(localRepo.getDefaultDirectory()); 558 break; 559 } 560 } 561 562 /** 563 * Set the default local repositories to be the kar directories for each of 564 * the modules in the system along with a default workflows directory. 565 */ 566 public void setDefaultLocalRepos() { 567 if (isDebugging) 568 log.debug("setDefaultLocalRepos()"); 569 570 571 // Set up a default list of local repository directories 572 _localRepositories = new LinkedHashMap<LocalRepository, String>(); 573 574 try { 575 String deleteAll = "delete from " + KAR_LOCAL_REPOS_TABLE_NAME; 576 if (isDebugging) 577 log.debug(deleteAll); 578 _stmt.executeUpdate(deleteAll); 579 } catch (SQLException sqle) { 580 log.error(sqle.getMessage()); 581 sqle.printStackTrace(); 582 } 583 584 final DotKeplerManager dkm = DotKeplerManager.getInstance(); 585 586 for (Module module : ModuleTree.instance()) { 587 if (isDebugging) log.debug("Checking for kar directory in " + module.getStemName()); 588 //String modName = m.getName(); 589 String modName = module.getStemName(); 590 File modDir = dkm.getPersistentModuleDirectory(modName); 591 File karDir = new File(modDir, "kar"); 592 593 if (karDir.isDirectory() && karDir.exists()) { 594 if (isDebugging) 595 log.debug(karDir + " " + modName); 596 try { 597 String repoName = getLocalRepositoryName(modName); 598 addLocalRepoRootDir(karDir, repoName); 599 } catch (Exception e) { 600 MessageHandler.error("Error adding local repository " + karDir, e); 601 } 602 } 603 604 // NOTE: use the fully versioned name of the module instead of 605 // the name without a version since the demo workflows can 606 // change between versions of the same module. 607 // 608 File workflowDir = dkm.getPersistentModuleWorkflowsDir(module.getName()); 609 610 // only add the demos to the library. 611 // <module>/workflows/data may contain XML files that generate 612 // errors when parsed. 613 // see http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5643 614 File demoDir = new File(workflowDir, "demos"); 615 if(demoDir.isDirectory() && demoDir.exists()) { 616 if (isDebugging) 617 log.debug(demoDir + " " + modName); 618 try { 619 String repoName = getLocalRepositoryName(modName); 620 addLocalRepoRootDir(demoDir, repoName); 621 } catch (Exception e) { 622 MessageHandler.error("Error adding local repository " + karDir, e); 623 } 624 } 625 /* 626 else if (modName.equals(Module.PTOLEMY) || modName.matches(Module.PTOLEMY+"-\\d+\\.\\d+") 627 || modName.matches(Module.PTOLEMY_KEPLER+"-\\d+\\.\\d+")) { 628 629 Project project = ProjectLocator.getAntProject(); 630 // NOTE: getAntProject() may return null; in this case 631 // create a new one. 632 if (project == null) { 633 project = new Project(); 634 } 635 final FileSet fileSet = new FileSet(); 636 fileSet.setProject(project); 637 fileSet.setDir(module.getSrc()); 638 XXX space added to prevent closing comment 639 fileSet.setIncludes("** /demo"); 640 fileSet.setExcludesfile(new File(project.getBaseDir(), 641 "build-area/settings/ptolemy-excludes")); 642 final String[] files = fileSet.getDirectoryScanner() 643 .getIncludedDirectories(); 644 for (String name : files) { 645 String repoName = modName; 646 repoName = repoName.substring(0, 1).toUpperCase() 647 + repoName.substring(1); 648 try { 649 System.out.println(name); 650 addLocalRepoRootDir(new File(module.getSrc(), name), 651 repoName); 652 } catch (Exception e) { 653 MessageHandler.error("Error adding local repository " + karDir, e); 654 } 655 } 656 } 657 */ 658 } 659 660 // Include a default workflows directory 661 try { 662 addLocalRepoRootDir(_defaultUserWorkflowDirectory, dkm.getPersistentUserWorkflowsDirName()); 663 } catch (Exception e) { 664 e.printStackTrace(); 665 } 666 667 if (getLocalRepositories().size() <= 0) { 668 log.error("No local repositories specified."); 669 } 670 671 } 672 673 /** 674 * Serialize the local save repository to a file on disk so it can be loaded 675 * the next time Kepler starts. 676 */ 677 private void serializeLocalSaveRepo() { 678 if (isDebugging) 679 log.debug("serializeLocalSaveRepo()"); 680 681 File localSaveRepoFile = new File(_localSaveRepoFileName); 682 if (localSaveRepoFile.exists()) { 683 if (isDebugging) 684 log.debug("delete " + localSaveRepoFile); 685 localSaveRepoFile.delete(); 686 } 687 try { 688 OutputStream os = new FileOutputStream(localSaveRepoFile); 689 ObjectOutputStream oos = new ObjectOutputStream(os); 690 oos.writeObject(_localSaveRepo.getDefaultDirectory()); 691 oos.flush(); 692 oos.close(); 693 if (isDebugging) { 694 log.debug("wrote " + localSaveRepoFile); 695 } 696 } catch (FileNotFoundException e) { 697 e.printStackTrace(); 698 } catch (IOException e) { 699 e.printStackTrace(); 700 } 701 } 702 703 /** 704 * Save the local repo dirs to a private variable so we can determine when 705 * they have changed between build points. 706 */ 707 public void setCheckpoint() { 708 _checkpointRepos = (LinkedHashMap<LocalRepository, String>) getLocalRepositories() 709 .clone(); 710 } 711 712 /** 713 * Reset the list of local repositories to be what it was the last time the 714 * setCheckpoint() method was called. 715 */ 716 public void restoreCheckpoint() { 717 _localRepositories = (LinkedHashMap<LocalRepository, String>) _checkpointRepos 718 .clone(); 719 } 720 721 /** 722 * Check to see if the LocalRepositories have changed since the last time 723 * setCheckpoint() was called. 724 * 725 * @return true if the local repositories have changed 726 */ 727 public boolean changedSinceCheckpoint() { 728 729 if (!_checkpointRepos.equals(getLocalRepositories())) { 730 return true; 731 } 732 return false; 733 } 734 735 public boolean isLocalRepositoryName(String name) { 736 for (String l : getLocalRepositories().values()) { 737 if (l.equals(name)) { 738 return true; 739 } 740 } 741 742 return false; 743 } 744 745 /** 746 * This method only removes the given directory from the in-memory repository 747 * list. To update the database you must call the synchronizeDB() method. 748 * 749 * @param directory 750 * @throws Exception 751 * if the directory could not be removed 752 */ 753 public void removeLocalRepoRootDir(File directory) throws Exception { 754 if (isDebugging) 755 log.debug("removeLocalRepoRootDir(" + directory + ")"); 756 757 // do not remove anything if there is only one local repo left 758 if (_localRepositories.size() == 1) { 759 throw new Exception( 760 "There must always be at least one local repository directory"); 761 } 762 LocalRepository repo = getRepositoryForFile(directory); 763 if(repo != null) { 764 boolean isSaveDir = false; 765 if (getSaveRepository().equals(directory)) { 766 isSaveDir = true; 767 } 768 769 int numLeft = repo.removeDir(directory); 770 if(numLeft == 0) { 771 _localRepositories.remove(repo); 772 } 773 774 if (isSaveDir) { 775 setDefaultSaveRepo(); 776 } 777 } else { 778 throw new Exception( 779 "Unable to remove directory " 780 + directory 781 + "\n No Local Repository directory matching that name was found."); 782 } 783 } 784 785 /** 786 * Synchronize the KAR_LOCAL_REPOS table with the _localRepositories private variable list. 787 * This method only removes rows from the table that are not in the list. 788 * It does not add rows to the table for extra entries that are in the list. 789 */ 790 public void synchronizeDB() { 791 792 LinkedHashMap<LocalRepository, String> localRepos = selectReposFromDB(); 793 for (LocalRepository repo : localRepos.keySet()) { 794 if (!_localRepositories.containsKey(repo)) { 795 try { 796 // this will cascade deletion of KARs and KAR contents 797 // from the tables 798 _deletePrepStmt.setString(1, repo.toString()); 799 _deletePrepStmt.executeUpdate(); 800 _conn.commit(); 801 } catch (SQLException sqle) { 802 sqle.printStackTrace(); 803 } 804 } 805 } 806 } 807 808 /** 809 * Change the name of a local repository. 810 * 811 * @param directory the default root directory of the repository 812 * @param name 813 * @throws Exception 814 */ 815 public void setLocalRepoName(File directory, String name) throws Exception { 816 if (_localRepositories.containsValue(name)) { 817 throw new Exception( 818 "This name is already assigned to a local repository directory."); 819 } 820 Iterator<NamedOntModel> models = OntologyCatalog.instance() 821 .getNamedOntModels(); 822 while (models.hasNext()) { 823 if (models.next().getName().equals(name)) { 824 throw new Exception( 825 "This name is already being used for an ontology."); 826 } 827 } 828 LocalRepository repo = getRepositoryForFile(directory); 829 if(repo == null) { 830 throw new Exception("No repository found with directory " + directory); 831 } 832 try { 833 _updateNamePrepStmt.setString(1, name); 834 _updateNamePrepStmt.setString(2, repo.getDirectoriesAsString()); 835 _updateNamePrepStmt.executeUpdate(); 836 _conn.commit(); 837 String oldName = _localRepositories.put(repo, name); 838 if (isDebugging) 839 log.debug(oldName + " was changed to " + name); 840 } catch (SQLException sqle) { 841 log.warn(sqle.getMessage()); 842 } 843 } 844 845 /** 846 * Given a file, return true if it is in a local repository, false if it is 847 * not. 848 * 849 * @param aFile 850 * @return 851 */ 852 public boolean isInLocalRepository(File aFile) { 853 LocalRepository containingRepo = getContainingLocalRepository(aFile); 854 if (containingRepo != null) { 855 return true; 856 } 857 return false; 858 } 859 860 /** 861 * Given a file, return the local repository that it is in or null if it is 862 * not in a local repository. This also returns null if the file passed in 863 * is a local repository. 864 * 865 * @param aFile 866 * @return 867 */ 868 public LocalRepository getContainingLocalRepository(File aFile) { 869 for (LocalRepository repo : getLocalRepositories().keySet()) { 870 for(File repoRootDir : repo.getDirectories()) { 871 try { 872 if (FileUtil.isSubdirectory(repoRootDir, aFile)) { 873 return repo; 874 } 875 } catch (Exception e) { 876 e.printStackTrace(); 877 } 878 } 879 } 880 return null; 881 } 882 883 /** 884 * Convenience method for addLocalRepoRootDir(File, String) 885 * 886 * @param directory 887 * @throws Exception 888 */ 889 public void addLocalRepoRootDir(File directory) throws Exception { 890 addLocalRepoRootDir(directory, directory.getName()); 891 } 892 893 /** Add a local repository for a given root directory and name. If 894 * a repository with that name already exists, the directory is added 895 * to the list of root directories for that repository. 896 * @param directory 897 * @throws Exception 898 */ 899 public void addLocalRepoRootDir(File directory, String name) 900 throws Exception { 901 if (isDebugging) 902 log.debug("addLocalRepoRootDir(" + directory + ", " + name + ")"); 903 if (!directory.isDirectory()) { 904 throw new Exception( 905 "The specified local repository root must be a directory"); 906 } 907 String selFileName = FileUtil.clean(directory); 908 909 LocalRepository existingRepo = null; 910 911 // check to make sure this directory is not a sub directory 912 // of any existing local repositories 913 for (Map.Entry<LocalRepository, String> entry : getLocalRepositories().entrySet()) { 914 915 final LocalRepository repo = entry.getKey(); 916 917 for(File localDir : repo.getDirectories()) { 918 // make sure this selection doesn't match an existing local repository exactly 919 if (FileUtil.clean(localDir).equals(selFileName)) { 920 throw new Exception( 921 "Local repository root directory was not added because \n" 922 + directory 923 + "\n is already listed as a local repository root directory."); 924 } 925 926 // make sure this selection is not a subdirecctory of an existing local repository 927 boolean selectionIsSub = FileUtil.isSubdirectory(localDir, directory); 928 if (selectionIsSub) { 929 throw new Exception( 930 "Local repository was not added because \n" 931 + directory + "\n is a subdirectory of \n" 932 + localDir); 933 } 934 935 // make sure this selection does not contain an existing local repository as a subdirectory 936 boolean repoIsSub = FileUtil.isSubdirectory(directory, localDir); 937 if (repoIsSub) { 938 throw new Exception( 939 "Local repository was not added because \n" 940 + localDir + "\n is a subdirectory of \n" 941 + directory); 942 } 943 } 944 945 final String repoName = entry.getValue(); 946 if(repoName.equals(name)) { 947 existingRepo = repo; 948 } 949 } 950 951 try { 952 953 // see if a repository with that name already exists 954 if(existingRepo == null) { 955 _insertPrepStmt.setString(1, name); 956 _insertPrepStmt.setString(2, directory.toString()); 957 _insertPrepStmt.executeUpdate(); 958 _localRepositories.put(new LocalRepository(directory), name); 959 } else { 960 String paths = existingRepo.addDirectory(directory); 961 _updatePathPrepStmt.setString(1, paths); 962 _updatePathPrepStmt.setString(2, name); 963 _updatePathPrepStmt.executeUpdate(); 964 } 965 966 _conn.commit(); 967 968 } catch (SQLException sqle) { 969 sqle.printStackTrace(); 970 } 971 } 972 973 public LinkedHashMap<LocalRepository, String> getLocalRepositories() { 974 return _localRepositories; 975 } 976 977 public File getSaveRepository() { 978 if(_localSaveRepo != null) { 979 List<File> dirs = _localSaveRepo.getDirectories(); 980 if(!dirs.isEmpty()) { 981 return dirs.get(0); 982 } 983 } 984 return null; 985 } 986 987 /** Get the repository for a file. Returns null if no repository 988 * has a root directory matching this path. 989 */ 990 public LocalRepository getRepositoryForFile(File file) { 991 for(LocalRepository repo : getLocalRepositories().keySet()) { 992 if(repo.isFileRepoDirectory(file)) { 993 return repo; 994 } 995 } 996 return null; 997 } 998 999 /** 1000 * Method for getting an instance of this singleton class. 1001 */ 1002 public static LocalRepositoryManager getInstance() { 1003 return LocalRepositoryManagerHolder.INSTANCE; 1004 } 1005 1006 private static class LocalRepositoryManagerHolder { 1007 private static final LocalRepositoryManager INSTANCE = new LocalRepositoryManager(); 1008 } 1009 1010 /** Get the display name of a local repository for a module. */ 1011 public static String getLocalRepositoryName(String moduleName) throws Exception { 1012 1013 String name = null; 1014 1015 // see if the module name is customized 1016 Module module = ModuleTree.instance().getModuleByStemName(moduleName); 1017 if(module != null) { 1018 ConfigurationProperty property = ConfigurationManager.getInstance().getProperty(module); 1019 if(property != null) { 1020 ConfigurationProperty nameProperty = property.getProperty(MODULE_DISPLAY_NAME); 1021 if(nameProperty != null) { 1022 name = nameProperty.getValue(); 1023 } 1024 } 1025 } 1026 1027 // the module name was not customized, so return a name the same as the 1028 // module name with the first letter upper-case. 1029 if(name == null) { 1030 name = moduleName; 1031 name = name.substring(0, 1).toUpperCase() + name.substring(1); 1032 } 1033 1034 return name; 1035 } 1036 1037 /** A repository on the local disk. The repository may have more than one 1038 * root directory. 1039 */ 1040 public static class LocalRepository { 1041 1042 /** Get the default directory. */ 1043 public File getDefaultDirectory() { 1044 return _directories.get(0); 1045 } 1046 1047 /** Returns true if the given file is one of the directories of this repository. */ 1048 public boolean isFileRepoDirectory(File file) { 1049 for(File dir : _directories) { 1050 if(dir.equals(file)) { 1051 return true; 1052 } 1053 } 1054 return false; 1055 } 1056 1057 /** Get the absolute path of the default root directory. */ 1058 @Override 1059 public String toString() { 1060 //System.out.println("in toString from: " + new Exception().getStackTrace()[1]); 1061 return getDefaultDirectory().toString(); 1062 } 1063 1064 /** Returns true iff the given object is a LocalRepository containing 1065 * the same directories this LocalRepository. 1066 */ 1067 @Override 1068 public boolean equals(Object object) { 1069 if(object == null || !(object instanceof LocalRepository)) { 1070 return false; 1071 } else { 1072 // see if the directories are the same 1073 return _directoriesString.equals(((LocalRepository)object)._directoriesString); 1074 } 1075 } 1076 1077 /** Get the directories in separated by File.pathSeparator character. */ 1078 private String getDirectoriesAsString() { 1079 return _directoriesString; 1080 } 1081 1082 /** Get the hash code of this LocalRepository. */ 1083 @Override 1084 public int hashCode() { 1085 // use the hash code of the directories in this repository 1086 return _directoriesString.hashCode(); 1087 } 1088 1089 /** Remove a root directory. 1090 * @return the number of remaining root directories. 1091 */ 1092 public int removeDir(File directory) { 1093 _directories.remove(directory); 1094 _updateDirectoriesString(); 1095 return _directories.size(); 1096 } 1097 1098 /** Create a new Repository with a root directory. */ 1099 private LocalRepository(File dir) { 1100 addDirectory(dir); 1101 } 1102 1103 /** Create a new Repository with a set of root directories. 1104 * @param paths A string of paths separated by File.pathSeparator. 1105 */ 1106 private LocalRepository(String paths) { 1107 String[] parts = paths.split(File.pathSeparator); 1108 for(String part : parts) { 1109 addDirectory(new File(part)); 1110 } 1111 } 1112 1113 /** Add a root directory. 1114 * @return A string of paths separated by File.pathSeparator. 1115 */ 1116 private String addDirectory(File directory) { 1117 _directories.add(directory); 1118 _updateDirectoriesString(); 1119 return _directoriesString; 1120 } 1121 1122 /** Get the root directories. */ 1123 private List<File> getDirectories() { 1124 return new LinkedList<File>(_directories); 1125 } 1126 1127 /** Update _directoriesString to be the sorted list of directories 1128 * in _directories separated by File.pathSeparator. */ 1129 private void _updateDirectoriesString() { 1130 // get the directories as an array and sort lexicographically 1131 final File[] array = _directories.toArray(new File[_directories.size()]); 1132 Arrays.sort(array); 1133 1134 StringBuilder buf = new StringBuilder(); 1135 for(int i = 0; i < array.length - 1; i++) { 1136 buf.append(array[i].getAbsolutePath()); 1137 buf.append(File.pathSeparatorChar); 1138 } 1139 buf.append(array[array.length - 1]); 1140 _directoriesString = buf.toString(); 1141 } 1142 1143 /** The root directories. */ 1144 private List<File> _directories = new LinkedList<File>(); 1145 1146 /** The root directories separated by File.pathSeparator character. */ 1147 private String _directoriesString = ""; 1148 } 1149 1150}