I've got a pretty simple script that simply accesses a resources properties via a ValueMap. Except for some reason I keep getting an unexpected token error were the if statement starts. This has to be something I'm just overlooking.
public class headerComponent{
ValueMap property = resource.adaptTo(ValueMap.class);
if(property != null) {
pageHeader = property.get("pageHeader", "");
}
}
Any ideas? Thanks for the help.
Because you are using if direct inside your class. This should be inside a function.
For Ex:
public class headerComponent{
ValueMap property = resource.adaptTo(ValueMap.class);
public void getMeProp()
{
if(property != null) {
pageHeader = property.get("pageHeader", "");
}
}
}
If you want to return your string then use public String getMeProp() and in the end of the function return pageHeader. Depends how you want to implement.
Related
Hi guys it is my addAll method and I think solving this way isn't optimize version, can you offer me a simpler version by the way. Any help into this would be appreciated!
#Override
public ApiResponse addAll(HttpHeaders headers, List<NewsDto> newsDtoList) {
for (NewsDto newsDto : Objects.requireNonNull(newsDtoList)) {
News news = new News();
if (newsDto.getUserId() != null) news.setUserId(newsDto.getUserId());
if (newsDto.getTitleRu() != null) news.setTitleRu(newsDto.getTitleRu());
if (newsDto.getTextRu() != null) news.setTextRu(newsDto.getTextRu());
if (newsDto.getTitleUz() != null) news.setTitleUz(newsDto.getTitleUz());
if (newsDto.getTextUz() != null) news.setTextUz(newsDto.getTextUz());
if (newsDto.getTitleEng() != null) news.setTitleEng(newsDto.getTitleEng());
if (newsDto.getTextEng() != null) news.setTextEng(newsDto.getTextEng());
newsRepo.save(news);
}
return new ApiResponse(true, "all list saved");
}
I try mapped with mapstruct but my entity class extend some fields at another class thats why mapstruct could't see any fields in class and I try solve this way.
Refactor your code slightly with the following considerations:
Your null checks aren't useful; if the field is null on one, it will be set to null on the other anyway. For example, in the original code you have:
if (newsDto.getUserId() != null) news.setUserId(newsDto.getUserId());
but if newsDto.getUserId() returns null then using this object on the setter doesn't change anything unless a default value is already presesnt.
So you can just write
News news = new News();
news.setUserId(newsDto.getUserId());
news.setTitleRu(newsDto.getTitleRu());
news.setTextRu(newsDto.getTextRu());
news.setTitleUz(newsDto.getTitleUz());
news.setTextUz(newsDto.getTextUz());
news.setTitleEng(newsDto.getTitleEng());
news.setTextEng(newsDto.getTextEng());
Or if you need some special logic for default values, you could make a method for that too
public String defaultValue(String raw) {
if (raw == null || raw.trim().isEmpty()) {
return null; // or return an empty string or whatever you want
}
return raw;
}
Then you can use this in your mapping method
News news = new News();
news.setUserId(defaultValue(newsDto.getUserId()));
news.setTitleRu(defaultValue(newsDto.getTitleRu()));
news.setTextRu(defaultValue(newsDto.getTextRu()));
news.setTitleUz(defaultValue(newsDto.getTitleUz()));
news.setTextUz(defaultValue(newsDto.getTextUz()));
news.setTitleEng(defaultValue(newsDto.getTitleEng()));
news.setTextEng(defaultValue(newsDto.getTextEng()));
The above code should be in its own method, something like
public News toEntity(NewsDto newsDto) {
... // see above
return news;
}
Then you can test this method for the conversion of one news dto to its respective entity class.
After that, use it in the loop you had:
#Override
public ApiResponse addAll(HttpHeaders headers, List<NewsDto> newsDtoList) {
for (NewsDto newsDto : Objects.requireNonNull(newsDtoList)) {
newsRepo.save(toEntity(newsDto));
}
return new ApiResponse(true, "all list saved");
}
You could use ModelMapper. It should map your DTO to Entity as long as the variable names match.
private static final ModelMapper modelMapper = new ModelMapper();
public static News convertNewsDTOToNewsEntity(NewsDTO newsDTO) {
return modelMapper.map(newsDTO, News.class);
}
I have a simple, but incredibly ugly looking method.
The issue I am having is that I feel this can be done a million times more elegantly. In addition, I would also like to scan a method for more than one annotation, and not just the Rest endpoint declarations.
I feel this can be done through a stream of Annotations[] (method.getDeclaredAnnotations()) and filtered by a List<Annotation> restEndpointAnnotationsAndOtherAnnotations, but I cannot seem to get it to work.
Any help would be greatly appreciated. I think it's probably a fairly fun challenge for some people. The primary issue I am getting (I think) is trying to convert Class<? extends Annotation> to Annotation, but perhaps I am missing the mark.
public RestEndpoint mapToRestEndpoint(Method method) {
String url = null;
if (method.isAnnotationPresent(GetMapping.class)) {
url = method.getAnnotation(GetMapping.class).value()[0];
} else
if (method.isAnnotationPresent(PutMapping.class)) {
url = method.getAnnotation(PutMapping.class).value()[0];
} else
if (method.isAnnotationPresent(PostMapping.class)) {
url = method.getAnnotation(PostMapping.class).value()[0];
} else
if (method.isAnnotationPresent(PatchMapping.class)) {
url = method.getAnnotation(PatchMapping.class).value()[0];
} else
if (method.isAnnotationPresent(DeleteMapping.class)) {
url = method.getAnnotation(DeleteMapping.class).value()[0];
} else
if (method.isAnnotationPresent(RequestMapping.class)) {
url = method.getAnnotation(RequestMapping.class).value()[0];
} else return null;
return new RestEndpoint(url, true);
}
Where RestEndpoint is a simple POJO
#Value
public class RestEndpoint {
#NonNull String endpoint;
boolean isPublic;
}
I can actually find where it matches the Rest mapping using streams, but I cannot then apply the .value() method to it (since it doesn't know what annotation it is, and would be just as tedious to then cast to multiple annotation types)
EDIT:
This is a pretty handy way of getting the information on methods if anyone is interested.
ApplicationContext context = ((ContextRefreshedEvent) event).getApplicationContext();
context.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
Problem is in getAnnotation as it need concrete annotation class to know that it has somethings like value(). You can create helper method that try to invoke value() on given object and do other parsing.
private String getUrl(Method method, Class<? extends Annotation> annotationClass){
Annotation annotation = method.getAnnotation(annotationClass);
String[] value;
try {
value = (String[])annotationClass.getMethod("value").invoke(annotation);
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
return null;
}
return value[0];
}
Then use it like this:
String url = Stream.of(
GetMapping.class, PutMapping.class, PostMapping.class, PatchMapping.class, DeleteMapping.class, RequestMapping.class)
.filter(clazz -> method.isAnnotationPresent(clazz))
.map(clazz -> getUrl(method, clazz))
.findFirst().orElse(null);
I have a below class in which isValid method is being called.
I am trying to extract few things from Record object in the isValid method. And then I am validating few of those fields. If they are valid, then I am populating the holder map with some additional fields and then I am populating my DataHolder builder class and finally return the DataHolder class back.
If they are not valid, I am returning null.
Below is my class:
public class ProcessValidate extends Validate {
private static final Logger logger = Logger.getInstance(ProcessValidate.class);
#Override
public DataHolder isValid(String processName, Record record) {
Map<String, String> holder = (Map<String, String>) DataUtils.extract(record, "holder");
String deviceId = (String) DataUtils.extract(record, "deviceId");
Integer payId = (Integer) DataUtils.extract(record, "payId");
Long oldTimestamp = (Long) DataUtils.extract(record, "oldTimestamp");
Long newTimestamp = (Long) DataUtils.extract(record, "newTimestamp");
String clientId = (String) DataUtils.extract(record, "clientId");
if (isValidClientIdDeviceId(processName, deviceId, clientId) && isValidPayId(processName, payId)
&& isValidHolder(processName, holder)) {
holder.put("isClientId", (clientId == null) ? "false" : "true");
holder.put("isDeviceId", (clientId == null) ? "true" : "false");
holder.put("abc", (clientId == null) ? deviceId : clientId);
holder.put("timestamp", String.valueOf(oldTimestamp));
DataHolder dataHolder =
new DataHolder.Builder(record).setClientId(clientId).setDeviceId(deviceId)
.setPayId(String.valueOf(payId)).setHolder(holder).setOldTimestamp(oldTimestamp)
.setNewTimestamp(newTimestamp).build();
return dataHolder;
} else {
return null;
}
}
private boolean isValidHolder(String processName, Map<String, String> holder) {
if (MapUtils.isEmpty(holder)) {
// send metrics using processName
logger.logError("invalid holder is coming.");
return false;
}
return true;
}
private boolean isValidpayId(String processName, Integer payId) {
if (payId == null) {
// send metrics using processName
logger.logError("invalid payId is coming.");
return false;
}
return true;
}
private boolean isValidClientIdDeviceId(String processName, String deviceId, String clientId) {
if (Strings.isNullOrEmpty(clientId) && Strings.isNullOrEmpty(deviceId)) {
// send metrics using processName
logger.logError("invalid clientId and deviceId is coming.");
return false;
}
return true;
}
}
Is my isValid method doing lot of things? Can it be broken down in multiple parts? Or is there any better way to write that code?
Also I don't feel great with the code I have in my else block where I return null if record is not valid. I am pretty sure it can written in much better way.
Update:
In my case this is what I was doing. I am calling it like this:
Optional<DataHolder> validatedDataHolder = processValidate.isValid(processName, record);
if (!validatedDataHolder.isPresent()) {
// log error message
}
// otherwise use DataHolder here
So now it means I have to do like this:
boolean validatedDataHolder = processValidate.isValid(processName, record);
if (!validatedDataHolder) {
// log error message
}
// now get DataHolder like this?
Optional<DataHolder> validatedDataHolder = processValidate.getDataHolder(processName, record);
You are correct isValid() is doing too many things. But not only that, when most of us see a method that is called isValid() - we expect a boolean value to be returned. In this case, we're getting back and instance of DataHolder which is counterintuitive.
Try to split the things that you do in the method, for example:
public static boolean isValid(String processName, Record record) {
return isValidClientIdDeviceId(processName, record) &&
isValidPayId(processName, record) &&
isValidHolder(processName, record);
}
and then construct DataHolder in a different method, say:
public static Optional<DataHolder> getDataHolder(String processName, Record record) {
Optional<DataHolder> dataHolder = Optional.empty();
if (isValid(processName, record)) {
dataHolder = Optional.of(buildDataHolder(processName, record));
// ...
}
return dataHolder;
}
It will make your program easier to both read and maintain!
I think things start with naming here.
As alfasin is correctly pointing out, the informal convention is that a method named isValid() should return a boolean value. If you really consider returning a DataHolder; my suggestion would be to change name (and semantics a bit), like this:
DataHolder fetchHolderWithChecks(String processName, Record ...)
And I wouldn't return null - either an Optional; or simply throw an exception. You see, don't you want to tell your user about that error that occured? So when throwing an exception, you would have a mean to provide error messages to higher levels.
On validation itself: I often use something like this:
interface OneAspectValidator {
void check(... // if you want to throw an exception
boolean isValid(... // if you want valid/invalid response
And then various implementations of that interface.
And then, the "validation entry point" would somehow create a list, like
private final static List<OneAspectValidator> validators = ...
to finally iterate that list to validate those aspects one by one.
The nice thing about that approach: you have the code for one kind of validation within one dedicated class; and you can easily enhance your validation; just by creating a new impl class; and adding a corresponding object to that existing list.
I know this might not be directly actionable, but the first thing you should do if you want to clean up this code is to use OO (Object-Orientation). If you are not using OO properly, then there is no point arguing the finer details of OO, like SRP.
What I mean is, I couldn't tell what you code is about. Your classnames are "ProcessValidate" (is that even a thing?), "Record", "DataHolder". That is pretty suspect right there.
The string literals reveal more about the domain ("payId", "deviceId", "clientId") than your identifiers, which is not a good sign.
Your code is all about getting data out of other "objects" instead of asking them to do stuff (the hallmark of OO).
Summary: Try to refactor the code into objects that reflect your domain. Make these objects perform tasks specific to their responsibilities. Try to avoid getting information out of objects. Try to avoid setting information into objects. When that is done, it will be much more clear what SRP is about.
I'm pretty new to Java so bear with me. I can't for the life of me figure out why I'm getting
a cannot find symbol error on resourceResolver.resolve. When on the line above it I'm defining the variable.
Maybe this is something simple I'm missing but I can't figure this out and I feel like I've
stared at this way to long.
private static final String ROOTCHILD = "rootChild";
public void setResource(Resource resource) {
this.resource = resource;
}
public void setProperties(ValueMap properties) {
this.properties = properties;
}
public Page getRootPage() {
ResourceResolver resourceResolver = getResource().getResourceResolver();
return (this.properties != null)
? resourceResolver.resolve(
properties.get( ROOTCHILD,currentPage.getPath())).adaptTo(Page.class)
: null;
}
My guess here (never worked with sling and haven't used Java for a while):
I think the problem is you initialized the ValueMap properties so that it doesn't contain Strings or HttpServletRequests, but something else. The .resolve() method only accepts either a String or an HttpServletRequest. (Or two parameters, but you're only passing one, so that one can't be the case.) There is no .resolve() method found accepting the parameters you try to give it, so that symbol is not found!
To see the true error, rewrite your code and compile it:
public Page getRootPage() {
if( properties == null ) {
return null;
}
YYYYYY resource = getResource();
ResourceResolver resourceResolver = resource.getResourceResolver();
String path = currentPage.getPath();
String rootChild = properties.get( ROOTCHILD, path );
XXXXXX rc = resourceResolver.resolve( rootChild );
return rc.adaptTo( Page.class );
}
I'm calling render() with a few arguments, the first of which is a String argument that I got as a parameter:
public static void action(String url) {
...
render(url,...);
}
I'm getting this error:
The template http://the.contents.of/urlParameter does not exist.
Now, I'm debugging through render(), where I see this snippet:
protected static void render(Object... args) {
String templateName = null;
if (args.length > 0 && args[0] instanceof String && LocalVariablesNamesTracer.getAllLocalVariableNames(args[0]).isEmpty()) {
// I'm getting into this branch
templateName = args[0].toString();
} else {
templateName = template();
}
renderTemplate(templateName, args);
}
What is the if trying to accomplish? Why am I getting into it - is it because I'm not using a local variable for url? Is this documented? What's the reasoning here?
I'm using version 1.2.x-c40cf37 (that's somewhere after 1.2.4).
If you provide a string as the first argument, then it assumes that it is the name of the template to render.
Example:
render("#password", url);
That will render the password-template and pass the url variable to it.
In your case you could do something like this instead:
render("#action", url);
EDIT:
As an alternative you could also do something like this:
renderArgs.put("url", url);
render();
Hope it helps.