/*
* 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.scripting.jython;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.pig.FuncSpec;
import org.apache.pig.backend.executionengine.ExecException;
import org.apache.pig.impl.PigContext;
import org.apache.pig.impl.logicalLayer.parser.ParseException;
import org.apache.pig.impl.util.Utils;
import org.apache.pig.scripting.ScriptEngine;
import org.python.core.PyFunction;
import org.python.core.PyObject;
import org.python.core.PyStringMap;
import org.python.core.PyTuple;
import org.python.util.PythonInterpreter;
/**
* Implementation of the script engine for Jython
*/
public class JythonScriptEngine extends ScriptEngine {
// Decorators -
// "schemaFunction"
// "outputSchema"
// "outputSchemaFunction"
/**
* Language Interpreter Uses static holder pattern
*/
private static class Interpreter {
static final PythonInterpreter interpreter = new PythonInterpreter();
static volatile ArrayList<String> filesLoaded = new ArrayList<String>();
static synchronized void init(String path) throws IOException {
if (!filesLoaded.contains(path)) {
// attempt addition of schema decorator handler, fail silently
interpreter.exec("def outputSchema(schema_def):\n"
+ " def decorator(func):\n"
+ " func.outputSchema = schema_def\n"
+ " return func\n"
+ " return decorator\n\n");
interpreter.exec("def outputSchemaFunction(schema_def):\n"
+ " def decorator(func):\n"
+ " func.outputSchemaFunction = schema_def\n"
+ " return func\n"
+ " return decorator\n");
interpreter.exec("def schemaFunction(schema_def):\n"
+ " def decorator(func):\n"
+ " func.schemaFunction = schema_def\n"
+ " return func\n"
+ " return decorator\n\n");
InputStream is = null;
File file = new File(path);
if (file.exists()) {
is = new FileInputStream(file);
}
else {
if (file.isAbsolute()) {
is = Interpreter.class.getResourceAsStream(path);
} else {
is = Interpreter.class.getResourceAsStream("/" + path);
}
}
if (is != null) {
interpreter.execfile(is);
filesLoaded.add(path);
is.close();
} else {
throw new IOException(
"Could not initialize interpreter with "+ path);
}
}
}
}
@Override
public void registerFunctions(String path, String namespace, PigContext pigContext)
throws IOException{
Interpreter.init(path);
pigContext.scriptJars.add(getJarPath(PythonInterpreter.class));
PythonInterpreter pi = Interpreter.interpreter;
@SuppressWarnings("unchecked")
List<PyTuple> locals = (List<PyTuple>) ((PyStringMap) pi.getLocals()).items();
if(namespace == null) {
namespace = "";
}
else {
namespace = namespace + namespaceSeparator;
}
try {
for (PyTuple item : locals) {
String key = (String) item.get(0);
Object value = item.get(1);
FuncSpec funcspec = null;
if (!key.startsWith("__") && !key.equals("schemaFunction")
&& !key.equals("outputSchema")
&& !key.equals("outputSchemaFunction")
&& (value instanceof PyFunction)
&& (((PyFunction)value).__findattr__("schemaFunction".intern())== null)) {
PyObject obj = ((PyFunction)value).__findattr__("outputSchema".intern());
if(obj != null) {
Utils.getSchemaFromString(obj.toString());
}
funcspec = new FuncSpec(JythonFunction.class.getCanonicalName() + "('"
+ path + "','" + key +"')");
pigContext.registerFunction(namespace + key, funcspec);
}
}
} catch (ParseException pe) {
throw new IOException("Error parsing schema for script function from the decorator " + pe);
}
}
public static PyFunction getFunction(String path, String functionName) throws IOException {
Interpreter.init(path);
return (PyFunction) Interpreter.interpreter.get(functionName);
}
}