How to set a resource property - aem

I have a Sling Resource object. What is the best way to set or update its property?

It depends on the Sling version:
Sling >= 2.3.0 (since CQ 5.6)
Adapt your resource to ModifiableValueMap, use its put method and commit the resource resolver:
ModifiableValueMap map = resource.adaptTo(ModifiableValueMap.class);
map.put("property", "value");
resource.getResourceResolver().commit();
Sling < 2.3.0 (CQ 5.5 and earlier)
Adapt your resource to PersistableValueMap, use its put and save methods:
PersistableValueMap map = resource.adaptTo(PersistableValueMap.class);
map.put("property", "value");
map.save();
JCR API
You may also adapt the resource to Node and use the JCR API to change property. However, it's a good idea to stick to one abstraction layer and in this case we somehow break the Resource abstraction provided by Sling.
Node node = resource.adaptTo(Node.class);
node.setProperty("property", "value");
node.getSession().save();

As many developers are not fond of using Node API. You can also use ValueMap and ModifiableValueMap APIs to read and update property respectively.
Read Value through ValueMap
ValueMap valueMap = resource.getValueMap();
valueMap.get("yourProperty", String.class);
Write/Modify property through ModifiableValueMap
ModifiableValueMap modifiableValueMap = resource.adaptTo(ModifiableValueMap.class);
modifiableValueMap.put("NewProperty", "Your Value"); //write
modifiableValueMap.put("OldProperty", "Updated Value"); // Modify
After writing property to a node, save these values by committing the 'resourceResolver'
You'll need system/service user for admin resourceResolver.
Go through this documentation for more info about service user.

It is not working in publish. But if user logged-In as admin it will work.
ModifiableValueMap map = resource.adaptTo(ModifiableValueMap.class);
map.put("property", "value");
resource.getResourceResolver().commit();

Related

how to edit custom resource removing just one finalizer

I'm using fabric8 and java operator sdk.
I'd like to remove one custom finalizer in the deleteResource handler of the ResourceController.
What is the suggested way to remove the finalizer and save the custom resource?
I think you should be able to add finalizers using Fabric8 Kubernetes Client. For example, if you want to add/remove finalizers like these in your Custom Resource's metadata:
finalizers:
- finalizer.stable.example.com
You can use either our typed API or typeless API for achieving this. I'm adding examples of both the approaches below using a Dummy Custom Resource provided in our CRDExample:
Typed API (Requires POJOs of Custom Resources)
MixedOperation<Dummy, DummyList, DoneableDummy, Resource<Dummy, DoneableDummy>> dummyCRClient = client
.customResources(dummyCRD, Dummy.class, DummyList.class, DoneableDummy.class);
// Fetch resource fom Kubernetes API server
Dummy dummy2 = dummyCRClient.inNamespace("default").withName("second-dummy").get();
// Get metadata
ObjectMeta objectMeta = dummy2.getMetadata();
// Modify metadata
objectMeta.setFinalizers(Collections.singletonList("finalizer.stable.example.com"));
// Patch it back in Custom Resource
dummy2.setMetadata(objectMeta);
// Patch to Kubernetes
dummyCRClient.inNamespace("default").withName("second-dummy").patch(dummy2);
Typeless API (Provides way to access raw JSON Custom Resources)
try (KubernetesClient client = new DefaultKubernetesClient()) {
CustomResourceDefinitionContext crdContext = new CustomResourceDefinitionContext.Builder()
.withGroup("demo.fabric8.io")
.withPlural("dummies")
.withScope("Namespaced")
.withVersion("v1")
.withName("dummies.demo.fabric8.io")
.build();
// Fetch resource fom Kubernetes API server
Map<String, Object> dummy2 = client.customResource(crdContext).get("default", "second-dummy");
JSONObject dummy2JsonObj = new JSONObject(dummy2);
// Get metadata
JSONObject dummy2ObjectMeta = dummy2JsonObj.getJSONObject("metadata");
// Modify metadata
dummy2ObjectMeta.put("finalizers", new String[] { "finalizer.stable.example.com"});
// Patch it back in Custom Resource
dummy2JsonObj.put("metadata", dummy2ObjectMeta);
// Patch to Kubernetes
client.customResource(crdContext).edit("default", "second-dummy", dummy2JsonObj.toString());
}

How to use DiscoveredResource to traverse to a single entity resource exposed by a RepositoryRestResource

I'm trying to set up a system with multiple applications connecting by use of a discovery server. I can traverse the hal responses to a specific resource, but I'm looking for a solution to get from a collection resource to a single resource and find the data for a specific entity.
In 1 application I have a RepositoryRestResource exposing some object:
#RestRepositoryResource(collectionResourceRel="things", itemResourceRel="thing") public interface ThingRepo extends CrudRepository<Thing,Long> {}
In some other application, I would like to get to a single thing. I have the id (let's say it's 1) and have the relation name of the collection and the single resource.
I would like to use a DiscoveredResource to get a link to this single item resource, or to the collection resource which I can then somehow expand using the ID (which would require a templated resource).
If at all possible I would not like to just add "/1" at the end of the URL.
this is how I currently create a DiscoveredResource to point to the collection resource:
new DiscoveredResource(new DynamicServiceInstanceProvider(discoveryClient, traverson -> traverson.follow("things"));
Should I and is it possible to add a templated link on a collection resource created by a #RepositoryRestResource. Or is there some other trick I am missing?
The solution here is to add a custom method as a #RestResource which exposes a relation with a templates URL you can then follow to.
Repo:
#RestRepositoryResource(collectionResourceRel="things", itemResourceRel="thing") public interface ThingRepo extends CrudRepository<Thing,Long> {
#RestResource(rel = "thing")
Thing findOneById(#Param("id") Long id);
}
Discovery + traverson:
DiscoveredResource resource = new DiscoveredResource(new DynamicServiceInstanceProvider(discoveryClient, traverson -> traverson.follow("things","search","thing"));
Link link = resource.getLink().expand(id);

Query builder API in Cq5

Hi I am implementing a Java module to fetch the pages which has a particular component.
Below is the code snippet which i use, but when running the module am getting an warning Saying that no PredicateEvaluator found for 'sling:resourceType'.
Kindly suggest me the proper way to give resourceType property as query parameter
Map<String, String> predicateMap = new HashMap<String, String>();
predicateMap.put("path","/content/geometrixx-outdoors/en/men");
predicateMap.put("type", "cq:Page");
predicateMap.put("sling:resourceType", "geometrixx-outdoors/components/title");
predicateMap.put("p.limit", "-1");
QueryBuilder queryBuilder = slingScriptHelper.getService(QueryBuilder.class);
com.day.cq.search.Query queryObj = queryBuilder.createQuery(PredicateGroup.create(predicateMap), session);
sling:resourceType is indeed not a valid predicate evaluator. You need to put it as a property:
predicateMap.put("property", "jcr:content/sling:resourceType");
predicateMap.put("property.value", "geometrixx-outdoors/components/title");
As you alre filtering type=cq:Page you have to also include jcr:content in the path to the property.

CQ - Check whether the resource object is valid

I need to check whether the resource object is valid or not for the below 'resource' object. For example If I pass any url like getResource("some path which is not available in cq") in this case I need to restrict it
Resource resource= resourceResolver.getResource(/content/rc/test/jcr:content");
Node node = resource.adaptTo(Node.class);
String parentPagePath= node.getProperty("someproperty").getValue().getString();
Is there any way to do?
If you are using getResource a null check is sufficient. If you use resolve, then you have to use the !ResourceUtil.isNonExistingResource(resource).
On Node level you can check the existence of a property with hasProperty.
As Thomas said , ResourceResolver.getResource() returns null if the path you provide as argument does not exist. A null check on the resource should get your problem solved .
Resource resource= resourceResolver.getResource("some path which is not available in cq");
if(resource != null){
Node node = resource.adaptTo(Node.class);
String parentPagePath= node.getProperty("someproperty").getValue().getString();
}
Just a side note :
Its mostly better to play around with a wrapper than to work with a lower level API unless there is a compelling reason to do so.
Hence , I would recommend you to deal with ValueMap(Sling API) to retrieve/set properties of Node rather than dealing with the Node (JCR API)
Resource resource= resourceResolver.getResource("some path which is not available in cq");
if(resource != null){
ValueMap mapWithAllTheValues = resource.adaptTo(ValueMap.class);
String parentPagePath= mapWithAllTheValues.get("someproperty", String.class);
}
Ref :
https://docs.adobe.com/docs/en/cq/5-6-1/javadoc/org/apache/sling/api/resource/ResourceResolver.html#getResource(java.lang.String)

Get a node form currenPage in CQ5

I have a component that needs to fetch properties from another component on the same page.
Is there a way to get a component's NODE object from currentPage ?
I have the name of the node I need to fetch available in the code.
Assuming the node you need is in Page/jcr:content/yournode:
the Page object has a method getContentResource() which returns you the jcr:content resource node by default. You can also use page.getContentResource("yournode") to get a specific node below jcr:content.
If your node, for some reason is sibling to jcr:content (it shouldn't btw), you can your iterate the children of a resource using resource.listChildren().
Remember, this is all Sling API, so you are managing resources, not nodes. You can get a JCR Node from a resource using resource.adaptTo(Node.class)
Usually every Page class has a method for retrieving nodes within jcr:content that’s a little cleaner:
Node node = CurrentPage.getContentResource("COMPONENTNAME").adaptTo(Node.class);
Developers should check for a resource's existence before adapting it to a Node.
Now that we have a node, we can extract properties from that node.
String title = node.getProperty("jcr:title").getString();
This way you can fetch any properties of a component.
I would like to add that this can be solved in more clean way by delegating request to a servlet provided the problem being solved requires separation of concerns - separate the view from the controller logic.
LINK EXPLAINING IN DETAIL HOW VALIDATION IS DONE USING SLING SERVLETS
Adds validator to a form using below logic that calls the sling servlet hosted
var url = CQ.HTTP.addParameter(dialog.path + '.validator.json', 'value', value);
var result = CQ.HTTP.eval(url);
and in the servlet we access the node and its parents using below logic
final Node currentNode = request.getResource().adaptTo(Node.class);
try {
final Node par = currentNode.getParent();
final NodeIterator nodes = par.getNodes();
// get all form names for the current paragraph system
while (nodes.hasNext()) { //and the business logic
See more at: http://www.citytechinc.com/us/en/blog/2012/04/complex_cq5_dialog_validation.html#sthash.wey3cwdI.dpuf