Does MyBatis not allow more than 1 argument in SelectProvider? - ibatis

I have a mapper method like this :
#InsertProvider(class=com.something.class, method="doSomething")
public void insertSomething(Set<Integer> set, int guideId);
and in the something class, I have a method :
public String doSomething(Set<Integer> set, int guideId){
// do something and returna a query
}
It gives me an error :
Error creating SqlSource for SqlProvider. Method 'doSomething' not
found in SqlProvider 'com.something.class'
When I debugged the issue.. I found that in the constructor of ProviderSqlResource, it throws this exception if the no. of arguments are 2 or more. I can't think of any reason why they would do that. What's the workaround ?
Here is the method :
public ProviderSqlSource(Configuration config, Object provider) {
String providerMethodName = null;
try {
this.sqlSourceParser = new SqlSourceBuilder(config);
this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
for (Method m : this.providerType.getMethods()) {
if (providerMethodName.equals(m.getName())) {
if (m.getParameterTypes().length < 2
&& m.getReturnType() == String.class) {
this.providerMethod = m;
this.providerTakesParameterObject = m.getParameterTypes().length == 1;
}
}
}
} catch (Exception e) {
throw new BuilderException("Error creating SqlSource for SqlProvider. Cause: " + e, e);
}
if (this.providerMethod == null) {
throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
+ providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
}
}

It turns out that we can pass any number of arguments in methods annotated with SelectProvider (or any other provider). But the method actually providing the query (doSomething, in my case) will actually receive a single argument i.e. a map wrapper around all the arguments. For example, if the arguments were as in the questions above (a set and an integer), we can access them from the map (called parametersMap) as follows :
Set<Integer> nums = (Set<Integer>) parametersMap.get("0");
int groupId = (Integer) parametersMap.get("1");
The first parameter is keyed with "0" and the second with "1" and so on.
IMHO, the arguments should have been keyed with their names so that we could do something like :
parametersMap.get("set");
parametersMap.get("guideId")
It would probably have been more clean. But that's how its implemented.

For providing multiple arguments use #Param tag in the arguments.

Related

How to interrupt JoinPoint execution in AOP

Is it possible to interrupt invoking the actual method from within the aspect execution?
For example:
public class CheckPermissionAspect {
#Around("#annotation(CheckPermission)")
public Object methodLogging( ProceedingJoinPoint joinPoint) throws Throwable {
// before method execution
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
log.info("Enter ==> " + signature.getMethod().getName());
if ( getPermission( principal.getName()) == false ) {
// break the execution of actual method
Object result = null; // ???
log.info("Break ==> " + signature.getMethod().getName());
} else {
// invoke the actual method
Object result = joinPoint.proceed();
// after method execution
log.debug("Result: " + result);
log.info("Leave ==> " + signature.getMethod().getName());
}
return result;
}
}
To set Object result = null; does not work.
Thank you for any help.
From Spring Reference documentation : Around Advice
Within the body of the advice method, you must invoke proceed() on the
ProceedingJoinPoint in order for the underlying method to run
If joinPoint.proceed() is removed from the code or an exception is thrown before joinPoint.proceed() statement or a return is issued before joinPoint.proceed() statement , the execution of the underlying method would get interrupted.
Update : to answer the comment
Spring AOP is proxy based. To understand this concept better , do read through : Understanding AOP proxies
An advice is placed between the calling method and the target. All calls to the target method gets intercepted and advised.
You could throw an exception upon validation ( Do note that the exception type thrown should match the exception type of the target method . Go through this Q&A).
The exception/return value from the advise would reach back the calling method .

TestNg load testing with parameters passed along with invocationCount and threadPoolSize

As we know that in TestNG we can have a method running parallay being called by multiple threads,
#Test(invocationCount=5,threadPoolSize=5)
public void testMethod()
{
///code to generate load
}
Now we want to do the same thing but with 5 sets pf parameters for each thread invocation in parlall.
You would need to use data providers in TestNG for doing this.
Here's a sample that shows this
//This method will provide data to any test method that declares that its Data Provider
//is named "test1"
#DataProvider(name = "test1")
public Object[][] createData1() {
return new Object[][] {
{ "Cedric", new Integer(36) },
{ "Anne", new Integer(37)},
};
}
//This test method declares that its data should be supplied by the Data Provider
//named "test1"
#Test(dataProvider = "test1")
public void verifyData1(String n1, Integer n2) {
System.out.println(n1 + " " + n2);
}
Now in order to enable parallel execution, please make sure you add the attribute data-provider-thread-count and set its value to a desired value. The default value for this attribute is 10. This attribute lets you control your thread pool size for data providers in TestNG.
For e.g.,
<suite name="Unit-test-suite" verbose="2" data-provider-thread-count="15">
Take a look at the official documentation for it from here.

Pointers, functions and arrays in D Programming Language

I'm writing a method to output to several output streams at once, the way I got it set up right now is that I have a LogController, LogFile and LogConsole, the latter two are implementations of the Log interface.
What I'm trying to do right now adding a method to the LogController that attaches any implementation of the Log interface.
How I want to do this is as follows: in the LogController I have an associative array, in which I store pointers to Log objects. When the writeOut method of the LogController is called, I want it to then run over the elements of the array and call their writeOut methods too. The latter I can do, but the previous is proving to be difficult.
Mage/Utility/LogController.d
module Mage.Utility.LogController;
import std.stdio;
interface Log {
public void writeOut(string s);
}
class LogController {
private Log*[string] m_Logs;
public this() {
}
public void attach(string name, ref Log l) {
foreach (string key; m_Logs.keys) {
if (name is key) return;
}
m_Logs[name] = &l;
}
public void writeOut(string s) {
foreach (Log* log; m_Logs) {
log.writeOut(s);
}
}
}
Mage/Utility/LogFile.d
module Mage.Utility.LogFile;
import std.stdio;
import std.datetime;
import Mage.Utility.LogController;
class LogFile : Log {
private File fp;
private string path;
public this(string path) {
this.fp = File(path, "a+");
this.path = path;
}
public void writeOut(string s) {
this.fp.writefln("[%s] %s", this.timestamp(), s);
}
private string timestamp() {
return Clock.currTime().toISOExtString();
}
}
I've already tried multiple things with the attach functions, and none of them. The build fails with the following error:
Mage\Root.d(0,0): Error: function Mage.Utility.LogController.LogController.attach (string name, ref Log l) is not callable using argument types (string, LogFile)
This is the incriminating function:
public void initialise(string logfile = DEFAULT_LOG_FILENAME) {
m_Log = new LogController();
LogFile lf = new LogFile(logfile);
m_Log.attach("Log File", lf);
}
Can anyone tell me where I'm going wrong here? I'm stumped and I haven't been able to find the answer anywhere. I've tried a multitude of different solutions and none of them work.
Classes and interfaces in D are reference types, so Log* is redundant - remove the *. Similarly, there is no need to use ref in ref Log l - that's like taking a pointer by reference in C++.
This is the cause of the error message you posted - variables passed by reference must match in type exactly. Removing the ref should solve the error.

How to read and add only numbers to array from a text file

I'm learning java and I have a question regarding reading from file
i want to read only numbers from file that contains strings as well.
here is an example of my file:
66.56
"3
JAVA
3-43
5-42
2.1
1
and here is my coding:
public class test {
public static void main (String [] args){
if (0 < args.length) {
File x = new File(args[0]);
try{
Scanner in = new Scanner( new FileInputStream(x));
ArrayList<Double> test = new ArrayList<>();
while(in.hasNext()){
if(in.hasNextDouble()){
Double f=in.nextDouble();
test.add(f);}
else
{in.next();}
}
catch(IOException e) { System.err.println("Exception during reading: " + e); }
}
my problem is it only add 66.56,2.1 and 1
it doesn't add 3 after "3 or it ignores 3-43 and 5-42
can you tell me how to skip Strings and only add doubles here?
thanks
All the said three ie; "3, 3-43 and 4-42 are strings
Either u read a string and split it and check for number at " and - or you put in a space between characters and integers.
The JVM after compilation would treat it all as string if it cannot be converted to a double.
And the File reader wont stop reading till at least a space or a newline.
Hence your code would never work the way you intend it to unless you do as I said above.
Solution 1:
Change your input file to something like this:
66.56
" 3
JAVA
3 - 43
5 - 42
2.1
1
Solution 2:
Considering the highly variable nature of your input file I am posting a solution only made for your current input. If the input changes a more versatile algorithm would need to be implemented.
public static void main(String[] args) {
File x = new File(args[0]);
try {
Scanner in = new Scanner(new FileInputStream(x));
ArrayList<Double> test = new ArrayList<>();
while (in.hasNext()) {
if (in.hasNextDouble()) {
Double f = in.nextDouble();
test.add(f);
} else {
String s=in.next();
if(s.contains("\"")){
String splits[]=s.split("\"");
test.add(Double.parseDouble(splits[1]));
}
else if (s.contains("-")){
String splits[]=s.split("-");
test.add(Double.parseDouble(splits[0]));
test.add(Double.parseDouble(splits[1]));
}
}
}
System.out.println(test);
} catch (IOException e) {
System.err.println("Exception during reading: " + e);
}
}
You can write custom Type Sensor Utility class to check whether the object can be converted to Integer or not. I would approach to this problem like this.
Moreover I can see that you have values like 2.1 and " 3 to handle these scenarios write additional methods like isDoubleType() or isLongType() etc.
Also you need to write some custom logic to solve this problem.
public class TypeSensor {
public String inferType(String value) throws NullValueException {
int formatIndex = -1;
if (null == value) {
throw new NullValueException("Value provided for type inference was null");
}else if (this.isIntegerType(value)) {
return "Integer";
}else{
LOGGER.info("Value " + value + " doesnt fit to any predefined types. Defaulting to String.");
return "String";
}
}
}
private boolean isIntegerType(String value) {
boolean isParseable = false;
try {
Integer.parseInt(value);
LOGGER.info("Parsing successful for " + value + " to Integer.");
isParseable = true;
} catch (NumberFormatException e) {
LOGGER.error("Value " + value + " doesn't seem to be of type Integer. This is not fatal. Exception message is->"
+ e.getMessage());
}
return isParseable;
}
}

String Arrays and Session

I am dealing one serious problem and seems i cannot find a logical solution.
Here it goes.
I have a string array in my code (jsp file). I want to pass the array in the same page , and i thought of making the array a session and call it again later in my code but it seems that i cannot take the session (with get.Attribute) and make it an array again. TO be more specise the following code might help you.
while (onomaq.next()) {
String onomatemp = onomaq.getString("one1");
String[] onoma = onomatemp.split(" ");
out.println(onoma[2]);
session.setAttribute("onoma", onoma);
}
} catch (Exception e) {
System.out.println("SQL Exception: " + e.toString());
}
%>
<%
try{
Object o = session.getAttribute("onoma");
String k=o.toString();
String[] name=k.split(",");
out.println(name[1]);
}
catch (Exception e)
{
System.out.println("SQL Exception: " + e.toString());
}
the out.println gives me a message lige [L.java.String and some characters.
Can anyone help me please?
Rather than calling toString() on your array object after obtainig it from session, just cast the object reference to an array (since your object IS an array) and use it.
This means, replace this code:
Object o = session.getAttribute("onoma");
String k=o.toString();
String[] name=k.split(",");
with
String[] name= (String[]) session.getAttribute("onoma");
p.s. purpose of toString() is somwhat different from what you seem to expect. See Javadoc.
String[] expected_array= (String[]) session.getAttribute("onoma");
Then run it inside a loop to retrieve. Example:
for(int i = 0; i < expected_array. length; i++)
{
String strings = expected_array[i] ;
}

Resources