You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by na...@apache.org on 2015/02/13 06:55:17 UTC
svn commit: r1659461 [1/3] - in /hive/trunk:
common/src/java/org/apache/hadoop/hive/common/
common/src/java/org/apache/hadoop/hive/conf/
contrib/src/test/results/clientnegative/
metastore/src/test/org/apache/hadoop/hive/metastore/ ql/src/java/org/apach...
Author: navis
Date: Fri Feb 13 05:55:16 2015
New Revision: 1659461
URL: http://svn.apache.org/r1659461
Log:
HIVE-2573 : Create per-session function registry (Navis reviewed by Jason Dere and Alexander Pivovarov)
Added:
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/Registry.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/plan/ReloadFunctionDesc.java
Removed:
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/CommonFunctionInfo.java
Modified:
hive/trunk/common/src/java/org/apache/hadoop/hive/common/JavaUtils.java
hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
hive/trunk/contrib/src/test/results/clientnegative/invalid_row_sequence.q.out
hive/trunk/metastore/src/test/org/apache/hadoop/hive/metastore/DummyRawStoreForJdoConnection.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionInfo.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionRegistry.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionTask.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/Utilities.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/WindowFunctionInfo.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/mr/ExecDriver.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/index/IndexPredicateAnalyzer.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/metadata/Hive.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/metadata/Partition.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/metadata/Table.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/IndexUtils.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/calcite/translator/SqlFunctionConverter.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/optimizer/ppr/PartitionPruner.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/parse/FunctionSemanticAnalyzer.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveLexer.g
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/parse/HiveParser.g
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/parse/IdentifiersParser.g
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/parse/IndexUpdater.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/parse/SemanticAnalyzerFactory.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/parse/WindowingSpec.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/plan/FunctionWork.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/session/SessionState.java
hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/udf/ptf/WindowingTableFunction.java
hive/trunk/ql/src/test/org/apache/hadoop/hive/ql/parse/TestMacroSemanticAnalyzer.java
hive/trunk/ql/src/test/queries/clientnegative/drop_native_udf.q
hive/trunk/ql/src/test/results/clientnegative/create_function_nonexistent_class.q.out
hive/trunk/ql/src/test/results/clientnegative/create_function_nonudf_class.q.out
hive/trunk/ql/src/test/results/clientnegative/drop_native_udf.q.out
hive/trunk/ql/src/test/results/clientnegative/udf_nonexistent_resource.q.out
Modified: hive/trunk/common/src/java/org/apache/hadoop/hive/common/JavaUtils.java
URL: http://svn.apache.org/viewvc/hive/trunk/common/src/java/org/apache/hadoop/hive/common/JavaUtils.java?rev=1659461&r1=1659460&r2=1659461&view=diff
==============================================================================
--- hive/trunk/common/src/java/org/apache/hadoop/hive/common/JavaUtils.java (original)
+++ hive/trunk/common/src/java/org/apache/hadoop/hive/common/JavaUtils.java Fri Feb 13 05:55:16 2015
@@ -70,9 +70,9 @@ public final class JavaUtils {
return classLoader;
}
- public static void closeClassLoadersTo(ClassLoader current, ClassLoader stop) {
+ public static boolean closeClassLoadersTo(ClassLoader current, ClassLoader stop) {
if (!isValidHierarchy(current, stop)) {
- return;
+ return false;
}
for (; current != null && current != stop; current = current.getParent()) {
try {
@@ -82,6 +82,7 @@ public final class JavaUtils {
Arrays.toString(((URLClassLoader) current).getURLs()), e);
}
}
+ return true;
}
// check before closing loaders, not to close app-classloader, etc. by mistake
Modified: hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
URL: http://svn.apache.org/viewvc/hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java?rev=1659461&r1=1659460&r2=1659461&view=diff
==============================================================================
--- hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java (original)
+++ hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java Fri Feb 13 05:55:16 2015
@@ -332,7 +332,7 @@ public class HiveConf extends Configurat
"When hive.exec.mode.local.auto is true, the number of tasks should less than this for local mode."),
DROPIGNORESNONEXISTENT("hive.exec.drop.ignorenonexistent", true,
- "Do not report an error if DROP TABLE/VIEW/Index specifies a non-existent table/view/index"),
+ "Do not report an error if DROP TABLE/VIEW/Index/Function specifies a non-existent table/view/index/function"),
HIVEIGNOREMAPJOINHINT("hive.ignore.mapjoin.hint", true, "Ignore the mapjoin hint"),
Modified: hive/trunk/contrib/src/test/results/clientnegative/invalid_row_sequence.q.out
URL: http://svn.apache.org/viewvc/hive/trunk/contrib/src/test/results/clientnegative/invalid_row_sequence.q.out?rev=1659461&r1=1659460&r2=1659461&view=diff
==============================================================================
--- hive/trunk/contrib/src/test/results/clientnegative/invalid_row_sequence.q.out (original)
+++ hive/trunk/contrib/src/test/results/clientnegative/invalid_row_sequence.q.out Fri Feb 13 05:55:16 2015
@@ -2,12 +2,10 @@ PREHOOK: query: -- Verify that a statefu
drop temporary function row_sequence
PREHOOK: type: DROPFUNCTION
-PREHOOK: Output: row_sequence
POSTHOOK: query: -- Verify that a stateful UDF cannot be used outside of the SELECT list
drop temporary function row_sequence
POSTHOOK: type: DROPFUNCTION
-POSTHOOK: Output: row_sequence
PREHOOK: query: create temporary function row_sequence as
'org.apache.hadoop.hive.contrib.udf.UDFRowSequence'
PREHOOK: type: CREATEFUNCTION
Modified: hive/trunk/metastore/src/test/org/apache/hadoop/hive/metastore/DummyRawStoreForJdoConnection.java
URL: http://svn.apache.org/viewvc/hive/trunk/metastore/src/test/org/apache/hadoop/hive/metastore/DummyRawStoreForJdoConnection.java?rev=1659461&r1=1659460&r2=1659461&view=diff
==============================================================================
--- hive/trunk/metastore/src/test/org/apache/hadoop/hive/metastore/DummyRawStoreForJdoConnection.java (original)
+++ hive/trunk/metastore/src/test/org/apache/hadoop/hive/metastore/DummyRawStoreForJdoConnection.java Fri Feb 13 05:55:16 2015
@@ -18,9 +18,9 @@
package org.apache.hadoop.hive.metastore;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.SortedSet;
import junit.framework.Assert;
@@ -28,7 +28,6 @@ import org.apache.hadoop.conf.Configurat
import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.metastore.api.AggrStats;
import org.apache.hadoop.hive.metastore.api.ColumnStatistics;
-import org.apache.hadoop.hive.metastore.api.ColumnStatisticsObj;
import org.apache.hadoop.hive.metastore.api.CurrentNotificationEventId;
import org.apache.hadoop.hive.metastore.api.Database;
import org.apache.hadoop.hive.metastore.api.Function;
@@ -44,12 +43,10 @@ import org.apache.hadoop.hive.metastore.
import org.apache.hadoop.hive.metastore.api.NotificationEventResponse;
import org.apache.hadoop.hive.metastore.api.Partition;
import org.apache.hadoop.hive.metastore.api.PartitionEventType;
-import org.apache.hadoop.hive.metastore.api.PartitionsStatsRequest;
import org.apache.hadoop.hive.metastore.api.PrincipalPrivilegeSet;
import org.apache.hadoop.hive.metastore.api.PrincipalType;
import org.apache.hadoop.hive.metastore.api.PrivilegeBag;
import org.apache.hadoop.hive.metastore.api.Role;
-import org.apache.hadoop.hive.metastore.api.SetPartitionsStatsRequest;
import org.apache.hadoop.hive.metastore.api.Table;
import org.apache.hadoop.hive.metastore.api.Type;
import org.apache.hadoop.hive.metastore.api.UnknownDBException;
@@ -145,13 +142,13 @@ public class DummyRawStoreForJdoConnecti
@Override
public List<String> getDatabases(String pattern) throws MetaException {
- return null;
+ return Collections.emptyList();
}
@Override
public List<String> getAllDatabases() throws MetaException {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -214,7 +211,7 @@ public class DummyRawStoreForJdoConnecti
public List<Partition> getPartitions(String dbName, String tableName, int max)
throws MetaException {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -227,41 +224,41 @@ public class DummyRawStoreForJdoConnecti
@Override
public List<String> getTables(String dbName, String pattern) throws MetaException {
- return null;
+ return Collections.emptyList();
}
@Override
public List<Table> getTableObjectsByName(String dbname, List<String> tableNames)
throws MetaException, UnknownDBException {
- return null;
+ return Collections.emptyList();
}
@Override
public List<String> getAllTables(String dbName) throws MetaException {
- return null;
+ return Collections.emptyList();
}
@Override
public List<String> listTableNamesByFilter(String dbName, String filter, short max_tables)
throws MetaException, UnknownDBException {
- return null;
+ return Collections.emptyList();
}
@Override
public List<String> listPartitionNames(String db_name, String tbl_name, short max_parts)
throws MetaException {
- return null;
+ return Collections.emptyList();
}
@Override
public List<String> listPartitionNamesByFilter(String db_name, String tbl_name, String filter,
short max_parts) throws MetaException {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -310,7 +307,7 @@ public class DummyRawStoreForJdoConnecti
public List<String> listIndexNames(String dbName, String origTableName, short max)
throws MetaException {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -324,14 +321,14 @@ public class DummyRawStoreForJdoConnecti
public List<Partition> getPartitionsByFilter(String dbName, String tblName, String filter,
short maxParts) throws MetaException, NoSuchObjectException {
- return null;
+ return Collections.emptyList();
}
@Override
public List<Partition> getPartitionsByNames(String dbName, String tblName,
List<String> partNames) throws MetaException, NoSuchObjectException {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -425,35 +422,35 @@ public class DummyRawStoreForJdoConnecti
public List<MGlobalPrivilege> listPrincipalGlobalGrants(String principalName,
PrincipalType principalType) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<MDBPrivilege> listPrincipalDBGrants(String principalName,
PrincipalType principalType, String dbName) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<MTablePrivilege> listAllTableGrants(String principalName,
PrincipalType principalType, String dbName, String tableName) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<MPartitionPrivilege> listPrincipalPartitionGrants(String principalName,
PrincipalType principalType, String dbName, String tableName, String partName) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<MTableColumnPrivilege> listPrincipalTableColumnGrants(String principalName,
PrincipalType principalType, String dbName, String tableName, String columnName) {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -461,7 +458,7 @@ public class DummyRawStoreForJdoConnecti
PrincipalType principalType, String dbName, String tableName, String partName,
String columnName) {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -487,18 +484,18 @@ public class DummyRawStoreForJdoConnecti
@Override
public List<String> listRoleNames() {
- return null;
+ return Collections.emptyList();
}
@Override
public List<MRoleMap> listRoles(String principalName, PrincipalType principalType) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<MRoleMap> listRoleMembers(String roleName) {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -514,14 +511,14 @@ public class DummyRawStoreForJdoConnecti
String userName, List<String> groupNames) throws MetaException, NoSuchObjectException,
InvalidObjectException {
- return null;
+ return Collections.emptyList();
}
@Override
public List<String> listPartitionNamesPs(String db_name, String tbl_name, List<String> part_vals,
short max_parts) throws MetaException, NoSuchObjectException {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -529,7 +526,7 @@ public class DummyRawStoreForJdoConnecti
List<String> part_vals, short max_parts, String userName, List<String> groupNames)
throws MetaException, InvalidObjectException, NoSuchObjectException {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -555,7 +552,7 @@ public class DummyRawStoreForJdoConnecti
@Override
public List<String> getAllTokenIdentifiers() {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -574,67 +571,67 @@ public class DummyRawStoreForJdoConnecti
@Override
public String[] getMasterKeys() {
- return null;
+ return new String[0];
}
@Override
public List<HiveObjectPrivilege> listPrincipalDBGrantsAll(
String principalName, PrincipalType principalType) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<HiveObjectPrivilege> listPrincipalTableGrantsAll(
String principalName, PrincipalType principalType) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<HiveObjectPrivilege> listPrincipalPartitionGrantsAll(
String principalName, PrincipalType principalType) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<HiveObjectPrivilege> listPrincipalTableColumnGrantsAll(
String principalName, PrincipalType principalType) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<HiveObjectPrivilege> listPrincipalPartitionColumnGrantsAll(
String principalName, PrincipalType principalType) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<HiveObjectPrivilege> listGlobalGrantsAll() {
- return null;
+ return Collections.emptyList();
}
@Override
public List<HiveObjectPrivilege> listDBGrantsAll(String dbName) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<HiveObjectPrivilege> listPartitionColumnGrantsAll(String dbName, String tableName, String partitionName, String columnName) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<HiveObjectPrivilege> listTableGrantsAll(String dbName, String tableName) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<HiveObjectPrivilege> listPartitionGrantsAll(String dbName, String tableName, String partitionName) {
- return null;
+ return Collections.emptyList();
}
@Override
public List<HiveObjectPrivilege> listTableColumnGrantsAll(String dbName, String tableName, String columnName) {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -689,7 +686,7 @@ public class DummyRawStoreForJdoConnecti
public List<ColumnStatistics> getPartitionColumnStatistics(String dbName,
String tblName, List<String> colNames, List<String> partNames)
throws MetaException, NoSuchObjectException {
- return null;
+ return Collections.emptyList();
}
@Override
@@ -738,7 +735,7 @@ public class DummyRawStoreForJdoConnecti
@Override
public List<String> getFunctions(String dbName, String pattern)
throws MetaException {
- return null;
+ return Collections.emptyList();
}
@Override
Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java?rev=1659461&r1=1659460&r2=1659461&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java Fri Feb 13 05:55:16 2015
@@ -428,6 +428,7 @@ public enum ErrorMsg {
ALTER_TABLE_PARTITION_CASCADE_NOT_SUPPORTED(10300,
"Alter table partition type {0} does not support cascade", true),
+ DROP_NATIVE_FUNCTION(10301, "Cannot drop native function"),
//========================== 20000 range starts here ========================//
SCRIPT_INIT_ERROR(20000, "Unable to initialize custom script."),
Modified: hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionInfo.java
URL: http://svn.apache.org/viewvc/hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionInfo.java?rev=1659461&r1=1659460&r2=1659461&view=diff
==============================================================================
--- hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionInfo.java (original)
+++ hive/trunk/ql/src/java/org/apache/hadoop/hive/ql/exec/FunctionInfo.java Fri Feb 13 05:55:16 2015
@@ -18,6 +18,7 @@
package org.apache.hadoop.hive.ql.exec;
+import org.apache.hadoop.hive.ql.session.SessionState;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFBridge;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFResolver;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
@@ -27,18 +28,28 @@ import org.apache.hadoop.hive.ql.udf.ptf
import org.apache.hadoop.hive.ql.udf.ptf.WindowingTableFunction;
import org.apache.hive.common.util.AnnotationUtils;
+import java.util.concurrent.atomic.AtomicBoolean;
+
/**
* FunctionInfo.
*
*/
-public class FunctionInfo implements CommonFunctionInfo {
+public class FunctionInfo {
+
+ public static enum FunctionType {
+ BUILTIN, PERSISTENT, TEMPORARY;
+ }
- private final boolean isNative;
+ private final FunctionType functionType;
private final boolean isInternalTableFunction;
private final String displayName;
+ private final FunctionResource[] resources;
+
+ private String className;
+
private GenericUDF genericUDF;
private GenericUDTF genericUDTF;
@@ -49,38 +60,55 @@ public class FunctionInfo implements Com
private boolean blockedFunction;
+ // for persistent function
+ // if the function is dropped, all functions registered to sessions are needed to be reloaded
+ private AtomicBoolean discarded;
+
+ public FunctionInfo(String displayName, String className, FunctionResource... resources) {
+ this.functionType = FunctionType.PERSISTENT;
+ this.displayName = displayName;
+ this.className = className;
+ this.isInternalTableFunction = false;
+ this.resources = resources;
+ this.discarded = new AtomicBoolean(false); // shared to all session functions
+ }
+
public FunctionInfo(boolean isNative, String displayName,
- GenericUDF genericUDF) {
- this.isNative = isNative;
+ GenericUDF genericUDF, FunctionResource... resources) {
+ this.functionType = isNative ? FunctionType.BUILTIN : FunctionType.TEMPORARY;
this.displayName = displayName;
this.genericUDF = genericUDF;
this.isInternalTableFunction = false;
+ this.resources = resources;
}
public FunctionInfo(boolean isNative, String displayName,
- GenericUDAFResolver genericUDAFResolver) {
- this.isNative = isNative;
+ GenericUDAFResolver genericUDAFResolver, FunctionResource... resources) {
+ this.functionType = isNative ? FunctionType.BUILTIN : FunctionType.TEMPORARY;
this.displayName = displayName;
this.genericUDAFResolver = genericUDAFResolver;
this.isInternalTableFunction = false;
+ this.resources = resources;
}
public FunctionInfo(boolean isNative, String displayName,
- GenericUDTF genericUDTF) {
- this.isNative = isNative;
+ GenericUDTF genericUDTF, FunctionResource... resources) {
+ this.functionType = isNative ? FunctionType.BUILTIN : FunctionType.TEMPORARY;
this.displayName = displayName;
this.genericUDTF = genericUDTF;
this.isInternalTableFunction = false;
+ this.resources = resources;
}
- public FunctionInfo(String displayName, Class<? extends TableFunctionResolver> tFnCls)
- {
+ public FunctionInfo(boolean isNative, String displayName, Class<? extends TableFunctionResolver> tFnCls,
+ FunctionResource... resources) {
+ this.functionType = isNative ? FunctionType.BUILTIN : FunctionType.TEMPORARY;
this.displayName = displayName;
this.tableFunctionResolver = tFnCls;
PartitionTableFunctionDescription def = AnnotationUtils.getAnnotation(
tableFunctionResolver, PartitionTableFunctionDescription.class);
- this.isNative = (def == null) ? false : def.isInternal();
- this.isInternalTableFunction = isNative;
+ this.isInternalTableFunction = def != null && def.isInternal();
+ this.resources = resources;
}
/**
@@ -154,7 +182,7 @@ public class FunctionInfo implements Com
* Native functions cannot be unregistered.
*/
public boolean isNative() {
- return isNative;
+ return functionType == FunctionType.BUILTIN || functionType == FunctionType.PERSISTENT;
}
/**
@@ -201,4 +229,56 @@ public class FunctionInfo implements Com
this.blockedFunction = blockedFunction;
}
+ public boolean isBuiltIn() {
+ return functionType == FunctionType.BUILTIN;
+ }
+
+ public boolean isPersistent() {
+ return functionType == FunctionType.PERSISTENT;
+ }
+
+ public String getClassName() {
+ return className;
+ }
+
+ public FunctionResource[] getResources() {
+ return resources;
+ }
+
+ public void discarded() {
+ if (discarded != null) {
+ discarded.set(true);
+ }
+ }
+
+ // for persistent function
+ public boolean isDiscarded() {
+ return discarded != null && discarded.get();
+ }
+
+ // for persistent function
+ public void shareStateWith(FunctionInfo function) {
+ if (function != null) {
+ function.discarded = discarded;
+ }
+ }
+
+ public static class FunctionResource {
+ private final SessionState.ResourceType resourceType;
+ private final String resourceURI;
+ public FunctionResource(SessionState.ResourceType resourceType, String resourceURI) {
+ this.resourceType = resourceType;
+ this.resourceURI = resourceURI;
+ }
+ public SessionState.ResourceType getResourceType() {
+ return resourceType;
+ }
+ public String getResourceURI() {
+ return resourceURI;
+ }
+ @Override
+ public String toString() {
+ return resourceType + ":" + resourceURI;
+ }
+ }
}