1   /*
2    * RDFpro - An extensible tool for building stream-oriented RDF processing libraries.
3    * 
4    * Written in 2014 by Francesco Corcoglioniti with support by Marco Amadori, Michele Mostarda,
5    * Alessio Palmero Aprosio and Marco Rospocher. Contact info on http://rdfpro.fbk.eu/
6    * 
7    * To the extent possible under law, the authors have dedicated all copyright and related and
8    * neighboring rights to this software to the public domain worldwide. This software is
9    * distributed without any warranty.
10   * 
11   * You should have received a copy of the CC0 Public Domain Dedication along with this software.
12   * If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
13   */
14  package eu.fbk.rdfpro.tool;
15  
16  import java.io.BufferedReader;
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.io.InputStreamReader;
20  import java.net.URL;
21  import java.nio.charset.Charset;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Properties;
28  
29  import javax.annotation.Nullable;
30  
31  import org.openrdf.rio.RDFHandler;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import eu.fbk.rdfpro.RDFHandlers;
36  import eu.fbk.rdfpro.RDFProcessor;
37  import eu.fbk.rdfpro.RDFProcessors;
38  import eu.fbk.rdfpro.util.Environment;
39  
40  /**
41   * RDFpro main class.
42   * <p>
43   * This class contains the {@link #main(String...)} method used to invoke the RDFpro tool from the
44   * command line. Please do not call this class from your code, as it explicitly calls
45   * {@link System#exit(int)} (to force termination even in case some thread in RDFpro or user code
46   * hanged).
47   * </p>
48   */
49  public final class Main {
50  
51      private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
52  
53      /**
54       * Main method.
55       *
56       * @param args
57       *            command line arguments
58       */
59      public static void main(final String... args) {
60  
61          try {
62              Class.forName("eu.fbk.rdfpro.tql.TQL");
63          } catch (final Throwable ex) {
64              // ignore - TQL will not be supported
65          }
66  
67          try {
68              Class.forName("eu.fbk.rdfpro.tool.GeonamesRDF");
69          } catch (final Throwable ex) {
70              // ignore - Geonames format will not be supported
71          }
72  
73          boolean showHelp = false;
74          boolean showVersion = false;
75          String logLevel = "INFO";
76  
77          int index = 0;
78          while (index < args.length) {
79              final String arg = args[index];
80              if (arg.startsWith("{") || arg.startsWith("@") || !arg.startsWith("-")) {
81                  break;
82              }
83              showHelp |= arg.equals("-h");
84              showVersion |= arg.equals("-v");
85              if (arg.equals("-V")) {
86                  logLevel = "DEBUG";
87              } else if (arg.equals("-VV")) {
88                  logLevel = "ALL";
89              }
90              ++index;
91          }
92          showHelp |= index == args.length;
93  
94          try {
95              final Logger root = LoggerFactory.getLogger("eu.fbk");
96              final Class<?> levelClass = Class.forName("ch.qos.logback.classic.Level");
97              final Class<?> loggerClass = Class.forName("ch.qos.logback.classic.Logger");
98              final Object level = levelClass.getDeclaredMethod("valueOf", String.class).invoke(
99                      null, logLevel);
100             loggerClass.getDeclaredMethod("setLevel", levelClass).invoke(root, level);
101         } catch (final Throwable ex) {
102             // ignore - no control on logging level
103         }
104 
105         if (showVersion) {
106             System.out.println(String.format(
107                     "RDF Processor Tool (RDFpro) %s\nJava %s bit (%s) %s\n"
108                             + "This is free software released into the public domain",
109                     readVersion("eu.fbk.rdfpro", "rdfpro-core", "unknown version"),
110                     System.getProperty("sun.arch.data.model"), System.getProperty("java.vendor"),
111                     System.getProperty("java.version")));
112             System.exit(0);
113         }
114 
115         if (showHelp) {
116             System.out.println(String.format(readResource(RDFProcessor.class.getResource("help")),
117                     readVersion("eu.fbk.rdfpro", "rdfpro-core", "unknown version"),
118                     readPluginDocs()));
119             System.exit(0);
120         }
121 
122         RDFProcessor processor = null;
123         try {
124             processor = RDFProcessors.parse(false, Arrays.copyOfRange(args, index, args.length));
125         } catch (final IllegalArgumentException ex) {
126             System.err.println("INVOCATION ERROR. " + ex.getMessage() + "\n");
127             System.exit(1);
128         }
129 
130         try {
131             final long ts = System.currentTimeMillis();
132             final RDFHandler handler = processor.wrap(RDFHandlers.NIL);
133             final int repetitions = processor.getExtraPasses() + 1;
134             for (int i = 0; i < repetitions; ++i) {
135                 if (repetitions > 1) {
136                     LOGGER.info("Pass {} of {}", i + 1, repetitions);
137                 }
138                 handler.startRDF();
139                 handler.endRDF();
140             }
141             LOGGER.info("Done in {} s", (System.currentTimeMillis() - ts) / 1000);
142             System.exit(0);
143 
144         } catch (final Throwable ex) {
145             System.err.println("EXECUTION FAILED. " + ex.getMessage() + "\n");
146             ex.printStackTrace();
147             System.exit(2);
148         }
149     }
150 
151     @Nullable
152     private static String readVersion(final String groupId, final String artifactId,
153             @Nullable final String defaultValue) {
154 
155         final URL url = RDFProcessor.class.getClassLoader().getResource(
156                 "META-INF/maven/" + groupId + "/" + artifactId + "/pom.properties");
157 
158         if (url != null) {
159             try {
160                 final InputStream stream = url.openStream();
161                 try {
162                     final Properties properties = new Properties();
163                     properties.load(stream);
164                     return properties.getProperty("version").trim();
165                 } finally {
166                     stream.close();
167                 }
168 
169             } catch (final IOException ex) {
170                 LOGGER.warn("Could not parse version string in " + url);
171             }
172         }
173 
174         return defaultValue;
175     }
176 
177     private static String readResource(final URL url) {
178         try {
179             final BufferedReader reader = new BufferedReader(new InputStreamReader(
180                     url.openStream(), Charset.forName("UTF-8")));
181             final StringBuilder builder = new StringBuilder();
182             try {
183                 String line;
184                 while ((line = reader.readLine()) != null) {
185                     builder.append(line).append("\n");
186                 }
187             } finally {
188                 reader.close();
189             }
190             return builder.toString();
191         } catch (final Throwable ex) {
192             throw new Error("Could not load resource " + url);
193         }
194     }
195 
196     private static String readPluginDocs() {
197 
198         final Map<String, String> descriptions = Environment.getPlugins(RDFProcessor.class);
199         final List<String> names = new ArrayList<String>(descriptions.keySet());
200         Collections.sort(names);
201 
202         final StringBuilder builder = new StringBuilder();
203         for (final String name : names) {
204             builder.append(builder.length() == 0 ? "" : "\n\n");
205             builder.append(descriptions.get(name).trim());
206         }
207 
208         return builder.toString();
209     }
210 
211 }