Sunday, October 30, 2011

How to read an external file in CQ

Use Case: You want to read something from external file

Solution : There are multiple ways you can do this I will mentioned two of them here,

1) You can put them under /resources folder under /src/main for example if you want to read a file called "test.txt" then you can put file under /src/resources/<any-sub-foler>/<file-to-read>

Then use following code to read file
InputStream stream = getClass().getResourceAsStream("<file-to-read>");
in above case it would be InputStream stream = getClass().getResourceAsStream("/test.txt");

Good practice is to follow convention, For example if your source code is under /src/main/java/com/test/mytest/Test.java Then your resource should be in location /resource/com/test/myTest/<Your File>

Then you can use something like


InputStream in = Test.class.getResourceAsStream("<Your File>");


2) You can put file in repository (Any repository location is file) and use Node data as stream to read.

then use following code,
InputStream in = node.getNode("jcr:content").getProperty("jcr:data").getStream();

How to Upload File in CRX


File file = new File("/mypath/mydocument.pdf");
FileInputStream is = new FileInputStream(file);
mimeType = "application/octet-stream";
Node node = session.getNode(parentNode);
ValueFactory valueFactory = session.getValueFactory();
Binary contentValue = valueFactory.createBinary(is);
Node fileNode = node.addNode(fileName, "nt:file");
fileNode.addMixin("mix:referenceable");
Node resNode = fileNode.addNode("jcr:content", "nt:resource");
resNode.setProperty("jcr:mimeType", mimeType);
resNode.setProperty("jcr:data", contentValue);
Calendar lastModified = Calendar.getInstance();
lastModified.setTimeInMillis(lastModified.getTimeInMillis());
resNode.setProperty("jcr:lastModified", lastModified);
session.save();
return fileNode.getPath();

Using Sling FSResource Provider (To convert File system resource to jcr resource):

Please see what all different kind of resource provider is present



FS resource provider essentially mapped your file system path with repository path. That mean you can adapt your file system resource to a sling resource object and can do fun stuff with it.

1) More information can be obtained here http://sling.apache.org/documentation/bundles/accessing-filesystem-resources-extensions-fsresource.html

2) FSResource provider is not present OOTB, so you have to first embed this in your project,

in your pom.xml do following (I hope you know how to do this, if not ping me :D),

<embedded>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.fsresource</artifactId>
<target>/apps/<your app>/install</target>
</embedded>

And

<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.fsresource</artifactId>
<version>1.1.3-SNAPSHOT</version>

</dependency>

3) create a osgi config with following setting 

Name of file: org.apache.sling.fsprovider.internal.FsResourceProvider-samples.xml

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
          jcr:primaryType="sling:OsgiConfig"
          provider.roots=“<some repository path, this can be any random path>"
          provider.file=“<Absolute path for file in file system>"
          provider.checkinterval="1000”/>
4) Then in your code you can do following,

import java.io.File;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceProvider;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component 
@Service
public class FileSystemFileTest implements FileSystemTest {
private static final Logger LOG = LoggerFactory.getLogger(FileSystemFileTest.class);
@Reference
ResourceResolverFactory resourceResolverFactory;
@Reference
ResourceProvider resourceProvider;
ResourceResolver resourceResolver = null;
@Override
public File getFile() {
try {
resourceResolver = resourceResolverFactory.getAdministrativeResourceResolver(null);
Resource resource =  resourceResolver.getResource("<some repository path from above>");
File file  = resource.adaptTo(File.class);
if(null!=file){
return file;
}
} catch (LoginException e) {
LOG.error(e.getMessage());
}finally{
if(null!=resourceResolver){
resourceResolver.close();
}
}
return null;
}
}

Note that in this case you need to have file in your file system. You will essentially use this approach if you want to get jcr resource view of your file system to do something with it. For example if your method can only accept resource object as argument.

Friday, October 28, 2011

How to set up User/Group/Profile reverse replication

Assumption: You are using CQ5.4

Use Case: You are creating users and group on publish and want to move them on author

Prerequisite: http://helpx.adobe.com/cq/kb/HowToUseReverseReplication.html

Solution:

Create two launcher on publish with run mode as publish which listen to rep:User and rep:Group on path /home/user and /home/group respectively and trigger reverse replication OR create one with rep:Authorizable and listen to /home
So it would be like

For User and Group Option 1

Event Type: Created
Nodetype: rep:User
Path: /home/users
Condition: cq:distribute!=
Workflow: /etc/workflow/models/reverse_replication
Run Mode: publish

AND

Event Type: Created
Nodetype: rep:Group
Path: /home/group
Condition: cq:distribute!=
Workflow: /etc/workflow/models/reverse_replication
Run Mode: publish

For User and Group Option 2

Event Type: Created
Nodetype: rep:Autherizable
Path: /home
Condition: cq:distribute!=
Workflow: /etc/workflow/models/reverse_replication
Run Mode: publish


For Profile

A. Code Changes
Change your code/form, that a profile update POST is adding
cq:distribute=true
source=authorname
as key=value into the /profile folder.

B. Setup on Publish Instance
Add Workflow Launcher Configuration:
Event Type: Modified
Nodetype: sling:Folder
Path: /home/users/(.*)/profile
Condition: cq:distribute!=
Workflow: /etc/workflow/models/reverse_replication
Run Mode: publish

C. Setup on Author Instance
Create Workflow Model:
One process step
Implementation: com.day.cq.wcm.workflow.process.ActivatePageProcess
Add Workflow Launcher Configuration:
Event Type: Modified
Nodetype: sling:Folder
Path: /home/users/(.*)/profile
Condition: source!=
Workflow: select your model created above
Run Mode: author


Important Note:
1) Above method will only work for created trigger. If you are modifying user profile on publish then you have to do same thing for modified trigger too.

2) If you have multiple publish instance then you have to make sure that all the publish instance are in sync. For that you need to again replicate those user back to other publish instance using launcher on author.
See an example how comment activation work. when user post a comment in publish instance a comment post servlet get called which add cq:distribute property to that node along with other properties -> this make node eligible for reverse replication -> publish instance put that node in outbox -> author reverse_publish agent poll data from publish and put it in /content/usergenerated -> Workflow launcher on Node Created /content/usergenerated of type cq:comment get called -> this calls workflow associated with it (Which is Comment Moderation workflow) -> workflow has logic to activate comment back to all publish instance based on whether it requires moderation or not (Simple replicator.replicate)


Thanks Cédric Hüsler from Adobe to provide this information

Wednesday, October 26, 2011

How CQ (Till CQ5.4) Starts when you click on quickstart jar file

1. the quickstart is unpacked to the crx-quickstart directory
2. then cqse is started
3. cqse starts the crx webapp
4. the crx webapp starts the repository
5. during repository startup, it checks the repository/install folder for content packages and installs them
6. then the launchpad webapp is started
7. felix starts the core bundles
8. one of the core bundles is the JCR installer, which searches all 'install' nodes in the repository and registers those bundles
9. felix starts the newly registered bundles
10. As a part of newly started bundles CQ apps load

Thanks Toby from Adobe for this information

Friday, October 21, 2011

How to make sure that Links are always valid on a page

Use Case: Some time link checker makes certain links as invalid (As it is not able to verify it).

Solution: You can add x-cq-linkchecker="valid" parameter in the <a> tag to make sure that links are always mark as valid by CQ. In this case link checker will check the link but will mark it valid.

You can optionally use x-cq-linkchecker="skip" in the <a> as well. In this case link checker will not even check for validity for link.

There are other options too to make all external link as valid,

Option 2: You can disable external link checker entirely by going to felix console



Option 3: Additionally you can add override pattern to disable link checking for certain domain



Example for override pattern: for http://www.day.com you will write ^http://www\.day\.com/

Note:
Following error with respect to external link checker,
com.day.cq.rewriter.linkchecker.impl.LinkInfoStorageImpl No more external links allowed for host <Host Name> Maximum of 128 reached. means,
External link checker only checks first 128 link per host basis, And you can find those entry under /var/linkchecker/<protocol>. Unfortunately this is not configurable yet.

Tuesday, October 18, 2011

How to flush Cache using Curl Command in Adobe CQ / AEM

Use Case Flush cache for a path in regular interval of time

Solution You can use following command to flush cache


curl -H "CQ-Action:Flush" -H "CQ-Handle: /content/geometrixx/en/toolbar" -H "CQ-Path:/content/geometrixx/en/toolbar" -H "Content-Length: 0" -H "Content-Type: application/octet-stream" http://dispatcher-server-hostname:port/dispatcher/invalidate.cache

In Newer version of dispatcher try this

curl -H "CQ-Action: DELETE" -H "CQ-Handle:/content/geometrixx/en/toolbar" -H "CQ-Path:/content/geometrixx/en/toolbar" -H "Content-Length: 0" -H "Content-Type: application/octet-stream" http://dispatcher-server-hostname:port/dispatcher/invalidate.cache

Expected Response: <H1>OK</H1>

If you see error like
[Thu May 30 09:23:53 2013] [D] [91650(140735211479424)] Found farm <Your FARM>
[Thu May 30 09:23:53 2013] [D] [91650(140735211479424)] checking [/dispatcher/invalidate.cache]
[Thu May 30 09:23:53 2013] [W] [91650(140735211479424)] Flushing rejected from <Something>
That mean you are not in allowed list of client to flush cache. In that case please allow your IP (<SOMETHING>) in above case in allowedClient section of dispatcher.any

 /allowedClients
        {
         /0000
          {
          /glob "*"
          /type "deny"
          }
        /0001
          {
          /glob "<Something>"
          /type "allow"
          }
... More allowed
        }
      }

Important Note: Make sure that from outside (DMZ may be) no one should be able to run this command. This command should be allowed to run only from some trusted boxes

Thanks Andrew Khoury from Adobe for this information

Monday, October 17, 2011

How to rotate TarJournal in Shared Nothing Clustering

Use Case: Your tar Journal is growing and consuming a lot of space.

Solution: If you are using shared clustered Please refer http://dev.day.com/content/kb/home/Crx/Troubleshooting/JournalTooMuchDiskSpace.html


For shared nothing clustering you need following configuration (If you want rotation after every 24 Hour) in repository.xml

<Journal class="com.day.crx.persistence.tar.TarJournal">
<param name="maxFileSize" value="104857600" />
<param name="maximumAge" value="PT24H" />
</Journal>

Please note that Age specified as duration in ISO 8601 or plain format. Journal files that are older than the configured age are automatically deleted. The default is "P1M", which means files older than one month are deleted.

More detail can be found here http://en.wikipedia.org/wiki/ISO_8601

Important Note : If you do above configuration then make sure that other instance in the cluster is not down for more than specified time in "maximumAge", Otherwise cluster will get Out Of sync because of rotation of tarJournal.

Saturday, October 15, 2011

How to find all the pages modified or activated after certain time

Use Case: You want to create package of all the nodes modified after certain date and time. This can be use ful in Migration Projects. If you have problem with one of the activation agent and you want to activate pages / Asset to that agent based on time.

Solution: You can use following packages to achieve this use case



You can also refer code form Here

1) Install attached package using package manager
2) Go to http://<host>:<port>/apps/tools/components/listPage.html
3) Select Date and time
4) Then click on submit
5) You can click on create package of modified pages to create package


Feel free to change the code based on your requirement

Wednesday, October 5, 2011

How to clone publish server in CQ

There are multiple way to clone the publish server

With downtime::

1) Create new replication agent for other server.
2) Stop publish server you want to clone
3) take backup of crx-quickstart folder
4) put it on other server
5) Start publish 1 and then publish 2
6) once both the server started pending replication queue will get clear

Without down time

1) Create new replication agent for other server
2) Take online back up of publish 1
3) put online backup on server 2
4) restore online backup
5) start publish 2
6) All the queue pending at publish2 replication agent will get clear.

After the clone change disaptcher.any to add extra renderer.