1 package jp.naist.se.stigmata;
\r
7 import java.io.ByteArrayInputStream;
\r
8 import java.io.ByteArrayOutputStream;
\r
10 import java.io.FileInputStream;
\r
11 import java.io.FileNotFoundException;
\r
12 import java.io.IOException;
\r
13 import java.io.InputStream;
\r
14 import java.lang.reflect.InvocationTargetException;
\r
15 import java.net.MalformedURLException;
\r
16 import java.util.ArrayList;
\r
17 import java.util.HashMap;
\r
18 import java.util.Iterator;
\r
19 import java.util.List;
\r
20 import java.util.Map;
\r
21 import java.util.Stack;
\r
22 import java.util.logging.Logger;
\r
24 import javax.imageio.spi.ServiceRegistry;
\r
26 import jp.naist.se.stigmata.event.OperationEvent;
\r
27 import jp.naist.se.stigmata.event.OperationListener;
\r
28 import jp.naist.se.stigmata.event.OperationStage;
\r
29 import jp.naist.se.stigmata.event.OperationType;
\r
30 import jp.naist.se.stigmata.event.WarningMessages;
\r
31 import jp.naist.se.stigmata.filter.ComparisonPairFilterManager;
\r
32 import jp.naist.se.stigmata.filter.FilteredComparisonResultSet;
\r
33 import jp.naist.se.stigmata.reader.ClassFileArchive;
\r
34 import jp.naist.se.stigmata.reader.ClassFileEntry;
\r
35 import jp.naist.se.stigmata.reader.ClasspathContext;
\r
36 import jp.naist.se.stigmata.reader.DefaultClassFileArchive;
\r
37 import jp.naist.se.stigmata.reader.JarClassFileArchive;
\r
38 import jp.naist.se.stigmata.reader.WarClassFileArchive;
\r
39 import jp.naist.se.stigmata.spi.BirthmarkSpi;
\r
40 import jp.naist.se.stigmata.utils.ConfigFileImporter;
\r
42 import org.apache.commons.beanutils.BeanUtils;
\r
45 * Birthmarking engine.
\r
47 * @author Haruaki TAMADA
\r
48 * @version $Revision$ $Date$
\r
50 public final class Stigmata{
\r
51 private static final Stigmata instance = new Stigmata();
\r
53 private BirthmarkContext defaultContext = BirthmarkContext.getDefaultContext();
\r
54 private boolean configDone = false;
\r
55 private Stack<WarningMessages> stack = new Stack<WarningMessages>();
\r
56 private WarningMessages warnings;
\r
57 private List<OperationListener> listeners = new ArrayList<OperationListener>();
\r
62 public static Stigmata getInstance(){
\r
66 public void addOperationListener(OperationListener listener){
\r
67 listeners.add(listener);
\r
70 public void removeOperationListener(OperationListener listener){
\r
71 listeners.remove(listener);
\r
74 public WarningMessages getWarnings(){
\r
78 public void configuration(){
\r
79 operationStart(OperationType.CONFIGURATION);
\r
80 configuration(null);
\r
81 operationDone(OperationType.CONFIGURATION);
\r
84 public void configuration(String filePath){
\r
85 InputStream target = null;
\r
86 if(filePath != null){
\r
88 target = new FileInputStream(filePath);
\r
89 } catch(FileNotFoundException e){
\r
94 if(filePath == null){
\r
95 File file = new File("stigmata.xml");
\r
97 file = new File(System.getProperty("user.home"), ".stigmata.xml");
\r
104 target = new FileInputStream(file);
\r
105 } catch (FileNotFoundException ex) {
\r
106 // never throwed this exception;
\r
107 throw new InternalError(ex.getMessage());
\r
111 if(target == null){
\r
112 target = getClass().getResourceAsStream("/resources/stigmata.xml");
\r
114 initConfiguration(target);
\r
118 * create a new {@link BirthmarkContext <code>BirthmarkContext</code>}.
\r
120 public BirthmarkContext createContext(){
\r
121 operationStart(OperationType.CREATE_CONTEXT);
\r
122 BirthmarkContext context = new BirthmarkContext();
\r
123 operationDone(OperationType.EXTRACT_BIRTHMARKS);
\r
127 public BirthmarkSet[] extract(String[] birthmarks, String[] files) throws BirthmarkExtractionException{
\r
128 operationStart(OperationType.EXTRACT_BIRTHMARKS);
\r
129 BirthmarkSet[] set = extract(birthmarks, files, createContext());
\r
130 operationDone(OperationType.EXTRACT_BIRTHMARKS);
\r
135 public BirthmarkSet[] extract(String[] birthmarks, String[] files,
\r
136 BirthmarkContext context) throws BirthmarkExtractionException{
\r
137 operationStart(OperationType.EXTRACT_BIRTHMARKS);
\r
139 return extractImpl(birthmarks, files, context);
\r
140 } catch(IOException e){
\r
141 throw new BirthmarkExtractionException(e);
\r
143 operationDone(OperationType.EXTRACT_BIRTHMARKS);
\r
147 public ComparisonResultSet compare(BirthmarkSet[] holders) throws IOException{
\r
148 operationStart(OperationType.COMPARE_BIRTHMARKS);
\r
149 ComparisonResultSet crs = compare(holders, createContext());
\r
150 operationDone(OperationType.COMPARE_BIRTHMARKS);
\r
155 public ComparisonResultSet compare(BirthmarkSet[] holders, BirthmarkContext context) throws IOException{
\r
156 operationStart(OperationType.COMPARE_BIRTHMARKS);
\r
157 ComparisonResultSet result = new RoundRobinComparisonResultSet(holders, context, true);
\r
158 operationDone(OperationType.COMPARE_BIRTHMARKS);
\r
163 public ComparisonResultSet compare(BirthmarkSet[] holders1, BirthmarkSet[] holders2) throws IOException{
\r
164 operationStart(OperationType.COMPARE_BIRTHMARKS);
\r
165 ComparisonResultSet crs = compare(holders1, holders2, createContext());
\r
166 operationDone(OperationType.COMPARE_BIRTHMARKS);
\r
170 public ComparisonResultSet compare(BirthmarkSet[] holders1, BirthmarkSet[] holders2, BirthmarkContext context) throws IOException{
\r
171 operationStart(OperationType.COMPARE_BIRTHMARKS);
\r
172 ComparisonResultSet result = new RoundRobinComparisonResultSet(holders1, holders2, context);
\r
173 operationDone(OperationType.COMPARE_BIRTHMARKS);
\r
178 public ComparisonResultSet filter(ComparisonResultSet resultset, String[] filters){
\r
179 operationStart(OperationType.FILTER_BIRTHMARKS);
\r
180 ComparisonResultSet crs = filter(resultset, filters, createContext());
\r
181 operationDone(OperationType.FILTER_BIRTHMARKS);
\r
186 public ComparisonResultSet filter(ComparisonResultSet resultset, String[] filters, BirthmarkContext context){
\r
187 operationStart(OperationType.FILTER_BIRTHMARKS);
\r
188 if(filters != null){
\r
189 List<ComparisonPairFilterSet> filterList = new ArrayList<ComparisonPairFilterSet>();
\r
190 ComparisonPairFilterManager manager = context.getFilterManager();
\r
191 for(int i = 0; i < filters.length; i++){
\r
192 ComparisonPairFilterSet fset = manager.getFilterSet(filters[i]);
\r
194 filterList.add(fset);
\r
197 Logger logger = Logger.getLogger(getClass().getName());
\r
198 logger.warning(filters[i] + ": filter not found");
\r
202 return filter(resultset, filterList.toArray(new ComparisonPairFilterSet[filterList.size()]));
\r
204 operationDone(OperationType.FILTER_BIRTHMARKS);
\r
208 public ComparisonResultSet filter(ComparisonResultSet resultset, ComparisonPairFilterSet[] filters){
\r
209 operationStart(OperationType.FILTER_BIRTHMARKS);
\r
210 ComparisonResultSet crs = filter(resultset, filters, createContext());
\r
211 operationDone(OperationType.FILTER_BIRTHMARKS);
\r
216 public ComparisonResultSet filter(ComparisonResultSet resultset, ComparisonPairFilterSet[] filters, BirthmarkContext context){
\r
217 operationStart(OperationType.FILTER_BIRTHMARKS);
\r
218 FilteredComparisonResultSet filterResultSet = new FilteredComparisonResultSet(resultset);
\r
219 operationDone(OperationType.FILTER_BIRTHMARKS);
\r
221 return filterResultSet;
\r
224 public double compareDetails(BirthmarkSet h1, BirthmarkSet h2){
\r
225 operationStart(OperationType.COMPARE_DETAIL_BIRTHMARKS);
\r
226 double similarity = compareDetails(h1, h2, createContext());
\r
227 operationDone(OperationType.COMPARE_DETAIL_BIRTHMARKS);
\r
231 public double compareDetails(BirthmarkSet h1, BirthmarkSet h2, BirthmarkContext context){
\r
232 operationStart(OperationType.COMPARE_DETAIL_BIRTHMARKS);
\r
234 List<Double> list = new ArrayList<Double>();
\r
236 for(Iterator<String> i = h1.birthmarkTypes(); i.hasNext(); ){
\r
237 String type = i.next();
\r
238 Birthmark b1 = h1.getBirthmark(type);
\r
239 Birthmark b2 = h2.getBirthmark(type);
\r
241 double similarity = Double.NaN;
\r
242 if(b1 != null && b2 != null){
\r
243 BirthmarkSpi spi = context.getService(type);
\r
244 BirthmarkComparator comparator = spi.getComparator();
\r
246 similarity = comparator.compare(b1, b2);
\r
249 list.add(similarity);
\r
252 double similarity = 0d;
\r
253 for(Double d: list){
\r
254 if(d.doubleValue() != Double.NaN){
\r
255 similarity += d.doubleValue();
\r
258 operationDone(OperationType.COMPARE_DETAIL_BIRTHMARKS);
\r
259 return similarity / count;
\r
262 private BirthmarkExtractor[] createExtractors(String[] birthmarkTypes, BirthmarkContext context){
\r
263 List<BirthmarkExtractor> list = new ArrayList<BirthmarkExtractor>();
\r
264 for(String type: birthmarkTypes){
\r
265 BirthmarkExtractor extractor = createExtractor(type, context);
\r
266 list.add(extractor);
\r
268 return list.toArray(new BirthmarkExtractor[list.size()]);
\r
271 private BirthmarkExtractor createExtractor(String birthmarkType, BirthmarkContext context){
\r
272 BirthmarkSpi spi = context.getService(birthmarkType);
\r
274 BirthmarkExtractor extractor = spi.getExtractor();
\r
276 Map props = BeanUtils.describe(extractor);
\r
277 props.remove("class");
\r
278 props.remove("provider");
\r
279 for(Object keyObject: props.keySet()){
\r
280 String key = "extractor." + spi.getType() + "." + String.valueOf(keyObject);
\r
281 if(context.getProperty(key) != null){
\r
282 BeanUtils.setProperty(extractor, (String)keyObject, context.getProperty(key));
\r
285 } catch(InvocationTargetException e){
\r
286 throw new InternalError(e.getMessage());
\r
287 } catch(NoSuchMethodException e){
\r
288 throw new InternalError(e.getMessage());
\r
289 } catch(IllegalAccessException e){
\r
290 throw new InternalError(e.getMessage());
\r
297 private byte[] inputStreamToByteArray(InputStream in) throws IOException{
\r
298 ByteArrayOutputStream bout = new ByteArrayOutputStream();
\r
300 byte[] dataBuffer = new byte[512];
\r
301 while((read = in.read(dataBuffer, 0, dataBuffer.length)) != -1){
\r
302 bout.write(dataBuffer, 0, read);
\r
304 byte[] data = bout.toByteArray();
\r
310 private void initConfiguration(InputStream in){
\r
312 ConfigFileImporter parser = new ConfigFileImporter(defaultContext);
\r
314 } catch(IOException e){
\r
315 throw new ApplicationInitializationError(e);
\r
317 for(Iterator<BirthmarkSpi> i = ServiceRegistry.lookupProviders(BirthmarkSpi.class); i.hasNext(); ){
\r
318 BirthmarkSpi service = i.next();
\r
319 defaultContext.addService(service);
\r
324 private void operationStart(OperationType type){
\r
325 if(type != OperationType.CONFIGURATION && !configDone){
\r
328 if(warnings == null){
\r
329 warnings = new WarningMessages(type);
\r
330 fireEvent(new OperationEvent(OperationStage.OPERATION_START, type, warnings));
\r
332 stack.push(warnings);
\r
333 fireEvent(new OperationEvent(OperationStage.SUB_OPERATION_START, type, warnings));
\r
336 private void operationDone(OperationType type){
\r
337 fireEvent(new OperationEvent(OperationStage.SUB_OPERATION_DONE, type, warnings));
\r
339 if(stack.size() == 0){
\r
340 fireEvent(new OperationEvent(OperationStage.OPERATION_DONE, type, warnings));
\r
345 private void fireEvent(OperationEvent e){
\r
346 for(OperationListener listener: listeners){
\r
347 switch(e.getStage()){
\r
348 case OPERATION_START:
\r
349 listener.operationStart(e);
\r
351 case SUB_OPERATION_START:
\r
352 listener.subOperationStart(e);
\r
354 case SUB_OPERATION_DONE:
\r
355 listener.subOperationDone(e);
\r
357 case OPERATION_DONE:
\r
358 listener.operationDone(e);
\r
361 throw new InternalError("unknown stage: " + e.getStage());
\r
366 private BirthmarkSet[] extractImpl(String[] birthmarks, String[] files, BirthmarkContext context) throws IOException, BirthmarkExtractionException{
\r
367 ClassFileArchive[] archives = createArchives(files, context);
\r
368 BirthmarkExtractor[] extractors = createExtractors(birthmarks, context);
\r
369 ExtractionUnit unit = context.getExtractionUnit();
\r
371 if(unit == ExtractionUnit.CLASS){
\r
372 return extractFromClass(archives, extractors, context);
\r
374 else if(unit == ExtractionUnit.PACKAGE){
\r
375 return extractFromPackage(archives, extractors, context);
\r
377 else if(unit == ExtractionUnit.ARCHIVE){
\r
378 return extractFromProduct(archives, extractors, context);
\r
383 private BirthmarkSet[] extractFromPackage(ClassFileArchive[] archives, BirthmarkExtractor[] extractors, BirthmarkContext context) throws IOException, BirthmarkExtractionException{
\r
384 Map<String, BirthmarkSet> list = new HashMap<String, BirthmarkSet>();
\r
386 for(ClassFileArchive archive: archives){
\r
387 for(ClassFileEntry entry: archive){
\r
389 String name = entry.getClassName();
\r
390 String packageName = parsePackageName(name);
\r
391 BirthmarkSet bs = list.get(packageName);
\r
393 bs = new BirthmarkSet(packageName, archive.getLocation());
\r
394 list.put(packageName, bs);
\r
397 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
\r
398 for(BirthmarkExtractor extractor: extractors){
\r
399 if(extractor.isAcceptable(ExtractionUnit.PACKAGE)){
\r
400 Birthmark b = bs.getBirthmark(extractor.getProvider().getType());
\r
402 b = extractor.createBirthmark();
\r
403 bs.addBirthmark(b);
\r
405 extractor.extract(b, new ByteArrayInputStream(data), context);
\r
408 } catch(IOException e){
\r
409 warnings.addMessage(e, archive.getName());
\r
414 return list.values().toArray(new BirthmarkSet[list.size()]);
\r
417 private String parsePackageName(String name){
\r
418 String n = name.replace('/', '.');
\r
419 int index = n.lastIndexOf('.');
\r
421 n = n.substring(0, index - 1);
\r
427 private BirthmarkSet[] extractFromClass(ClassFileArchive[] archives, BirthmarkExtractor[] extractors, BirthmarkContext context) throws IOException, BirthmarkExtractionException{
\r
428 List<BirthmarkSet> list = new ArrayList<BirthmarkSet>();
\r
430 for(ClassFileArchive archive: archives){
\r
431 for(ClassFileEntry entry: archive){
\r
433 BirthmarkSet birthmarkset = new BirthmarkSet(entry.getClassName(), entry.getLocation());
\r
434 list.add(birthmarkset);
\r
435 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
\r
436 for(BirthmarkExtractor extractor: extractors){
\r
437 if(extractor.isAcceptable(ExtractionUnit.CLASS)){
\r
438 Birthmark b = extractor.extract(new ByteArrayInputStream(data), context);
\r
439 birthmarkset.addBirthmark(b);
\r
442 } catch(IOException e){
\r
443 warnings.addMessage(e, entry.getClassName());
\r
447 return list.toArray(new BirthmarkSet[list.size()]);
\r
450 private BirthmarkSet[] extractFromProduct(ClassFileArchive[] archives, BirthmarkExtractor[] extractors, BirthmarkContext context) throws IOException, BirthmarkExtractionException{
\r
451 List<BirthmarkSet> list = new ArrayList<BirthmarkSet>();
\r
453 for(ClassFileArchive archive: archives){
\r
454 BirthmarkSet birthmarkset = new BirthmarkSet(archive.getName(), archive.getLocation());
\r
455 list.add(birthmarkset);
\r
457 for(ClassFileEntry entry: archive){
\r
459 byte[] data = inputStreamToByteArray(entry.getLocation().openStream());
\r
460 for(BirthmarkExtractor extractor: extractors){
\r
461 if(extractor.isAcceptable(ExtractionUnit.ARCHIVE)){
\r
462 Birthmark b = birthmarkset.getBirthmark(extractor.getProvider().getType());
\r
464 b = extractor.createBirthmark();
\r
465 birthmarkset.addBirthmark(b);
\r
467 extractor.extract(b, new ByteArrayInputStream(data), context);
\r
470 } catch(IOException e){
\r
471 warnings.addMessage(e, entry.getClassName());
\r
475 for(Iterator<BirthmarkSet> i = list.iterator(); i.hasNext(); ){
\r
476 BirthmarkSet set = i.next();
\r
477 if(set.getBirthmarksCount() == 0){
\r
482 return list.toArray(new BirthmarkSet[list.size()]);
\r
485 private ClassFileArchive[] createArchives(String[] files, BirthmarkContext context) throws IOException, MalformedURLException{
\r
486 ClasspathContext bytecode = context.getClasspathContext();
\r
487 List<ClassFileArchive> archives = new ArrayList<ClassFileArchive>();
\r
488 for(int i = 0; i < files.length; i++){
\r
490 if(files[i].endsWith(".class")){
\r
491 archives.add(new DefaultClassFileArchive(files[i]));
\r
493 else if(files[i].endsWith(".jar") || files[i].endsWith(".zip")){
\r
494 archives.add(new JarClassFileArchive(files[i]));
\r
495 bytecode.addClasspath(new File(files[i]).toURI().toURL());
\r
497 else if(files[i].endsWith(".war")){
\r
498 archives.add(new WarClassFileArchive(files[i]));
\r
500 } catch(IOException e){
\r
501 warnings.addMessage(e, files[i]);
\r
504 return archives.toArray(new ClassFileArchive[archives.size()]);
\r