001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * docs/licenses/cddl.txt 011 * or http://www.opensource.org/licenses/cddl1.php. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * docs/licenses/cddl.txt. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2011-2014 UnboundID Corp. 026 */ 027 package com.unboundid.directory.sdk.common.types; 028 029 030 import java.io.File; 031 import java.io.FileFilter; 032 import java.io.FileNotFoundException; 033 import java.io.IOException; 034 import java.util.jar.JarFile; 035 036 /** 037 * This class represents the physical state of an UnboundID Server 038 * extension bundle. All the operations are dependent upon the root directory 039 * that is specified in the constructor. 040 */ 041 public class ExtensionBundle 042 { 043 /** 044 * The name of the jar file manifest attribute that provides the extension 045 * bundle name. 046 */ 047 public static final String MANIFEST_ATTR_TITLE = 048 "Implementation-Title"; 049 050 051 052 /** 053 * The name of the jar file manifest attribute that provides the extension 054 * bundle version. 055 */ 056 public static final String MANIFEST_ATTR_VERSION = 057 "Implementation-Version"; 058 059 060 061 /** 062 * The name of the jar file manifest attribute that provides the extension 063 * bundle vendor. 064 */ 065 public static final String MANIFEST_ATTR_VENDOR = 066 "Implementation-Vendor"; 067 068 069 070 /** 071 * The name of the jar file manifest attribute that provides the extension 072 * bundle vendor ID. 073 */ 074 public static final String MANIFEST_ATTR_VENDOR_ID = 075 "Implementation-Vendor-Id"; 076 077 078 079 /** 080 * The name of the jar file manifest attribute that provides the extension 081 * bundle URL. 082 */ 083 public static final String MANIFEST_ATTR_URL = 084 "Implementation-URL"; 085 086 087 088 /** 089 * The name of the jar file manifest attribute that provides the extension 090 * bundle support contact. 091 */ 092 public static final String MANIFEST_ATTR_SUPPORT_CONTACT = 093 "Extension-Support-Contact"; 094 095 096 097 /** 098 * The name of the jar file manifest attribute that provides the server SDK 099 * version number. 100 */ 101 public static final String MANIFEST_ATTR_SERVER_SDK_VERSION = 102 "UnboundID-Server-SDK-Version"; 103 104 /** 105 * The relative path where the config files are. 106 */ 107 public static final String CONFIG_PATH_RELATIVE = "config"; 108 109 /** 110 * The relative path where all the libraries (jar files) are. 111 */ 112 public static final String LIBRARIES_PATH_RELATIVE = "lib"; 113 114 /** 115 * The relative path where all the documentation files are. 116 */ 117 public static final String DOCUMENTATION_PATH_RELATIVE = "docs"; 118 119 /** 120 * The relative path where the config files are. 121 */ 122 public static final String HISTORY_PATH_RELATIVE = "history"; 123 124 /** 125 * Path to the config/update directory where update base files 126 * are stored. 127 */ 128 public static final String UPDATE_PATH = "update"; 129 130 /** 131 * File written to history/[timestamp]-[version] to document whatever 132 * modifications were made to the file system during an update. 133 */ 134 public static final String UPDATE_LOG_NAME = "update.log"; 135 136 // A FileFilter for JAR files. 137 private static final FileFilter JAR_FILTER = new FileFilter() 138 { 139 140 /** 141 * Must be a Jar file. 142 */ 143 public boolean accept(final File pathname) 144 { 145 if (!pathname.isFile()) 146 { 147 return false; 148 } 149 150 String name = pathname.getName(); 151 return name.endsWith(".jar"); 152 } 153 154 }; 155 156 private final File extensionBundleDir; 157 private final File extensionJarFile; 158 private final java.util.jar.Manifest manifest; 159 private final String vendorId; 160 private final String title; 161 private final String version; 162 private final String bundleId; 163 private final String sdkVersion; 164 165 /** 166 * Creates a new instance from a root directory specified as a File. 167 * 168 * @param extensionBundleDir root directory of this extension bundle. 169 * @throws IOException if a error occurs while opening the bundle. 170 * @throws IllegalArgumentException if a error occurs while loading the 171 * extension JAR. 172 */ 173 public ExtensionBundle(final File extensionBundleDir) throws IOException, 174 IllegalArgumentException { 175 File jarFile = null; 176 java.util.jar.Manifest jarManifest = null; 177 String extensionVendorId = null; 178 String extensionTitle = null; 179 String extensionVersion = null; 180 String extensionSdkVersion = null; 181 boolean isValid = false; 182 for(File file : extensionBundleDir.listFiles(JAR_FILTER)) 183 { 184 jarFile = file; 185 JarFile jar = new JarFile(file); 186 jarManifest = jar.getManifest(); 187 extensionVendorId = jarManifest.getMainAttributes().getValue( 188 MANIFEST_ATTR_VENDOR_ID); 189 extensionTitle = jarManifest.getMainAttributes().getValue( 190 MANIFEST_ATTR_TITLE); 191 extensionVersion = jarManifest.getMainAttributes().getValue( 192 MANIFEST_ATTR_VERSION); 193 extensionSdkVersion = jarManifest.getMainAttributes().getValue( 194 MANIFEST_ATTR_SERVER_SDK_VERSION); 195 if(extensionVendorId == null || extensionVendorId.isEmpty()) 196 { 197 continue; 198 } 199 if(extensionTitle == null || extensionTitle.isEmpty()) 200 { 201 continue; 202 } 203 if(extensionVersion == null || extensionVersion.isEmpty()) 204 { 205 continue; 206 } 207 if(extensionSdkVersion == null || extensionSdkVersion.isEmpty()) 208 { 209 continue; 210 } 211 isValid = true; 212 break; 213 } 214 if(!isValid) 215 { 216 throw new FileNotFoundException("Cannot find a valid extension jar in " + 217 "bundle"); 218 } 219 220 checkCompatibility(extensionSdkVersion); 221 222 this.extensionBundleDir = extensionBundleDir; 223 this.extensionJarFile = jarFile; 224 this.manifest = jarManifest; 225 this.vendorId = extensionVendorId; 226 this.title = extensionTitle; 227 this.version = extensionVersion; 228 this.sdkVersion = extensionSdkVersion; 229 this.bundleId = extensionVendorId + "." + extensionTitle; 230 } 231 232 /** 233 * Retrieves the title of this extension bundle. 234 * 235 * @return The title of this extension bundle. 236 */ 237 public String getTitle() 238 { 239 return title; 240 } 241 242 /** 243 * Retrieves the vendor name of this extension bundle. 244 * 245 * @return The vendor name of this extension bundle. 246 */ 247 public String getVendor() 248 { 249 return manifest.getMainAttributes().getValue(MANIFEST_ATTR_VENDOR); 250 } 251 252 /** 253 * Retrieves the vendor ID of this extension bundle. 254 * 255 * @return The vendor ID of this extension bundle. 256 */ 257 public String getVendorId() 258 { 259 return vendorId; 260 } 261 262 /** 263 * Retrieves the version string of this extension bundle. 264 * 265 * @return The version string of this extension bundle. 266 */ 267 public String getVersion() 268 { 269 return version; 270 } 271 272 /** 273 * Get the server SDK version number the extensions where built against. 274 * 275 * @return The server SDK version number the extensions where built against. 276 */ 277 public String getServerSDKVersion() 278 { 279 return sdkVersion; 280 } 281 282 283 /** 284 * Get the URL of the extension bundle. 285 * 286 * @return The URL of the extension bundle. 287 */ 288 public String getUrl() 289 { 290 return manifest.getMainAttributes().getValue(MANIFEST_ATTR_URL); 291 } 292 293 /** 294 * Get the support contact for this extension bundle. 295 * 296 * @return The support contact for this extension bundle. 297 */ 298 public String getSupportContact() 299 { 300 return manifest.getMainAttributes().getValue(MANIFEST_ATTR_SUPPORT_CONTACT); 301 } 302 303 /** 304 * Get the ID string that could be used to uniquely identify this bundle. 305 * 306 * @return The ID string that could be used to uniquely identify this bundle. 307 */ 308 public String getBundleId() 309 { 310 return bundleId; 311 } 312 313 /** 314 * Get the root directory of this extension bundle. 315 * 316 * @return The root directory of this extension bundle. 317 */ 318 public File getExtensionBundleDir() 319 { 320 return extensionBundleDir; 321 } 322 323 /** 324 * Get the extension jar file in the bundle. 325 * 326 * @return The extension jar file in the bundle. 327 */ 328 public File getExtensionJarFile() 329 { 330 return extensionJarFile; 331 } 332 333 /** 334 * Get the configuration directory for this extension bundle. 335 * 336 * @return The configuration directory for this extension bundle. 337 */ 338 public File getConfigDir() 339 { 340 return new File(extensionBundleDir, CONFIG_PATH_RELATIVE); 341 } 342 343 /** 344 * Get the update directory for this extension bundle. 345 * 346 * @return The update directory for this extension bundle. 347 */ 348 public File getConfigUpdateDir() 349 { 350 return new File(getConfigDir(), UPDATE_PATH); 351 } 352 353 /** 354 * Get the lib directory for this extension bundle. 355 * 356 * @return The lib directory for this extension bundle. 357 */ 358 public File getLibrariesDir() 359 { 360 return new File(extensionBundleDir, LIBRARIES_PATH_RELATIVE); 361 } 362 363 /** 364 * Get the documentation directory for this extension bundle. 365 * 366 * @return The documentation directory for this extension bundle. 367 */ 368 public File getDocumentationDir() 369 { 370 return new File(extensionBundleDir, DOCUMENTATION_PATH_RELATIVE); 371 } 372 373 /** 374 * Get the history directory for this extension bundle. 375 * 376 * @return The history directory for this extension bundle. 377 */ 378 public File getHistoryDir() 379 { 380 return new File(extensionBundleDir, HISTORY_PATH_RELATIVE); 381 } 382 383 /** 384 * Checks to make sure the SDK version specified in the extension JAR file 385 * is compatible with this server instance. 386 * 387 * @param serverSDKVersionString The server SDK version string. 388 * @throws IllegalArgumentException if the version string is invalid or 389 * not compatible. 390 */ 391 public static void checkCompatibility(final String serverSDKVersionString) 392 throws IllegalArgumentException 393 { 394 final int jarSDKMajor; 395 final int jarSDKMinor; 396 final int jarSDKPoint; 397 final int jarSDKPatch; 398 try 399 { 400 final String[] versionComps = serverSDKVersionString.split("\\."); 401 jarSDKMajor = Integer.parseInt(versionComps[0]); 402 jarSDKMinor = Integer.parseInt(versionComps[1]); 403 jarSDKPoint = Integer.parseInt(versionComps[2]); 404 jarSDKPatch = Integer.parseInt(versionComps[3]); 405 } 406 catch (final Exception e) 407 { 408 throw new IllegalArgumentException("Value '" + serverSDKVersionString + 409 "' for manifest attribute '" + MANIFEST_ATTR_SERVER_SDK_VERSION + 410 "' is malformed", e); 411 } 412 413 boolean acceptable = true; 414 if (jarSDKMajor > Version.MAJOR_VERSION) 415 { 416 acceptable = false; 417 } 418 else if (jarSDKMajor == Version.MAJOR_VERSION) 419 { 420 if (jarSDKMinor > Version.MINOR_VERSION) 421 { 422 acceptable = false; 423 } 424 else if (jarSDKMinor == Version.MINOR_VERSION) 425 { 426 if (jarSDKPoint > Version.POINT_VERSION) 427 { 428 acceptable = false; 429 } 430 else if (jarSDKPoint == Version.POINT_VERSION) 431 { 432 acceptable = jarSDKPatch <= Version.PATCH_VERSION; 433 } 434 } 435 } 436 437 if (! acceptable) 438 { 439 final String maxSupportedVersion = 440 Version.MAJOR_VERSION + "." + Version.MINOR_VERSION + '.' + 441 Version.POINT_VERSION + '.' + Version.PATCH_VERSION; 442 throw new IllegalArgumentException("Extensions built using " + 443 Version.PRODUCT_NAME + " version " + serverSDKVersionString + ", " + 444 "which is newer than the maximum server SDK version of " + 445 maxSupportedVersion); 446 } 447 } 448 }