KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > repo > content > RoutingContentService


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.repo.content;
18
19 import java.io.Serializable JavaDoc;
20 import java.util.Collection JavaDoc;
21 import java.util.HashSet JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Set JavaDoc;
24
25 import org.alfresco.error.AlfrescoRuntimeException;
26 import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy;
27 import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy;
28 import org.alfresco.repo.content.filestore.FileContentStore;
29 import org.alfresco.repo.content.transform.ContentTransformer;
30 import org.alfresco.repo.content.transform.ContentTransformerRegistry;
31 import org.alfresco.repo.policy.ClassPolicyDelegate;
32 import org.alfresco.repo.policy.JavaBehaviour;
33 import org.alfresco.repo.policy.PolicyComponent;
34 import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
35 import org.alfresco.service.cmr.dictionary.DictionaryService;
36 import org.alfresco.service.cmr.dictionary.InvalidTypeException;
37 import org.alfresco.service.cmr.dictionary.PropertyDefinition;
38 import org.alfresco.service.cmr.repository.ContentData;
39 import org.alfresco.service.cmr.repository.ContentIOException;
40 import org.alfresco.service.cmr.repository.ContentReader;
41 import org.alfresco.service.cmr.repository.ContentService;
42 import org.alfresco.service.cmr.repository.ContentStreamListener;
43 import org.alfresco.service.cmr.repository.ContentWriter;
44 import org.alfresco.service.cmr.repository.NoTransformerException;
45 import org.alfresco.service.cmr.repository.NodeRef;
46 import org.alfresco.service.cmr.repository.NodeService;
47 import org.alfresco.service.namespace.NamespaceService;
48 import org.alfresco.service.namespace.QName;
49 import org.alfresco.service.transaction.TransactionService;
50 import org.alfresco.util.EqualsHelper;
51 import org.alfresco.util.TempFileProvider;
52 import org.apache.commons.logging.Log;
53 import org.apache.commons.logging.LogFactory;
54
55
56 /**
57  * A content service that determines at runtime the store that the
58  * content associated with a node should be routed to.
59  *
60  * @author Derek Hulley
61  */

62 public class RoutingContentService implements ContentService
63 {
64     private static Log logger = LogFactory.getLog(RoutingContentService.class);
65     
66     private TransactionService transactionService;
67     private DictionaryService dictionaryService;
68     private NodeService nodeService;
69     /** a registry of all available content transformers */
70     private ContentTransformerRegistry transformerRegistry;
71     /** TEMPORARY until we have a map to choose from at runtime */
72     private ContentStore store;
73     /** the store for all temporarily created content */
74     private ContentStore tempStore;
75     
76     /**
77      * The policy component
78      */

79     private PolicyComponent policyComponent;
80     
81     /**
82      * Policies delegate
83      */

84     ClassPolicyDelegate<ContentServicePolicies.OnContentUpdatePolicy> onContentUpdateDelegate;
85     ClassPolicyDelegate<ContentServicePolicies.OnContentReadPolicy> onContentReadDelegate;
86     
87     /**
88      * Default constructor sets up a temporary store
89      */

90     public RoutingContentService()
91     {
92         this.tempStore = new FileContentStore(TempFileProvider.getTempDir().getAbsolutePath());
93     }
94
95     public void setTransactionService(TransactionService transactionService)
96     {
97         this.transactionService = transactionService;
98     }
99     
100     public void setDictionaryService(DictionaryService dictionaryService)
101     {
102         this.dictionaryService = dictionaryService;
103     }
104     
105     public void setNodeService(NodeService nodeService)
106     {
107         this.nodeService = nodeService;
108     }
109     
110     public void setTransformerRegistry(ContentTransformerRegistry transformerRegistry)
111     {
112         this.transformerRegistry = transformerRegistry;
113     }
114     
115     public void setStore(ContentStore store)
116     {
117         this.store = store;
118     }
119     
120     public void setPolicyComponent(PolicyComponent policyComponent)
121     {
122         this.policyComponent = policyComponent;
123     }
124     
125     /**
126      * Service initialise
127      */

128     public void init()
129     {
130         // Bind on update properties behaviour
131
this.policyComponent.bindClassBehaviour(
132                 QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
133                 this,
134                 new JavaBehaviour(this, "onUpdateProperties"));
135         
136         // Register on content update policy
137
this.onContentUpdateDelegate = this.policyComponent.registerClassPolicy(OnContentUpdatePolicy.class);
138         this.onContentReadDelegate = this.policyComponent.registerClassPolicy(OnContentReadPolicy.class);
139     }
140     
141     /**
142      * Update properties policy behaviour
143      *
144      * @param nodeRef the node reference
145      * @param before the before values of the properties
146      * @param after the after values of the properties
147      */

148     public void onUpdateProperties(
149             NodeRef nodeRef,
150             Map JavaDoc<QName, Serializable JavaDoc> before,
151             Map JavaDoc<QName, Serializable JavaDoc> after)
152     {
153         boolean fire = false;
154         boolean newContent = false;
155         // check if any of the content properties have changed
156
for (QName propertyQName : after.keySet())
157         {
158             // is this a content property?
159
PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
160             if (propertyDef == null)
161             {
162                 // the property is not recognised
163
continue;
164             }
165             if (!propertyDef.getDataType().getName().equals(DataTypeDefinition.CONTENT))
166             {
167                 // not a content type
168
continue;
169             }
170             
171             try
172             {
173                 ContentData beforeValue = (ContentData) before.get(propertyQName);
174                 ContentData afterValue = (ContentData) after.get(propertyQName);
175                 if (afterValue != null && afterValue.getContentUrl() == null)
176                 {
177                     // no URL - ignore
178
}
179                 else if (!EqualsHelper.nullSafeEquals(beforeValue, afterValue))
180                 {
181                     // So debug ...
182
if (logger.isDebugEnabled() == true)
183                     {
184                         String JavaDoc beforeString = "";
185                         if (beforeValue != null)
186                         {
187                             beforeString = beforeValue.toString();
188                         }
189                         String JavaDoc afterString = "";
190                         if (afterValue != null)
191                         {
192                             afterString = afterValue.toString();
193                         }
194                         logger.debug("onContentUpate: before = " + beforeString + "; after = " + afterString);
195                     }
196                     
197                     // Figure out if the content is new or not
198
String JavaDoc beforeContentUrl = null;
199                     if (beforeValue != null)
200                     {
201                         beforeContentUrl = beforeValue.getContentUrl();
202                     }
203                     String JavaDoc afterContentUrl = null;
204                     if (afterValue != null)
205                     {
206                         afterContentUrl = afterValue.getContentUrl();
207                     }
208                     if (beforeContentUrl == null && afterContentUrl != null)
209                     {
210                         newContent = true;
211                     }
212                     
213                     // the content changed
214
// at the moment, we are only interested in this one change
215
fire = true;
216                     break;
217                 }
218             }
219             catch (ClassCastException JavaDoc e)
220             {
221                 // properties don't conform to model
222
continue;
223             }
224         }
225         // fire?
226
if (fire)
227         {
228             // Fire the content update policy
229
Set JavaDoc<QName> types = new HashSet JavaDoc<QName>(this.nodeService.getAspects(nodeRef));
230             types.add(this.nodeService.getType(nodeRef));
231             OnContentUpdatePolicy policy = this.onContentUpdateDelegate.get(types);
232             policy.onContentUpdate(nodeRef, newContent);
233         }
234     }
235     
236     public ContentReader getReader(NodeRef nodeRef, QName propertyQName)
237     {
238         return getReader(nodeRef, propertyQName, true);
239     }
240     
241     private ContentReader getReader(NodeRef nodeRef, QName propertyQName, boolean fireContentReadPolicy)
242     {
243         ContentData contentData = null;
244         Serializable JavaDoc propValue = nodeService.getProperty(nodeRef, propertyQName);
245         if (propValue instanceof Collection JavaDoc)
246         {
247             Collection JavaDoc colPropValue = (Collection JavaDoc)propValue;
248             if (colPropValue.size() > 0)
249             {
250                 propValue = (Serializable JavaDoc)colPropValue.iterator().next();
251             }
252         }
253         if (propValue instanceof ContentData)
254         {
255             contentData = (ContentData)propValue;
256         }
257
258         if (contentData == null)
259         {
260             // if no value or a value other content, and a property definition has been provided, ensure that it's CONTENT or ANY
261
PropertyDefinition contentPropDef = dictionaryService.getProperty(propertyQName);
262             if (contentPropDef != null &&
263                 (!(contentPropDef.getDataType().getName().equals(DataTypeDefinition.CONTENT) ||
264                    contentPropDef.getDataType().getName().equals(DataTypeDefinition.ANY))))
265             {
266                 throw new InvalidTypeException("The node property must be of type content: \n" +
267                         " node: " + nodeRef + "\n" +
268                         " property name: " + propertyQName + "\n" +
269                         " property type: " + ((contentPropDef == null) ? "unknown" : contentPropDef.getDataType()),
270                         propertyQName);
271             }
272         }
273
274         // check that the URL is available
275
if (contentData == null || contentData.getContentUrl() == null)
276         {
277             // there is no URL - the interface specifies that this is not an error condition
278
return null;
279         }
280         String JavaDoc contentUrl = contentData.getContentUrl();
281         
282         // TODO: Choose the store to read from at runtime
283
ContentReader reader = store.getReader(contentUrl);
284         
285         // set extra data on the reader
286
reader.setMimetype(contentData.getMimetype());
287         reader.setEncoding(contentData.getEncoding());
288         
289         // Fire the content read policy
290
if (reader != null && fireContentReadPolicy == true)
291         {
292             // Fire the content update policy
293
Set JavaDoc<QName> types = new HashSet JavaDoc<QName>(this.nodeService.getAspects(nodeRef));
294             types.add(this.nodeService.getType(nodeRef));
295             OnContentReadPolicy policy = this.onContentReadDelegate.get(types);
296             policy.onContentRead(nodeRef);
297         }
298         
299         // we don't listen for anything
300
// result may be null - but interface contract says we may return null
301
return reader;
302     }
303
304     public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update)
305     {
306         // check for an existing URL - the get of the reader will perform type checking
307
ContentReader existingContentReader = getReader(nodeRef, propertyQName, false);
308         
309         // TODO: Choose the store to write to at runtime
310

311         // get the content using the (potentially) existing content - the new content
312
// can be wherever the store decides.
313
ContentWriter writer = store.getWriter(existingContentReader, null);
314
315         // set extra data on the reader if the property is pre-existing
316
Serializable JavaDoc contentValue = nodeService.getProperty(nodeRef, propertyQName);
317         if (contentValue != null && contentValue instanceof ContentData)
318         {
319             ContentData contentData = (ContentData)contentValue;
320             writer.setMimetype(contentData.getMimetype());
321             writer.setEncoding(contentData.getEncoding());
322         }
323         
324         // attach a listener if required
325
if (update)
326         {
327             // need a listener to update the node when the stream closes
328
WriteStreamListener listener = new WriteStreamListener(nodeService, nodeRef, propertyQName, writer);
329             writer.addListener(listener);
330             writer.setTransactionService(transactionService);
331         }
332         
333         // give back to the client
334
return writer;
335     }
336
337     /**
338      * @return Returns a writer to an anonymous location
339      */

340     public ContentWriter getTempWriter()
341     {
342         // there is no existing content and we don't specify the location of the new content
343
return tempStore.getWriter(null, null);
344     }
345
346     /**
347      * @see org.alfresco.repo.content.transform.ContentTransformerRegistry
348      * @see org.alfresco.repo.content.transform.ContentTransformer
349      */

350     public void transform(ContentReader reader, ContentWriter writer)
351             throws NoTransformerException, ContentIOException
352     {
353         // check that source and target mimetypes are available
354
String JavaDoc sourceMimetype = reader.getMimetype();
355         if (sourceMimetype == null)
356         {
357             throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader);
358         }
359         String JavaDoc targetMimetype = writer.getMimetype();
360         if (targetMimetype == null)
361         {
362             throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer);
363         }
364         // look for a transformer
365
ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype);
366         if (transformer == null)
367         {
368             throw new NoTransformerException(sourceMimetype, targetMimetype);
369         }
370         // we have a transformer, so do it
371
transformer.transform(reader, writer);
372         // done
373
}
374     
375     /**
376      * @see org.alfresco.repo.content.transform.ContentTransformerRegistry
377      * @see org.alfresco.repo.content.transform.ContentTransformer
378      */

379     public boolean isTransformable(ContentReader reader, ContentWriter writer)
380     {
381         // check that source and target mimetypes are available
382
String JavaDoc sourceMimetype = reader.getMimetype();
383         if (sourceMimetype == null)
384         {
385             throw new AlfrescoRuntimeException("The content reader mimetype must be set: " + reader);
386         }
387         String JavaDoc targetMimetype = writer.getMimetype();
388         if (targetMimetype == null)
389         {
390             throw new AlfrescoRuntimeException("The content writer mimetype must be set: " + writer);
391         }
392         
393         // look for a transformer
394
ContentTransformer transformer = transformerRegistry.getTransformer(sourceMimetype, targetMimetype);
395         return (transformer != null);
396     }
397
398     /**
399      * Ensures that, upon closure of the output stream, the node is updated with
400      * the latest URL of the content to which it refers.
401      * <p>
402      * The listener close operation does not need a transaction as the
403      * <code>ContentWriter</code> takes care of that.
404      *
405      * @author Derek Hulley
406      */

407     private static class WriteStreamListener implements ContentStreamListener
408     {
409         private NodeService nodeService;
410         private NodeRef nodeRef;
411         private QName propertyQName;
412         private ContentWriter writer;
413         
414         public WriteStreamListener(
415                 NodeService nodeService,
416                 NodeRef nodeRef,
417                 QName propertyQName,
418                 ContentWriter writer)
419         {
420             this.nodeService = nodeService;
421             this.nodeRef = nodeRef;
422             this.propertyQName = propertyQName;
423             this.writer = writer;
424         }
425         
426         public void contentStreamClosed() throws ContentIOException
427         {
428             try
429             {
430                 // set the full content property
431
ContentData contentData = writer.getContentData();
432                 nodeService.setProperty(
433                         nodeRef,
434                         propertyQName,
435                         contentData);
436                 // done
437
if (logger.isDebugEnabled())
438                 {
439                     logger.debug("Stream listener updated node: \n" +
440                             " node: " + nodeRef + "\n" +
441                             " property: " + propertyQName + "\n" +
442                             " value: " + contentData);
443                 }
444             }
445             catch (Throwable JavaDoc e)
446             {
447                 throw new ContentIOException("Failed to set content property on stream closure: \n" +
448                         " node: " + nodeRef + "\n" +
449                         " property: " + propertyQName + "\n" +
450                         " writer: " + writer,
451                         e);
452             }
453         }
454     }
455 }
456
Popular Tags