001    /*
002     * JBoss DNA (http://www.jboss.org/dna)
003     * See the COPYRIGHT.txt file distributed with this work for information
004     * regarding copyright ownership.  Some portions may be licensed
005     * to Red Hat, Inc. under one or more contributor license agreements.
006     * See the AUTHORS.txt file in the distribution for a full listing of 
007     * individual contributors. 
008     *
009     * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010     * is licensed to you under the terms of the GNU Lesser General Public License as
011     * published by the Free Software Foundation; either version 2.1 of
012     * the License, or (at your option) any later version.
013     *
014     * JBoss DNA is distributed in the hope that it will be useful,
015     * but WITHOUT ANY WARRANTY; without even the implied warranty of
016     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     * Lesser General Public License for more details.
018     *
019     * You should have received a copy of the GNU Lesser General Public
020     * License along with this software; if not, write to the Free
021     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023     */
024    package org.jboss.example.dna.sequencer;
025    
026    import java.net.URL;
027    import java.util.ArrayList;
028    import java.util.Calendar;
029    import java.util.List;
030    import java.util.Map;
031    import java.util.Properties;
032    import java.util.TreeMap;
033    import java.util.concurrent.TimeUnit;
034    import javax.jcr.Node;
035    import javax.jcr.NodeIterator;
036    import javax.jcr.PathNotFoundException;
037    import javax.jcr.Property;
038    import javax.jcr.PropertyIterator;
039    import javax.jcr.Repository;
040    import javax.jcr.RepositoryException;
041    import javax.jcr.Session;
042    import javax.jcr.Value;
043    import javax.jcr.ValueFormatException;
044    import org.jboss.dna.graph.SecurityContext;
045    import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
046    import org.jboss.dna.jcr.JcrConfiguration;
047    import org.jboss.dna.jcr.JcrEngine;
048    import org.jboss.dna.jcr.SecurityContextCredentials;
049    import org.jboss.dna.repository.sequencer.SequencingService;
050    import org.jboss.dna.repository.util.SessionFactory;
051    
052    /**
053     * @author Randall Hauch
054     */
055    public class SequencingClient {
056    
057        public static final String DEFAULT_REPOSITORY_NAME = "repo";
058        public static final String DEFAULT_WORKSPACE_NAME = "default";
059        public static final String DEFAULT_USERNAME = "jsmith";
060        public static final char[] DEFAULT_PASSWORD = "secret".toCharArray();
061    
062        public static void main( String[] args ) {
063            // Configure the DNA configuration. This could be done by loading a configuration from a file, or by
064            // using a (local or remote) configuration repository, or by setting up the configuration programmatically.
065            // This example uses the programmatic approach...
066            String repositoryId = "content";
067            String workspaceName = "default";
068            JcrConfiguration config = new JcrConfiguration();
069            // Set up the in-memory source where we'll upload the content and where the sequenced output will be stored ...
070            config.repositorySource("store")
071                  .usingClass(InMemoryRepositorySource.class)
072                  .setDescription("The repository for our content")
073                  .setProperty("defaultWorkspaceName", workspaceName);
074            // Set up the JCR repository to use the source ...
075            config.repository(repositoryId).addNodeTypes("sequencing.cnd").setSource("store");
076            // Set up the image sequencer ...
077            config.sequencer("Image Sequencer")
078                  .usingClass("org.jboss.dna.sequencer.image.ImageMetadataSequencer")
079                  .loadedFromClasspath()
080                  .setDescription("Sequences image files to extract the characteristics of the image")
081                  .sequencingFrom("//(*.(jpg|jpeg|gif|bmp|pcx|png|iff|ras|pbm|pgm|ppm|psd)[*])/jcr:content[@jcr:data]")
082                  .andOutputtingTo("/images/$1");
083            // Set up the MP3 sequencer ...
084            config.sequencer("MP3 Sequencer")
085                  .usingClass("org.jboss.dna.sequencer.mp3.Mp3MetadataSequencer")
086                  .loadedFromClasspath()
087                  .setDescription("Sequences mp3 files to extract the id3 tags of the audio file")
088                  .sequencingFrom("//(*.mp3[*])/jcr:content[@jcr:data]")
089                  .andOutputtingTo("/mp3s/$1");
090            // Set up the Java source file sequencer ...
091            config.sequencer("Java Sequencer")
092                  .usingClass("org.jboss.dna.sequencer.java.JavaMetadataSequencer")
093                  .loadedFromClasspath()
094                  .setDescription("Sequences Java files to extract the AST structure of the Java source code")
095                  .sequencingFrom("//(*.java[*])/jcr:content[@jcr:data]")
096                  .andOutputtingTo("/java/$1");
097    
098            // Now start the client and tell it which repository and workspace to use ...
099            SequencingClient client = new SequencingClient(config, repositoryId, workspaceName);
100            client.setUserInterface(new ConsoleInput(client));
101        }
102    
103        private final String repositoryName;
104        private final String workspaceName;
105        private final JcrConfiguration configuration;
106        private JcrEngine engine;
107        private UserInterface userInterface;
108        private Repository repository;
109    
110        public SequencingClient( JcrConfiguration config,
111                                 String repositoryName,
112                                 String workspaceName ) {
113            this.configuration = config;
114            this.repositoryName = repositoryName != null ? repositoryName : DEFAULT_REPOSITORY_NAME;
115            this.workspaceName = workspaceName != null ? workspaceName : DEFAULT_WORKSPACE_NAME;
116        }
117    
118        /**
119         * Set the user interface that this client should use.
120         * 
121         * @param userInterface
122         */
123        public void setUserInterface( UserInterface userInterface ) {
124            this.userInterface = userInterface;
125        }
126    
127        /**
128         * Start up the JCR repository, using an implementation-specific API.
129         * 
130         * @throws Exception
131         */
132        public void startRepository() throws Exception {
133            if (this.repository == null) {
134                try {
135                    // Start the DNA engine ...
136                    this.engine = this.configuration.build();
137                    this.engine.start();
138    
139                    // Now get the repository instance ...
140                    this.repository = this.engine.getRepository(repositoryName);
141    
142                } catch (Exception e) {
143                    this.repository = null;
144                    throw e;
145                }
146            }
147        }
148    
149        /**
150         * Shutdown the repository. This method only uses the JCR API.
151         * 
152         * @throws Exception
153         */
154        public void shutdownRepository() throws Exception {
155            if (this.repository != null) {
156                try {
157                    this.engine.shutdown();
158                    this.engine.awaitTermination(4, TimeUnit.SECONDS);
159                } finally {
160                    this.repository = null;
161                }
162            }
163        }
164    
165        /**
166         * Get the sequencing statistics.
167         * 
168         * @return the statistics; never null
169         */
170        public SequencingService.Statistics getStatistics() {
171            return this.engine.getSequencingService().getStatistics();
172        }
173    
174        /**
175         * Prompt the user interface for the file to upload into the JCR repository, then upload it using the JCR API.
176         * 
177         * @throws Exception
178         */
179        public void uploadFile() throws Exception {
180            URL url = this.userInterface.getFileToUpload();
181            // Grab the last segment of the URL path, using it as the filename
182            String filename = url.getPath().replaceAll("([^/]*/)*", "");
183            String nodePath = this.userInterface.getRepositoryPath("/a/b/" + filename);
184            String mimeType = getMimeType(url);
185    
186            if (mimeType == null) {
187                System.err.println("Could not determine mime type for file.  Cancelling upload.");
188                return;
189            }
190    
191            // Now use the JCR API to upload the file ...
192            Session session = createSession();
193            JcrTools tools = new JcrTools();
194            try {
195                // Create the node at the supplied path ...
196                Node node = tools.findOrCreateNode(session, nodePath, "nt:folder", "nt:file");
197    
198                // Upload the file to that node ...
199                Node contentNode = tools.findOrCreateChild(session, node, "jcr:content", "nt:resource");
200                contentNode.setProperty("jcr:mimeType", mimeType);
201                contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
202                contentNode.setProperty("jcr:data", url.openStream());
203    
204                // Save the session ...
205                session.save();
206            } finally {
207                session.logout();
208            }
209        }
210    
211        /**
212         * Perform a search of the repository for all image metadata automatically created by the image sequencer.
213         * 
214         * @throws Exception
215         */
216        public void search() throws Exception {
217            // Use JCR to search the repository for image metadata ...
218            List<ContentInfo> infos = new ArrayList<ContentInfo>();
219            Session session = createSession();
220            try {
221                // Find the node ...
222                Node root = session.getRootNode();
223    
224                if (root.hasNode("images") || root.hasNode("mp3s")) {
225                    Node mediasNode;
226                    if (root.hasNode("images")) {
227                        mediasNode = root.getNode("images");
228    
229                        for (NodeIterator iter = mediasNode.getNodes(); iter.hasNext();) {
230                            Node mediaNode = iter.nextNode();
231                            if (mediaNode.hasNode("image:metadata")) {
232                                infos.add(extractMediaInfo("image:metadata", "image", mediaNode));
233                            }
234                        }
235                    }
236                    if (root.hasNode("mp3s")) {
237                        mediasNode = root.getNode("mp3s");
238    
239                        for (NodeIterator iter = mediasNode.getNodes(); iter.hasNext();) {
240                            Node mediaNode = iter.nextNode();
241                            if (mediaNode.hasNode("mp3:metadata")) {
242                                infos.add(extractMediaInfo("mp3:metadata", "mp3", mediaNode));
243                            }
244                        }
245                    }
246    
247                }
248                if (root.hasNode("java")) {
249                    Map<String, List<Properties>> tree = new TreeMap<String, List<Properties>>();
250                    // Find the compilation unit node ...
251                    List<Properties> javaElements;
252                    if (root.hasNode("java")) {
253                        Node javaSourcesNode = root.getNode("java");
254                        for (NodeIterator i = javaSourcesNode.getNodes(); i.hasNext();) {
255    
256                            Node javaSourceNode = i.nextNode();
257    
258                            if (javaSourceNode.hasNodes()) {
259                                Node javaCompilationUnit = javaSourceNode.getNodes().nextNode();
260                                // package informations
261    
262                                javaElements = new ArrayList<Properties>();
263                                try {
264                                    Node javaPackageDeclarationNode = javaCompilationUnit.getNode("java:package/java:packageDeclaration");
265                                    javaElements.add(extractJavaInfo(javaPackageDeclarationNode));
266                                    tree.put("Class package", javaElements);
267                                } catch (PathNotFoundException e) {
268                                    // do nothing
269                                }
270    
271                                // import informations
272                                javaElements = new ArrayList<Properties>();
273                                try {
274                                    for (NodeIterator singleImportIterator = javaCompilationUnit.getNode("java:import/java:importDeclaration/java:singleImport")
275                                                                                                .getNodes(); singleImportIterator.hasNext();) {
276                                        Node javasingleTypeImportDeclarationNode = singleImportIterator.nextNode();
277                                        javaElements.add(extractJavaInfo(javasingleTypeImportDeclarationNode));
278                                    }
279                                    tree.put("Class single Imports", javaElements);
280                                } catch (PathNotFoundException e) {
281                                    // do nothing
282                                }
283    
284                                javaElements = new ArrayList<Properties>();
285                                try {
286                                    for (NodeIterator javaImportOnDemandIterator = javaCompilationUnit.getNode("java:import/java:importDeclaration/java:importOnDemand")
287                                                                                                      .getNodes(); javaImportOnDemandIterator.hasNext();) {
288                                        Node javaImportOnDemandtDeclarationNode = javaImportOnDemandIterator.nextNode();
289                                        javaElements.add(extractJavaInfo(javaImportOnDemandtDeclarationNode));
290                                    }
291                                    tree.put("Class on demand imports", javaElements);
292    
293                                } catch (PathNotFoundException e) {
294                                    // do nothing
295                                }
296                                // class head informations
297                                javaElements = new ArrayList<Properties>();
298                                Node javaNormalDeclarationClassNode = javaCompilationUnit.getNode("java:unitType/java:classDeclaration/java:normalClass/java:normalClassDeclaration");
299                                javaElements.add(extractJavaInfo(javaNormalDeclarationClassNode));
300                                tree.put("Class head information", javaElements);
301    
302                                // field member informations
303                                javaElements = new ArrayList<Properties>();
304                                for (NodeIterator javaFieldTypeIterator = javaCompilationUnit.getNode("java:unitType/java:classDeclaration/java:normalClass/java:normalClassDeclaration/java:field/java:fieldType")
305                                                                                             .getNodes(); javaFieldTypeIterator.hasNext();) {
306                                    Node rootFieldTypeNode = javaFieldTypeIterator.nextNode();
307                                    if (rootFieldTypeNode.hasNode("java:primitiveType")) {
308                                        Node javaPrimitiveTypeNode = rootFieldTypeNode.getNode("java:primitiveType");
309                                        javaElements.add(extractJavaInfo(javaPrimitiveTypeNode));
310                                        // more informations
311                                    }
312    
313                                    if (rootFieldTypeNode.hasNode("java:simpleType")) {
314                                        Node javaSimpleTypeNode = rootFieldTypeNode.getNode("java:simpleType");
315                                        javaElements.add(extractJavaInfo(javaSimpleTypeNode));
316                                    }
317                                    if (rootFieldTypeNode.hasNode("java:parameterizedType")) {
318                                        Node javaParameterizedType = rootFieldTypeNode.getNode("java:parameterizedType");
319                                        javaElements.add(extractJavaInfo(javaParameterizedType));
320                                    }
321                                    if (rootFieldTypeNode.hasNode("java:arrayType")) {
322                                        Node javaArrayType = rootFieldTypeNode.getNode("java:arrayType[2]");
323                                        javaElements.add(extractJavaInfo(javaArrayType));
324                                    }
325                                }
326                                tree.put("Class field members", javaElements);
327    
328                                // constructor informations
329                                javaElements = new ArrayList<Properties>();
330                                for (NodeIterator javaConstructorIterator = javaCompilationUnit.getNode("java:unitType/java:classDeclaration/java:normalClass/java:normalClassDeclaration/java:constructor")
331                                                                                               .getNodes(); javaConstructorIterator.hasNext();) {
332                                    Node javaConstructor = javaConstructorIterator.nextNode();
333                                    javaElements.add(extractJavaInfo(javaConstructor));
334                                }
335                                tree.put("Class constructors", javaElements);
336    
337                                // method informations
338                                javaElements = new ArrayList<Properties>();
339                                for (NodeIterator javaMethodIterator = javaCompilationUnit.getNode("java:unitType/java:classDeclaration/java:normalClass/java:normalClassDeclaration/java:method")
340                                                                                          .getNodes(); javaMethodIterator.hasNext();) {
341                                    Node javaMethod = javaMethodIterator.nextNode();
342                                    javaElements.add(extractJavaInfo(javaMethod));
343                                }
344                                tree.put("Class member functions", javaElements);
345    
346                                JavaInfo javaInfo = new JavaInfo(javaCompilationUnit.getPath(), javaCompilationUnit.getName(),
347                                                                 "java source", tree);
348                                infos.add(javaInfo);
349                            }
350                        }
351                    }
352    
353                }
354            } finally {
355                session.logout();
356            }
357    
358            // Display the search results ...
359            this.userInterface.displaySearchResults(infos);
360        }
361    
362        private MediaInfo extractMediaInfo( String metadataNodeName,
363                                            String mediaType,
364                                            Node mediaNode ) throws RepositoryException, PathNotFoundException, ValueFormatException {
365            String nodePath = mediaNode.getPath();
366            String nodeName = mediaNode.getName();
367            mediaNode = mediaNode.getNode(metadataNodeName);
368    
369            // Create a Properties object containing the properties for this node; ignore any children ...
370            Properties props = new Properties();
371            for (PropertyIterator propertyIter = mediaNode.getProperties(); propertyIter.hasNext();) {
372                Property property = propertyIter.nextProperty();
373                String name = property.getName();
374                String stringValue = null;
375                if (property.getDefinition().isMultiple()) {
376                    StringBuilder sb = new StringBuilder();
377                    boolean first = true;
378                    for (Value value : property.getValues()) {
379                        if (!first) {
380                            sb.append(", ");
381                            first = false;
382                        }
383                        sb.append(value.getString());
384                    }
385                    stringValue = sb.toString();
386                } else {
387                    stringValue = property.getValue().getString();
388                }
389                props.put(name, stringValue);
390            }
391            // Create the image information object, and add it to the collection ...
392            return new MediaInfo(nodePath, nodeName, mediaType, props);
393        }
394    
395        /**
396         * Extract informations from a specific node.
397         * 
398         * @param node - node, that contains informations.
399         * @return a properties of keys/values.
400         * @throws RepositoryException
401         * @throws IllegalStateException
402         * @throws ValueFormatException
403         */
404        private Properties extractJavaInfo( Node node ) throws ValueFormatException, IllegalStateException, RepositoryException {
405            if (node.hasProperties()) {
406                Properties properties = new Properties();
407                for (PropertyIterator propertyIter = node.getProperties(); propertyIter.hasNext();) {
408                    Property property = propertyIter.nextProperty();
409                    String name = property.getName();
410                    String stringValue = property.getValue().getString();
411                    properties.put(name, stringValue);
412                }
413                return properties;
414            }
415            return null;
416        }
417    
418        /**
419         * Utility method to create a new JCR session from the execution context's {@link SessionFactory}.
420         * 
421         * @return the session
422         * @throws RepositoryException
423         */
424        protected Session createSession() throws RepositoryException {
425            // Normally we'd just use SimpleCredentials or some other custom Credentials implementation,
426            // but that would require JAAS (since JBoss DNA uses that used by default). Since we don't
427            // have a JAAS implementation, we will use the SecurityContextCredentials to wrap
428            // another SecurityContext implementation. This is how you might integrate a non-JAAS security
429            // system into JBoss DNA. See the repository example for how to set up with JAAS.
430            SecurityContext securityContext = new MyCustomSecurityContext();
431            SecurityContextCredentials credentials = new SecurityContextCredentials(securityContext);
432            return this.repository.login(credentials, workspaceName);
433        }
434    
435        protected String getMimeType( URL file ) {
436            String filename = file.getPath().toLowerCase();
437            if (filename.endsWith(".gif")) return "image/gif";
438            if (filename.endsWith(".png")) return "image/png";
439            if (filename.endsWith(".pict")) return "image/x-pict";
440            if (filename.endsWith(".bmp")) return "image/bmp";
441            if (filename.endsWith(".jpg")) return "image/jpeg";
442            if (filename.endsWith(".jpe")) return "image/jpeg";
443            if (filename.endsWith(".jpeg")) return "image/jpeg";
444            if (filename.endsWith(".ras")) return "image/x-cmu-raster";
445            if (filename.endsWith(".mp3")) return "audio/mpeg";
446            if (filename.endsWith(".java")) return "text/x-java-source";
447            return null;
448        }
449    
450        protected class MyCustomSecurityContext implements SecurityContext {
451            /**
452             * {@inheritDoc}
453             * 
454             * @see org.jboss.dna.graph.SecurityContext#getUserName()
455             */
456            public String getUserName() {
457                return "Fred";
458            }
459    
460            /**
461             * {@inheritDoc}
462             * 
463             * @see org.jboss.dna.graph.SecurityContext#hasRole(java.lang.String)
464             */
465            public boolean hasRole( String roleName ) {
466                return true;
467            }
468    
469            /**
470             * {@inheritDoc}
471             * 
472             * @see org.jboss.dna.graph.SecurityContext#logout()
473             */
474            public void logout() {
475                // do something
476            }
477        }
478    
479    }