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 }