METRON-1136 Metron Extensions System and Parser Extensions Feature Branch (ottobackwards) closes apache/metron#720
Project: http://git-wip-us.apache.org/repos/asf/metron/repo Commit: http://git-wip-us.apache.org/repos/asf/metron/commit/5f7454e4 Tree: http://git-wip-us.apache.org/repos/asf/metron/tree/5f7454e4 Diff: http://git-wip-us.apache.org/repos/asf/metron/diff/5f7454e4 Branch: refs/heads/feature/METRON-1136-extensions-parsers Commit: 5f7454e4ad4574574a6bcf3cefb9a4fd95ef4c8d Parents: 8560d2d Author: ottobackwards <ottobackwa...@gmail.com> Authored: Wed Aug 30 11:03:32 2017 -0400 Committer: otto <o...@apache.org> Committed: Wed Aug 30 11:37:00 2017 -0400 ---------------------------------------------------------------------- .gitignore | 1 + .travis.yml | 1 + README.md | 11 +- bundles-lib/README.md | 171 +++ bundles-lib/pom.xml | 185 +++ .../metron/bundles/BundleClassLoaders.java | 353 ++++++ .../apache/metron/bundles/BundleCloseable.java | 86 ++ .../metron/bundles/BundleManifestEntry.java | 48 + .../org/apache/metron/bundles/BundleMapper.java | 191 +++ .../org/apache/metron/bundles/BundleSystem.java | 202 +++ .../bundles/BundleThreadContextClassLoader.java | 211 ++++ .../apache/metron/bundles/ExtensionManager.java | 534 ++++++++ .../apache/metron/bundles/ExtensionMapping.java | 156 +++ .../metron/bundles/InstanceClassLoader.java | 161 +++ .../metron/bundles/NotInitializedException.java | 38 + .../metron/bundles/VFSBundleClassLoader.java | 520 ++++++++ .../bundles/VFSBundleClassLoaderResource.java | 110 ++ .../behavior/RequiresInstanceClassLoading.java | 37 + .../apache/metron/bundles/bundle/Bundle.java | 48 + .../bundles/bundle/BundleCoordinates.java | 96 ++ .../metron/bundles/bundle/BundleDetails.java | 205 +++ .../metron/bundles/util/BundleProperties.java | 247 ++++ .../metron/bundles/util/BundleSelector.java | 48 + .../apache/metron/bundles/util/BundleUtil.java | 145 +++ .../metron/bundles/util/DummyFileObject.java | 230 ++++ .../bundles/util/FileSystemManagerFactory.java | 87 ++ .../apache/metron/bundles/util/FileUtils.java | 35 + .../bundles/util/ImmutableCollectionUtils.java | 65 + .../apache/metron/bundles/util/StringUtils.java | 118 ++ .../org/apache/metron/bundles/AbstractFoo.java | 24 + .../metron/bundles/BundleClassLoadersTest.java | 110 ++ .../apache/metron/bundles/BundleMapperTest.java | 180 +++ .../apache/metron/bundles/BundleSystemTest.java | 56 + .../BundleThreadContextClassLoaderTest.java | 142 +++ .../apache/metron/bundles/BundleUtilTest.java | 125 ++ .../bundles/ExtensionClassInitializerTest.java | 33 + .../metron/bundles/ExtensionManagerTest.java | 82 ++ .../BundleMapperIntegrationTest.java | 146 +++ .../util/ImmutableCollectionUtilsTest.java | 85 ++ .../metron/bundles/util/ResourceCopier.java | 60 + .../apache/metron/bundles/util/TestUtil.java | 55 + .../parsers/interfaces/MessageParser.java | 22 + .../BundleMapper/conf/bundle.properties | 21 + .../lib/metron-parser-bar-bundle-0.4.1.bundle | Bin 0 -> 21972 bytes .../lib2/metron-parser-foo-bundle-0.4.1.bundle | Bin 0 -> 21983 bytes .../org.apache.metron.bundles.AbstractFoo | 16 + .../src/test/resources/bundle.properties | 22 + .../bundle-with-versioning/META-INF/MANIFEST.MF | 15 + .../META-INF/MANIFEST.MF | 12 + .../META-INF/MANIFEST.MF | 7 + bundles-maven-plugin/LICENSE | 202 +++ bundles-maven-plugin/NOTICE | 5 + bundles-maven-plugin/README.md | 89 ++ bundles-maven-plugin/pom.xml | 328 +++++ .../maven/plugins/bundles/BundleMojo.java | 743 +++++++++++ .../bundles/BundleProvidedDependenciesMojo.java | 328 +++++ .../resources/META-INF/plexus/components.xml | 52 + metron-analytics/metron-maas-service/README.md | 2 +- .../METRON/CURRENT/configuration/metron-env.xml | 42 + .../common-services/METRON/CURRENT/metainfo.xml | 39 + .../CURRENT/package/scripts/metron_service.py | 43 +- .../package/scripts/params/params_linux.py | 8 + .../package/scripts/params/status_params.py | 9 + .../CURRENT/package/scripts/parser_commands.py | 62 +- .../package/templates/bundle.properties.j2 | 21 + .../CURRENT/package/templates/global.json.j2 | 3 +- .../METRON/CURRENT/package/templates/metron.j2 | 1 + .../docker/rpm-docker/SPECS/metron.spec | 498 +++++++- .../packaging/docker/rpm-docker/pom.xml | 79 ++ .../sensor-config-single-parser.e2e-spec.ts | 2 +- .../sensor-field-schema.component.spec.ts | 4 +- ...sensor-parser-config-readonly.component.html | 2 +- ...sor-parser-config-readonly.component.spec.ts | 4 +- .../sensor-parser-config.component.spec.ts | 51 +- .../sensor-parser-config.component.ts | 12 +- .../sensor-parser-list.component.spec.ts | 16 +- .../src/app/service/grok-validation.service.ts | 9 +- .../sensor-parser-config.service.spec.ts | 2 +- metron-interface/metron-rest/README.md | 33 + metron-interface/metron-rest/pom.xml | 34 + .../src/main/config/rest_application.yml | 4 + .../apache/metron/rest/MetronRestConstants.java | 4 +- .../metron/rest/config/BundleSystemConfig.java | 64 + .../apache/metron/rest/config/GrokConfig.java | 8 +- .../metron/rest/controller/GrokController.java | 13 +- .../controller/ParserExtensionController.java | 114 ++ .../metron/rest/service/ExtensionService.java | 58 + .../apache/metron/rest/service/GrokService.java | 4 +- .../apache/metron/rest/service/HdfsService.java | 4 + .../rest/service/SensorParserConfigService.java | 4 +- .../rest/service/impl/ExtensionServiceImpl.java | 562 +++++++++ .../rest/service/impl/GrokServiceImpl.java | 72 +- .../rest/service/impl/HdfsServiceImpl.java | 15 + .../impl/SensorParserConfigServiceImpl.java | 110 +- .../src/main/resources/application-test.yml | 4 + .../src/main/resources/application-vagrant.yml | 3 +- .../apache/metron/rest/config/TestConfig.java | 27 +- .../GrokControllerIntegrationTest.java | 4 +- .../KafkaControllerIntegrationTest.java | 4 +- ...arserExtensionControllerIntegrationTest.java | 165 +++ ...richmentConfigControllerIntegrationTest.java | 3 +- ...IndexingConfigControllerIntegrationTest.java | 3 +- ...orParserConfigControllerIntegrationTest.java | 74 +- .../service/impl/GrokServiceImplNoMockTest.java | 122 ++ .../rest/service/impl/GrokServiceImplTest.java | 31 +- .../impl/SensorParserConfigServiceImplTest.java | 95 +- ...on-parser-test-assembly-0.4.0-archive.tar.gz | Bin 0 -> 24772 bytes .../test/resources/zookeeper/bundle.properties | 21 + .../src/test/resources/zookeeper/global.json | 25 + metron-maven-archetypes/README.md | 57 + .../metron-parser-extension-archetype/README.md | 83 ++ .../metron-parser-extension-archetype/pom.xml | 46 + .../META-INF/maven/archetype-metadata.xml | 81 ++ .../pom.xml | 49 + .../src/main/assembly/assembly.xml | 56 + .../metron-parser-__parserName__-bundle/pom.xml | 41 + .../metron-parser-__parserName__/README.md | 5 + .../metron-parser-__parserName__/pom.xml | 83 ++ .../elasticsearch/__parserName___index.template | 124 ++ .../zookeeper/enrichments/__parserName__.json | 20 + .../zookeeper/indexing/__parserName__.json | 18 + .../zookeeper/parsers/__parserName__.json | 7 + .../__parserClassName__Parser.java | 79 ++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../src/main/resources/patterns/common | 96 ++ .../__parserClassName__ParserTest.java | 51 + .../__parserClassName__IntegrationTest.java | 52 + .../config/zookeeper/bundle.properties | 23 + .../test/resources/config/zookeeper/global.json | 27 + .../src/test/resources/data/parsed/test.parsed | 2 + .../src/test/resources/data/raw/test.raw | 2 + .../src/test/resources/log4j.properties | 37 + .../main/resources/archetype-resources/pom.xml | 36 + .../projects/basic/archetype.properties | 8 + .../src/test/resources/projects/basic/goal.txt | 0 metron-maven-archetypes/pom.xml | 34 + metron-maven-archetypes/project.png | Bin 0 -> 127469 bytes .../org/apache/metron/common/Constants.java | 1 + .../metron/common/bolt/ConfiguredBolt.java | 29 +- .../common/configuration/ConfigurationType.java | 15 +- .../configuration/ConfigurationsUtils.java | 89 +- .../extensions/ParserExtensionConfig.java | 192 +++ .../metron/common/utils/ResourceLoader.java | 174 +++ .../common/cli/ConfigurationsUtilsTest.java | 18 +- .../SensorEnrichmentConfigTest.java | 7 +- .../extensions/ParserExtensionConfigTest.java | 39 + .../metron/common/utils/ResourceLoaderTest.java | 90 ++ .../main/config/zookeeper/enrichments/asa.json | 8 - .../main/config/zookeeper/enrichments/bro.json | 18 - .../config/zookeeper/enrichments/snort.json | 29 - .../config/zookeeper/enrichments/websphere.json | 18 - .../main/config/zookeeper/enrichments/yaf.json | 20 - .../components/ConfigUploadComponent.java | 11 + metron-platform/metron-extensions/README.md | 46 + .../metron-extensions/extension_deployment.md | 20 + .../metron-extensions/extension_packaging.md | 16 + .../metron-extensions/extension_terms.md | 25 + .../metron-parser-extensions/README.md | 9 + .../adding_system_parsers.md | 345 +++++ .../metron-parser-asa-assembly/pom.xml | 50 + .../src/main/assembly/assembly.xml | 65 + .../metron-parser-asa-bundle/pom.xml | 42 + .../metron-parser-asa/README.md | 3 + .../metron-parser-asa/pom.xml | 86 ++ .../main/config/zookeeper/enrichments/asa.json | 8 + .../src/main/config/zookeeper/indexing/asa.json | 18 + .../src/main/config/zookeeper/parsers/asa.json | 7 + .../metron/parsers/asa/BasicAsaParser.java | 240 ++++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../src/main/resources/patterns/asa | 179 +++ .../src/main/resources/patterns/common | 96 ++ .../metron/parsers/asa/BasicAsaParserTest.java | 187 +++ .../asa/integration/AsaIntegrationTest.java | 49 + .../src/test/resources/data/parsed/test.parsed | 128 ++ .../src/test/resources/data/raw/test.raw | 128 ++ .../src/test/resources/log4j.properties | 34 + .../metron-parser-asa-extension/pom.xml | 36 + .../metron-parser-bro-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 42 + .../metron-parser-bro-bundle/pom.xml | 41 + .../metron-parser-bro/README.md | 3 + .../metron-parser-bro/pom.xml | 83 ++ .../config/elasticsearch/bro_index.template | 972 ++++++++++++++ .../main/config/zookeeper/enrichments/bro.json | 17 + .../src/main/config/zookeeper/indexing/bro.json | 18 + .../src/main/config/zookeeper/parsers/bro.json | 6 + .../metron/parsers/bro/BasicBroParser.java | 180 +++ .../apache/metron/parsers/bro/JSONCleaner.java | 91 ++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../metron/parsers/bro/BasicBroParserTest.java | 1189 ++++++++++++++++++ .../parsers/integration/BroIntegrationTest.java | 48 + .../test/resources/TestSchemas/BroSchema.json | 28 + .../test/resources/config/BroParserTest.config | 20 + .../src/test/resources/data/parsed/test.parsed | 27 + .../src/test/resources/data/raw/test.raw | 27 + .../src/test/resources/log4j.properties | 34 + .../metron-parser-bro-extension/pom.xml | 36 + .../metron-parser-bundle-tests/pom.xml | 51 + .../parsers/ASABundleHDFSIntegrationTest.java | 265 ++++ ...etron-parser-test-bundle-1.0-SNAPSHOT.bundle | Bin 0 -> 22129 bytes .../metron-parser-asa-bundle-0.4.0.bundle | Bin 0 -> 45283 bytes .../test/resources/zookeeper/bundle.properties | 23 + .../resources/zookeeper/enrichments/test.json | 75 ++ .../src/test/resources/zookeeper/global.json | 27 + .../test/resources/zookeeper/indexing/test.json | 18 + .../metron-parser-cef-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 42 + .../metron-parser-cef-bundle/pom.xml | 41 + .../metron-parser-cef/README.md | 3 + .../metron-parser-cef/pom.xml | 89 ++ .../main/config/zookeeper/enrichments/cef.json | 19 + .../src/main/config/zookeeper/indexing/cef.json | 18 + .../src/main/config/zookeeper/parsers/cef.json | 7 + .../apache/metron/parsers/cef/CEFParser.java | 274 ++++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../metron/parsers/cef/CEFParserTest.java | 291 +++++ .../src/test/resources/log4j.properties | 34 + .../org/apache/metron/parsers/cef/adallom.cef | 1 + .../apache/metron/parsers/cef/adallom.schema | 37 + .../org/apache/metron/parsers/cef/cyberark.cef | 1 + .../org/apache/metron/parsers/cef/cyberark.json | 21 + .../apache/metron/parsers/cef/cyberark.schema | 38 + .../org/apache/metron/parsers/cef/palo.cef | 1 + .../org/apache/metron/parsers/cef/palo.schema | 38 + .../org/apache/metron/parsers/cef/waf.cef | 1 + .../org/apache/metron/parsers/cef/waf.schema | 67 + .../metron-parser-cef-extension/pom.xml | 36 + .../metron-parser-extensions-testing/pom.xml | 88 ++ .../metron-parser-fireeye-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 53 + .../metron-parser-fireeye-bundle/pom.xml | 41 + .../metron-parser-fireeye/README.md | 3 + .../metron-parser-fireeye/pom.xml | 89 ++ .../config/zookeeper/enrichments/fireeye.json | 20 + .../main/config/zookeeper/indexing/fireeye.json | 18 + .../main/config/zookeeper/parsers/fireeye.json | 7 + .../parsers/fireeye/BasicFireEyeParser.java | 222 ++++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../src/main/resources/patterns/common | 96 ++ .../src/main/resources/patterns/fireeye | 9 + .../parsers/fireeye/BasicFireEyeParserTest.java | 76 ++ .../config/BasicFireEyeParserTest.config | 20 + .../src/test/resources/data/parsed/test.parsed | 2 + .../src/test/resources/data/raw/test.raw | 2 + .../src/test/resources/log4j.properties | 34 + .../resources/logData/FireEyeParserTest.txt | 8 + .../metron-parser-fireeye-extension/pom.xml | 36 + .../metron-parser-ise-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 42 + .../metron-parser-ise-bundle/pom.xml | 41 + .../metron-parser-ise/README.md | 3 + .../metron-parser-ise/pom.xml | 89 ++ .../main/config/zookeeper/enrichments/ise.json | 20 + .../src/main/config/zookeeper/indexing/ise.json | 18 + .../src/main/config/zookeeper/parsers/ise.json | 7 + .../metron/parsers/ise/BasicIseParser.java | 101 ++ .../apache/metron/parsers/ise/ISEParser.java | 665 ++++++++++ .../org/apache/metron/parsers/ise/ISEParser.jj | 12 + .../metron/parsers/ise/ISEParserConstants.java | 69 + .../parsers/ise/ISEParserTokenManager.java | 676 ++++++++++ .../metron/parsers/ise/JavaCharStream.java | 633 ++++++++++ .../metron/parsers/ise/ParseException.java | 204 +++ .../org/apache/metron/parsers/ise/Token.java | 148 +++ .../metron/parsers/ise/TokenMgrError.java | 164 +++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../metron/parsers/ise/BasicIseParserTest.java | 56 + .../test/resources/TestSchemas/IseSchema.json | 21 + .../resources/config/BasicIseParserTest.config | 20 + .../src/test/resources/log4j.properties | 34 + .../test/resources/logData/IseParserTest.txt | 308 +++++ .../metron-parser-ise-extension/pom.xml | 36 + .../metron-parser-lancope-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 42 + .../metron-parser-lancope-bundle/pom.xml | 41 + .../metron-parser-lancope/README.md | 3 + .../metron-parser-lancope/pom.xml | 89 ++ .../config/zookeeper/enrichments/lancope.json | 20 + .../main/config/zookeeper/indexing/lancope.json | 18 + .../main/config/zookeeper/parsers/lancope.json | 7 + .../parsers/lancope/BasicLancopeParser.java | 95 ++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../parsers/lancope/BasicLancopeParserTest.java | 57 + .../resources/TestSchemas/LancopeSchema.json | 28 + .../config/BasicLancopeParserTest.config | 20 + .../src/test/resources/log4j.properties | 34 + .../resources/logData/LancopeParserTest.txt | 1 + .../metron-parser-lancope-extension/pom.xml | 36 + .../metron-parser-logstash-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 42 + .../metron-parser-logstash-bundle/pom.xml | 41 + .../metron-parser-logstash/README.md | 3 + .../metron-parser-logstash/pom.xml | 83 ++ .../config/zookeeper/enrichments/logstash.json | 20 + .../config/zookeeper/indexing/logstash.json | 18 + .../main/config/zookeeper/parsers/logstash.json | 7 + .../parsers/logstash/BasicLogstashParser.java | 95 ++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../logstash/BasicLogstashParserTest.java | 46 + .../src/test/resources/log4j.properties | 34 + .../metron-parser-logstash-extension/pom.xml | 36 + .../metron-parser-paloalto-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 42 + .../metron-parser-paloalto-bundle/pom.xml | 41 + .../metron-parser-paloalto/README.md | 3 + .../metron-parser-paloalto/pom.xml | 89 ++ .../config/zookeeper/enrichments/paloalto.json | 20 + .../config/zookeeper/indexing/paloalto.json | 18 + .../main/config/zookeeper/parsers/paloalto.json | 7 + .../paloalto/BasicPaloAltoFirewallParser.java | 215 ++++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../BasicPaloAltoFirewallParserTest.java | 57 + .../BasicPaloAltoFirewallParserTest.config | 20 + .../src/test/resources/log4j.properties | 34 + .../logData/PaloAltoFirewallParserTest.txt | 2 + .../metron-parser-paloalto-extension/pom.xml | 36 + .../metron-parser-snort-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 53 + .../metron-parser-snort-bundle/pom.xml | 41 + .../metron-parser-snort/README.md | 3 + .../metron-parser-snort/pom.xml | 83 ++ .../config/elasticsearch/snort_index.template | 183 +++ .../config/zookeeper/enrichments/snort.json | 29 + .../main/config/zookeeper/indexing/snort.json | 18 + .../main/config/zookeeper/parsers/snort.json | 6 + .../metron/parsers/snort/BasicSnortParser.java | 214 ++++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../integration/SnortIntegrationTest.java | 49 + .../metron/parsers/snort/SnortParserTest.java | 149 +++ .../src/test/resources/data/parsed/test.parsed | 3 + .../src/test/resources/data/raw/test.raw | 3 + .../src/test/resources/log4j.properties | 34 + .../metron-parser-snort-extension/pom.xml | 36 + .../metron-parser-sourcefire-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 53 + .../metron-parser-sourcefire-bundle/pom.xml | 41 + .../metron-parser-sourcefire/README.md | 3 + .../metron-parser-sourcefire/pom.xml | 89 ++ .../zookeeper/enrichments/sourcefire.json | 20 + .../config/zookeeper/indexing/sourcefire.json | 18 + .../config/zookeeper/parsers/sourcefire.json | 7 + .../sourcefire/BasicSourcefireParser.java | 129 ++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../src/main/resources/patterns/common | 96 ++ .../src/main/resources/patterns/sourcefire | 30 + .../sourcefire/BasicSourcefireParserTest.java | 58 + .../resources/TestSchemas/SourcefireSchema.json | 34 + .../config/BasicSourcefireParserTest.config | 20 + .../src/test/resources/global.json | 27 + .../src/test/resources/log4j.properties | 34 + .../resources/logData/SourcefireParserTest.txt | 3 + .../metron-parser-sourcefire-extension/pom.xml | 36 + .../metron-parser-squid-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 55 + .../metron-parser-squid-bundle/pom.xml | 41 + .../metron-parser-squid/README.md | 3 + .../metron-parser-squid/pom.xml | 83 ++ .../config/zookeeper/enrichments/squid.json | 20 + .../main/config/zookeeper/indexing/squid.json | 18 + .../main/config/zookeeper/parsers/squid.json | 19 + .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../src/main/resources/patterns/common | 96 ++ .../src/main/resources/patterns/squid | 2 + .../integration/SquidIntegrationTest.java | 48 + .../metron/parsers/squid/SquidParserTest.java | 102 ++ .../src/test/resources/data/parsed/test.parsed | 2 + .../src/test/resources/data/raw/test.raw | 2 + .../src/test/resources/log4j.properties | 34 + .../metron-parser-squid-extension/pom.xml | 38 + .../metron-parser-websphere-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 53 + .../metron-parser-websphere-bundle/pom.xml | 41 + .../metron-parser-websphere/README.md | 3 + .../metron-parser-websphere/pom.xml | 83 ++ .../config/zookeeper/enrichments/websphere.json | 18 + .../config/zookeeper/indexing/websphere.json | 18 + .../config/zookeeper/parsers/websphere.json | 11 + .../parsers/websphere/GrokWebSphereParser.java | 143 +++ .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../src/main/resources/patterns/common | 96 ++ .../src/main/resources/patterns/websphere | 37 + .../integration/WebSphereIntegrationTest.java | 52 + .../websphere/GrokWebSphereParserTest.java | 269 ++++ .../src/test/resources/data/parsed/test.parsed | 5 + .../src/test/resources/data/raw/test.raw | 5 + .../src/test/resources/global.json | 27 + .../src/test/resources/log4j.properties | 34 + .../metron-parser-websphere-extension/pom.xml | 36 + .../metron-parser-yaf-assembly/pom.xml | 49 + .../src/main/assembly/assembly.xml | 55 + .../metron-parser-yaf-bundle/pom.xml | 41 + .../metron-parser-yaf/README.md | 3 + .../metron-parser-yaf/pom.xml | 83 ++ .../config/elasticsearch/yaf_index.template | 205 +++ .../main/config/zookeeper/enrichments/yaf.json | 20 + .../src/main/config/zookeeper/indexing/yaf.json | 18 + .../src/main/config/zookeeper/parsers/yaf.json | 18 + .../src/main/resources/META-INF/LICENSE | 602 +++++++++ .../src/main/resources/META-INF/NOTICE | 40 + .../src/main/resources/patterns/common | 96 ++ .../src/main/resources/patterns/yaf | 2 + .../parsers/integration/YafIntegrationTest.java | 48 + .../metron/parsers/yaf/YafParserTest.java | 101 ++ .../test/resources/data/indexed/test.indexed | 10 + .../src/test/resources/data/parsed/test.parsed | 10 + .../src/test/resources/data/raw/test.raw | 10 + .../src/test/resources/log4j.properties | 34 + .../metron-parser-yaf-extension/pom.xml | 38 + .../parser_extension_deployment.md | 240 ++++ .../parser_extension_packaging.md | 84 ++ .../metron-parser-extensions/pom.xml | 97 ++ metron-platform/metron-extensions/pom.xml | 60 + .../src/main/config/zookeeper/indexing/asa.json | 18 - .../src/main/config/zookeeper/indexing/bro.json | 17 - .../main/config/zookeeper/indexing/snort.json | 17 - .../config/zookeeper/indexing/websphere.json | 18 - .../src/main/config/zookeeper/indexing/yaf.json | 17 - .../src/main/config/zookeeper/bundle.properties | 20 + .../extensions/parsers/metron-test-parsers.json | 7 + .../src/main/config/zookeeper/global.json | 3 +- .../src/main/sample/data/asa/parsed/asa_parsed | 128 -- .../src/main/sample/data/asa/raw/asa_raw | 128 -- .../sample/data/bro/parsed/BroExampleParsed | 27 - .../main/sample/data/bro/raw/BroExampleOutput | 27 - .../main/sample/data/snort/parsed/SnortParsed | 3 - .../src/main/sample/data/snort/raw/SnortOutput | 3 - .../sample/data/squid/parsed/SquidExampleParsed | 2 - .../sample/data/squid/raw/SquidExampleOutput | 2 - .../data/websphere/parsed/WebsphereParsed | 5 - .../data/websphere/raw/WebsphereOutput.txt | 5 - .../src/main/sample/data/yaf/indexed/YafIndexed | 10 - .../sample/data/yaf/parsed/YafExampleParsed | 10 - .../main/sample/data/yaf/raw/YafExampleOutput | 10 - metron-platform/metron-management/README.md | 8 +- .../management/ConfigurationFunctions.java | 52 +- .../management/ConfigurationFunctionsTest.java | 53 +- .../management/ParserConfigFunctionsTest.java | 6 +- metron-platform/metron-parsers/README.md | 6 +- metron-platform/metron-parsers/pom.xml | 10 + .../src/main/config/zookeeper/parsers/asa.json | 7 - .../src/main/config/zookeeper/parsers/bro.json | 5 - .../main/config/zookeeper/parsers/snort.json | 5 - .../main/config/zookeeper/parsers/squid.json | 19 - .../config/zookeeper/parsers/websphere.json | 11 - .../src/main/config/zookeeper/parsers/yaf.json | 18 - .../org/apache/metron/parsers/BasicParser.java | 1 + .../org/apache/metron/parsers/GrokParser.java | 211 ---- .../metron/parsers/asa/BasicAsaParser.java | 240 ---- .../apache/metron/parsers/bolt/ParserBolt.java | 25 +- .../metron/parsers/bolt/ParserLoader.java | 95 ++ .../metron/parsers/bro/BasicBroParser.java | 180 --- .../apache/metron/parsers/bro/JSONCleaner.java | 91 -- .../apache/metron/parsers/cef/CEFParser.java | 274 ---- .../parsers/fireeye/BasicFireEyeParser.java | 222 ---- .../apache/metron/parsers/grok/GrokBuilder.java | 231 ++++ .../apache/metron/parsers/grok/GrokParser.java | 175 +++ .../parsers/interfaces/MessageParser.java | 3 + .../metron/parsers/ise/BasicIseParser.java | 101 -- .../apache/metron/parsers/ise/ISEParser.java | 660 ---------- .../org/apache/metron/parsers/ise/ISEParser.jj | 12 - .../metron/parsers/ise/ISEParserConstants.java | 69 - .../parsers/ise/ISEParserTokenManager.java | 676 ---------- .../metron/parsers/ise/JavaCharStream.java | 633 ---------- .../metron/parsers/ise/ParseException.java | 204 --- .../org/apache/metron/parsers/ise/Token.java | 148 --- .../metron/parsers/ise/TokenMgrError.java | 164 --- .../parsers/lancope/BasicLancopeParser.java | 95 -- .../parsers/logstash/BasicLogstashParser.java | 95 -- .../paloalto/BasicPaloAltoFirewallParser.java | 215 ---- .../metron/parsers/snort/BasicSnortParser.java | 214 ---- .../sourcefire/BasicSourcefireParser.java | 129 -- .../parsers/topology/ParserTopologyBuilder.java | 10 +- .../parsers/websphere/GrokWebSphereParser.java | 143 --- .../src/main/resources/patterns/asa | 179 --- .../src/main/resources/patterns/fireeye | 9 - .../src/main/resources/patterns/sourcefire | 30 - .../src/main/resources/patterns/squid | 2 - .../src/main/resources/patterns/websphere | 37 - .../src/main/resources/patterns/yaf | 2 - .../apache/metron/parsers/GrokParserTest.java | 95 -- .../metron/parsers/SampleGrokParserTest.java | 97 -- .../apache/metron/parsers/SnortParserTest.java | 150 --- .../apache/metron/parsers/SquidParserTest.java | 101 -- .../apache/metron/parsers/YafParserTest.java | 100 -- .../metron/parsers/asa/BasicAsaParserTest.java | 188 --- .../metron/parsers/bro/BasicBroParserTest.java | 1189 ------------------ .../metron/parsers/cef/CEFParserTest.java | 291 ----- .../parsers/fireeye/BasicFireEyeParserTest.java | 76 -- .../metron/parsers/grok/GrokBuilderTest.java | 83 ++ .../metron/parsers/grok/GrokParserTest.java | 106 ++ .../parsers/grok/SampleGrokParserTest.java | 97 ++ .../parsers/integration/AsaIntegrationTest.java | 37 - .../parsers/integration/BroIntegrationTest.java | 37 - .../integration/JSONMapIntegrationTest.java | 4 +- .../integration/ParserIntegrationTest.java | 62 +- .../integration/SnortIntegrationTest.java | 37 - .../integration/SquidIntegrationTest.java | 37 - .../integration/WebSphereIntegrationTest.java | 40 - .../parsers/integration/YafIntegrationTest.java | 37 - .../validation/PathedSampleDataValidation.java | 61 + .../validation/SampleDataValidation.java | 22 +- .../metron/parsers/ise/BasicIseParserTest.java | 56 - .../parsers/lancope/BasicLancopeParserTest.java | 57 - .../BasicPaloAltoFirewallParserTest.java | 57 - .../sourcefire/BasicSourcefireParserTest.java | 58 - .../websphere/GrokWebSphereParserTest.java | 260 ---- ...pleHbaseEnrichmentWriterIntegrationTest.java | 32 +- .../integration/WriterBoltIntegrationTest.java | 47 +- .../test/resources/TestSchemas/BroSchema.json | 28 - .../test/resources/TestSchemas/IseSchema.json | 21 - .../resources/TestSchemas/LancopeSchema.json | 28 - .../test/resources/TestSchemas/PcapSchema.json | 22 - .../resources/TestSchemas/SourcefireSchema.json | 34 - .../config/BasicFireEyeParserTest.config | 20 - .../resources/config/BasicIseParserTest.config | 20 - .../config/BasicLancopeParserTest.config | 20 - .../BasicPaloAltoFirewallParserTest.config | 20 - .../config/BasicSourcefireParserTest.config | 20 - .../test/resources/config/BroParserTest.config | 20 - .../resources/logData/FireEyeParserTest.txt | 8 - .../test/resources/logData/IseParserTest.txt | 308 ----- .../resources/logData/LancopeParserTest.txt | 1 - .../logData/PaloAltoFirewallParserTest.txt | 2 - .../resources/logData/SourcefireParserTest.txt | 3 - .../org/apache/metron/parsers/cef/adallom.cef | 1 - .../apache/metron/parsers/cef/adallom.schema | 37 - .../org/apache/metron/parsers/cef/cyberark.cef | 1 - .../org/apache/metron/parsers/cef/cyberark.json | 21 - .../apache/metron/parsers/cef/cyberark.schema | 38 - .../org/apache/metron/parsers/cef/palo.cef | 1 - .../org/apache/metron/parsers/cef/palo.schema | 38 - .../org/apache/metron/parsers/cef/waf.cef | 1 - .../org/apache/metron/parsers/cef/waf.schema | 67 - .../src/test/resources/otherPatterns/common | 96 ++ .../src/test/resources/patterns/test | 2 + .../java/org/apache/metron/TestConstants.java | 4 + .../metron/test/utils/ResourceCopier.java | 96 ++ .../metron/test/utils/SampleDataUtils.java | 17 +- metron-platform/pom.xml | 1 + pom.xml | 17 +- .../metron-maas-service/index.html | 2 +- .../metron-management/index.html | 6 +- .../metron-platform/metron-parsers/index.html | 6 +- 555 files changed, 38637 insertions(+), 9757 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/metron/blob/5f7454e4/.gitignore ---------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore index 12fd7cd..bcb3642 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ target/ */target/ *dependency-reduced-pom.xml +*.versionsBackup .idea *.iml *.iws http://git-wip-us.apache.org/repos/asf/metron/blob/5f7454e4/.travis.yml ---------------------------------------------------------------------- diff --git a/.travis.yml b/.travis.yml index 917b184..d85a7ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ before_install: - time build_utils/verify_licenses.sh install: + - cd bundles-maven-plugin && mvn -q install && cd .. - time mvn -q -T 2C -DskipTests clean install script: http://git-wip-us.apache.org/repos/asf/metron/blob/5f7454e4/README.md ---------------------------------------------------------------------- diff --git a/README.md b/README.md index 8599f0f..308c82a 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,12 @@ To start exploring the capabilities of Apache Metron [follow these instructions # Building Metron +Build the maven plugin required for Metron ( this should only have to be one once ) + +``` +$cd bundles-maven-plugin && mvn -q install && cd .. +``` + Build the full project and run tests: ``` $ mvn clean install @@ -109,10 +115,13 @@ component and Apache Kafka as the unified data bus. Some high level links to the relevant subparts of the architecture, for more information: -* [Parsers](metron-platform/metron-parsers) : Parsing data from kafka into the Metron data model and passing it downstream to Enrichment. +* [Parsers](metron-platform/metron-parsers) : Base classes for parsing data from kafka into the Metron data model and passing it downstream to Enrichment. * [Enrichment](metron-platform/metron-enrichment) : Enriching data post-parsing and providing the ability to tag a message as an alert and assign a risk triage level via a custom rule language. * [Indexing](metron-platform/metron-indexing) : Indexing the data post-enrichment into HDFS, Elasticsearch or Solr. +Metron Extensions +* [Metron Parser Extensions](metron-platform/metron-extensions/metron-parser-extensions) : Parser Extensions parsing data from supported sources + Some useful utilities that cross all of these parts of the architecture: * [Stellar](metron-platform/metron-common) : A custom data transformation language that is used throughout metron from simple field transformation to expressing triage rules. * [Model as a Service](metron-analytics/metron-maas-service) : A Yarn application which can deploy machine learning and statistical models onto the cluster along with the associated Stellar functions to be able to call out to them in a scalable manner. http://git-wip-us.apache.org/repos/asf/metron/blob/5f7454e4/bundles-lib/README.md ---------------------------------------------------------------------- diff --git a/bundles-lib/README.md b/bundles-lib/README.md new file mode 100644 index 0000000..d2b41a4 --- /dev/null +++ b/bundles-lib/README.md @@ -0,0 +1,171 @@ +# Apache Metron Bundles + +Apache Metron Bundles and this documentation are a derivitave of the [Apache Nifi](http://www.nifi.apache.org) [NARs](http://nifi.apache.org/developer-guide.html). + +When software from many different organizations is all hosted within +the same environment, Java ClassLoaders quickly +become a concern. If multiple components have a dependency on the same +library but each depends on a different +version, many problems arise, typically resulting in unexpected +behavior or `NoClassDefFoundError` errors occurring. +In order to prevent these issues from becoming problematic, Apache NiFi +introduces the notion of a NiFi Archive, or NAR. The Apache Metron project has adapted and extended the NAR system as +Apache Metron Bundles. + +A BUNDLE allows several components and their dependencies to be packaged +together into a single package. +The BUNDLE package is then provided ClassLoader isolation from other BUNDLES +packages. Developers should always deploy their Apache Metron Extensions as BUNDLE packages. + +To achieve this, a developer creates a new Maven Artifact, which we +refer to as the BUNDLE artifact. The packaging is +set to `bundle`. The `dependencies` section of the POM is then created so +that the BUNDLE has a dependency on all Extension Components that are to be included within the BUNDLE. + +In order to use a packaging of `bundle`, we must use the `bundles-maven-plugin` module. +This is included by adding the following snippet to the bundle's pom.xml:> +> 0.4.1 below should be the current metron version +```xml +<build> + <plugins> + <plugin> + <groupId>org.apache.metron</groupId> + <artifactId>bundles-maven-plugin</artifactId> + <version>0.4.1</version> + <extensions>true</extensions> + </plugin> + </plugins> +</build> +``` + +The bundles-maven-plugin is included in the projects created by Apache Metron Extension maven archetypes. + + +The BUNDLE is able to have one dependency that is of type `bundle`. If more +than one dependency is specified that is of type +`bundle`, then the bundles-maven-plugin will error. If BUNDLE A adds a +dependency on BUNDLE B, this will *not* result in +BUNDLE A packaging all of the components of BUNDLE B. Rather, this will add +a `Bundle-Dependency-Id` element to the `MANIFEST.MF` +file of BUNDLE A. This will result in setting the ClassLoader of BUNDLE B as +the Parent ClassLoader of BUNDLE A. In this case, +we refer to BUNDLE B as the _Parent_ of BUNDLE A. + +## Per-Instance ClassLoading + +The bundles-lib provides the `@RequiresInstanceClassLoading` annotation to further expand and isolate the libraries +available on a componentâs classpath. You can annotate a extension class with `@RequiresInstanceClassLoading` +to indicate that the instance ClassLoader for the component requires a copy of all the resources in the +component's BUNDLE ClassLoader. When `@RequiresInstanceClassLoading` is not present, the +instance ClassLoader simply has it's parent ClassLoader set to the BUNDLE ClassLoader, rather than +copying resources. + +Because @RequiresInstanceClassLoading copies resources from the BUNDLE ClassLoader for each instance of the +extension, use this capability judiciously in an environment where many extensions may be present. If ten instances of one extension are created, all classes +from the component's BUNDLE ClassLoader are loaded into memory ten times. This could eventually increase the +memory footprint significantly when enough instances of the component are created. + + +## Apache VFS + +The bundles-lib utilizes the Apache VFS library for loading bundles. Bundles, which are zip files of a known structure, containing +jars of dependencies may be loaded by VFS as File Systems *themselves*, and in tern each jar in the bundle can be loaded by VFS as a File System. + +The VFSBundleClassloader therefore loads the bundle as a Jar File System, and also each dependency jar as a filesystem. This allows +for the complete loading of the bundle without having to unpack the bundle into a working directory. + +This is significantly different from the original Nifi implementation. + +## BundleSystem + +The BundleSystem class provides a useful and simple interface for using Bundles and instantiated class instances. +While the raw classes may be used, in many cases the BundleSystem will be sufficient and reduce complexity. + +Without BundleSystem, the minimum required to instantiate a class (simplified): + +```java +public static Optional<MessageParser<JSONObject>> loadParser(Map stormConfig, CuratorFramework client, SensorParserConfig parserConfig){ + MessageParser<JSONObject> parser = null; + try { + // fetch the BundleProperties from zookeeper + Optional<BundleProperties> bundleProperties = getBundleProperties(client); + + // create the FileSystemManager + FileSystemManager fileSystemManager = FileSystemManagerFactory.createFileSystemManager(new String[] {props.getArchiveExtension()}); + + // ADD in the classes we are going to support for plugins + ArrayList<Class> classes = new ArrayList<>(); + for( Map.Entry<String,String> entry : props.getBundleExtensionTypes().entrySet()){ + classes.add(Class.forName(entry.getValue())); + } + + // create the SystemBundle ( the bundle for the types that may be in the system classloader already ) + Bundle systemBundle = ExtensionManager.createSystemBundle(fileSystemManager, props); + + // get the correct bundle library directories from properties + List<URI> libDirs = props.getBundleLibraryDirectories(); + List<FileObject> libFileObjects = new ArrayList<>(); + for(URI libUri : libDirs){ + FileObject fileObject = fileSystemManager.resolveFile(libUri); + if(fileObject.exists()){ + libFileObjects.add(fileObject); + } + } + + // Initialize everything + BundleClassLoaders.getInstance().init(fileSystemManager, libFileObjects, props); + ExtensionManager.getInstance().init(classes, systemBundle, BundleClassLoaders.getInstance().getBundles()); + + // everything is ready, create our instance + parser = BundleThreadContextClassLoader.createInstance(parserConfig.getParserClassName(),MessageParser.class,props); + + }catch(Exception e){ + LOG.error("Failed to load parser " + parserConfig.getParserClassName(),e); + return Optional.empty(); + } + return Optional.of(parser); + } + +``` + +This is a lot to do, and a lot of unnecessary boilerplate code + +Using the BundleSystem class however: + +```java +public static Optional<MessageParser<JSONObject>> loadParser(Map stormConfig, + CuratorFramework client, SensorParserConfig parserConfig) { + MessageParser<JSONObject> parser = null; + try { + // fetch the BundleProperties from zookeeper + Optional<BundleProperties> bundleProperties = getBundleProperties(client); + BundleProperties props = bundleProperties.get(); + + // create the BundleSystem + // we only need to pass in the properties + BundleSystem bundleSystem = new BundleSystem.Builder().withBundleProperties(props).build(); + + // create our instance + parser = bundleSystem + .createInstance(parserConfig.getParserClassName(), MessageParser.class); + } else { + LOG.error("BundleProperties are missing!"); + } + } catch (Exception e) { + LOG.error("Failed to load parser " + parserConfig.getParserClassName(), e); + return Optional.empty(); + } + return Optional.of(parser); +} + +``` + +As we can see, this is much easier. + +With the BundleSystem, you may the defaults by calling the builder with : + +- withSystemBundle +- withFileSystemManager +- withExtensionClasses + + http://git-wip-us.apache.org/repos/asf/metron/blob/5f7454e4/bundles-lib/pom.xml ---------------------------------------------------------------------- diff --git a/bundles-lib/pom.xml b/bundles-lib/pom.xml new file mode 100644 index 0000000..0aecde9 --- /dev/null +++ b/bundles-lib/pom.xml @@ -0,0 +1,185 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software + Foundation (ASF) under one or more contributor license agreements. See the + NOTICE file distributed with this work for additional information regarding + copyright ownership. The ASF licenses this file to You under the Apache License, + Version 2.0 (the "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software distributed + under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES + OR CONDITIONS OF ANY KIND, either express or implied. See the License for + the specific language governing permissions and limitations under the License. + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.metron</groupId> + <artifactId>Metron</artifactId> + <version>0.4.1</version> + </parent> + + <artifactId>bundles-lib</artifactId> + <packaging>jar</packaging> + + <licenses> + <license> + <name>The Apache Software License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> + <distribution>repo</distribution> + </license> + </licenses> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + <dependencies> + <dependency> + <groupId>org.apache.accumulo</groupId> + <artifactId>accumulo-start</artifactId> + <version>${global_accumulo_version}</version> + <exclusions> + <exclusion> + <groupId>org.apache.hadoop</groupId> + <artifactId>hadoop-client</artifactId> + </exclusion> + <exclusion> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.atteo.classindex</groupId> + <artifactId>classindex</artifactId> + <version>${global_classindex_version}</version> + </dependency> + <dependency> + <groupId>org.apache.hadoop</groupId> + <artifactId>hadoop-common</artifactId> + <version>${global_hadoop_version}</version> + <exclusions> + <exclusion> + <artifactId>servlet-api</artifactId> + <groupId>javax.servlet</groupId> + </exclusion> + <exclusion> + <artifactId>slf4j-log4j12</artifactId> + <groupId>org.slf4j</groupId> + </exclusion> + <exclusion> + <artifactId>log4j</artifactId> + <groupId>log4j</groupId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.hadoop</groupId> + <artifactId>hadoop-hdfs</artifactId> + <version>${global_hadoop_version}</version> + <exclusions> + <exclusion> + <artifactId>servlet-api</artifactId> + <groupId>javax.servlet</groupId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.hbase</groupId> + <artifactId>hbase-client</artifactId> + <version>${global_hbase_version}</version> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </exclusion> + <exclusion> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </exclusion> + </exclusions> + </dependency> + <!-- from accumulo --> + <!-- <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-vfs2-project</artifactId> + <version>2.1</version> + </dependency> --> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${global_slf4j_version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <version>${global_slf4j_version}</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>${global_junit_version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-junit4</artifactId> + <version>1.6.6</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.hadoop</groupId> + <artifactId>hadoop-common</artifactId> + <version>${global_hadoop_version}</version> + <classifier>tests</classifier> + <exclusions> + <exclusion> + <artifactId>slf4j-log4j12</artifactId> + <groupId>org.slf4j</groupId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.hadoop</groupId> + <artifactId>hadoop-hdfs</artifactId> + <version>${global_hadoop_version}</version> + <classifier>tests</classifier> + </dependency> + <dependency> + <groupId>org.apache.hadoop</groupId> + <artifactId>hadoop-client</artifactId> + <version>${global_hadoop_version}</version> + <classifier>tests</classifier> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.metron</groupId> + <artifactId>metron-integration-test</artifactId> + <version>${project.parent.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>${global_jar_version}</version> + <executions> + <execution> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> http://git-wip-us.apache.org/repos/asf/metron/blob/5f7454e4/bundles-lib/src/main/java/org/apache/metron/bundles/BundleClassLoaders.java ---------------------------------------------------------------------- diff --git a/bundles-lib/src/main/java/org/apache/metron/bundles/BundleClassLoaders.java b/bundles-lib/src/main/java/org/apache/metron/bundles/BundleClassLoaders.java new file mode 100644 index 0000000..946c71a --- /dev/null +++ b/bundles-lib/src/main/java/org/apache/metron/bundles/BundleClassLoaders.java @@ -0,0 +1,353 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.metron.bundles; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import java.lang.invoke.MethodHandles; +import java.net.URISyntaxException; +import java.util.*; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.FileSystemManager; +import org.apache.metron.bundles.bundle.Bundle; +import org.apache.metron.bundles.bundle.BundleCoordinates; +import org.apache.metron.bundles.bundle.BundleDetails; +import org.apache.metron.bundles.util.BundleProperties; +import org.apache.metron.bundles.util.BundleSelector; +import org.apache.metron.bundles.util.FileUtils; +import org.apache.metron.bundles.util.BundleUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A singleton class used to initialize the extension and framework classloaders. + */ +public final class BundleClassLoaders { + + private static volatile BundleClassLoaders bundleClassLoaders; + private static volatile InitContext initContext; + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + /** + * Holds the context from {@code BundleClassLoaders} initialization, + * being the coordinate to bundle mapping. + * + * After initialization these are not changed, and as such they + * are immutable. + * + */ + private final static class InitContext { + + private final List<FileObject> extensionDirs; + private final Map<String, Bundle> bundles; + private final BundleProperties properties; + + private InitContext( + final List<FileObject> extensionDirs, + final Map<String, Bundle> bundles, + final BundleProperties properties) { + this.extensionDirs = ImmutableList.copyOf(extensionDirs); + this.bundles = ImmutableMap.copyOf(bundles); + this.properties = properties; + } + } + + private BundleClassLoaders() { + } + + /** + * @return The singleton instance of the BundleClassLoaders + * @throws NotInitializedException if BundleClassLoaders has not been init'd + */ + public static BundleClassLoaders getInstance() throws NotInitializedException { + BundleClassLoaders result = bundleClassLoaders; + if (result == null) { + throw new NotInitializedException("BundleClassLoaders not initialized"); + } + return result; + } + + /** + * Uninitializes the BundleClassloaders. After calling this <code>init</code> must be called + * before the rest of the methods are called afterwards. + * This is for TESTING ONLY at this time. Reset does not unload or clear any loaded classloaders. + */ + @VisibleForTesting + public static void reset() { + synchronized (BundleClassLoaders.class) { + initContext = null; + bundleClassLoaders = null; + } + } + + /** + * Initializes and loads the BundleClassLoaders. This method must be called before the rest of the + * methods to access the classloaders are called. Multiple calls to this method will have no effect, + * unless a different set of extension directories is passed, which will result in an <code>IllegaStateException</code> + * + * @param fileSystemManager the FileSystemManager + * @param extensionsDirs where to find extension artifacts + * @param props BundleProperties + * @throws FileSystemException if any issue occurs while working with the bundle files. + * @throws java.lang.ClassNotFoundException if unable to load class definition + * @throws IllegalStateException when already initialized with a given set of extension directories + * and extensionDirs does not match + */ + public static void init(final FileSystemManager fileSystemManager, final List<FileObject> extensionsDirs, + BundleProperties props) + throws FileSystemException, ClassNotFoundException, URISyntaxException { + if (extensionsDirs == null || fileSystemManager == null) { + throw new NullPointerException("cannot have empty arguments"); + } + synchronized (BundleClassLoaders.class) { + if (bundleClassLoaders != null) { + throw new IllegalStateException("BundleClassloader already exists"); + } + BundleClassLoaders b = new BundleClassLoaders(); + InitContext ic = b.load(fileSystemManager, extensionsDirs, props); + initContext = ic; + bundleClassLoaders = b; + } + } + + private InitContext load(final FileSystemManager fileSystemManager, + final List<FileObject> extensionsDirs, BundleProperties props) + throws FileSystemException, ClassNotFoundException, URISyntaxException { + // get the system classloader + final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); + + // find all bundle files and create class loaders for them. + final Map<String, Bundle> directoryBundleLookup = new LinkedHashMap<>(); + final Map<String, ClassLoader> coordinateClassLoaderLookup = new HashMap<>(); + final Map<String, Set<BundleCoordinates>> idBundleLookup = new HashMap<>(); + + for (FileObject extensionsDir : extensionsDirs) { + // make sure the bundle directory is there and accessible + FileUtils.ensureDirectoryExistAndCanRead(extensionsDir); + + final List<FileObject> bundleDirContents = new ArrayList<>(); + FileObject[] dirFiles = extensionsDir.findFiles(new BundleSelector(props.getArchiveExtension())); + if (dirFiles != null) { + List<FileObject> fileList = Arrays.asList(dirFiles); + bundleDirContents.addAll(fileList); + } + + if (!bundleDirContents.isEmpty()) { + final List<BundleDetails> bundleDetails = new ArrayList<>(); + final Map<String, String> bundleCoordinatesToBundleFile = new HashMap<>(); + + // load the bundle details which includes bundle dependencies + for (final FileObject bundleFile : bundleDirContents) { + if(!bundleFile.exists() || !bundleFile.isFile()) { + continue; + } + BundleDetails bundleDetail = null; + try { + bundleDetail = getBundleDetails(bundleFile, props); + } catch (IllegalStateException e) { + logger.warn("Unable to load BUNDLE {} due to {}, skipping...", + new Object[]{bundleFile.getURL(), e.getMessage()}); + } + + // prevent the application from starting when there are two BUNDLEs with same group, id, and version + final String bundleCoordinate = bundleDetail.getCoordinates().getCoordinates(); + if (bundleCoordinatesToBundleFile.containsKey(bundleCoordinate)) { + final String existingBundleWorkingDir = bundleCoordinatesToBundleFile + .get(bundleCoordinate); + throw new IllegalStateException( + "Unable to load BUNDLE with coordinates " + bundleCoordinate + + " and bundle file " + bundleDetail.getBundleFile() + + " because another BUNDLE with the same coordinates already exists at " + + existingBundleWorkingDir); + } + + bundleDetails.add(bundleDetail); + bundleCoordinatesToBundleFile.put(bundleCoordinate, + bundleDetail.getBundleFile().getURL().toURI().toString()); + } + + for (final Iterator<BundleDetails> bundleDetailsIter = bundleDetails.iterator(); + bundleDetailsIter.hasNext(); ) { + final BundleDetails bundleDetail = bundleDetailsIter.next(); + // populate bundle lookup + idBundleLookup.computeIfAbsent(bundleDetail.getCoordinates().getId(), + id -> new HashSet<>()).add(bundleDetail.getCoordinates()); + } + + int bundleCount; + do { + // record the number of bundles to be loaded + bundleCount = bundleDetails.size(); + + // attempt to create each bundle class loader + for (final Iterator<BundleDetails> bundleDetailsIter = bundleDetails.iterator(); + bundleDetailsIter.hasNext(); ) { + final BundleDetails bundleDetail = bundleDetailsIter.next(); + final BundleCoordinates bundleDependencyCoordinate = bundleDetail + .getDependencyCoordinates(); + + // see if this class loader is eligible for loading + ClassLoader potentialBundleClassLoader = null; + if (bundleDependencyCoordinate == null) { + potentialBundleClassLoader = createBundleClassLoader(fileSystemManager, + bundleDetail.getBundleFile(), ClassLoader.getSystemClassLoader()); + } else { + final String dependencyCoordinateStr = bundleDependencyCoordinate + .getCoordinates(); + + // if the declared dependency has already been loaded + if (coordinateClassLoaderLookup.containsKey(dependencyCoordinateStr)) { + final ClassLoader bundleDependencyClassLoader = coordinateClassLoaderLookup + .get(dependencyCoordinateStr); + potentialBundleClassLoader = createBundleClassLoader( + fileSystemManager, bundleDetail.getBundleFile(), + bundleDependencyClassLoader); + } else { + // get all bundles that match the declared dependency id + final Set<BundleCoordinates> coordinates = idBundleLookup + .get(bundleDependencyCoordinate.getId()); + + // ensure there are known bundles that match the declared dependency id + if (coordinates != null && !coordinates + .contains(bundleDependencyCoordinate)) { + // ensure the declared dependency only has one possible bundle + if (coordinates.size() == 1) { + // get the bundle with the matching id + final BundleCoordinates coordinate = coordinates.stream() + .findFirst().get(); + + // if that bundle is loaded, use it + if (coordinateClassLoaderLookup + .containsKey(coordinate.getCoordinates())) { + logger.warn(String.format( + "While loading '%s' unable to locate exact BUNDLE dependency '%s'. Only found one possible match '%s'. Continuing...", + bundleDetail.getCoordinates().getCoordinates(), + dependencyCoordinateStr, + coordinate.getCoordinates())); + + final ClassLoader bundleDependencyClassLoader = coordinateClassLoaderLookup + .get(coordinate.getCoordinates()); + potentialBundleClassLoader = createBundleClassLoader( + fileSystemManager, bundleDetail.getBundleFile(), + bundleDependencyClassLoader); + } + } + } + } + } + + // if we were able to create the bundle class loader, store it and remove the details + final ClassLoader bundleClassLoader = potentialBundleClassLoader; + if (bundleClassLoader != null) { + directoryBundleLookup + .put(bundleDetail.getBundleFile().getURL().toURI().toString(), + new Bundle(bundleDetail, bundleClassLoader)); + coordinateClassLoaderLookup + .put(bundleDetail.getCoordinates().getCoordinates(), + bundleClassLoader); + bundleDetailsIter.remove(); + } + } + + // attempt to load more if some were successfully loaded this iteration + } while (bundleCount != bundleDetails.size()); + + // see if any bundle couldn't be loaded + for (final BundleDetails bundleDetail : bundleDetails) { + logger.warn(String + .format("Unable to resolve required dependency '%s'. Skipping BUNDLE '%s'", + bundleDetail.getDependencyCoordinates().getId(), + bundleDetail.getBundleFile().getURL().toURI().toString())); + } + } + } + return new InitContext(extensionsDirs, new LinkedHashMap<>(directoryBundleLookup), props); + } + + /** + * Creates a new BundleClassLoader. The parentClassLoader may be null. + * + * @param bundleFile the Bundle File + * @param parentClassLoader parent classloader of bundle + * @return the bundle classloader + * @throws FileSystemException ioe + * @throws ClassNotFoundException cfne + */ + private static ClassLoader createBundleClassLoader(final FileSystemManager fileSystemManager, + final FileObject bundleFile, final ClassLoader parentClassLoader) + throws FileSystemException, ClassNotFoundException { + logger.debug("Loading Bundle file: " + bundleFile.getURL()); + final ClassLoader bundleClassLoader = new VFSBundleClassLoader.Builder() + .withFileSystemManager(fileSystemManager) + .withBundleFile(bundleFile) + .withParentClassloader(parentClassLoader).build(); + logger.info( + "Loaded Bundle file: " + bundleFile.getURL() + " as class loader " + bundleClassLoader); + return bundleClassLoader; + } + + /** + * Loads the details for the specified BUNDLE. The details will be extracted from the manifest + * file. + * + * @param bundleFile the bundle file + * @return details about the Bundle + * @throws FileSystemException ioe + */ + private static BundleDetails getBundleDetails(final FileObject bundleFile, BundleProperties props) + throws FileSystemException { + return BundleUtil.fromBundleFile(bundleFile, props); + } + + /** + * @param extensionFile the bundle file + * @return the bundle for the specified bundle file. Returns null when no bundle exists + * @throws IllegalStateException if the bundles have not been loaded + */ + public Bundle getBundle(final FileObject extensionFile) { + if (initContext == null) { + throw new IllegalStateException("Extensions class loaders have not been loaded."); + } + + try { + return initContext.bundles.get(extensionFile.getURL().toURI().toString()); + } catch (URISyntaxException | FileSystemException e) { + if (logger.isDebugEnabled()) { + logger.debug("Unable to get extension classloader for bundle '{}'", + extensionFile.getName().toString()); + } + return null; + } + } + + /** + * @return the extensions that have been loaded + * @throws IllegalStateException if the extensions have not been loaded + */ + public Set<Bundle> getBundles() { + if (initContext == null) { + throw new IllegalStateException("Bundles have not been loaded."); + } + + return new LinkedHashSet<>(initContext.bundles.values()); + } + +} http://git-wip-us.apache.org/repos/asf/metron/blob/5f7454e4/bundles-lib/src/main/java/org/apache/metron/bundles/BundleCloseable.java ---------------------------------------------------------------------- diff --git a/bundles-lib/src/main/java/org/apache/metron/bundles/BundleCloseable.java b/bundles-lib/src/main/java/org/apache/metron/bundles/BundleCloseable.java new file mode 100644 index 0000000..1f25c0e --- /dev/null +++ b/bundles-lib/src/main/java/org/apache/metron/bundles/BundleCloseable.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.metron.bundles; + +import java.io.Closeable; + +import java.lang.invoke.MethodHandles; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + */ +public class BundleCloseable implements Closeable { + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + public static BundleCloseable withBundleLoader() throws NotInitializedException{ + final ClassLoader current = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(BundleThreadContextClassLoader.getInstance()); + return new BundleCloseable(current); + } + + /** + * Sets the current thread context class loader to the specific appropriate class loader for the given + * component. If the component requires per-instance class loading then the class loader will be the + * specific class loader for instance with the given identifier, otherwise the class loader will be + * the BundleClassLoader. + * + * @param componentClass the component class + * @param componentIdentifier the identifier of the component + * @return BundleCloseable with the current thread context classloader jailed to the Bundle + * or instance class loader of the component + */ + public static BundleCloseable withComponentBundleLoader(final Class componentClass, final String componentIdentifier) throws NotInitializedException{ + final ClassLoader current = Thread.currentThread().getContextClassLoader(); + + ClassLoader componentClassLoader = ExtensionManager.getInstance().getInstanceClassLoader(componentIdentifier); + if (componentClassLoader == null) { + componentClassLoader = componentClass.getClassLoader(); + } + + Thread.currentThread().setContextClassLoader(componentClassLoader); + return new BundleCloseable(current); + } + + /** + * Sets the current thread context class loader to the provided class loader, and returns a BundleCloseable that will + * return the current thread context class loader to it's previous state. + * + * @param componentBundleLoader the class loader to set as the current thread context class loader + * + * @return BundleCloseable that will return the current thread context class loader to its previous state + */ + public static BundleCloseable withComponentBundleLoader(final ClassLoader componentBundleLoader) { + final ClassLoader current = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(componentBundleLoader); + return new BundleCloseable(current); + } + private final ClassLoader toSet; + + private BundleCloseable(final ClassLoader toSet) { + this.toSet = toSet; + } + + @Override + public void close() { + if (toSet != null) { + Thread.currentThread().setContextClassLoader(toSet); + } + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/5f7454e4/bundles-lib/src/main/java/org/apache/metron/bundles/BundleManifestEntry.java ---------------------------------------------------------------------- diff --git a/bundles-lib/src/main/java/org/apache/metron/bundles/BundleManifestEntry.java b/bundles-lib/src/main/java/org/apache/metron/bundles/BundleManifestEntry.java new file mode 100644 index 0000000..2df4172 --- /dev/null +++ b/bundles-lib/src/main/java/org/apache/metron/bundles/BundleManifestEntry.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.metron.bundles; + +/** + * Enumeration of entries that will be in a BUNDLE MANIFEST file. + */ +public enum BundleManifestEntry { + + PRE_GROUP("-Group"), + PRE_ID("-Id"), + PRE_VERSION("-Version"), + PRE_DEPENDENCY_GROUP("-Dependency-Group"), + PRE_DEPENDENCY_ID("-Dependency-Id"), + PRE_DEPENDENCY_VERSION("-Dependency-Version"), + BUILD_TAG("Build-Tag"), + BUILD_REVISION("Build-Revision"), + BUILD_BRANCH("Build-Branch"), + BUILD_TIMESTAMP("Build-Timestamp"), + BUILD_JDK("Build-Jdk"), + BUILT_BY("Built-By") + ; + + final String manifestName; + + BundleManifestEntry(String manifestName) { + this.manifestName = manifestName; + } + + public String getManifestName() { + return manifestName; + } + +} http://git-wip-us.apache.org/repos/asf/metron/blob/5f7454e4/bundles-lib/src/main/java/org/apache/metron/bundles/BundleMapper.java ---------------------------------------------------------------------- diff --git a/bundles-lib/src/main/java/org/apache/metron/bundles/BundleMapper.java b/bundles-lib/src/main/java/org/apache/metron/bundles/BundleMapper.java new file mode 100644 index 0000000..6da5762 --- /dev/null +++ b/bundles-lib/src/main/java/org/apache/metron/bundles/BundleMapper.java @@ -0,0 +1,191 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.metron.bundles; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.invoke.MethodHandles; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSystemManager; +import org.apache.metron.bundles.bundle.BundleCoordinates; +import org.apache.metron.bundles.util.BundleProperties; +import org.apache.metron.bundles.util.BundleSelector; +import org.apache.metron.bundles.util.BundleUtil; +import org.apache.metron.bundles.util.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The BundleMapper loads all the Bundles available to the system and maps their extensions + * + */ +public final class BundleMapper { + + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + private static String archiveExtension = BundleProperties.DEFAULT_ARCHIVE_EXTENSION; + private final static String META_FMT = "META-INF/services/%s"; + + public static ExtensionMapping mapBundles(final FileSystemManager fileSystemManager, + BundleProperties props) { + try { + final List<URI> bundleLibraryDirs = props.getBundleLibraryDirectories(); + final Map<FileObject, BundleCoordinates> bundlesToCoordinates = new HashMap<>(); + archiveExtension = props.getArchiveExtension(); + + final List<FileObject> bundleFiles = new ArrayList<>(); + + for (URI bundleLibraryDir : bundleLibraryDirs) { + + FileObject bundleDir = fileSystemManager.resolveFile(bundleLibraryDir); + + if(bundleDir.exists() == false) { + continue; + } + // Test if the source BUNDLEs can be read + FileUtils.ensureDirectoryExistAndCanRead(bundleDir); + + FileObject[] dirFiles = bundleDir.findFiles(new BundleSelector(archiveExtension)); + if (dirFiles != null) { + List<FileObject> fileList = Arrays.asList(dirFiles); + bundleFiles.addAll(fileList); + } + } + + if (!bundleFiles.isEmpty()) { + for (FileObject bundleFile : bundleFiles) { + bundlesToCoordinates + .put(bundleFile, BundleUtil.coordinateFromBundleFile(bundleFile, props)); + } + } + + final ExtensionMapping extensionMapping = new ExtensionMapping(); + mapExtensions(bundlesToCoordinates, extensionMapping, props); + return extensionMapping; + } catch (IOException | URISyntaxException e) { + logger.warn("Unable to load BUNDLE library bundles due to " + e + + " Will proceed without loading any further bundles"); + if (logger.isDebugEnabled()) { + logger.warn("", e); + } + } + + return null; + } + + private static void mapExtensions(final Map<FileObject, BundleCoordinates> bundlesToCoordinates, + final ExtensionMapping mapping, BundleProperties props) throws IOException { + for (final Map.Entry<FileObject, BundleCoordinates> entry : bundlesToCoordinates.entrySet()) { + final FileObject bundle = entry.getKey(); + final BundleCoordinates bundleCoordinates = entry.getValue(); + + mapExtentionsForCoordinate(mapping, bundleCoordinates, bundle, props); + } + } + + private static void mapExtentionsForCoordinate(final ExtensionMapping mapping, + final BundleCoordinates bundleCoordinates, final FileObject bundle, BundleProperties props) + throws IOException { + final FileObject bundleFileSystem = bundle.getFileSystem().getFileSystemManager() + .createFileSystem(bundle); + final FileObject deps = bundleFileSystem.resolveFile(VFSBundleClassLoader.DEPENDENCY_PATH); + final FileObject[] directoryContents = deps.getChildren(); + if (directoryContents != null) { + for (final FileObject file : directoryContents) { + if (file.getName().getExtension().equals("jar")) { + mapExtensionsForJarFileObject(bundleCoordinates, file, mapping, props); + } + } + } + } + + private static void mapExtensionsForJarFileObject(final BundleCoordinates coordinate, final FileObject jar, + final ExtensionMapping extensionMapping, final BundleProperties props) throws IOException { + final ExtensionMapping jarExtensionMapping = buildExtensionMappingForJar(coordinate, jar, + props); + + // skip if there are not components to document + if (jarExtensionMapping.isEmpty()) { + return; + } + + // merge the extension mapping found in this jar + extensionMapping.merge(jarExtensionMapping); + } + + + private static ExtensionMapping buildExtensionMappingForJar(final BundleCoordinates coordinate, + final FileObject jar, final BundleProperties props) throws IOException { + final ExtensionMapping mapping = new ExtensionMapping(); + + // The BundleProperties has configuration for the extension names and classnames + final Map<String, String> extensions = props.getBundleExtensionTypes(); + if (extensions.isEmpty()) { + logger.info("No Extensions configured in properties"); + return mapping; + } + JarEntry jarEntry; + try (final JarInputStream jarFile = new JarInputStream(jar.getContent().getInputStream())) { + + while ((jarEntry = jarFile.getNextJarEntry()) != null) { + for (Map.Entry<String, String> extensionEntry : extensions.entrySet()) { + if (jarEntry.getName().equals(String.format(META_FMT, extensionEntry.getValue()))) { + mapping.addAllExtensions(extensionEntry.getKey(), coordinate, + buildExtensionMappingForJar(jarFile, jarEntry)); + } + } + } + } + return mapping; + + } + + private static List<String> buildExtensionMappingForJar(final JarInputStream jarFile, + final JarEntry jarEntry) throws IOException { + final List<String> componentNames = new ArrayList<>(); + + if (jarEntry == null) { + return componentNames; + } + final BufferedReader reader = new BufferedReader(new InputStreamReader( + jarFile)); + String line; + while ((line = reader.readLine()) != null) { + final String trimmedLine = line.trim(); + if (!trimmedLine.isEmpty() && !trimmedLine.startsWith("#")) { + final int indexOfPound = trimmedLine.indexOf("#"); + final String effectiveLine = (indexOfPound > 0) ? trimmedLine.substring(0, + indexOfPound) : trimmedLine; + componentNames.add(effectiveLine); + } + } + return componentNames; + } + + private BundleMapper() { + } +}