I have a function that returns String.
private String processQuery(String[] args){
//code logic
}
Returned result can either be a answer (Your account detail is $account_detail.) or response (Sorry I cannot understand you?). Depending upon the result, code will do separate things.
What I came up with is to user Either<String, String>.
private Either<String,String> processQuery(String[] args){
//code logic
}
private void reply(String[] args){
//code logic
var either = processQuery(args);
return either.fold((l){
//returned result is answer
},(r){
//returned result is response
});
}
If it returns left then it is answer, if it returns right it is response. But since there is not Either type in java so I tried passing AtomicBoolean around.
What is the better solution for this only using java stl?
One solution is to make the method take two lambda functions that corresponds to a correct and an incorrect answer and then call only the appropriate one
private void processQuery(String[] args, Consumer<String> correct, Consumer<String> incorrect){
if (args.length == 0) {
incorrect.accept("Sorry I cannot understand you?");
return;
}
correct.accept("Your account detail is $account_detail.");
}
which can be called like this
private void reply(String[] args){
processQuery(args, (
reply -> System.out.println("Success!, " + reply)
),
(
reply -> System.out.println("Fail, " + reply)
)
);
}
or create variables for the different functions
Consumer<String> badAnswer = reply -> System.out.println("Fail, " + reply);
Consumer<String> goodAnswer = reply -> System.out.println("Success!, " + reply);
private void reply(String[] args){
processQuery(args, goodAnswer, badAnswer);
}
You can use Pair:
Pair<String, String> pair = Pair.with("Blah", "Blee");
See some example here
A better approach, if your responses actually represent some kind of an error, will be to throw an exception of some kind, and to keep the return value of String to represent a "good" flow.
Related
I have the following methods.
#Transactional
public Mono<Long> processPollResult(final PollReport pollReport, final String xmlResult,
final String ref, final String clientId, int orgId) {
return pollOrderRepository.save(toNewOrphanPollOrder(clientId, orgId)).flatMap(pollOder -> {
List<Mono<?>> monoPubs = new ArrayList<>();
monoPubs.add(pollOrderOrphanRepository.save(toPollOrderOrphan(pollOder.getPollOrderId())));
labReport.getLabResults().getLabResultList().forEach(labResult -> {
monoPubs.add((Mono<?>) saveOrphanPollOrderResult(ref, clientId, pollOder, pollResult).subscribe());
});
List<Polls> polls = buildAcknowledgePoll(ref, clientId);
return Mono.when(monoPubs).then(deviceService.acknowledgePolls(polls, clientId)).thenReturn(pollOder.getPollOrderId());
});
}
private Mono<Long> saveOrphanPollOrderResult(final String ref, final String clientId, PollOrder pollOder,
PollResult pollResult) {
return pollOrderTestRepository.save(toPollOrderTest(pollOrder, pollResult.getPollResultHeader()))
.flatMap(pollOrderTestRes -> {
if (pollResult.getPollResultItems() != null
&& pollResult.getPollResultItems().getPollResultItemsList() != null) {
List<PollOrderTestItem> testResultItemsList = labResult.getPollResultItems()
.getPollResultItemsList().stream().map(pollOrderItem -> toPollOrderItem(pollOrderItem,
pollOrder.getPollOrderId(), pollResult.getPollResultHeader().getTestCode()))
.collect(Collectors.toList());
return pollOrderTestItemRepository.saveAll(testResultItemsList).collectList()
.flatMap(pollOrderItemResp -> {
return Mono.just(pollOrder.getPollOrderId());
});
} else {
return Mono.just(pollOrder.getPollOrderId());
}
});
}
Here,
1.
monoPubs.add((Mono<?>) saveOrphanPollOrderResult(ref, clientId, pollOder, pollResult).subscribe()); is troubling me.
Here, getting type cast error.
2.
If I dont put .subscribe(), the statements inside the saveOrphanPollOrderResult is not getting invoked.
Keeping without subscribe here,
monoPubs.add(saveOrphanPollOrderResult(ref, clientId, pollOder, pollResult));
and adding subscribe() inside saveOrphanPollOrderResult like the one below causing compile time error as it cannot return any value and unable to add this inside the monos list.
private Mono<Long> saveOrphanPollOrderResult(final String ref, final String clientId, PollOrder pollOder,
PollResult pollResult) {
return pollOrderTestRepository.save(toPollOrderTest(pollOrder, pollResult.getPollResultHeader()))
.flatMap(pollOrderTestRes -> {
if (pollResult.getPollResultItems() != null
&& pollResult.getPollResultItems().getPollResultItemsList() != null) {
List<PollOrderTestItem> testResultItemsList = labResult.getPollResultItems()
.getPollResultItemsList().stream().map(pollOrderItem -> toPollOrderItem(pollOrderItem,
pollOrder.getPollOrderId(), pollResult.getPollResultHeader().getTestCode()))
.collect(Collectors.toList());
return pollOrderTestItemRepository.saveAll(testResultItemsList).collectList()
.flatMap(pollOrderItemResp -> {
return Mono.just(pollOrder.getPollOrderId());
});
} else {
return Mono.just(pollOrder.getPollOrderId());
}
}).subscribe(); /// -->
}
your problem is here:
var id = pollOder.getPollOrderId();
var pollOrderOrphan = toPollOrderOrphan(id);
List<Mono<?>> monoPubs = new ArrayList<>();
monoPubs.add(pollOrderOrphanRepository.save(pollOrderOrphan));
labReport.getLabResults().getLabResultList().forEach(labResult -> {
monoPubs.add((Mono<?>) saveOrphanPollOrderResult(ref, clientId, pollOder, pollResult).subscribe());
});
There are several issues, first of all using Mono<?> means that you want to store several different types in a List<T> this is bad practice of many different reasons you are probably aware of. Please use the type system.
Second of all is that you can't use a forEach loop. That is imperative programming, not reactive, that assumes that you have the items that you want to place in your list. Which you don't.
That's the reason to why you have to call subscribe, because you need to "wait in the concrete items" so you can place them in your list.
What you need to do is to start using Flux instead, which is a stream of presumed items.
i don't know your exact logic here since your code is quite messy, but as an example:
var labresultList = labReport.getLabResults().getLabResultList();
Mono<List<PollOrderResult> = Flux.fromIterable(labResultList)
.flatMap(saveOrphanPollOrderResult(ref, clientId, pollOder, pollResult))
.collectList();
And some OT:
I dont know what your item types are, but please try to avoid using <?> as that will most likely come back to bite you later.
And please use a bit more functions for readability, as the code is very hard to read, functions are free to use. Assigning variables instead of nesting calls also improves readability.
First off: I absolutely LOVE Project Lombok. Awesome tool! There's so many excellent aspects to this 'compile time' library.
Loving the #ExtensionMethods, I have already hit this 'feature' a few times, so now it's time for me to ask this question:
Suppose I have the following classes:
#UtilityClass
public class AObject {
static public String message(final Object pObject) {
return "AObject = " + (pObject != null);
}
}
#UtilityClass
public class AString {
static public String message(final String pObject) {
return "AString = " + (pObject != null);
}
}
#ExtensionMethod({ AObject.class, AString.class })
public class Run_Object_String {
public static void main(final String[] args) {
System.out.println("\nRun_Object_String.main()");
final String s = "Bier!";
final Object o = new Object();
System.out.println("Testing s: " + s.message());
System.out.println("Testing o: " + o.message());
System.out.println("Testing s: " + s.message());
}
}
#ExtensionMethod({ AString.class, AObject.class })
public class Run_String_Object {
public static void main(final String[] args) {
System.out.println("\nRun_String_Object.main()");
final String s = "Bier!";
final Object o = new Object();
System.out.println("Testing s: " + s.message());
System.out.println("Testing o: " + o.message());
System.out.println("Testing s: " + s.message());
}
}
public class ClassPrevalenceTest {
public static void main(final String[] args) {
Run_Object_String.main(args);
Run_String_Object.main(args);
}
}
With the output:
Run_Object_String.main()
Testing s: AObject = true
Testing o: AObject = true
Testing s: AObject = true
Run_String_Object.main()
Testing s: AString = true
Testing o: AObject = true
Testing s: AString = true
Why is this?
Why is the message(String) not called in the first example, even though it has a better method signature fit than message(Object)?
Why is #ExtensionMethod dependent on sequence of the arguments?
Here's what I blindly assume:
when parsing for ExtensionMethods, Lombok will process annotation values from left to right
For Run_Object_String that means: first AObject, then AString
For Run_String_Object that means: first AString, then AObject
Object-String: When patching AObject into class Run_Object_String, the message(Object) method will be added. And when patching in AString with the message(String) method, it will not be added.
Presumably because the message(Object) also matches a call to message(String), so message(String) will not be added.
String-Object: When patching AString into class Run_String_Object, the message(String) method will be added.
When patching in AObject class with message(Object), the old and present message(String) method will NOT accept the call message(Object), thus the method message(Object) will be added.
So, apart from taking great care of what order I add the #UtilityClass references, are there any other solutions to this?
Can the Lombok preprocessor be extended and made more sensible when adding in extension methods?
Do you guys have any suggestions regarding this, or an explanation of what is really happening (as opposed to my assumptions)
This is a fascinating use of Lombok I wasn't aware of. The best place I think you could delve to find your answers is the source itself since the docs on this experimental work seems pretty light, understandably.
Take a look on git here: HandleExtensionMethod.
I am guessing based on the logic that the area that's effectively "fitting" the right method from the annotation is as below..
Instead of trying for a "best" fit, it seems to be aiming for a "first" fit.
That is, it appears to iterate over List<Extension> extensions. Since it's a Java list, we assume ordering is preserved in the order the extensions were specified in the original annotation.
It appears to simply work in order of the list and return as soon as something matches the right method and type shape.
Types types = Types.instance(annotationNode.getContext());
for (Extension extension : extensions) {
TypeSymbol extensionProvider = extension.extensionProvider;
if (surroundingTypeSymbol == extensionProvider) continue;
for (MethodSymbol extensionMethod : extension.extensionMethods) {
if (!methodName.equals(extensionMethod.name.toString())) continue;
Type extensionMethodType = extensionMethod.type;
if (!MethodType.class.isInstance(extensionMethodType) && !ForAll.class.isInstance(extensionMethodType)) continue;
Type firstArgType = types.erasure(extensionMethodType.asMethodType().argtypes.get(0));
if (!types.isAssignable(receiverType, firstArgType)) continue;
methodCall.args = methodCall.args.prepend(receiver);
methodCall.meth = chainDotsString(annotationNode, extensionProvider.toString() + "." + methodName);
recursiveSetGeneratedBy(methodCall.meth, methodCallNode);
return;
}
}
You can look at the rest of the code for other insight as there doesn't seem to be too much there (i.e. number of lines) to look at, though admittedly it's an impressive enough a feat to do in that space.
Hello I'm testing the class that has some validating methods and I've been wondering if there is a way to reduce the duplicated code.
#Test
void testCorrectEmailValidator() {
List<String> correctEmails = Arrays.asList("test#test.com", "test123#test123.com", "test#test.com.in",
"test.test2#test.com", "test.test2.test3#test.com", "TEST.2test#test.com");
for (String email : correctEmails) {
boolean isValid = UserCredentialsValidator.emailValidator(email);
System.out.println("Email is valid: " + email + ": " + isValid);
assertTrue(isValid);
}
}
#Test
void testCorrectUsernameValidator() {
List<String> correctUsernames = Arrays.asList("username", "123username", "username3", "user2name",
"USERNAME", "USERNAME123", "123USERNAME123", "2uSERname33");
for(String username : correctUsernames) {
boolean isValid = UserCredentialsValidator.usernameValidation(username, userList);
System.out.println("Username is valid: " + username + " : " + isValid);
assertTrue(isValid);
}
}
I also have validators for other fields such as username etc. I was thinking about implementing a helper method that would accept: tested credential as String, List but I've got a problem with last parameter - a validating method, not sure how to pass that.
The code i would like to replace with some method is the for loop.
I am afraid your tests are of low quality.
The problems that should be fixed immediately include
UserCredentialsValidator.usernameValidation(username, userList); The method shouldn't take the second argument. The place from where that list is retrieved should be concealed from the API consumer.
List<String> correctEmails = Arrays.asList(...) and List<String> correctUsernames = Arrays.asList(...) should be removed. You'd better make the tests parameterised with #ParameterizedTest and #ValueSource.
I'd rather remove the System.out.println statements. They make little sense in tests.
#ParameterizedTest
#ValueSource(strings = {"test#test.com", "test123#test123.com"})
void testUserEmailValidationWithValidUserEmailShouldPass(String validUserEmail) {
boolean isValid = UserCredentialsValidator.emailValidator(validUserEmail);
assertTrue(isValid);
}
#ParameterizedTest
#ValueSource(strings = {"username", "123username"})
void testUserNameValidationWithValidUserNameShouldPass(String validUserName) {
boolean isValid = UserCredentialsValidator.usernameValidation(validUserName);
assertTrue(isValid);
}
Now there is nothing to reduce.
As I already stated in my comment to your question, I'm not sure rearranging your code would help much. However, as a comparision here's a Java8+ version which uses a common method:
#Test
void testCorrectEmailValidator() {
List<String> correctEmails = Arrays.asList("test#test.com", "test123#test123.com", "test#test.com.in",
"test.test2#test.com", "test.test2.test3#test.com", "TEST.2test#test.com");
testValidator( "Email", correctEmails , email -> UserCredentialsValidator.emailValidator(email) );
}
#Test
void testCorrectUsernameValidator() {
List<String> correctUsernames = Arrays.asList("username", "123username", "username3", "user2name",
"USERNAME", "USERNAME123", "123USERNAME123", "2uSERname33");
//I don't know where userList does come from but it would need to be final to be used here
testValidator( "Username", correctUsernames, username -> UserCredentialsValidator.usernameValidation(username, userList) );
}
void testValidator( String name, List<String> data, Predicate<String> validator) {
for( String element : data ) {
boolean isValid = validator.test( element );
System.out.println( name + " is valid: " + element + " : " + isValid);
assertTrue(isValid);
}
}
In that particular case both approaches would be 23 lines long while the second one might be easier to reuse but harder to understand and less flexible (e.g. if you'd need to pass additional parameters etc.)
Use parameterized tests:
static Stream<String> emailsSource() {
return Stream.of("test#test.com", "test123#test123.com", "test#test.com.in",
"test.test2#test.com", "test.test2.test3#test.com", "TEST.2test#test.com");
}
#Test
#MethodSource("emailsSource")
void testCorrectEmailValidator(String email) {
boolean isValid = UserCredentialsValidator.emailValidator(email);
assertTrue(isValid);
}
Repeat for usernameSource etc. IMHO, this is sufficient to eliminate duplicities.
However if you want to go further and generalize it, use method references. I wouldn't recommend it though.
static Stream<Pair<String,Predicate<String>>> allSources() {
return Stream.of(
Pair.of("test#test.com", UserCredentialsValidator::emailValidator),
Pair.of("username", UserCredentialsValidator::usernameValidationOneArg), // like usernameValidation but with argument userList fixed
...
);
}
#Test
#MethodSource("allSources")
void testAll(Pair<String,Predicate<String>> p) {
String s = p.getLeft();
Predicate<String> test = p.getRight();
boolean isValid = test.apply(email);
assertTrue(isValid);
}
Fact that you're struggling to test is indicating a design smell.
Its good time for you to explore strategy design pattern here.
Basically you main code would look something like
interface IValidator {
boolean isValid(List<String> yourDataToBeValidated);
}
Now create multiple validator classes for different fields like email, username etc.
class EmailValidator implements IValidator {
boolean isValid(List<String> yourDataToBeValidated){
//Email specific validations goes here
}
}
You can create more validators as you need on the go.
Now in your unit tests create new EmailValidator() or new UsernameValidator() and pass your emailIds or usernames to be isValid() method, something like below :
boolean isValid = new EmailValidator().isValid(Arrays.asList("test#test.com", "test123#test123.com");
assertTrue(isValid);
I'm invoking some method of Class's instance using the method.invoke(instance, args...) way but for each method inside the instance, as the invoke Javadoc rightly points out, each argument must be manually specified.
Thinking about Spring... how it could valorize parameters in controller's method behind the hood during HTTP calls? (but surely it does in a completely different way I think...)
I wonder if there's any way in Java to dynamically pass parameters in reflection (or not even reflection) without specifying each of them singularly.
EDIT
The instance class declaration is something like:
public class Something {
public void doSth(String par1, String par2, Integer par3) {
//....
}
public void doSthElse(String par1, Boolean par2) {
//....
}
public void doSthElseMore(Integer par1) {
//....
}
}
How I'm invoking each method:
...
for (Method method : instance.getDeclaredMethods()) {
Object[] array = //BL: build array of values to pass to the invoke method.
//1. doSth may be new Object[] {"abc", "def", 123}
//2. doSthElse iteration may be new Object[] {"abc", false}
//3. doSthElseMore iteration may be new Object[] {123}
return method.invoke(instance, array);
}
...
As shown above, each method inside Something class (instance) have a different number of parameters.
On each iteration, the array have a different number of values to pass to the invoke.
Actually as #Boris says all I had to do to complete my job was to convert each parameters to the correct type. In this way Java managed to invoke the correct method of the Something class with the correct parameters types.
My project is a Vert.x application using Vavr and jodd but the last return statement shows how I managed to solve.
public Object invokeMethod(Object service, Method method, RoutingContext routingContext) throws Exception {
MultiMap queryParams = routingContext.queryParams();
Map<String, String> pathParams = routingContext.pathParams();
Buffer body = routingContext.getBody();
// 1. type, 2. name, 3. value
List<Tuple3<Class<?>, String, Object>> list = List.empty();
for (Parameter par : method.getParameters()) {
ParamQuery paramQuery = par.getAnnotation(ParamQuery.class);
if (paramQuery != null) {
list = list.push(new Tuple3<Class<?>, String, Object>(par.getType(), paramQuery.value(),
queryParams.get(paramQuery.value())));
}
}
// TypeConverterManager used to "covnert" each object (String) from the HTTP call to the correct data type
return method.invoke(service, list.reverse()
.map(mapper -> TypeConverterManager.lookup(mapper._1()).convert(mapper._3())).toJavaArray());
}
However, this project can be found on GitHub
Since I notice you are using an Integer instead of a int (so no primitives parameters in your examples), you can send null to all your methods without any problems.
So you can create an array of the correct length and this will work in your case.
public static Object[] getParametersArray(Parameter[] param){
Object[] array = new Object[param.length];
// create default primitive values based on param[#].getType()
return array;
}
Then, all you have to do is to iterate the method:
Labo l = new Labo();
for(Method m : Labo.class.getDeclaredMethods()){
if((m.getModifiers() & Modifier.STATIC) > 0){
System.out.println("SKIP " + m.getName());
continue;
}
try {
m.invoke(l, getParametersArray(m.getParameters()));
} catch (Exception e) {
e.printStackTrace();
}
}
Notice the skipped static method, mostly because if you run this in the method containing the main method, you will have a recursive call.
This was tested with :
public void test(String s){
System.out.println("test String " + s);
}
public void test2(String s1, String s2){
System.out.println("test String " + s1 + " | String " + s2);
}
public void test(Integer s){
System.out.println("test Integer " + s);
}
SKIP main
test String null
test Integer null
SKIP getParametersArray
test String null | String null
Note : If you need to manage some primitive values, you will need to get the type of the parameter to provide a default value instead of null
EDIT: The problem was two-fold, first dictionary should be static and also i was using .contains() where i should have used .containsKey()
I'm trying to do a simple java client and server set up, this is what i have got and i can't seem to find anything wrong with the way i have done it but whenever i run the code i get the output:
Result = Added
Result = This word is not in the dictionary, please use the add function.
Which tells me that the server isn't storing the change made the the map when i am adding a word, is there something really simple i am missing here?
I can add anymore information needed if asked.
This is my client code:
public class Client {
#WebServiceRef(wsdlLocation =
"http://localhost:8080/P1Server/ServerService?wsdl")
public static void main(String[] args) {
try {
package1.ServerService service = new package1.ServerService();
package1.Server port = service.getServerPort();
String result = port.addWord("Test", "This is a test.");
System.out.println("Result = " + result);
result = port.getDefiniton("Test");
System.out.println("Result = " + result);
}catch(Exception ex)
{
System.out.println("Gone Wrong");
}
This is my relevant server code:
#WebService
public class Server {
private **static**ConcurrentHashMap<String,String> dictionary;
public Server() {
this.dictionary = new ConcurrentHashMap<>();
}
#WebMethod
public String addWord(String word, String definition){
if(dictionary.contains(word.toLowerCase())){
return "This word is already in the dictionary, "
+ "please use the update function.";
}else{
dictionary.put(word.toLowerCase(), definition);
return "Added";
}
}
#WebMethod
public String getDefiniton(String word){
if(dictionary.contains(word.toLowerCase())){
return dictionary.get(word);
}else{
return "This word is not in the dictionary, "
+ "please use the add function.";
}
}
Your problem has noting to with webservice.
Issue is with you logic
Modify your methods as follows :
public String addWord(String word, String definition) {
if (dictionary.containsKey(word.toLowerCase())) {
return "This word is already in the dictionary, "
+ "please use the update function.";
} else {
dictionary.put(word.toLowerCase(), definition);
return "Added";
}
}
public String getDefiniton(String word) {
if (dictionary.containsKey(word.toLowerCase())) {
return dictionary.get(word.toLowerCase());
} else {
return "This word is not in the dictionary, "
+ "please use the add function.";
}
}
It will work.
Hope this helps.
Web services are stateless by nature. Every web request will get its own contexts and instances. So, the instance of Server that served the port.addWord() request can be different than the one that served port.getDefinition(). In that case, the dictionary map that had the result placed into it is different than the one used to retrieve the results.
In order for this to work, the data needs to be persisted somehow on the Server side. This can be done via a database. Or, if you're just doing this for testing purposes, change the definition of dictionary to be static so that all instances of Server share the same map.
Define dictionary as static variable. So that every instance of Web service instances that get created in the server side will use same dictionary to put/get data.
private static ConcurrentHashMap<String,String> dictionary;