diff --git a/core/build.gradle b/core/build.gradle index ac7f2ead..3cf77601 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -55,6 +55,7 @@ dependencies { // groovy "org.codehaus.groovy:groovy-ant:${project.ext.gaelykGroovyVersion}" // for groovy-doc only compile "com.google.appengine:appengine-api-1.0-sdk:${project.ext.gaelykAppEngineVersion}" compile "com.google.appengine:appengine-api-labs:${project.ext.gaelykAppEngineVersion}" + compile "com.google.appengine.tools:appengine-gcs-client:0.5" compile 'javax.servlet:servlet-api:2.5' // groovy "org.codehaus.groovy:groovy-test:${project.ext.gaelykGroovyVersion}" // for tests only compile 'javax.servlet.jsp:jsp-api:2.2' diff --git a/core/src/main/groovyx/gaelyk/extensions/FilesExtensions.java b/core/src/main/groovyx/gaelyk/extensions/FilesExtensions.java new file mode 100644 index 00000000..2db99dbf --- /dev/null +++ b/core/src/main/groovyx/gaelyk/extensions/FilesExtensions.java @@ -0,0 +1,268 @@ +/* + * Copyright 2009-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package groovyx.gaelyk.extensions; + +import groovy.lang.Closure; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.nio.channels.Channels; +import java.util.HashMap; +import java.util.Map; + +import org.codehaus.groovy.runtime.IOGroovyMethods; + +import com.google.appengine.api.blobstore.BlobKey; +import com.google.appengine.tools.cloudstorage.GcsFileOptions; +import com.google.appengine.tools.cloudstorage.GcsFilename; +import com.google.appengine.tools.cloudstorage.GcsInputChannel; +import com.google.appengine.tools.cloudstorage.GcsOutputChannel; +import com.google.appengine.tools.cloudstorage.GcsServiceFactory; +import com.google.appengine.api.blobstore.BlobstoreServiceFactory; + +/** + * Google Cloud Storage extension methods + * + * @author Guillaume Laforge + * @author Scott Murphy + */ +public class FilesExtensions { + + /** + * Method creating a writer for the GcsFilename, writing textual content to it, and closes it when done. + * + *
+ * def file = new GcsFilename("mybucket", "hello.txt")
+ *
+ * // with default options
+ * file.withWriter { writer ->
+ * writer << "some content"
+ * }
+ *
+ *
+ *
+ * @param file the GcsFilename to write to
+ * @param closure the closure with the writer as parameter
+ * @return the original file, for chaining purpose
+ * @throws IOException
+ */
+ public static GcsFilename withWriter(GcsFilename file, Closure> closure) throws IOException {
+ return withWriter(file, new HashMap
+ * def file = new GcsFilename("mybucket", "hello.txt")
+ *
+ * // with default options
+ * file.withWriter { writer ->
+ * writer << "some content"
+ * }
+ *
+ * // with specific options:
+ * file.withWriter(new GcsFileOptions.Builder().mimeType("text/plain")) { writer ->
+ * writer << "some content
+ * }
+ *
+ *
+ * @param file the GcsFilename to write to
+ * @param options an optional map containing three possible keys:
+ * encoding (a String, the encoding to be used for the writer -- UTF8 by default),
+ * options GcsFileOptions,
+ * @param closure the closure with the writer as parameter
+ * @return the original file, for chaining purpose
+ * @throws IOException
+ */
+ public static GcsFilename withWriter(GcsFilename file, Map
+ * def file = new GcsFilename("mybucket", "hello.txt")
+ *
+ * // with default options
+ * file.withOutputStream { stream ->
+ * stream << "some content".bytes
+ * }
+ *
+ *
+ *
+ * @param file the GcsFilename to write to
+ * @param closure the closure with the output stream as parameter
+ * @return the original file, for chaining purpose
+ * @throws IOException
+ */
+ public static GcsFilename withOutputStream(GcsFilename file, Closure> closure) throws IOException {
+ return withOutputStream(file, new HashMap
+ * def file = new GcsFilename("mybucket", "hello.txt")
+ *
+ * // with specific options:
+ * file.withOutputStream(locked: true, finalize: false) { writer ->
+ * stream << "some content".bytes
+ * }
+ *
+ *
+ * @param file the GcsFilename to write to
+ * @param options an optional map containing three possible keys:
+ * options GcsFileOptions,
+ * @param closure the closure with the output stream as parameter
+ * @return the original file, for chaining purpose
+ * @throws IOException
+ */
+ public static GcsFilename withOutputStream(GcsFilename file, Map
+ * def file = new GcsFilename("mybucket", someStringPath)
+ *
+ * // with default options
+ * file.withReader { reader ->
+ * log.info reader.text
+ * }
+ *
+ *
+ *
+ * @param file the GcsFilename to read from
+ * @param closure the closure with the reader as parameter
+ * @return the original file, for chaining purpose
+ * @throws IOException
+ */
+ public static GcsFilename withReader(GcsFilename file, Closure> closure) throws IOException {
+ return withReader(file, new HashMap
+ * def file = new GcsFilename("mybucket", someStringPath)
+ *
+ * // with specific options:
+ * file.withReader(encoding: "US-ASCII") { reader ->
+ * log.info reader.text
+ * }
+ *
+ *
+ * @param file the GcsFilename to read from
+ * @param options an optional map containing two possible keys:
+ * encoding (a String, the encoding to be used for the reader -- UTF8 by default),
+ * @param closure the closure with the reader as parameter
+ * @return the original file, for chaining purpose
+ * @throws IOException
+ */
+ public static GcsFilename withReader(GcsFilename file, Map
+ * def file = new GcsFilename("mybucket", someStringPath)
+ *
+ * // with default options
+ * file.withInputStream { stream ->
+ * // read from the buffered input stream
+ * }
+ *
+ *
+ *
+ * @param file the GcsFilename to read from
+ * @param closure the closure with the input stream as parameter
+ * @return the original file, for chaining purpose
+ * @throws IOException
+ */
+ public static GcsFilename withInputStream(GcsFilename file, Closure> closure) throws IOException {
+ GcsInputChannel readChannel = GcsServiceFactory.createGcsService().openReadChannel(file, 0);
+ BufferedInputStream stream = new BufferedInputStream(Channels.newInputStream(readChannel));
+
+ IOGroovyMethods.withStream(stream, closure);
+ readChannel.close();
+
+ return file;
+ }
+
+ /**
+ * Delete an GcsFilename file from the blobstore.
+ *
+ * @param file the file to delete
+ * @throws IOException
+ */
+ public static void delete(GcsFilename file) throws IOException {
+ GcsServiceFactory.createGcsService().delete(file);
+ }
+
+ /**
+ * Retrieves the blob key associated with an App Engine file.
+ *
+ * def file = new GcsFilename("mybucket", someStringPath)
+ * def key = file.blobKey
+ *
+ *
+ * @param file the file to get the blob key of
+ * @return the blob key associated with the GcsFilename
+ */
+ public static BlobKey getBlobKey(GcsFilename file) {
+ return BlobstoreServiceFactory.getBlobstoreService().createGsBlobKey("/gs/"+file.getBucketName()+"/"+file.getObjectName());
+ }
+
+}
diff --git a/core/src/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule b/core/src/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
index b8dd9edf..f83ad44f 100644
--- a/core/src/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
+++ b/core/src/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
@@ -1,4 +1,4 @@
moduleName=gaelyk-extension-module
moduleVersion=2.1.2
-extensionClasses=groovy.servlet.ServletCategory,groovyx.gaelyk.extensions.ServletExtensions,groovyx.gaelyk.extensions.DatastoreExtensions,groovyx.gaelyk.extensions.MemcacheExtensions,groovyx.gaelyk.extensions.BlobstoreExtensions,groovyx.gaelyk.extensions.MailExtensions,groovyx.gaelyk.extensions.TaskQueueExtensions,groovyx.gaelyk.extensions.SearchExtensions,groovyx.gaelyk.extensions.UrlFetchExtensions,groovyx.gaelyk.extensions.ChannelExtensions,groovyx.gaelyk.extensions.CapabilitiesExtensions,groovyx.gaelyk.extensions.XmppExtensions,groovyx.gaelyk.extensions.ImageExtensions,groovyx.gaelyk.extensions.BackendExtensions,groovyx.gaelyk.extensions.MiscExtensions,groovyx.gaelyk.extensions.ExpirationTimeExtensionMethods
+extensionClasses=groovy.servlet.ServletCategory,groovyx.gaelyk.extensions.ServletExtensions,groovyx.gaelyk.extensions.DatastoreExtensions,groovyx.gaelyk.extensions.MemcacheExtensions,groovyx.gaelyk.extensions.BlobstoreExtensions,groovyx.gaelyk.extensions.FilesExtensions,groovyx.gaelyk.extensions.MailExtensions,groovyx.gaelyk.extensions.TaskQueueExtensions,groovyx.gaelyk.extensions.SearchExtensions,groovyx.gaelyk.extensions.UrlFetchExtensions,groovyx.gaelyk.extensions.ChannelExtensions,groovyx.gaelyk.extensions.CapabilitiesExtensions,groovyx.gaelyk.extensions.XmppExtensions,groovyx.gaelyk.extensions.ImageExtensions,groovyx.gaelyk.extensions.BackendExtensions,groovyx.gaelyk.extensions.MiscExtensions,groovyx.gaelyk.extensions.ExpirationTimeExtensionMethods
staticExtensionClasses=groovyx.gaelyk.extensions.NamespaceStaticExtensions