/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.ignite.internal.processors.hadoop.impl.fs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.mapreduce.MRJobConfig;
import org.apache.ignite.IgniteException;
import org.apache.ignite.hadoop.fs.v1.IgniteHadoopFileSystem;
import org.apache.ignite.internal.util.GridStringBuilder;
import org.apache.ignite.internal.util.typedef.F;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.net.URI;
/**
* File system cache utility methods used by Map-Reduce tasks and jobs.
*/
public class HadoopFileSystemCacheUtils {
/**
* A common static factory method. Creates new HadoopLazyConcurrentMap.
* @return a new HadoopLazyConcurrentMap.
*/
public static HadoopLazyConcurrentMap<FsCacheKey, FileSystem> createHadoopLazyConcurrentMap() {
return new HadoopLazyConcurrentMap<>(
new HadoopLazyConcurrentMap.ValueFactory<FsCacheKey, FileSystem>() {
@Override public FileSystem createValue(FsCacheKey key) throws IOException {
try {
assert key != null;
// Explicitly disable FileSystem caching:
URI uri = key.uri();
String scheme = uri.getScheme();
// Copy the configuration to avoid altering the external object.
Configuration cfg = new Configuration(key.configuration());
String prop = HadoopFileSystemsUtils.disableFsCachePropertyName(scheme);
cfg.setBoolean(prop, true);
return FileSystem.get(uri, cfg, key.user());
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Failed to create file system due to interrupt.", e);
}
}
}
);
}
/**
* Gets non-null user name as per the Hadoop viewpoint.
* @param cfg the Hadoop job configuration, may be null.
* @return the user name, never null.
*/
private static String getMrHadoopUser(Configuration cfg) throws IOException {
String user = cfg.get(MRJobConfig.USER_NAME);
if (user == null)
user = IgniteHadoopFileSystem.getFsHadoopUser();
return user;
}
/**
* Common method to get the V1 file system in MapRed engine.
* It gets the filesystem for the user specified in the
* configuration with {@link MRJobConfig#USER_NAME} property.
* The file systems are created and cached in the given map upon first request.
*
* @param uri The file system uri.
* @param cfg The configuration.
* @param map The caching map.
* @return The file system.
* @throws IOException On error.
*/
public static FileSystem fileSystemForMrUserWithCaching(@Nullable URI uri, Configuration cfg,
HadoopLazyConcurrentMap<FsCacheKey, FileSystem> map)
throws IOException {
assert map != null;
assert cfg != null;
final String usr = getMrHadoopUser(cfg);
assert usr != null;
if (uri == null)
uri = FileSystem.getDefaultUri(cfg);
final FileSystem fs;
try {
final FsCacheKey key = new FsCacheKey(uri, usr, cfg);
fs = map.getOrCreate(key);
}
catch (IgniteException ie) {
throw new IOException(ie);
}
assert fs != null;
assert !(fs instanceof IgniteHadoopFileSystem) || F.eq(usr, ((IgniteHadoopFileSystem)fs).user());
return fs;
}
/**
* Takes Fs URI using logic similar to that used in FileSystem#get(1,2,3).
* @param uri0 The uri.
* @param cfg The cfg.
* @return Correct URI.
*/
private static URI fixUri(URI uri0, Configuration cfg) {
if (uri0 == null)
return FileSystem.getDefaultUri(cfg);
String scheme = uri0.getScheme();
String authority = uri0.getAuthority();
if (authority == null) {
URI dfltUri = FileSystem.getDefaultUri(cfg);
if (scheme == null || (scheme.equals(dfltUri.getScheme()) && dfltUri.getAuthority() != null))
return dfltUri;
}
return uri0;
}
/**
* Note that configuration is not a part of the key.
* It is used solely to initialize the first instance
* that is created for the key.
*/
public static final class FsCacheKey {
/** */
private final URI uri;
/** */
private final String usr;
/** */
private final String equalityKey;
/** */
private final Configuration cfg;
/**
* Constructor
*/
public FsCacheKey(URI uri, String usr, Configuration cfg) {
assert uri != null;
assert usr != null;
assert cfg != null;
this.uri = fixUri(uri, cfg);
this.usr = usr;
this.cfg = cfg;
this.equalityKey = createEqualityKey();
}
/**
* Creates String key used for equality and hashing.
*/
private String createEqualityKey() {
GridStringBuilder sb = new GridStringBuilder("(").a(usr).a(")@");
if (uri.getScheme() != null)
sb.a(uri.getScheme().toLowerCase());
sb.a("://");
if (uri.getAuthority() != null)
sb.a(uri.getAuthority().toLowerCase());
return sb.toString();
}
/**
* The URI.
*/
public URI uri() {
return uri;
}
/**
* The User.
*/
public String user() {
return usr;
}
/**
* The Configuration.
*/
public Configuration configuration() {
return cfg;
}
/** {@inheritDoc} */
@SuppressWarnings("SimplifiableIfStatement")
@Override public boolean equals(Object obj) {
if (obj == this)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
return equalityKey.equals(((FsCacheKey)obj).equalityKey);
}
/** {@inheritDoc} */
@Override public int hashCode() {
return equalityKey.hashCode();
}
/** {@inheritDoc} */
@Override public String toString() {
return equalityKey;
}
}
}