/*
* 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.jena.permissions;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.apache.jena.assembler.Assembler ;
import org.apache.jena.assembler.Mode ;
import org.apache.jena.assembler.assemblers.AssemblerBase ;
import org.apache.jena.assembler.exceptions.AssemblerException ;
import org.apache.jena.rdf.model.* ;
/**
* A simple assembler for a SecurityEvaluator
* <p>
* This assembler load the specified class, locates the first constructor that accepts
* the number of arguments in the assembler file, and calls it passing the arguments.
* Generally the result of this assembler is passed to a sec:Model construction.
* @see SecuredAssembler
* </p>
* <ul>
* <li>The evaluator must have one and only one public constructor
* that takes the number of arguments specified in the assembler file.
* </li>
*
* <li>
* The evaluator may have more constructors but they may not have the same
* number of arguments as specified in the assembler file.
* </li>
*
* <li>
* The arguments must be specified in the assembler file in the order that they
* should be passed to the constructor.
* </li>
* </ul>
*
* <p>
* Literal arguments are converted to their data type before calling the constructor. For example
* "13"^^xsd:int will be converted to an Integer with the value of 13.
* </p>
* The assembler file should include the following
* <code><pre>
* @prefix xsd: <http://www.w3.org/2001/XMLSchema#>
*
* <>; ja:loadClass "org.apache.jena.security.SecuredAssembler" .
*
* sec:Model rdfs:subClassOf ja:NamedModel .
*
* </pre></code>
*
* The model definition should include something like.
*
* <code><pre>
* ex:myEvaluator a sec:Evaluator ;
* sec:args [ rdf:_1 "argument 1 for my evaluator constructor" ;
* rdf:_2 "13"^^xsd:int ; ];
* sec:evaluatorClass "evaluatorClassname";
* .
* </pre></code>
*
* Terms used in above example:
*
* <dl>
* <dt>my:secEvaluator</dt>
* <dd>The security evaluator as referenced in the assembler file.</dd>
*
* <dt>sec:Evaluator</dt>
* <dd>Identifies my:secEvaluator as a SecurityEvaluator</dd>
*
* <dt>sec:args</dt>
* <dd>Identifies the argument list</dd>
*
* <dt>rdf:_1</dt>
* <dd>The first argument</dd>
*
* <dt>rdf:_2</dt>
* <dd>The second argument (an integer in this case</dd>
*
* <dt>sec:evaluatorClass</dt>
* <dd>The fully qualified name of the SecurityEvaluator class to call. This class must extend
* SecurityEvaluator, and must have one and only one constructor that takes the number of arguments
* specified in sec:args</dd>
*
* </p>
*
*/
public class SecurityEvaluatorAssembler extends AssemblerBase implements Assembler, AssemblerConstants {
// initialization and registration is performed by SecuredAssembler
@Override
public SecurityEvaluator open(Assembler a, Resource root, Mode mode) {
Literal className = getUniqueLiteral( root, EVALUATOR_CLASS );
if (className == null)
{
throw new AssemblerException( root, String.format( NO_X_PROVIDED, EVALUATOR_CLASS, root ));
}
Class<?> clazz;
try {
clazz = Class.forName(className.getString());
} catch (ClassNotFoundException e1) {
throw new AssemblerException( root, String.format( "Can not locate class %s as specified by %s in %s", className, EVALUATOR_CLASS, root ));
}
if ( ! SecurityEvaluator.class.isAssignableFrom( clazz ))
{
throw new AssemblerException( root, String.format( "Class %s as specified by %s in %s does not implement SecurityEvaluator", className, EVALUATOR_CLASS, root ));
}
// get the arguments as specified.
List<Object> args = new ArrayList<Object>();
Resource argRes = getUniqueResource( root, ARGUMENT_LIST );
if (argRes != null)
{
Seq seq = argRes.as( Seq.class );
NodeIterator iter = seq.iterator();
RDFNode n = null;
while (iter.hasNext())
{
n = iter.next();
if (n.isLiteral())
{
args.add( n.asLiteral().getValue());
}
else if (n.isResource())
{
args.add( a.open( a, n.asResource(), mode ) );
}
else
{
throw new AssemblerException( root, String.format( "%s must be a literal or a resource", n ));
}
}
}
for (Constructor<?> c : clazz.getConstructors())
{
if (c.getParameterTypes().length == args.size())
{
try {
if (args.size() == 0)
{
return (SecurityEvaluator) c.newInstance();
}
else
{
return (SecurityEvaluator) c.newInstance( args.toArray() );
}
} catch (InstantiationException e) {
throw new AssemblerException( root, e.getMessage(), e );
} catch (IllegalAccessException e) {
throw new AssemblerException( root, e.getMessage(), e );
} catch (IllegalArgumentException e) {
throw new AssemblerException( root, e.getMessage(), e );
} catch (InvocationTargetException e) {
throw new AssemblerException( root, e.getMessage(), e );
}
}
}
throw new AssemblerException( root, String.format( "Class %s does not have a %s argument constructor", className, args.size() ));
}
}