I use an NFC library from grundid in Clojure but I get a nasty bug when I try to write. In Java it works:
public class TextWriter implements NdefOperationsListener {
#Override
public void onNdefOperations(NdefOperations ndefOperations) {
System.out.println("Formated: " + ndefOperations.isFormatted() + " Writable: " + ndefOperations.isWritable());
if (ndefOperations.isWritable()) {
System.out.println("Writing NDEF data...");
TextRecord record = new TextRecord("It works!");
if (ndefOperations.isFormatted())
ndefOperations.writeNdefMessage(record);
else
ndefOperations.format(record);
System.out.println("Done");
}
else
System.out.println("Tag not writable");
}
It gets started with this code:
protected void launchDemo(NfcTagListener... listeners) throws IOException {
NfcAdapter nfcAdapter = new NfcAdapter(TerminalUtils.getAvailableTerminal(), TerminalMode.INITIATOR, this);
for (NfcTagListener tagListener : listeners)
nfcAdapter.registerTagListener(tagListener);
nfcAdapter.startListening();
System.out.println("Waiting for tags, press ENTER to exit");
System.in.read();
}
And this code gets launched by:
test.launchDemo(new MfClassicNfcTagListener(new TextWriter()));
Anyway this works.
The clojure code:
(defn ndef-writer []
(proxy [NdefOperationsListener] []
(onNdefOperations [ndefOperations]
(println (str "Formatted: " (.isFormatted ndefOperations) " Writable: " (.isWritable ndefOperations)))
(if (.isWritable ndefOperations)
(do
(println "Writing NDEF data...")
(if (.isFormatted ndefOperations)
(.writeNdefMessage ndefOperations test-record)
(.format ndefOperations test-record)))
(println "Tag not writable")))
(defn write-demo []
(doto #nfc-adapter (.registerTagListener (new MfClassicNfcTagListener (ndef-writer))))
(.startListening #nfc-adapter)
(println "Waiting for tag..."))
(defn write-nfc-card []
(try
(write-demo)
(catch Exception e (str "caught exception: " (.getMessage e)))))
But when I run the clojure code I get this error:
java.lang.ClassCastException: Cannot cast org.nfctools.ndef.wkt.records.TextRecord to [Lorg.nfctools.ndef.Record;
at java.lang.Class.cast(Class.java:3258)
at clojure.lang.Reflector.boxArg(Reflector.java:427)
at clojure.lang.Reflector.boxArgs(Reflector.java:460)
at clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:58)
at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:28)
at duva_desktop.nfc.write$ndef_writer$fn__6403.invoke(write.clj:48)
at duva_desktop.nfc.write.proxy$java.lang.Object$NdefOperationsListener$62ccf694.onNdefOperations(Unknown Source)
at org.nfctools.mf.classic.MfClassicNfcTagListener.handleTag(MfClassicNfcTagListener.java:54)
at org.nfctools.NfcAdapter.onTag(NfcAdapter.java:81)
at org.nfctools.spi.acs.InitiatorTerminalTagScanner.handleCard(InitiatorTerminalTagScanner.java:89)
at org.nfctools.spi.acs.InitiatorTerminalTagScanner.run(InitiatorTerminalTagScanner.java:55)
at java.lang.Thread.run(Thread.java:745)
I basically coded the same code, but then in Clojure and it fails because it cant cast TextRecord to Record (TextRecord extends WellKnownRecord, which extends Record) (it shouldn't try to cast anyway?)
(instance? Record test-record) true
Thanks in advance!
p.s.
(def test-record
(new TextRecord "it workedddd"))
I'm not familiar with NFC, but the problem is well explained in the error message :
java.lang.ClassCastException: Cannot cast org.nfctools.ndef.wkt.records.TextRecord to [Lorg.nfctools.ndef.Record;
Notice the L in front of the class name. The L means that it is an array. The method you are calling is mostly probably taking varargs, which in java means that it is array. Therefore your code works in java, but not clojure. In clojure you have to explicitly pass array, which you can do like this :
(into-array [test-record])
Related
a bit new to Kotlin and testing it... I am trying to test a dao object wrapper with using a suspend method which uses an awaitFirst() for an SQL return object. However, when I wrote the unit test for it, it is just stuck in a loop. And I would think it is due to the awaitFirst() is not in the same scope of the testing
Implementation:
suspend fun queryExecution(querySpec: DatabaseClient.GenericExecuteSpec): OrderDomain {
var result: Map<String, Any>?
try {
result = querySpec.fetch().first().awaitFirst()
} catch (e: Exception) {
if (e is DataAccessResourceFailureException)
throw CommunicationException(
"Cannot connect to " + DatabaseConstants.DB_NAME +
DatabaseConstants.ORDERS_TABLE + " when executing querySelect",
"querySelect",
e
)
throw InternalException("Encountered R2dbcException when executing SQL querySelect", e)
}
if (result == null)
throw ResourceNotFoundException("Resource not found in Aurora DB")
try {
return OrderDomain(result)
} catch (e: Exception) {
throw InternalException("Exception when parsing to OrderDomain entity", e)
} finally {
logger.info("querySelect;stage=end")
}
}
Unit Test:
#Test
fun `get by orderid id, null`() = runBlocking {
// Assign
Mockito.`when`(fetchSpecMock.first()).thenReturn(monoMapMock)
Mockito.`when`(monoMapMock.awaitFirst()).thenReturn(null)
// Act & Assert
val exception = assertThrows<ResourceNotFoundException> {
auroraClientWrapper.queryExecution(
databaseClient.sql("SELECT * FROM orderTable WHERE orderId=:1").bind("1", "123") orderId
)
}
assertEquals("Resource not found in Aurora DB", exception.message)
}
I noticed this issue on https://github.com/Kotlin/kotlinx.coroutines/issues/1204 but none of the work around has worked for me...
Using runBlocking within Unit Test just causes my tests to never complete. Using runBlockingTest explicitly throws an error saying "Job never completed"... Anyone has any idea? Any hack at this point?
Also I fairly understand the point of you should not be using suspend with a block because that kinda defeats the purposes of suspend since it is releasing the thread to continue later versus blocking forces the thread to wait for a result... But then how does this work?
private suspend fun queryExecution(querySpec: DatabaseClient.GenericExecuteSpec): Map {
var result: Map<String, Any>?
try {
result = withContext(Dispatchers.Default) {
querySpec.fetch().first().block()
}
return result
}
Does this mean withContext will utilize a new thread, and re-use the old thread elsewhere? Which then doesnt really optimize anything since I will still have one thread that is being blocked regardless of spawning a new context?
Found the solution.
The monoMapMock is a mock value from Mockito. Seems like the kotlinx-test coroutines can't intercept an async to return a mono. So I forced the method that I can mock, to return a real Mono value instead of a Mocked Mono. To do so, as suggested by Louis. I stop mocking it and return a real value
#Test
fun `get by orderid id, null`() = runBlocking {
// Assign
Mockito.`when`(fetchSpecMock.first()).thenReturn(Mono.empty())
Mockito.`when`(monoMapMock.awaitFirst()).thenReturn(null)
// Act & Assert
val exception = assertThrows<ResourceNotFoundException> {
auroraClientWrapper.queryExecution(
databaseClient.sql("SELECT * FROM orderTable WHERE orderId=:1").bind("1", "123") orderId
)
}
assertEquals("Resource not found in Aurora DB", exception.message)
}
I cannot invoke more than 64 synchronous lambda functions without getting an Ops Limit: 64 exception, and I have no idea why.
There is no mention of this in the Lambda limits documentation. In fact, it says you can have up to 1000 concurrent executions. The lambda scaling docs additionally state that temporary bursts up to 3k invocations are supported.
So why is my paltry 65 invocations causing Lambda to reject things?
Reproducing:
On my account I have a dead simple Lambda which sits for 5 seconds (to simulate work) and then returns the default json blob.
import json
import time
def lambda_handler(event, context):
# TODO implement
time.sleep(5)
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
Excluding the time.sleep call, this is the exact code generated when you create a new Python Lambda function. So, no weirdness going on here.
Invoking
Now to invoke it, I just submit the invocation task into a thread pool.
(defn invoke-python [_]
(aws/invoke
lambda
{:op :Invoke
:request {:FunctionName "ExampleSlowPythonFunction"
:Payload (json/write-str payload)}})
Here's all the call is doing. Just a straight invoke call to AWS. (The library here is Cognitect's AWS API. But it just defers down to the REST APIs, so shouldn't matter.)
The thread pool is just one of Java's executors. I hand it a size and the tasks, and it executes said tasks in a pool of size n.
(defn call-it-a-bunch
[n tasks]
(let [pool (Executors/newFixedThreadPool n)]
(let [futures (.invokeAll pool tasks)]
(.shutdown pool)
(mapv #(.get %) futures))))
64 invocations: no problem
(def sixty-four-invoke-tasks (map invoke-python (range 64)))
(call-it-a-bunch 64 sixty-four-invoke-tasks)
A-OK. No problemo.
65 invocations: PROBLEM
(def sixty-FIVE-invoke-tasks (map invoke-python (range 65)))
(call-it-a-bunch 65 sixty-FIVE-invoke-tasks
I will get get an Ops limit reached: 64 on that 65th request.
I have no other Lambda's running on my account. I've tried dialing up the reserved instances on the Python Lambda Function to make double sure that the lambda IS available.
However, the Ops limit error remains.
Why can I not invoke my function more than 64 times concurrently despite having a bucket 1000 concurrency available on my account?
I'm unable to reproduce your problem. I'm using regular Java. I have your exact Python Lambda and have have a Runnable:
import com.amazonaws.regions.Regions;
import com.amazonaws.services.lambda.AWSLambda;
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
import com.amazonaws.services.lambda.model.InvokeRequest;
import com.amazonaws.services.lambda.model.InvokeResult;
public class LambdaThread implements Runnable {
private String name;
public LambdaThread(String name) {
this.name = name;
}
#Override
public void run() {
System.out.println( "starting thread " + name);
Regions region = Regions.fromName("us-west-2");
AWSLambda client = AWSLambdaClientBuilder.standard()
.withRegion(region).build();
InvokeRequest req = new InvokeRequest()
.withFunctionName("test-load");
InvokeResult result = client.invoke(req);
System.out.println( "result from thread " + name + " is " + new String( result.getPayload().array()) );
}
}
And a runner:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class LambdaRunner {
public static void main(String[] argv) {
long startTime = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(999);
for (int i = 0; i < 999; i++) {
Runnable worker = new LambdaThread("thread " + i);
executor.execute(worker);
}
executor.shutdown();
while (!executor.isTerminated()) {
try { Thread.sleep(1000); } catch ( InterruptedException ie ) { /* ignored */ }
System.out.println( "waiting for thread termination...");
}
System.out.println("Finished all threads in " + (System.currentTimeMillis() - startTime) + "ms");
}
}
To me, the error is inline with some of the wording in Clojure in general. What version of the Cognitect library are you using? I can't see this message in the master branch.
If I run this with say 2000 threads I get:
Exception in thread "pool-1-thread-1990"
com.amazonaws.services.lambda.model.TooManyRequestsException: Rate
Exceeded. (Service: AWSLambda; Status Code: 429; Error Code:
TooManyRequestsException; Request ID:
b7f1426b-419a-4d40-902d-e0ed306ff120)
Nothing related to ops.
I am on Windows 10.
I succeed to compile and run this jar.
But when I try to connect to Socket REPL from telnet I get this error and telnet wrote Connection closed by foreign host.
ERROR
Exception in thread "Clojure Connection mine 1" java.io.FileNotFoundException: Could not locate 'clojure/core/server__init.class or 'clojure/core/server.clj on classpath.
Could you please explain what is wrong? :(
package com.echo;
import clojure.java.api.Clojure;
import clojure.lang.IFn;
import clojure.lang.RT;
import clojure.lang.Var;
import clojure.lang.Symbol;
public class Echo {
public static void main(String[] args) {
try {
IFn plus = Clojure.var("clojure.core", "+");
System.out.println( plus.invoke(1, 2).getClass().getSimpleName() );
IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("clojure.core.server"));
IFn startServer = Clojure.var("clojure.core.server","start-server");
//Object options = Clojure.read("\"{:port 4555 :accept 'clojure.core.server/repl :name :repl2 :server-daemon false}\"");
startServer.invoke(
Clojure.read("{:port 4555 :accept 'clojure.core.server/repl :name mine :server-daemon false}")
);
System.out.println( "Started" );
}
catch(Exception e)
{
System.out.println( e.getMessage() );
}
}
}
I run like this
java -cp ".\target\echo-1.0-SNAPSHOT.jar;C:\lib\ext\clojure-1.8.0.jar" com.echo.Echo
You shouldn't quote a symbol that you're reading from a string: use clojure.core.server/repl instead of 'clojure.core.server/repl. Quoting is used to prevent evaluation; but since you are not evaluating, this just results in an extra quote thrown in where it doesn't belong.
I'm writing a simple utility which reads XML files, converts their nodes into POJOs, loads them into a Drools' WM and finally applies some rules to them. You can find the whole project on my GitHub profile. Unfortunately, despite all my efforts, I couldn't make Drools to "like" any instance of classes that were compiled at runtime. I saw many people also having problems with the ClassLoader so I suspect it might be its fault... I prepared a Minimal Working Example for you to try which is available on GitHub and here below. It requires a few other small files (MemoryFileManager, MemoryJavaClassObject and MemoryJavaFileObject) which are only available on GitHub for brevity. In order to work properly, this example requires that your JVM is a JDK >= 6, and that you have tools.jar or classes.jar on your classpath. The example is the following:
public class Example {
/**
* #param args
*/
public static void main(String[] args) {
// Setting the strings that we are going to use...
String name = "Person";
String content = "public class " + name + " {\n";
content += " private String name;\n";
content += " public Person() {\n";
content += " }\n";
content += " public Person(String name) {\n";
content += " this.name = name;\n";
content += " }\n";
content += " public String getName() {\n";
content += " return name;\n";
content += " }\n";
content += " public void setName(String name) {\n";
content += " this.name = name;\n";
content += " }\n";
content += " #Override\n";
content += " public String toString() {\n";
content += " return \"Hello, \" + name + \"!\";\n";
content += " }\n";
content += "}\n";
String value = "HAL";
String rules = "rule \"Alive\"\n";
rules += "when\n";
rules += "then\n";
rules += " System.out.println(\"I'm alive!\")\n";
rules += "end\n";
rules += "\n";
rules += "rule \"Print\"\n";
rules += "when\n";
rules += " $o: Object()\n";
rules += "then\n";
rules += " System.out.println(\"DRL> \" + $o.toString())\n";
rules += "end\n";
// Compiling the given class in memory
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
JavaFileManager manager = new MemoryFileManager(compiler.getStandardFileManager(null, null, null));
ClassLoader classLoader = manager.getClassLoader(null);
List<JavaFileObject> files = new ArrayList<JavaFileObject>();
files.add(new MemoryJavaFileObject(name, content));
compiler.getTask(null, manager, null, null, null, files).call();
try {
// Instantiate and set the new class
Class<?> person = classLoader.loadClass(name);
Method method = person.getMethod("setName", String.class);
Object instance = person.newInstance();
method.invoke(instance, value);
System.out.println(instance);
System.out.println("We get a salutation, so Person is now a compiled class in memory loaded by the given ClassLoader.");
// Use the same instance in Drools (by means of the shared ClassLoader)
KnowledgeBuilderConfiguration config1 = KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(null, classLoader);
KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder(config1);
builder.add(ResourceFactory.newByteArrayResource(rules.getBytes()), ResourceType.DRL);
if (builder.hasErrors()) {
for (KnowledgeBuilderError error : builder.getErrors())
System.out.println(error.toString());
System.exit(-1);
}
KnowledgeBaseConfiguration config2 = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null, classLoader);
KnowledgeBase base = KnowledgeBaseFactory.newKnowledgeBase(config2);
base.addKnowledgePackages(builder.getKnowledgePackages());
StatefulKnowledgeSession session = base.newStatefulKnowledgeSession();
session.insert(instance);
session.fireAllRules();
} catch (ClassNotFoundException e) {
System.out.println("Class not found!");
} catch (IllegalAccessException e) {
System.out.println("Illegal access!");
} catch (InstantiationException e) {
System.out.println("Instantiation!");
} catch (NoSuchMethodException e) {
System.out.println("No such method!");
} catch (InvocationTargetException e) {
System.out.println("Invocation target!");
}
System.out.println("Done.");
}
}
If I run the example, I get the following output:
Hello, HAL!
We get a salutation, so Person is now a compiled class in memory loaded by the given ClassLoader.
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Exception in thread "main" java.lang.NoClassDefFoundError: Object (wrong name: Person)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at bragaglia.skimmer.core.MemoryFileManager$1.findClass(MemoryFileManager.java:33)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:340)
at org.drools.util.CompositeClassLoader$CachingLoader.load(CompositeClassLoader.java:258)
at org.drools.util.CompositeClassLoader$CachingLoader.load(CompositeClassLoader.java:237)
at org.drools.util.CompositeClassLoader.loadClass(CompositeClassLoader.java:88)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.drools.base.ClassTypeResolver.resolveType(ClassTypeResolver.java:155)
at org.drools.rule.builder.PatternBuilder.build(PatternBuilder.java:174)
at org.drools.rule.builder.PatternBuilder.build(PatternBuilder.java:135)
at org.drools.rule.builder.GroupElementBuilder.build(GroupElementBuilder.java:67)
at org.drools.rule.builder.RuleBuilder.build(RuleBuilder.java:85)
at org.drools.compiler.PackageBuilder.addRule(PackageBuilder.java:3230)
at org.drools.compiler.PackageBuilder.compileRules(PackageBuilder.java:1038)
at org.drools.compiler.PackageBuilder.compileAllRules(PackageBuilder.java:946)
at org.drools.compiler.PackageBuilder.addPackage(PackageBuilder.java:938)
at org.drools.compiler.PackageBuilder.addPackageFromDrl(PackageBuilder.java:470)
at org.drools.compiler.PackageBuilder.addKnowledgeResource(PackageBuilder.java:698)
at org.drools.builder.impl.KnowledgeBuilderImpl.add(KnowledgeBuilderImpl.java:51)
at org.drools.builder.impl.KnowledgeBuilderImpl.add(KnowledgeBuilderImpl.java:40)
at bragaglia.skimmer.core.Example.main(Example.java:91)
As you can see, the Person class is successfully compiled in memory and instantiated (see the message Hello, HAL! in the output), however if I add it to a WM I get an Exception in thread "main" java.lang.NoClassDefFoundError: Object (wrong name: Person) even if no rule explicitly relies on Persons. Now, I investigated the exception a little bit and I realised that it gets fired when the given class (Person) is not found within the ClassLoader used by Drools. Therefore I changed my code by adding a configuration with a reference to the very same ClassLoader used to compile and instantiate HAL to both the KnowledgeBuilder and the KnowledgeBase, however I might be doing something wrong because I still get the same exception.
Do you have any idea why this happens and how to work around it? Many thanks in advance!
Thanks to a discussion with a fiends of mine (#dsotty, if you read this, I'm referring to you), I have found a solution.
The problem was in my MemoryFileManager which was only saving the bytecode of the last compiled class. Anytime I was trying to retrieve a class in a later time, I could only find the result of the last compilation. Drools needs to access the byte code of each class that enters its WM, therefore the MemoryFileManager was raising the ClassNotFoundException that was blocking the execution.
Now, the solution is as simple as replacing the private MemoryJavaClassObject object; inside it with a private Map<String, MemoryJavaClassObject> objects; where the bytecodes of all the classes can be successfully stored. So, anytime I try to compile something, I also store the bytecode in objects and when I try to find a class, I first have to look for an entry with the given name within objects. That's it!
The code for MemoryFileManager follows, for your convenience and ease of understanding:
public class MemoryFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
private Map<String, MemoryJavaClassObject> objects;
public MemoryFileManager(StandardJavaFileManager manager) {
super(manager);
this.objects = new HashMap<>();
}
#Override
public ClassLoader getClassLoader(Location location) {
return new SecureClassLoader() {
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
MemoryJavaClassObject object = objects.get(name);
if (null == object)
throw new ClassNotFoundException("Class '" + name + "' not found.");
byte[] b = object.getBytes();
return super.defineClass(name, b, 0, b.length);
}
};
}
#Override
public JavaFileObject getJavaFileForOutput(Location location, String name, Kind kind, FileObject sibling) throws IOException {
MemoryJavaClassObject object = new MemoryJavaClassObject(name, kind);
objects.put(name, object);
return object;
}
}
Further improvements are possible but are not included for brevity.
Is it possible to cast in clojure with java style?
This is java code which I want to implement in clojure:
public class JavaSoundRecorder {
// the line from which audio data is captured
TargetDataLine line;
/**
* Captures the sound and record into a WAV file
*/
void start() {
try {
AudioFormat format = new AudioFormat(16000, 8,
2, true, true);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
System.out.println(AudioSystem.isLineSupported(info));
// checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line not supported");
System.exit(0);
}
//line = (TargetDataLine) AudioSystem.getLine(info);
line = AudioSystem.getTargetDataLine(format);
line.open(format);
line.start(); // start capturing
System.out.println("Start capturing...");
AudioInputStream ais = new AudioInputStream(line);
System.out.println("Start recording...");
// start recording
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File("RecordAudio.wav"));
} catch (LineUnavailableException ex) {
ex.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
* Closes the target data line to finish capturing and recording
*/
void finish() {
line.stop();
line.close();
System.out.println("Finished");
}
/**
* Entry to run the program
*/
public static void main(String[] args) {
final JavaSoundRecorder recorder = new JavaSoundRecorder();
// creates a new thread that waits for a specified
// of time before stopping
Thread stopper = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(6000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
recorder.finish();
}
});
stopper.start();
// start recording
recorder.start();
}
}
And this is what I made in clojure
(def audioformat (new javax.sound.sampled.AudioFormat 16000 8 2 true true))
(def info (new javax.sound.sampled.DataLine$Info javax.sound.sampled.TargetDataLine audioformat))
(if (not= (javax.sound.sampled.AudioSystem/isLineSupported info))(print "dataline not supported")(print "ok lets start\n"))
(def line (javax.sound.sampled.AudioSystem/getTargetDataLine audioformat))
(.open line audioformat)
are there any solutions?
this issue was explained rather well on the Clojure group here:
https://groups.google.com/forum/#!topic/clojure/SNcT6d-TTaQ
You should not need to do the cast (see the discussion in the comments about the super types of the object we have), however you will need to type hint the invocation of open:
(.open ^javax.sound.sampled.TargetDataLine line audioformat)
Remember that java casts don't really do very much (not like C++ where a cast might completely transform an underlying object).
I am not sure what this code is supposed to do, so I don't know whether it has worked or not. Certainly, I can now run your example without error.