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 );
}
Related
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 want to format some files in SpringBoot with one request for each file. With each request, I have to call the getOutputFolder(dirName) method to get an output path in order to save the file in the expected path but my solution comes with high at overhead cost. I want to define one constant and then when I have to call the function I instead call this. But I feel it seems to be wrong or at least like a sneaky way to do. Is there any better way to solve this problem?
private static final String OUTPUT_FOLDER_PATH = getOutputFolderPath();
private String getOutputFolder(String dirName) {
String pathStr = getOutputFolderPath() + dirName + File.separator + "submit" + File.separator;
Path outputDirPath = Paths.get(pathStr);
Path path = null;
boolean dirExists = Files.exists(outputDirPath);
if (!dirExists) {
try {
path = Files.createDirectories(outputDirPath);
} catch (IOException io) {
logger.error("Error occur when create the folder at: {}", pathStr);
}
}
return dirExists ? pathStr : Objects.requireNonNull(path).toString();
}
You might want to look at jcache.
To do this, you need to install it to your Spring Boot project
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'javax.cache:cache-api:1.1.0'
// or the maven equivalent if you are using maven
Then create a org.springframework.cache.CacheManager bean to configure the cache.
#Bean
public CacheManager cacheManager() {
CachingProvider cachingProvider = Caching.getCachingProvider();
CacheManager cacheManager = cachingProvider.getCacheManager();
// The class arguments is <String, String> because the method to cache accepts a String and returns a String
// just explore this object for the config you need.
MutableConfiguration<String, String> configuration = new MutableConfiguration<>();
String cacheName = "OUTPUT_FOLDER_CACHE";
cacheManager.createCache(cacheName, configuration);
return cacheManager;
}
When this is setup, you can now annotated the method to be cached.
#Cacheable(
cacheNames = { "OUTPUT_FOLDER_CACHE" }, // The same string in config
unless = "#result == null" // Dont' cache null result; or do, if you need it.
)
String getOutputFolder(String dirName) {
// method contents...
}
When properly configured: the method will return the cache value if it exists, or run the actual method, cache the result and return the result if the cached value does not exist.
You can solve that issue by using ThreadLocal.
A threadlocal can store value and you can make useful for yourself. Suppose if your getOutputFolderPath() is different for different request then you can
store the getOutputFolderPath() value while a new request dispatched on server and you can do your all operation upto your request live.
See Threadlocal Docs
#Service
public class FileSaveService {
private static final ThreadLocal<String> path=new ThreadLocal<>();
#PostConstruct
public void init() {
setPath(getOutputFolderPath());
}
public void setPath(String pathString) {
path.set(pathString);
}
public String getPath() {
if(path.get() == null) return getOutputFolderPath();
return path.get();
}
#PreDestroy
public void destroy() {
path.remove();
}
}
Changing path to hardcoded string - works, however this is not an option.
Test fails because it cannot find the resource, even though it's there.
Cities.java
/*
This Singleton Class loads cities.json into an ArrayList and by using a boolean method isCity can tell if passed
String is city or not.
*/
public class Cities
{
private static Cities single_instance = null;
ArrayList<City> cityArrayList;
private Cities() throws IOException
{
String path = new ClassPathResource("cities.json").getFile().getAbsolutePath();
Gson gson = new Gson();
Type cityListType = new TypeToken<ArrayList<City>>()
{
}.getType();
cityArrayList = gson.fromJson(new FileReader(path), cityListType);
}
public static Cities getInstance() throws IOException
{
if (single_instance == null)
single_instance = new Cities();
return single_instance;
}
public boolean isCity(String cityToLookFor)
{
for (City city : cityArrayList)
{
if (city.getName().strip().equalsIgnoreCase(cityToLookFor))
{
return true;
}
}
return false;
}
}
CitiesTest.java
class CitiesTest
{
Cities cities = Cities.getInstance();
CitiesTest() throws IOException {}
#Test
void isCityTest2()
{
assertFalse(cities.isCity("USA"), "City not found in localDbase");
}
#Test
void isCityTest3()
{
assertEquals(true, cities.isCity("Paris"), "City not found in localDbase");
}
}
pom.xml
https://pastebin.com/fMQNknM1
java.io.FileNotFoundException: class path resource [cities.json] cannot be resolved to URL because it does not exist
at org.springframework.core.io.ClassPathResource.getURL(ClassPathResource.java:195)
at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:150)
at gr.serresparc.palantir.repository.Cities.<init>(Cities.java:24)
at gr.serresparc.palantir.repository.Cities.getInstance(Cities.java:35)
at gr.serresparc.palantir.repository.CitiesTest.<init>(CitiesTest.java:12)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at
Remove the <targetPath>..</targetPath> from the resources section of the pom.xml. You might as well remove the whole resources section.
The executed test code is not looking for the file in within src/main/resources but within target/classes. The classpath resource cities.json needs to appear there.
Please be also aware that if the application is shipped as a spring boot jar file then it is not possible to read a classpath resource as file.
Try to change the code as below,
String path = new ClassPathResource("classpath:cities.json").getFile().getAbsolutePath();
Please, note I have added classpath: in front of cities.json.
EDIT
You can also get the InputStreamReader like below, First, inject ResourceLoader.
#Autowired
ResourceLoader resourceLoader;
Then load json file just like below:
cityArrayList = gson.fromJson(new JsonReader(resourceLoader.getResource("classpath:cities.json").getInputStream()), cityListType);
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.
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.