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 }