001/* 002 * $URL: $ 003 * $Author: $ 004 * $Revision: $ 005 * $Date: $ 006 */ 007/* 008 * 009 * Written by Tom Gutwin - WebARTS Design. 010 * Copyright (C) 2013 WebARTS Design, North Vancouver Canada 011 * http://www.webarts.bc.ca 012 * 013 * This program is free software; you can redistribute it and/or modify 014 * it under the terms of the GNU General Public License as published by 015 * the Free Software Foundation; either version 2 of the License, or 016 * (at your option) any later version. 017 * 018 * This program is distributed in the hope that it will be useful, 019 * but WITHOUT ANY WARRANTY; without_ even the implied warranty of 020 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 021 * GNU General Public License for more details. 022 * 023 * You should have received a copy of the GNU General Public License 024 * along with this program; if not, write to the Free Software 025 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 026 */ 027package ca.bc.webarts.tools.gapi; 028 029import com.google.api.client.auth.oauth2.Credential; 030import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; 031import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; 032import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; 033import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; 034import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets.Details; 035import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; 036import com.google.api.client.googleapis.media.MediaHttpDownloader; 037import com.google.api.client.googleapis.media.MediaHttpUploader; 038import com.google.api.client.googleapis.media.MediaHttpDownloaderProgressListener; 039import com.google.api.client.googleapis.media.MediaHttpUploaderProgressListener; 040import com.google.api.client.http.FileContent; 041import com.google.api.client.http.GenericUrl; 042import com.google.api.client.http.HttpTransport; 043import com.google.api.client.json.JsonFactory; 044import com.google.api.client.json.jackson2.JacksonFactory; 045import com.google.api.client.util.Preconditions; 046import com.google.api.client.util.store.DataStoreFactory; 047import com.google.api.client.util.store.FileDataStoreFactory; 048import com.google.api.services.drive.Drive; 049import com.google.api.services.drive.DriveScopes; 050import com.google.api.services.drive.model.File; 051 052import java.io.FileOutputStream; 053import java.io.IOException; 054import java.io.InputStreamReader; 055import java.io.OutputStream; 056import java.security.GeneralSecurityException; 057import java.text.NumberFormat; 058import java.util.Collections; 059import java.util.List; 060import java.util.Properties; 061import java.util.Vector; 062 063import com.aftexsw.util.bzip.BZip; 064 065/** 066 * A simple abstract application that sends and receives files against the Drive API v2 and authenticates using OAuth 2.0. 067 * <ul> 068 * <li><a href="https://developers.google.com/api-client-library/java/apis/drive/v2"> 069 * https://developers.google.com/api-client-library/java/apis/drive/v2</a></li> 070 * <li><a href="https://google-developers.appspot.com/drive/quickstart-java"> 071 * Java Drive Quickstart</a></li> 072 * <li><a href="https://developers.google.com/accounts/docs/OAuth2"> 073 * https://developers.google.com/accounts/docs/OAuth2</a></li></ul> 074 * See also the javadoc 075 * <a href="https://developers.google.com/resources/api-libraries/documentation/drive/v2/java/latest/"> 076 * https://developers.google.com/resources/api-libraries/documentation/drive/v2/java/latest/</a><br /><br /> 077 * <b>You will have to get your own OAuth registraion secrets</b> and enter them in the appropriate class varables before use. 078 * See the OAuth link above for instructions.<br /><hr /> 079 * It provides the basic authentication and send and receive methods that an extending class can use and extend.<br /><br /> 080 * <b>all you need to do is:</b> 081 * <pre> 082 * class GDriveCrypter extends GDriver 083 * { 084 * // .... whatever 085 * public static void main(String[] args) 086 * { 087 * String fileNameToUpload = "someFile.txt"; 088 * GDriveCrypter instance = new GDriveCrypter(fileNameToUpload); // super is provided by GDriver 089 * //Authorize GDrive 090 * instance.initDrive(instance.authorize()); // provided by GDriver 091 * 092 * // Encrypt 093 * String encryptedFilename = instance.encrypt(instance.getloadFilePath()); 094 * if (encryptedFilename!=null && !encryptedFilename.equals("")) 095 * { 096 * // Send File to GDrive 097 * instance.setloadFilePath(encryptedFilename); // provided by GDriver 098 * File uploadedFile = instance.uploadFile(false); // provided by GDriver 099 * } 100 * } 101 * } 102 * </pre> 103 * 104 **/ 105public abstract class GDriver 106{ 107 /** A holder for this clients System File Separator. */ 108 public final static String SYSTEM_FILE_SEPERATOR = java.io.File.separator; 109 110 /** A holder for this clients System line termination separator. */ 111 public final static String SYSTEM_LINE_SEPERATOR = 112 System.getProperty("line.separator"); 113 /** Class constant holding an application name for use by logger or whatever. **/ 114 protected static final String APPLICATION_NAME = "WebARTSDesign-GDriver/0.1"; 115 /** Class constant holding a default location for downloading into. **/ 116 protected static final String DEFAULT_DOWNLOAD_DIR = "."; 117 /** Class constant holding a default filename to download into. **/ 118 protected static final String DEFAULT_LOAD_FILENAME = "./gDriveDocument.txt"; 119 /** Class constant holding a defaultdatastore directory. **/ 120 protected static final String DEFAULT_DATASTORE_DIR = System.getProperty("user.home")+ 121 SYSTEM_FILE_SEPERATOR+ 122 ".store"+ 123 SYSTEM_FILE_SEPERATOR+ 124 "GDrive"; 125 126 /** The VM classpath (used in some methods).. */ 127 public static String CLASSPATH = System.getProperty("class.path"); 128 129 /** The users home ditrectory. */ 130 public static String USERHOME = System.getProperty("user.home"); 131 132 /** The users pwd ditrectory. */ 133 public static String USERDIR = System.getProperty("user.dir"); 134 135 /** A holder This classes name (used when logging). */ 136 protected static String CLASSNAME ; 137 138 139 /** Classvar holding the GoogleClientSecrets for this and extended classes.**/ 140 protected static GoogleClientSecrets clientSecrets_ = null; //new GoogleClientSecrets(); 141 142 /** Classvar holding the GDrive logine secrets specific to your login ID**/ 143 protected String loadFilePath_ = DEFAULT_LOAD_FILENAME; 144 /** Classvar holding the directory to download into.**/ 145 protected String dirForDownload_ = DEFAULT_DOWNLOAD_DIR; 146 protected java.io.File loadFile_ = new java.io.File(loadFilePath_); 147 148 /** Directory to store user credentials. */ 149 protected java.io.File dataStoreDir_ = null; 150 151 /** 152 * Global instance of the {@link DataStoreFactory}. The best practice is to make it a single 153 * globally shared instance across your application. 154 */ 155 protected FileDataStoreFactory dataStoreFactory_ = null; 156 157 /** Class instance of the HTTP transport. */ 158 protected HttpTransport httpTransport_ = null; 159 160 /** Class instance of the JSON factory. */ 161 protected JsonFactory jsonFactory_ = null; 162 163 /** Class Drive API client. */ 164 protected Drive drive_ = null; 165 166 167 /** Default constructor that gets all the basic class settimgs setup and gives you a class instance to do the work. 168 * Then all you need to do is: 169 * <pre> 170 * GDriver instance = new GDriveCrypter(); // uses the DEFAULT_LOAD_FILENAME 171 * //Authorize GDrive 172 * instance.initDrive(instance.authorize()); 173 * 174 * // Encrypt 175 * String encryptedFilename = instance.encrypt(instance.getloadFilePath()); 176 * if (encryptedFilename!=null && !encryptedFilename.equals("")) 177 * { 178 * // Send File to GDrive 179 * instance.setloadFilePath(encryptedFilename); 180 * File uploadedFile = instance.uploadFile(false); 181 * } 182 * </pre> 183 * 184 **/ 185 public GDriver() throws GeneralSecurityException, IOException 186 { 187 loadFilePath_ = DEFAULT_LOAD_FILENAME; 188 dirForDownload_ = DEFAULT_DOWNLOAD_DIR; 189 loadFile_ = new java.io.File(loadFilePath_); 190 dataStoreDir_ = new java.io.File(DEFAULT_DATASTORE_DIR); 191 httpTransport_ = GoogleNetHttpTransport.newTrustedTransport(); 192 193 dataStoreFactory_ = new FileDataStoreFactory(dataStoreDir_); 194 jsonFactory_ = JacksonFactory.getDefaultInstance(); 195 } 196 197 198 /** Constructor that gets all the basic class settimgs setup, setsup the filename to upload and gives you a class instance to do the work. 199 * Then all you need to do is: 200 * <pre> 201 * String fileNameToUpload = "someFile.txt"; 202 * GDriveCrypter instance = new GDriveCrypter(fileNameToUpload); 203 204 * //Authorize GDrive 205 * instance.initDrive(instance.authorize()); 206 * 207 * // Encrypt 208 * String encryptedFilename = instance.encrypt(instance.getloadFilePath()); 209 * if (encryptedFilename!=null && !encryptedFilename.equals("")) 210 * { 211 * // Send File to GDrive 212 * instance.setloadFilePath(encryptedFilename); 213 * File uploadedFile = instance.uploadFile(false); 214 * } 215 * </pre> 216 * 217 **/ 218 public GDriver(String filenameToLoad) throws GeneralSecurityException, IOException 219 { 220 loadFilePath_ = filenameToLoad; 221 dirForDownload_ = DEFAULT_DOWNLOAD_DIR; 222 loadFile_ = new java.io.File(loadFilePath_); 223 dataStoreDir_ = new java.io.File(DEFAULT_DATASTORE_DIR); 224 httpTransport_ = GoogleNetHttpTransport.newTrustedTransport(); 225 226 dataStoreFactory_ = new FileDataStoreFactory(dataStoreDir_); 227 jsonFactory_ = JacksonFactory.getDefaultInstance(); 228 } 229 230 /** Set the filename path for the file that will be uploaded. **/ 231 protected void setloadFilePath(String path) 232 { 233 loadFilePath_ = path; 234 loadFile_ = new java.io.File(loadFilePath_); 235 } 236 237 238 /** get the filename path for the file that will be uploaded. **/ 239 protected String getloadFilePath() { return loadFilePath_; } 240 241 242 /** Initializes, with the passed in credentials, the Drive class instance -drive_, that gets used for the target upload. 243 * Use the authorize method to get yuor credentials. 244 * @param cred is the Credential to use for the upload. 245 **/ 246 protected void initDrive(Credential cred) 247 { 248 // set up the class Drive instance 249 drive_ = new Drive.Builder(httpTransport_, 250 jsonFactory_, 251 cred 252 ).setApplicationName(APPLICATION_NAME).build(); 253 } 254 255 /** Loads the Google secrets / api usage authentication properties for this class. **/ 256 protected GoogleClientSecrets initSecrets() throws IOException 257 { 258 boolean readFromJSON = true; 259 260 if(readFromJSON) 261 { 262 java.io.InputStream is = GDriveCrypter.class.getResourceAsStream("gapi.secretsFile"); 263 clientSecrets_ = GoogleClientSecrets.load( jsonFactory_, 264 new InputStreamReader(is) 265 ); 266 } 267 else 268 { 269 //GoogleClientSecrets.Details secretDetails = clientSecrets_.getDetails(); 270 GoogleClientSecrets.Details secretDetails = new GoogleClientSecrets.Details(); 271 272 secretDetails.setClientId("gapi.clientId"); 273 secretDetails.setAuthUri("gapi.authUri"); 274 secretDetails.setClientSecret("gapi.clientSecret"); 275 secretDetails.setTokenUri("gapi.tokenUri"); 276 Vector <String> v = new <String> Vector(); 277 v.add("gapi.redirectUris.1"); 278 v.add("gapi.redirectUris.2"); 279 secretDetails.setRedirectUris(v); 280 secretDetails.set("auth_provider_x509_cert_url","https://www.googleapis.com/oauth2/v1/certs"); 281 secretDetails.set("client_x509_cert_url",""); 282 secretDetails.set("client_email",""); 283 clientSecrets_.setInstalled(secretDetails); 284 } 285 return clientSecrets_; 286 } 287 288 289 /** Authorizes the installed application to access user's protected data. */ 290 protected Credential authorize() throws Exception 291 { 292 // load client secrets 293 initSecrets(); 294 295 /* 296 if (clientSecrets_.getDetails().getClientId().startsWith("Enter") || 297 clientSecrets_.getDetails().getClientSecret().startsWith("Enter ")) 298 { 299 System.out.println ("Enter Client ID and Secret from https://code.google.com/apis/console/?api=drive " + "into drive-cmdline-sample/src/main/resources/client_secrets.json"); 300 System.exit(1); 301 } 302 */ 303 304 // set up authorization code flow 305 GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( httpTransport_, 306 jsonFactory_, 307 clientSecrets_, 308 Collections.singleton(DriveScopes.DRIVE_FILE) 309 ).setDataStoreFactory(dataStoreFactory_).build(); 310 // authorize 311 return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user"); 312 } 313 314 315 /** recursively creates parent dirs and the requested dir **/ 316 public static void ensureFolderExists(java.io.File folder) 317 { 318 if ((folder != null) && !(folder.isDirectory() )) 319 { 320 ensureFolderExists(folder.getParentFile()); 321 boolean suc = folder.mkdir(); 322 } 323 } 324 325 326 /** UnBzips the passed file (that must have a '.bz2' extension) to its same filename 327 * in the same dir without the '.bz2' extension. 328 * @param fo The File object to unzip - must have a '.bz2' extension. 329 **/ 330 public static long unBzip2It(java.io.File fo) 331 { 332 final String methodName = CLASSNAME + ".bzip2It(File)"; 333 //logger_.debug("Entering " + methodName); 334 long retVal = 0L; 335 336 String fullFilename = fo.getAbsolutePath().trim(); 337 java.io.File outFile = null; 338 if (fullFilename.lastIndexOf(".bz2")>-1) 339 { 340 String unzippedFilename = fullFilename.substring(0, fullFilename.lastIndexOf(".bz2")); 341 outFile = new java.io.File(unzippedFilename); 342 BZip.decompress(fo, outFile); 343 } 344 retVal = outFile.length(); 345 346 //logger_.debug("Exiting " + methodName); 347 return retVal; 348 } 349 350 351 /** 352 * Wrapper method to accept a dir or individual file in the passed in File 353 * object AND then calls the method that writes the passed file/dir to this 354 * MultiZip instances zip output stream. This is a recursive function. If the 355 * passed File object is a file then it calls the function to write to zip 356 * output stream. If it is a directory it gets the list of file objects in 357 * the child directory and recurses on them. 358 * 359 * @param fo The File object to zip up (can be an actual file or dir). 360 * @return the new length in bytes of the zipped file 361 */ 362 public static long bzip2It(java.io.File fo) 363 { 364 final String methodName = CLASSNAME + ".bzip2It(File)"; 365 //logger_.debug("Entering " + methodName); 366 long retVal = 0L; 367 368 String fullFilename = fo.getAbsolutePath().trim(); 369 if (!fo.isDirectory()) 370 { 371 //logger_.info("Bzip2ing "+fullFilename); 372 StringBuffer outLocation = new StringBuffer(); 373 // zip it in place 374 //logger_.info("Bzip2ing "+fullFilename); 375 java.io.File outFile = new java.io.File(fullFilename + ".bz2"); 376 377 ensureFolderExists(outFile.getParentFile()); 378 try 379 { 380 BZip.compress(fo, outFile, 9); 381 //compressedFilenames_.add(fullFilename + ".bz2"); 382 retVal += outFile.length(); 383 } 384 catch (java.lang.ArithmeticException mathEx) 385 { 386 //logger_.error("BZip2 ERROR: " + fullFilename); 387 //logger_.error(mathEx.getMessage()); 388 } 389 } 390 else 391 { 392 //logger_.info("Recursing " + fo.getPath()); 393 String srcFileNames[] = fo.list(); 394 for (int i = 0; i < srcFileNames.length; i++) 395 { 396 retVal += bzip2It(new java.io.File(fullFilename + java.io.File.separator + 397 srcFileNames[i])); 398 } 399 } 400 401 //logger_.debug("Exiting " + methodName); 402 return retVal; 403 } 404 405 406 /** Uploads a file using either resumable or direct media upload. */ 407 protected File uploadFile(boolean useDirectUpload) throws IOException 408 { 409 File retVal = null; 410 if (drive_!=null) 411 { 412 File fileMetadata = new File(); 413 fileMetadata.setTitle(loadFile_.getName()); 414 415 //FileContent mediaContent = new FileContent("image/jpeg", loadFile_); 416 FileContent mediaContent = new FileContent("text/plain", loadFile_); 417 418 Drive.Files.Insert insert = drive_.files().insert(fileMetadata, mediaContent); 419 MediaHttpUploader uploader = insert.getMediaHttpUploader(); 420 uploader.setDirectUploadEnabled(useDirectUpload); 421 uploader.setProgressListener(new FileUploadProgressListener()); 422 retVal = insert.execute(); 423 } 424 return retVal; 425 } 426 427 428 /** Updates the name of the uploaded file to have a "drivetest-" prefix. */ 429 private File updateFileWithTestSuffix(String id) throws IOException 430 { 431 File retVal = null; 432 if (drive_!=null) 433 { 434 File fileMetadata = new File(); 435 fileMetadata.setTitle("drivetest-" + loadFile_.getName()); 436 437 Drive.Files.Update update = drive_.files().update(id, fileMetadata); 438 retVal = update.execute(); 439 } 440 return retVal; 441 } 442 443 444 /** Downloads a file using either resumable or direct media download. */ 445 private void downloadFile(boolean useDirectDownload, java.io.File fileToDownload) throws IOException 446 { 447 if (drive_!=null) 448 { 449 // create parent directory (if necessary) 450 java.io.File parentDir = new java.io.File(dirForDownload_); 451 if (!parentDir.exists() && !parentDir.mkdirs()) 452 { 453 throw new IOException("Unable to create parent directory"); 454 } 455 OutputStream out = new FileOutputStream(new java.io.File(parentDir, loadFilePath_)); 456 457 MediaHttpDownloader downloader = new MediaHttpDownloader(httpTransport_, drive_.getRequestFactory().getInitializer()); 458 downloader.setDirectDownloadEnabled(useDirectDownload); 459 downloader.setProgressListener(new FileDownloadProgressListener()); 460 // downloader.download(new GenericUrl(uploadedFile.getDownloadUrl()), out); 461 } 462 } 463} 464 465 466/** 467 * Utility methods to print to the command line. 468 */ 469class View { 470 471 static void header1(String name) { 472 System.out.println(); 473 System.out.println("================== " + name + " =================="); 474 System.out.println(); 475 } 476 477 static void header2(String name) { 478 System.out.println(); 479 System.out.println("~~~~~~~~~~~~~~~~~~ " + name + " ~~~~~~~~~~~~~~~~~~"); 480 System.out.println(); 481 } 482} 483 484 485/** 486 * The File Upload Progress Listener. 487 * 488 * @author rmistry@google.com (Ravi) 489 */ 490class FileUploadProgressListener implements MediaHttpUploaderProgressListener { 491 492 @Override 493 public void progressChanged(MediaHttpUploader uploader) throws IOException { 494 switch (uploader.getUploadState()) { 495 case INITIATION_STARTED: 496 View.header2("Upload Initiation has started."); 497 break; 498 case INITIATION_COMPLETE: 499 View.header2("Upload Initiation is Complete."); 500 break; 501 case MEDIA_IN_PROGRESS: 502 View.header2("Upload is In Progress: " 503 + NumberFormat.getPercentInstance().format(uploader.getProgress())); 504 break; 505 case MEDIA_COMPLETE: 506 View.header2("Upload is Complete!"); 507 break; 508 } 509 } 510} 511 512 513/** 514 * The File Download Progress Listener. 515 * 516 * @author rmistry@google.com (Ravi) 517 */ 518class FileDownloadProgressListener implements MediaHttpDownloaderProgressListener { 519 520 @Override 521 public void progressChanged(MediaHttpDownloader downloader) { 522 switch (downloader.getDownloadState()) { 523 case MEDIA_IN_PROGRESS: 524 View.header2("Download is in progress: " + downloader.getProgress()); 525 break; 526 case MEDIA_COMPLETE: 527 View.header2("Download is Complete!"); 528 break; 529 } 530 } 531}