/*
* 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.pig.impl.util;
import java.io.IOException;
//import java.io.Serializable;
import java.util.HashMap;
import java.util.Properties;
import org.apache.hadoop.conf.Configuration;
import org.apache.pig.impl.util.ObjectSerializer;
public class UDFContext {
private Configuration jconf = null;
private HashMap<Integer, Properties> udfConfs;
private Properties clientSysProps;
private static final String CLIENT_SYS_PROPS = "pig.client.sys.props";
private static final String UDF_CONTEXT = "pig.udf.context";
private static UDFContext self = null;
private UDFContext() {
udfConfs = new HashMap<Integer, Properties>();
}
public static UDFContext getUDFContext() {
if (self == null) {
self = new UDFContext();
}
return self;
}
// internal pig use only - should NOT be called from user code
public void setClientSystemProps() {
clientSysProps = System.getProperties();
}
/**
* Get the System Properties (Read only) as on the client machine from where Pig
* was launched. This will include command line properties passed at launch
* time
* @return client side System Properties including command line properties
*/
public Properties getClientSystemProps() {
return clientSysProps;
}
/**
* Adds the JobConf to this singleton. Will be
* called on the backend by the Map and Reduce
* functions so that UDFs can obtain the JobConf
* on the backend.
*/
public void addJobConf(Configuration conf) {
jconf = conf;
}
/**
* Get the JobConf. This should only be called on
* the backend. It will return null on the frontend.
* @return JobConf for this job. This is a copy of the
* JobConf. Nothing written here will be kept by the system.
* getUDFConf should be used for recording UDF specific
* information.
*/
public Configuration getJobConf() {
if (jconf != null) return new Configuration(jconf);
else return null;
}
/**
* Get a properties object that is specific to this UDF.
* Note that if a given UDF is called multiple times in a script,
* and each instance passes different arguments, then each will
* be provided with different configuration object.
* This can be used by loaders to pass their input object path
* or URI and separate themselves from other instances of the
* same loader. Constructor arguments could also be used,
* as they are available on both the front and back end.
*
* Note that this can only be used to share information
* across instantiations of the same function in the front end
* and between front end and back end. It cannot be used to
* share information between instantiations (that is, between
* map and/or reduce instances) on the back end at runtime.
* @param c of the UDF obtaining the properties object.
* @param args String arguments that make this instance of
* the UDF unique.
* @return A reference to the properties object specific to
* the calling UDF. This is a reference, not a copy.
* Any changes to this object will automatically be
* propogated to other instances of the UDF calling this
* function.
*/
@SuppressWarnings("unchecked")
public Properties getUDFProperties(Class c, String[] args) {
Integer k = generateKey(c, args);
Properties p = udfConfs.get(k);
if (p == null) {
p = new Properties();
udfConfs.put(k, p);
}
return p;
}
/**
* Get a properties object that is specific to this UDF.
* Note that if a given UDF is called multiple times in a script,
* they will all be provided the same configuration object. It
* is up to the UDF to make sure the multiple instances do not
* stomp on each other.
*
* It is guaranteed that this properties object will be separate
* from that provided to any other UDF.
*
* Note that this can only be used to share information
* across instantiations of the same function in the front end
* and between front end and back end. It cannot be used to
* share information between instantiations (that is, between
* map and/or reduce instances) on the back end at runtime.
* @param c of the UDF obtaining the properties object.
* @return A reference to the properties object specific to
* the calling UDF. This is a reference, not a copy.
* Any changes to this object will automatically be
* propogated to other instances of the UDF calling this
* function.
*/
@SuppressWarnings("unchecked")
public Properties getUDFProperties(Class c) {
Integer k = generateKey(c);
Properties p = udfConfs.get(k);
if (p == null) {
p = new Properties();
udfConfs.put(k, p);
}
return p;
}
/**
* Serialize the UDF specific information into an instance
* of JobConf. This function is intended to be called on
* the front end in preparation for sending the data to the
* backend.
* @param conf JobConf to serialize into
* @throws IOException if underlying serialization throws it
*/
public void serialize(Configuration conf) throws IOException {
conf.set(UDF_CONTEXT, ObjectSerializer.serialize(udfConfs));
conf.set(CLIENT_SYS_PROPS, ObjectSerializer.serialize(clientSysProps));
}
/**
* Populate the udfConfs field. This function is intended to
* be called by Map.configure or Reduce.configure on the backend.
* It assumes that addJobConf has already been called.
* @throws IOException if underlying deseralization throws it
*/
@SuppressWarnings("unchecked")
public void deserialize() throws IOException {
udfConfs = (HashMap<Integer, Properties>)ObjectSerializer.deserialize(jconf.get(UDF_CONTEXT));
clientSysProps = (Properties)ObjectSerializer.deserialize(
jconf.get(CLIENT_SYS_PROPS));
}
@SuppressWarnings("unchecked")
private int generateKey(Class c) {
return c.getName().hashCode();
}
@SuppressWarnings("unchecked")
private int generateKey(Class c, String[] args) {
int hc = c.getName().hashCode();
for (int i = 0; i < args.length; i++) {
hc <<= 1;
hc ^= args[i].hashCode();
}
return hc;
}
public void reset() {
udfConfs.clear();
}
public boolean isUDFConfEmpty() {
return udfConfs.isEmpty();
}
}