/*
* 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.giraph.debugger.utils;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import org.apache.giraph.utils.WritableUtils;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Writable;
import com.google.protobuf.ByteString;
import com.google.protobuf.GeneratedMessage;
/**
* Base class for all wrapper classes that wrap a protobuf.
*
* author: semihsalihoglu
*/
public abstract class BaseWrapper {
/**
* @param <U> type of the upperBound class.
* @param clazz a {@link Class} object that will be cast.
* @param upperBound another {@link Class} object that clazz will be cast
* into.
* @return clazz cast to upperBound.
*/
@SuppressWarnings("unchecked")
protected <U> Class<U> castClassToUpperBound(Class<?> clazz,
Class<U> upperBound) {
if (!upperBound.isAssignableFrom(clazz)) {
throw new IllegalArgumentException("The class " + clazz.getName() +
" is not a subclass of " + upperBound.getName());
}
return (Class<U>) clazz;
}
/**
* Utility method to read the contents of a {@link ByteString} to the given
* {@link Writable}.
* @param byteString a {@link ByteString} object.
* @param writable a {@link Writable} object.
*/
void fromByteString(ByteString byteString, Writable writable) {
if (writable != null) {
WritableUtils.readFieldsFromByteArray(byteString.toByteArray(), writable);
}
}
/**
* @param writable a {@link Writable} object.
* @return the contents of writable as {@link ByteString}.
*/
ByteString toByteString(Writable writable) {
return ByteString.copyFrom(WritableUtils.writeToByteArray(writable));
}
/**
* Saves this wrapper object to a file.
* @param fileName the full path of the file to save this wrapper object.
* @throws IOException thrown when there is an exception during the writing.
*/
public void save(String fileName) throws IOException {
try (FileOutputStream output = new FileOutputStream(fileName)) {
buildProtoObject().writeTo(output);
output.close();
}
}
/**
* Saves this wrapper object to a file in HDFS.
* @param fs {@link FileSystem} to use for saving to HDFS.
* @param fileName the full path of the file to save this wrapper object.
* @throws IOException thrown when there is an exception during the writing.
*/
public void saveToHDFS(FileSystem fs, String fileName) throws IOException {
AsyncHDFSWriteService.writeToHDFS(buildProtoObject(), fs, fileName);
}
/**
* @return the protobuf representing this wrapper object.
*/
public abstract GeneratedMessage buildProtoObject();
/**
* Loads a protocol buffer stored in a file into this wrapper object.
* @param fileName the full path of the file where the protocol buffer is
* stored.
*/
public void load(String fileName) throws ClassNotFoundException, IOException,
InstantiationException, IllegalAccessException {
try (FileInputStream inputStream = new FileInputStream(fileName)) {
loadFromProto(parseProtoFromInputStream(inputStream));
}
}
/**
* Loads a protocol buffer stored in a file in HDFS into this wrapper object.
* @param fs {@link FileSystem} to use for reading from HDFS.
* @param fileName the full path of the file where the protocol buffer is
* stored.
*/
public void loadFromHDFS(FileSystem fs, String fileName)
throws ClassNotFoundException, IOException, InstantiationException,
IllegalAccessException {
try (FSDataInputStream inputStream = fs.open(new Path(fileName))) {
loadFromProto(parseProtoFromInputStream(inputStream));
}
}
/**
* Constructs a protobuf representing this wrapper object from an
* {@link InputStream}.
* @param inputStream {@link InputStream} containing the contents of this
* wrapper object.
* @return the protobuf version of this wrapper object.
*/
public abstract GeneratedMessage parseProtoFromInputStream(
InputStream inputStream) throws IOException;
/**
* Constructs this wrapper object from a protobuf.
* @param protoObject protobuf to read when constructing this wrapper object.
*/
public abstract void loadFromProto(GeneratedMessage protoObject)
throws ClassNotFoundException, IOException, InstantiationException,
IllegalAccessException;
/**
* Add given URLs to the CLASSPATH before loading from HDFS. To do so, we hack
* the system class loader, assuming it is an URLClassLoader.
*
* XXX Setting the currentThread's context class loader has no effect on
* Class#forName().
*
* @see http://stackoverflow.com/a/12963811/390044
* @param fs {@link FileSystem} to use for reading from HDFS.
* @param fileName the name of the file in HDFS.
* @param classPaths a possible list of class paths that may contain the
* directories containing the file.
*/
public void loadFromHDFS(FileSystem fs, String fileName, URL... classPaths)
throws ClassNotFoundException, InstantiationException,
IllegalAccessException, IOException {
for (URL url : classPaths) {
addPath(url);
}
loadFromHDFS(fs, fileName);
}
/**
* @param u
* the URL to add to the CLASSPATH
* @see http://stackoverflow.com/a/252967/390044
*/
private static void addPath(URL u) {
// need to do add path to Classpath with reflection since the
// URLClassLoader.addURL(URL url) method is protected:
ClassLoader cl = ClassLoader.getSystemClassLoader();
if (cl instanceof URLClassLoader) {
URLClassLoader urlClassLoader = (URLClassLoader) cl;
Class<URLClassLoader> urlClass = URLClassLoader.class;
try {
Method method = urlClass.getDeclaredMethod("addURL",
new Class[] { URL.class });
method.setAccessible(true);
method.invoke(urlClassLoader, u);
} catch (NoSuchMethodException | SecurityException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
throw new IllegalStateException("Cannot add URL to system ClassLoader",
e);
}
} else {
throw new IllegalStateException(
"Cannot add URL to system ClassLoader of type " +
cl.getClass().getSimpleName());
}
}
}