org.linkedin.groovy.util.io.GroovyIOUtils.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.linkedin.util-groovy Show documentation
Show all versions of org.linkedin.util-groovy Show documentation
Set of utility classes used by other LinkedIn open source projects
The newest version!
/*
* Copyright 2010-2010 LinkedIn, Inc
* Portions Copyright (c) 2011 Yan Pujante
*
* 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 org.linkedin.groovy.util.io
import org.linkedin.util.io.resource.Resource
import org.linkedin.util.io.IOUtils
import org.linkedin.util.io.PathUtils
import org.linkedin.groovy.util.net.GroovyNetUtils
import org.linkedin.groovy.util.ant.AntUtils
import org.linkedin.groovy.util.lang.GroovyLangUtils
/**
* IO related utilities
*
* @author [email protected]
*/
class GroovyIOUtils extends IOUtils
{
/**
* returns a file... handles File
, URI, URL, string, null
*/
static File toFile(s)
{
toFile(s, null)
}
/**
* returns a file... handles File
, URI, URL, string, null
* @param tempFolder if the file is not local, where to store it temporarilly (null
* means standard java vm temp folder)
*/
static File toFile(s, File tempFolder)
{
(File) toFileWithTempStatus(s, tempFolder).file
}
/**
* handles File
, URI, URL, string, null
* returns a map with file
and tempStatus
(boolean
)
* @param tempFolder if the file is not local, where to store it temporarilly (null
* means standard java vm temp folder)
*/
static Map toFileWithTempStatus(s, File tempFolder)
{
if(s == null)
return [file: null, tempStatus: false]
if(s instanceof File)
return [file: s, tempStatus: false]
if(s instanceof Resource)
return [file: s.file, tempStatus: false]
if(s instanceof String || s instanceof GString)
{
def uri
try
{
uri = new URI(s)
}
catch(URISyntaxException e)
{
// ok will handle below
}
if(!uri?.scheme)
{
return [file: new File(s), tempStatus: false]
}
}
URI uri = GroovyNetUtils.toURI(s)
if(uri.scheme == 'file')
return [file: new File(uri.path), tempStatus: false]
// this is not a local file => make it local...
File tempFile = AntUtils.tempFile(destdir: tempFolder, prefix: 'toFile')
tempFile.withOutputStream { out ->
uri.toURL().withInputStream { ins ->
out << ins
}
}
return [file: tempFile, tempStatus: true]
}
/**
* Will convert s
into a File
object (if possible) and call
* the closure with it. Once the closure is over, if the file was not local (and as a result was
* copied locally for the duration of the closure) it will be deleted. As a result the closure
* should *not* store the File
object around for later use or assume that the
* underlying file will still exist at a later point. Use {@link #toFile(Object)} instead if
* this is what you want to do.
*
* @param s handles File
, URI, URL, string, null
* @param closure will be called back with a File
object (or null
)
* @return whatever the closure returns
*/
static def withFile(s, Closure closure)
{
Map m = toFileWithTempStatus(s, null)
try
{
return closure(m.file)
}
finally
{
if(m.tempStatus)
IOUtils.deleteFile((File) m.file)
}
}
/**
* true
if child is really a child of parent or in other words if child
* is located in a subpath of parent (handle canonical path properly)
*/
static boolean isChild(File parent, File child)
{
if(child == null || parent == null)
return false
return child.canonicalPath.startsWith(parent.canonicalPath)
}
/**
* Ex: parent=/a/b/c child='/a/b/c/d/e'... would return d/e
* @return a file object which contains the relative part from the child to the parent.
* null
if child is not a child of parent! If child is relative the return child
*/
static File makeRelativeToParent(File parent, File child)
{
if(child == null || parent == null)
return null
if(!child.isAbsolute())
return child
def puri = parent.toURI().normalize()
def curi = child.toURI().normalize()
def relative = puri.relativize(curi)
if(relative.isAbsolute())
{
// this means that the child is not relative to the parent
return null
}
else
{
return new File(relative.path)
}
}
/**
* The difference between reader.eachLine()
and this method is that
* as soon as the closure returns false
then the iteration is stopped. There is no
* way to stop the iteration with reader.eachLine()
.
*/
static void eachLine(Reader reader, Closure closure)
{
if(!(reader instanceof BufferedReader))
{
reader = new BufferedReader(reader)
}
String line = null
while((line = reader.readLine()) != null)
{
if(!closure(line))
return
}
}
/**
* Convenient call which calls {@link #eachLine(Reader, Closure).
*/
static void eachLine(URL url, Closure closure)
{
url.withReader { reader ->
eachLine(reader, closure)
}
}
/**
* Every child resource of this resource (recursively) is being passed to the closure. If the
* closure returns true
then it will be part of the result.
*/
static def findAll(Resource resource, Closure closure)
{
def matchingResources = []
eachChildRecurse(resource) { Resource r ->
if(closure(r))
matchingResources << r
}
return matchingResources
}
/**
* The closure will be called for every child (recursively) of the provided resource
* @return the resource passed it
*/
static Resource eachChildRecurse(Resource resource, Closure closure)
{
def dirs = []
resource.list().each { Resource r ->
closure(r)
if(r.isDirectory())
dirs << r
}
dirs.each { eachChildRecurse(it, closure) }
return resource
}
/**
* Creates the directory and parents of the provided directory. Returns dir.
*/
static File mkdirs(File dir)
{
if(dir)
return AntUtils.mkdirs(dir)
else
return null
}
/**
* This convenient call takes a file you want to (over)write to and a closure. The closure is
* called back with another file in the same folder that you can write to and then rename the file
* to the one you wanted. The fact that it is in the same folder ensures that the rename should
* be quick and not really require any copy thus is less likely to fail. If the rename fails it
* throws an exception, thus ensuring that if there was an original file it won't be in a partial
* state.
*
* Note that this method automatically creates the parent folders if they don't exist.
*
* @param toFile the final file where you want your output to be
* @param closure takes a File
as a parameter that you should use
* @return whatever the closure returns
*/
static def safeOverwrite(File toFile, Closure closure)
{
safeOverwrite(toFile,
{ File f ->
new File(f.parentFile,
"++tmp.${f.name}.${UUID.randomUUID().toString()}.tmp++")},
closure)
}
/**
* This variant takes a tempFileFactory
if you want to control precisely where
* and how the temporary file is created (note that if the tempoary file is not created in the
* same folder as toFile
then the rename operation may actually be a copy/delete
* instead of just a rename thus defeating the purpose of this method!)
*
* @param toFile the final file where you want your output to be
* @param tempFileFactory closure which takes toFile
as an argument and returns
* a temporary file
* @param closure takes a File
as a parameter that you should use
* @return whatever the closure returns
*/
static def safeOverwrite(File toFile, Closure tempFileFactory, Closure closure)
{
if(toFile == null)
return null
mkdirs(toFile.parentFile)
File newFile = tempFileFactory(toFile)
try
{
def res = closure(newFile)
if(newFile.exists() && !newFile.renameTo(toFile))
{
// somehow the rename operation did not work => delete new file (will happen in the finally)
// and throw an exception thus effectively leaving the file system in the same state as
// when the method was called
throw new IOException("Unable to rename ${newFile} to ${toFile}")
}
return res
}
finally
{
// always deleting the newFile in the finally ensuring that the filesystem remain in
// the same state as when enterred
GroovyLangUtils.noExceptionWithMessage(toFile) {
deleteFile(newFile)
}
}
}
/**
* Fetches the file pointed to by the location. The location can be File
,
* a String
or URI
and must contain a scheme. Example of locations:
* http://locahost:8080/file.txt'
, file:/tmp/file.txt
,
* ivy:/org.linkedin/util-core/1.0.0
.
*
* @param location where is the content you want to retrieve locally
* @param destination where to store the content locally
* @return destination
*/
static File fetchContent(location, File destination) throws IOException
{
URI uri = GroovyNetUtils.toURI(location)
if(uri == null)
throw new FileNotFoundException(location.toString())
// See http://download.oracle.com/javase/1.4.2/docs/api/java/net/URI.html#getUserInfo()
def params = [src: uri, dest: destination]
// Extract the user:pass if it exists
def userInfo = uri.userInfo
if(userInfo)
{
userInfo = userInfo.split(":")
if(userInfo.length == 2)
{
params.username = userInfo[0]
params.password = userInfo[1]
}
}
try
{
AntUtils.withBuilder { ant ->
ant.get(params)
}
}
catch(IOException e)
{
throw e
}
catch(Exception e)
{
IOException ioe = new FileNotFoundException(location?.toString())
ioe.initCause(e)
throw ioe
}
return destination
}
/**
* Returns the content of the location as a String
*
* @throws IOException if the file is not reachable
*/
static String cat(location) throws IOException
{
File tempFile = File.createTempFile('GroovyIOUtils.cat', '.txt')
try
{
fetchContent(location, tempFile)
return tempFile.text
}
finally
{
tempFile.delete()
}
}
protected GroovyIOUtils()
{
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy