1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.content.observer;
35
36 import info.magnolia.cms.security.UserManager;
37 import info.magnolia.content.filter.BootstrapPathFilter;
38 import info.magnolia.context.Context;
39 import info.magnolia.context.MgnlContext;
40 import info.magnolia.dirwatch.WatcherCallback;
41 import info.magnolia.importexport.DataTransporter;
42 import info.magnolia.importexport.postprocessors.ActivationStatusImportPostProcessor;
43 import info.magnolia.init.MagnoliaConfigurationProperties;
44 import info.magnolia.jcr.util.PropertyUtil;
45 import info.magnolia.objectfactory.Components;
46 import info.magnolia.registry.RegistrationException;
47 import info.magnolia.task.Task;
48 import info.magnolia.task.TasksManager;
49 import info.magnolia.task.definition.registry.TaskDefinitionRegistry;
50 import info.magnolia.content.task.definition.ContentImporterTaskDefinition;
51
52 import java.io.File;
53 import java.io.IOException;
54 import java.nio.file.FileVisitResult;
55 import java.nio.file.Files;
56 import java.nio.file.Path;
57 import java.nio.file.SimpleFileVisitor;
58 import java.nio.file.attribute.BasicFileAttributes;
59 import java.security.NoSuchAlgorithmException;
60 import java.util.Arrays;
61 import java.util.Collection;
62 import java.util.Date;
63 import java.util.Map;
64
65 import javax.inject.Inject;
66 import javax.jcr.Node;
67 import javax.jcr.RepositoryException;
68 import javax.jcr.Session;
69
70 import org.apache.commons.lang3.StringUtils;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73
74 import com.google.common.collect.ImmutableMap;
75
76
77
78
79
80 public class TaskCreatorWatcherCallback implements WatcherCallback {
81
82 private static final Logger log = LoggerFactory.getLogger(TaskCreatorWatcherCallback.class);
83
84 private final TasksManager tasksManager;
85 private final BootstrapPathFilter pathFilter;
86 private final TaskDefinitionRegistry taskDefinitionRegistry;
87 private final Context context;
88 private final MagnoliaConfigurationProperties properties;
89
90 @Inject
91 public TaskCreatorWatcherCallback(TasksManager tasksManager, TaskDefinitionRegistry taskDefinitionRegistry, BootstrapPathFilter pathFilter, Context context, MagnoliaConfigurationProperties properties) {
92 this.tasksManager = tasksManager;
93 this.taskDefinitionRegistry = taskDefinitionRegistry;
94 this.pathFilter = pathFilter;
95 this.context = context;
96 this.properties = properties;
97 }
98
99
100
101
102 public TaskCreatorWatcherCallback(TasksManager tasksManager, TaskDefinitionRegistry taskDefinitionRegistry, Context context) {
103 this(tasksManager, taskDefinitionRegistry, Components.getComponent(BootstrapPathFilter.class), context, Components.getComponent(MagnoliaConfigurationProperties.class));
104 }
105
106
107
108
109 public TaskCreatorWatcherCallback(TasksManager tasksManager, TaskDefinitionRegistry taskDefinitionRegistry) {
110 this(tasksManager, taskDefinitionRegistry, Components.getComponent(BootstrapPathFilter.class), Components.getComponent(Context.class), Components.getComponent(MagnoliaConfigurationProperties.class));
111 }
112
113
114
115
116 @Deprecated
117 public TaskCreatorWatcherCallback(TasksManager tasksManager) {
118 this(tasksManager, Components.getComponent(TaskDefinitionRegistry.class));
119 }
120
121 @Override
122 public void added(Path path) {
123
124
125 if (Files.isDirectory(path)) {
126 try {
127 Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
128 @Override
129 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
130 if (pathFilter.apply(file)) {
131 added(file);
132 }
133 return FileVisitResult.CONTINUE;
134 }
135 });
136 } catch (IOException e) {
137 log.error("Failed to communicate file system resource changes recursively: {}", e.getMessage(), e);
138 }
139 }
140 if (Files.exists(path) && pathFilter.apply(path)) {
141 process(path);
142 }
143 }
144
145 @Override
146 public void removed(Path path) {
147
148 }
149
150 @Override
151 public void modified(Path path) {
152 added(path);
153 }
154
155
156
157
158
159 private void process(Path file) {
160 try {
161 String repository = StringUtils.substringBefore(file.getFileName().toString(), ".");
162 Session session = context.getJCRSession(repository);
163 String createTasks = StringUtils.defaultIfEmpty(properties.getProperty(ContentImporterModule.CONTENT_BOOTSTRAP_CREATE_TASKS_PROPERTY_KEY), CreateTasksStrategy.always.name());
164 String checksum = ContentImporterModule.generateChecksum(file);
165 String path = ContentImporterModule.getJCRPath(file);
166 String original = "";
167 if (session.itemExists(path)) {
168 original = PropertyUtil.getString(session.getNode(path), ContentImporterModule.MGNL_CHECKSUM_PROPERTY, StringUtils.EMPTY);
169 }
170 if (!checksum.equals(original)) {
171 if (createTasks.equals(CreateTasksStrategy.always.name()) || (createTasks.equals(CreateTasksStrategy.onchange.name()) && session.itemExists(path))) {
172 if (getTasksForPath(file.toAbsolutePath()).size() == 0) {
173 addTask(file);
174 }
175 } else {
176 log.info("New file detected at '{}', bootstrapping.", file);
177 MgnlContext.setInstance(context);
178 DataTransporter.executeBootstrapImport(file.toFile(), repository);
179 Node node = session.getNode(path);
180 PropertyUtil.setProperty(node, ContentImporterModule.MGNL_CHECKSUM_PROPERTY, checksum);
181 new ActivationStatusImportPostProcessor().postProcessNode(node);
182 session.save();
183 }
184 }
185 } catch (IOException | RepositoryException | NoSuchAlgorithmException e) {
186 log.error("Error while processing changed file at '{}'.", file, e);
187 }
188 }
189
190 private Collection<Task> getTasksForPath(Path path) {
191 Collection<Task> tasks = tasksManager.findTasksByStatus(Arrays.asList(Task.Status.Created, Task.Status.InProgress));
192 tasks.removeIf(task -> !path.toString().equals(task.getContent().get("path")));
193 return tasks;
194 }
195
196
197
198
199
200 private void addTask(Path path) {
201 final String contentTask = "content";
202 try {
203 final ContentImporterTaskDefinitionion/ContentImporterTaskDefinition.html#ContentImporterTaskDefinition">ContentImporterTaskDefinition contentImporterTaskDefinition = (ContentImporterTaskDefinition) taskDefinitionRegistry.get(contentTask);
204 File changedFile = path.toFile();
205 if (changedFile.isFile()) {
206 Task task = new Task();
207 task.setName(contentImporterTaskDefinition.getName());
208 task.setRequestor(UserManager.SYSTEM_USER);
209 task.setStatus(Task.Status.Created);
210 task.setActorIds(contentImporterTaskDefinition.getUsers());
211 task.setGroupIds(contentImporterTaskDefinition.getGroups());
212 task.setComment(changedFile.getName());
213
214 String repository = StringUtils.substringBefore(changedFile.getName(), ".");
215 Map<String, Object> content = ImmutableMap.of(
216 "repository", repository,
217 "path", changedFile.getAbsolutePath(),
218 "modificationDate", new Date());
219
220 task.setContent(content);
221 tasksManager.addTask(task);
222 log.info("File change detected at '{}', pulse task has been sent.", changedFile.getAbsolutePath());
223 }
224 } catch (RegistrationException e) {
225 log.error("Could not retrieve task definition for [{}] task. Content won't be bootstrapped.", contentTask, e);
226 }
227
228 }
229 }