/*
* Copyright 2008 The Topaz Foundation
*
* Licensed 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.
*
* Contributions:
*/
package org.mulgara.resolver;
// Java 2 standard packages;
import java.net.URI;
// Third party packages
import javax.transaction.xa.XAResource;
import org.apache.log4j.Logger;
import org.jrdf.graph.Node;
import org.jrdf.graph.URIReference;
// Locally written packages
import org.mulgara.query.Constraint;
import org.mulgara.query.LocalNode;
import org.mulgara.query.QueryException;
import org.mulgara.query.TuplesException;
import org.mulgara.resolver.spi.DummyXAResource;
import org.mulgara.resolver.spi.EmptyResolution;
import org.mulgara.resolver.spi.GlobalizeException;
import org.mulgara.resolver.spi.Resolution;
import org.mulgara.resolver.spi.Resolver;
import org.mulgara.resolver.spi.ResolverException;
import org.mulgara.resolver.spi.ResolverSession;
import org.mulgara.resolver.spi.Statements;
/**
* Simple mock resolver. All operations are dummys: model creation, removing, and modifying are
* no-ops, and queries return an empty resolution. The model name is parsed for commands to
* execute: the model must have a query string consisting of an '&' separated list of commands
* where each command is of the form "name=value" (the value may be empty) (i.e. follow http
* query string syntax for parameters).
*
* <p>Currently the only commands supported: are 'active', 'wait', and 'hardWait':
* <dl>
* <dt>active</dt>
* <dd>the value must be any combination of the characters 'c', 'd', 'm', 'r', 'f', 'n', or
* 'l', which stand for 'Resolver.createModel', 'Resolver.dropModel', 'Resolver.modifyModel',
* 'Resolver.resolve', 'Answer.beforeFirst', 'Answer.next', and 'Answer.cLose', respectively.
* The commands after this one are only executed for these operations. If not specified, the
* default is 'cdmrfnl' (i.e. all operations)</dd>
* <dt>wait</dt>
* <dd>the value is the number of milliseconds to sleep before continuing.</dd>
* <dt>hardWait</dt>
* <dd>like <var>wait</var>, but ignore interrupts.</dd>
* </dl>
*
* @created 2009-07-05
* @author Ronald Tschalär
* @copyright ©2008 <a href="http://www.topazproject.org/">Topaz Foundation</a>
* @licence Apache License v2.0
*/
public class MockResolver implements Resolver {
/** Logger */
private static final Logger logger = Logger.getLogger(MockResolver.class);
/** the next XAResource to return */
private static XAResource nextXARes = null;
/** The session that this resolver is associated with */
private final ResolverSession resolverSession;
public static synchronized void setNextXAResource(XAResource xaRes) {
nextXARes = xaRes;
}
MockResolver(ResolverSession resolverSession) {
this.resolverSession = resolverSession;
}
public void createModel(long model, URI modelTypeURI) throws ResolverException {
processCommands(model, 'c', ResolverException.class);
}
public XAResource getXAResource() {
synchronized (MockResolver.class) {
return (nextXARes != null) ? nextXARes : new DummyXAResource(10);
}
}
public void modifyModel(long model, Statements statements, boolean occurs) throws ResolverException {
processCommands(model, 'm', ResolverException.class);
}
public void removeModel(long model) throws ResolverException {
processCommands(model, 'd', ResolverException.class);
}
public Resolution resolve(Constraint constraint) throws QueryException {
long model = ((LocalNode) constraint.getModel()).getValue();
processCommands(model, 'r', QueryException.class);
return new MockEmptyResolution(constraint, model);
}
public void abort() {}
private <T extends Throwable> void processCommands(long model, char op, Class<T> exc) throws T {
URI modelUri = toURI(model, exc);
logger.debug("model-uri='" + modelUri + "', op='" + op + "'");
String query = modelUri.getQuery();
if (query == null) {
logger.debug("no query found, no commands");
return;
}
for (String param : query.split("&")) {
String name = param.substring(0, param.indexOf('='));
String value = param.substring(param.indexOf('=') + 1);
logger.debug("processing command '" + name + "' with value '" + value + "'");
if (name.equals("active")) {
if (value.indexOf(op) < 0) {
break;
}
} else if (name.equals("wait")) {
logger.debug("sleeping '" + value + "' milliseconds");
try {
Thread.sleep(Long.parseLong(value));
} catch (InterruptedException ie) {
throw MulgaraTransactionFactory.newExceptionOrCause(exc, "sleep interrupted", ie);
}
} else if (name.equals("hardWait")) {
logger.debug("sleeping '" + value + "' milliseconds");
long targetDate = System.currentTimeMillis() + Long.parseLong(value);
while (true) {
long wait = targetDate - System.currentTimeMillis();
if (wait <= 0)
break;
try {
Thread.sleep(wait);
} catch (InterruptedException ie) {
}
}
} else {
logger.info("Unknown command '" + name + "' - ignoring");
}
}
}
private <T extends Throwable> URI toURI(long model, Class<T> exc) throws T {
try {
Node globalModel = resolverSession.globalize(model);
return ((URIReference) globalModel).getURI();
} catch (GlobalizeException ge) {
throw MulgaraTransactionFactory.newExceptionOrCause(exc, "Couldn't globalize model", ge);
}
}
private class MockEmptyResolution extends EmptyResolution {
private final long model;
public MockEmptyResolution(Constraint constraint, long model) {
super(constraint, true);
this.model = model;
}
public void beforeFirst(long[] prefix, int suffixTruncation) throws TuplesException {
processCommands(model, 'f', TuplesException.class);
super.beforeFirst(prefix, suffixTruncation);
}
public boolean next() throws TuplesException {
processCommands(model, 'n', TuplesException.class);
return super.next();
}
public void close() {
processCommands(model, 'l', RuntimeException.class);
super.close();
}
}
}